Reduce memory footprint after functions substitution
Do not recreate new instances if descriptors' parts remain effectively the same
This commit is contained in:
@@ -20,14 +20,18 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|||||||
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
|
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi2ir.findFirstFunction
|
import org.jetbrains.kotlin.psi2ir.findFirstFunction
|
||||||
|
import org.jetbrains.kotlin.psi2ir.findSingleFunction
|
||||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import org.jetbrains.kotlin.test.TestJdkKind
|
import org.jetbrains.kotlin.test.TestJdkKind
|
||||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||||
import org.jetbrains.kotlin.types.FlexibleType
|
import org.jetbrains.kotlin.types.FlexibleType
|
||||||
|
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
||||||
import org.jetbrains.kotlin.types.lowerIfFlexible
|
import org.jetbrains.kotlin.types.lowerIfFlexible
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@@ -54,4 +58,49 @@ class MemoryOptimizationsTest : KtUsefulTestCase() {
|
|||||||
// NullableSimpleType should store and return the same instance as lower bound of flexible type
|
// NullableSimpleType should store and return the same instance as lower bound of flexible type
|
||||||
assertTrue(parameterType.lowerIfFlexible() === upperBound.makeNullableAsSpecified(false))
|
assertTrue(parameterType.lowerIfFlexible() === upperBound.makeNullableAsSpecified(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSubstitutorDoNotRecreateUnchangedDescriptor() {
|
||||||
|
val text =
|
||||||
|
"""
|
||||||
|
|package test
|
||||||
|
|interface A<T> : java.lang.Appendable {
|
||||||
|
| fun foo(x: T)
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
val environment =
|
||||||
|
KotlinTestUtils
|
||||||
|
.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(
|
||||||
|
myTestRootDisposable, ConfigurationKind.ALL, TestJdkKind.FULL_JDK
|
||||||
|
)
|
||||||
|
val moduleDescriptor =
|
||||||
|
JvmResolveUtil.analyze(
|
||||||
|
KotlinTestUtils.createFile("main.kt", text, environment.project),
|
||||||
|
environment
|
||||||
|
).moduleDescriptor
|
||||||
|
|
||||||
|
val aClass =
|
||||||
|
moduleDescriptor.findClassAcrossModuleDependencies(ClassId.topLevel(FqName("test.A")))!!
|
||||||
|
|
||||||
|
val memberScope =
|
||||||
|
aClass.getMemberScope(
|
||||||
|
TypeConstructorSubstitution.create(
|
||||||
|
aClass.typeConstructor, listOf(moduleDescriptor.builtIns.stringType.asTypeProjection())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val append =
|
||||||
|
memberScope.findFirstFunction("append") {
|
||||||
|
it.valueParameters.singleOrNull()?.type?.let(KotlinBuiltIns::isChar) == false
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(append.original === append)
|
||||||
|
|
||||||
|
val foo = memberScope.findSingleFunction(Name.identifier("foo"))
|
||||||
|
|
||||||
|
assertTrue(foo.original !== foo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -56,6 +56,7 @@ public abstract class AbstractReceiverParameterDescriptor extends DeclarationDes
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (substitutedType == null) return null;
|
if (substitutedType == null) return null;
|
||||||
|
if (substitutedType == getType()) return this;
|
||||||
|
|
||||||
return new ReceiverParameterDescriptorImpl(getContainingDeclaration(), new TransientReceiver(substitutedType));
|
return new ReceiverParameterDescriptorImpl(getContainingDeclaration(), new TransientReceiver(substitutedType));
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-5
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.descriptors.impl;
|
package org.jetbrains.kotlin.descriptors.impl;
|
||||||
|
|
||||||
|
import kotlin.collections.CollectionsKt;
|
||||||
import kotlin.jvm.functions.Function0;
|
import kotlin.jvm.functions.Function0;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -334,7 +335,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
if (originalSubstitutor.isEmpty()) {
|
if (originalSubstitutor.isEmpty()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return newCopyBuilder(originalSubstitutor).setOriginal(getOriginal()).build();
|
return newCopyBuilder(originalSubstitutor).setOriginal(getOriginal()).setJustForTypeSubstitution(true).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -370,6 +371,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
private boolean isHiddenForResolutionEverywhereBesideSupercalls = isHiddenForResolutionEverywhereBesideSupercalls();
|
private boolean isHiddenForResolutionEverywhereBesideSupercalls = isHiddenForResolutionEverywhereBesideSupercalls();
|
||||||
private Map<UserDataKey<?>, Object> userDataMap = new LinkedHashMap<UserDataKey<?>, Object>();
|
private Map<UserDataKey<?>, Object> userDataMap = new LinkedHashMap<UserDataKey<?>, Object>();
|
||||||
private Boolean newHasSynthesizedParameterNames = null;
|
private Boolean newHasSynthesizedParameterNames = null;
|
||||||
|
protected boolean justForTypeSubstitution = false;
|
||||||
|
|
||||||
public CopyConfiguration(
|
public CopyConfiguration(
|
||||||
@NotNull TypeSubstitution substitution,
|
@NotNull TypeSubstitution substitution,
|
||||||
@@ -553,6 +555,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
public TypeSubstitution getSubstitution() {
|
public TypeSubstitution getSubstitution() {
|
||||||
return substitution;
|
return substitution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public CopyConfiguration setJustForTypeSubstitution(boolean value) {
|
||||||
|
justForTypeSubstitution = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -571,6 +579,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
protected FunctionDescriptor doSubstitute(@NotNull CopyConfiguration configuration) {
|
protected FunctionDescriptor doSubstitute(@NotNull CopyConfiguration configuration) {
|
||||||
|
boolean[] wereChanges = new boolean[1];
|
||||||
Annotations resultAnnotations =
|
Annotations resultAnnotations =
|
||||||
configuration.additionalAnnotations != null
|
configuration.additionalAnnotations != null
|
||||||
? AnnotationsKt.composeAnnotations(getAnnotations(), configuration.additionalAnnotations)
|
? AnnotationsKt.composeAnnotations(getAnnotations(), configuration.additionalAnnotations)
|
||||||
@@ -583,10 +592,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
List<TypeParameterDescriptor> unsubstitutedTypeParameters =
|
List<TypeParameterDescriptor> unsubstitutedTypeParameters =
|
||||||
configuration.newTypeParameters == null ? getTypeParameters() : configuration.newTypeParameters;
|
configuration.newTypeParameters == null ? getTypeParameters() : configuration.newTypeParameters;
|
||||||
|
|
||||||
|
wereChanges[0] |= !unsubstitutedTypeParameters.isEmpty();
|
||||||
|
|
||||||
List<TypeParameterDescriptor> substitutedTypeParameters =
|
List<TypeParameterDescriptor> substitutedTypeParameters =
|
||||||
new ArrayList<TypeParameterDescriptor>(unsubstitutedTypeParameters.size());
|
new ArrayList<TypeParameterDescriptor>(unsubstitutedTypeParameters.size());
|
||||||
final TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(
|
final TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(
|
||||||
unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters
|
unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters, wereChanges
|
||||||
);
|
);
|
||||||
|
|
||||||
KotlinType substitutedReceiverParameterType = null;
|
KotlinType substitutedReceiverParameterType = null;
|
||||||
@@ -595,6 +606,8 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
if (substitutedReceiverParameterType == null) {
|
if (substitutedReceiverParameterType == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wereChanges[0] |= substitutedReceiverParameterType != configuration.newExtensionReceiverParameterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReceiverParameterDescriptor substitutedExpectedThis = null;
|
ReceiverParameterDescriptor substitutedExpectedThis = null;
|
||||||
@@ -613,11 +626,13 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
if (substitutedExpectedThis == null) {
|
if (substitutedExpectedThis == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wereChanges[0] |= substitutedExpectedThis != configuration.dispatchReceiverParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ValueParameterDescriptor> substitutedValueParameters = getSubstitutedValueParameters(
|
List<ValueParameterDescriptor> substitutedValueParameters = getSubstitutedValueParameters(
|
||||||
substitutedDescriptor, configuration.newValueParameterDescriptors, substitutor, configuration.dropOriginalInContainingParts,
|
substitutedDescriptor, configuration.newValueParameterDescriptors, substitutor, configuration.dropOriginalInContainingParts,
|
||||||
configuration.preserveSourceElement
|
configuration.preserveSourceElement, wereChanges
|
||||||
);
|
);
|
||||||
if (substitutedValueParameters == null) {
|
if (substitutedValueParameters == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -628,6 +643,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wereChanges[0] |= substitutedReturnType != configuration.newReturnType;
|
||||||
|
|
||||||
|
if (!wereChanges[0] && configuration.justForTypeSubstitution) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
substitutedDescriptor.initialize(
|
substitutedDescriptor.initialize(
|
||||||
substitutedReceiverParameterType,
|
substitutedReceiverParameterType,
|
||||||
substitutedExpectedThis,
|
substitutedExpectedThis,
|
||||||
@@ -682,7 +703,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
|
|
||||||
if (configuration.copyOverrides && !getOriginal().getOverriddenDescriptors().isEmpty()) {
|
if (configuration.copyOverrides && !getOriginal().getOverriddenDescriptors().isEmpty()) {
|
||||||
if (configuration.substitution.isEmpty()) {
|
if (configuration.substitution.isEmpty()) {
|
||||||
Function0<Set<FunctionDescriptor>> overriddenFunctionsTask = lazyOverriddenFunctionsTask;
|
Function0<Collection<FunctionDescriptor>> overriddenFunctionsTask = lazyOverriddenFunctionsTask;
|
||||||
if (overriddenFunctionsTask != null) {
|
if (overriddenFunctionsTask != null) {
|
||||||
substitutedDescriptor.lazyOverriddenFunctionsTask = overriddenFunctionsTask;
|
substitutedDescriptor.lazyOverriddenFunctionsTask = overriddenFunctionsTask;
|
||||||
}
|
}
|
||||||
@@ -753,7 +774,8 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
@NotNull List<ValueParameterDescriptor> unsubstitutedValueParameters,
|
@NotNull List<ValueParameterDescriptor> unsubstitutedValueParameters,
|
||||||
@NotNull TypeSubstitutor substitutor,
|
@NotNull TypeSubstitutor substitutor,
|
||||||
boolean dropOriginal,
|
boolean dropOriginal,
|
||||||
boolean preserveSourceElement
|
boolean preserveSourceElement,
|
||||||
|
@Nullable boolean[] wereChanges
|
||||||
) {
|
) {
|
||||||
List<ValueParameterDescriptor> result = new ArrayList<ValueParameterDescriptor>(unsubstitutedValueParameters.size());
|
List<ValueParameterDescriptor> result = new ArrayList<ValueParameterDescriptor>(unsubstitutedValueParameters.size());
|
||||||
for (ValueParameterDescriptor unsubstitutedValueParameter : unsubstitutedValueParameters) {
|
for (ValueParameterDescriptor unsubstitutedValueParameter : unsubstitutedValueParameters) {
|
||||||
@@ -763,6 +785,11 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
KotlinType substituteVarargElementType =
|
KotlinType substituteVarargElementType =
|
||||||
varargElementType == null ? null : substitutor.substitute(varargElementType, Variance.IN_VARIANCE);
|
varargElementType == null ? null : substitutor.substitute(varargElementType, Variance.IN_VARIANCE);
|
||||||
if (substitutedType == null) return null;
|
if (substitutedType == null) return null;
|
||||||
|
if (substitutedType != unsubstitutedValueParameter.getType() || varargElementType != substituteVarargElementType) {
|
||||||
|
if (wereChanges != null) {
|
||||||
|
wereChanges[0] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
result.add(
|
result.add(
|
||||||
new ValueParameterDescriptorImpl(
|
new ValueParameterDescriptorImpl(
|
||||||
substitutedDescriptor,
|
substitutedDescriptor,
|
||||||
|
|||||||
+1
-1
@@ -385,7 +385,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
|
|||||||
if (newSetter != null) {
|
if (newSetter != null) {
|
||||||
List<ValueParameterDescriptor> substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters(
|
List<ValueParameterDescriptor> substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters(
|
||||||
newSetter, setter.getValueParameters(), substitutor, /* dropOriginal = */ false,
|
newSetter, setter.getValueParameters(), substitutor, /* dropOriginal = */ false,
|
||||||
false
|
false, null
|
||||||
);
|
);
|
||||||
if (substitutedValueParameters == null) {
|
if (substitutedValueParameters == null) {
|
||||||
// The setter is projected out, e.g. in this case:
|
// The setter is projected out, e.g. in this case:
|
||||||
|
|||||||
+2
-1
@@ -179,7 +179,8 @@ class TypeAliasConstructorDescriptorImpl private constructor(
|
|||||||
|
|
||||||
val valueParameters =
|
val valueParameters =
|
||||||
FunctionDescriptorImpl.getSubstitutedValueParameters(
|
FunctionDescriptorImpl.getSubstitutedValueParameters(
|
||||||
typeAliasConstructor, constructor.valueParameters, substitutorForUnderlyingClass, false, false
|
typeAliasConstructor, constructor.valueParameters, substitutorForUnderlyingClass, false, false,
|
||||||
|
null
|
||||||
) ?: return null
|
) ?: return null
|
||||||
|
|
||||||
val returnType = run {
|
val returnType = run {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.Mutable;
|
import org.jetbrains.annotations.Mutable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.annotations.ReadOnly;
|
import org.jetbrains.annotations.ReadOnly;
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement;
|
import org.jetbrains.kotlin.descriptors.SourceElement;
|
||||||
@@ -38,6 +39,17 @@ public class DescriptorSubstitutor {
|
|||||||
@NotNull TypeSubstitution originalSubstitution,
|
@NotNull TypeSubstitution originalSubstitution,
|
||||||
@NotNull DeclarationDescriptor newContainingDeclaration,
|
@NotNull DeclarationDescriptor newContainingDeclaration,
|
||||||
@NotNull @Mutable List<TypeParameterDescriptor> result
|
@NotNull @Mutable List<TypeParameterDescriptor> result
|
||||||
|
) {
|
||||||
|
return substituteTypeParameters(typeParameters, originalSubstitution, newContainingDeclaration, result, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static TypeSubstitutor substituteTypeParameters(
|
||||||
|
@ReadOnly @NotNull List<TypeParameterDescriptor> typeParameters,
|
||||||
|
@NotNull TypeSubstitution originalSubstitution,
|
||||||
|
@NotNull DeclarationDescriptor newContainingDeclaration,
|
||||||
|
@NotNull @Mutable List<TypeParameterDescriptor> result,
|
||||||
|
@Nullable boolean[] wereChanges
|
||||||
) {
|
) {
|
||||||
Map<TypeConstructor, TypeProjection> mutableSubstitution = new HashMap<TypeConstructor, TypeProjection>();
|
Map<TypeConstructor, TypeProjection> mutableSubstitution = new HashMap<TypeConstructor, TypeProjection>();
|
||||||
|
|
||||||
@@ -69,6 +81,11 @@ public class DescriptorSubstitutor {
|
|||||||
for (KotlinType upperBound : descriptor.getUpperBounds()) {
|
for (KotlinType upperBound : descriptor.getUpperBounds()) {
|
||||||
KotlinType substitutedBound = substitutor.substitute(upperBound, Variance.IN_VARIANCE);
|
KotlinType substitutedBound = substitutor.substitute(upperBound, Variance.IN_VARIANCE);
|
||||||
assert substitutedBound != null : "Upper bound failed to substitute: " + descriptor;
|
assert substitutedBound != null : "Upper bound failed to substitute: " + descriptor;
|
||||||
|
|
||||||
|
if (substitutedBound != upperBound && wereChanges != null) {
|
||||||
|
wereChanges[0] = true;
|
||||||
|
}
|
||||||
|
|
||||||
substituted.addUpperBound(substitutedBound);
|
substituted.addUpperBound(substitutedBound);
|
||||||
}
|
}
|
||||||
substituted.setInitialized();
|
substituted.setInitialized();
|
||||||
|
|||||||
Reference in New Issue
Block a user