Reduce memory footprint after functions substitution

Do not recreate new instances if descriptors' parts remain
effectively the same
This commit is contained in:
Denis Zharkov
2017-05-24 16:13:54 +03:00
parent 4a73fbb70a
commit e2ce285ec3
6 changed files with 102 additions and 7 deletions
@@ -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)
}
} }
@@ -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));
} }
@@ -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,
@@ -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:
@@ -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();