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.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi2ir.findFirstFunction
|
||||
import org.jetbrains.kotlin.psi2ir.findSingleFunction
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.types.FlexibleType
|
||||
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
||||
import org.jetbrains.kotlin.types.lowerIfFlexible
|
||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||
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
|
||||
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 == getType()) return this;
|
||||
|
||||
return new ReceiverParameterDescriptorImpl(getContainingDeclaration(), new TransientReceiver(substitutedType));
|
||||
}
|
||||
|
||||
+32
-5
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.descriptors.impl;
|
||||
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -334,7 +335,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
if (originalSubstitutor.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
return newCopyBuilder(originalSubstitutor).setOriginal(getOriginal()).build();
|
||||
return newCopyBuilder(originalSubstitutor).setOriginal(getOriginal()).setJustForTypeSubstitution(true).build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -370,6 +371,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
private boolean isHiddenForResolutionEverywhereBesideSupercalls = isHiddenForResolutionEverywhereBesideSupercalls();
|
||||
private Map<UserDataKey<?>, Object> userDataMap = new LinkedHashMap<UserDataKey<?>, Object>();
|
||||
private Boolean newHasSynthesizedParameterNames = null;
|
||||
protected boolean justForTypeSubstitution = false;
|
||||
|
||||
public CopyConfiguration(
|
||||
@NotNull TypeSubstitution substitution,
|
||||
@@ -553,6 +555,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
public TypeSubstitution getSubstitution() {
|
||||
return substitution;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CopyConfiguration setJustForTypeSubstitution(boolean value) {
|
||||
justForTypeSubstitution = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -571,6 +579,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
|
||||
@Nullable
|
||||
protected FunctionDescriptor doSubstitute(@NotNull CopyConfiguration configuration) {
|
||||
boolean[] wereChanges = new boolean[1];
|
||||
Annotations resultAnnotations =
|
||||
configuration.additionalAnnotations != null
|
||||
? AnnotationsKt.composeAnnotations(getAnnotations(), configuration.additionalAnnotations)
|
||||
@@ -583,10 +592,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
List<TypeParameterDescriptor> unsubstitutedTypeParameters =
|
||||
configuration.newTypeParameters == null ? getTypeParameters() : configuration.newTypeParameters;
|
||||
|
||||
wereChanges[0] |= !unsubstitutedTypeParameters.isEmpty();
|
||||
|
||||
List<TypeParameterDescriptor> substitutedTypeParameters =
|
||||
new ArrayList<TypeParameterDescriptor>(unsubstitutedTypeParameters.size());
|
||||
final TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(
|
||||
unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters
|
||||
unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters, wereChanges
|
||||
);
|
||||
|
||||
KotlinType substitutedReceiverParameterType = null;
|
||||
@@ -595,6 +606,8 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
if (substitutedReceiverParameterType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
wereChanges[0] |= substitutedReceiverParameterType != configuration.newExtensionReceiverParameterType;
|
||||
}
|
||||
|
||||
ReceiverParameterDescriptor substitutedExpectedThis = null;
|
||||
@@ -613,11 +626,13 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
if (substitutedExpectedThis == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
wereChanges[0] |= substitutedExpectedThis != configuration.dispatchReceiverParameter;
|
||||
}
|
||||
|
||||
List<ValueParameterDescriptor> substitutedValueParameters = getSubstitutedValueParameters(
|
||||
substitutedDescriptor, configuration.newValueParameterDescriptors, substitutor, configuration.dropOriginalInContainingParts,
|
||||
configuration.preserveSourceElement
|
||||
configuration.preserveSourceElement, wereChanges
|
||||
);
|
||||
if (substitutedValueParameters == null) {
|
||||
return null;
|
||||
@@ -628,6 +643,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
return null;
|
||||
}
|
||||
|
||||
wereChanges[0] |= substitutedReturnType != configuration.newReturnType;
|
||||
|
||||
if (!wereChanges[0] && configuration.justForTypeSubstitution) {
|
||||
return this;
|
||||
}
|
||||
|
||||
substitutedDescriptor.initialize(
|
||||
substitutedReceiverParameterType,
|
||||
substitutedExpectedThis,
|
||||
@@ -682,7 +703,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
|
||||
if (configuration.copyOverrides && !getOriginal().getOverriddenDescriptors().isEmpty()) {
|
||||
if (configuration.substitution.isEmpty()) {
|
||||
Function0<Set<FunctionDescriptor>> overriddenFunctionsTask = lazyOverriddenFunctionsTask;
|
||||
Function0<Collection<FunctionDescriptor>> overriddenFunctionsTask = lazyOverriddenFunctionsTask;
|
||||
if (overriddenFunctionsTask != null) {
|
||||
substitutedDescriptor.lazyOverriddenFunctionsTask = overriddenFunctionsTask;
|
||||
}
|
||||
@@ -753,7 +774,8 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
@NotNull List<ValueParameterDescriptor> unsubstitutedValueParameters,
|
||||
@NotNull TypeSubstitutor substitutor,
|
||||
boolean dropOriginal,
|
||||
boolean preserveSourceElement
|
||||
boolean preserveSourceElement,
|
||||
@Nullable boolean[] wereChanges
|
||||
) {
|
||||
List<ValueParameterDescriptor> result = new ArrayList<ValueParameterDescriptor>(unsubstitutedValueParameters.size());
|
||||
for (ValueParameterDescriptor unsubstitutedValueParameter : unsubstitutedValueParameters) {
|
||||
@@ -763,6 +785,11 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
||||
KotlinType substituteVarargElementType =
|
||||
varargElementType == null ? null : substitutor.substitute(varargElementType, Variance.IN_VARIANCE);
|
||||
if (substitutedType == null) return null;
|
||||
if (substitutedType != unsubstitutedValueParameter.getType() || varargElementType != substituteVarargElementType) {
|
||||
if (wereChanges != null) {
|
||||
wereChanges[0] = true;
|
||||
}
|
||||
}
|
||||
result.add(
|
||||
new ValueParameterDescriptorImpl(
|
||||
substitutedDescriptor,
|
||||
|
||||
+1
-1
@@ -385,7 +385,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
|
||||
if (newSetter != null) {
|
||||
List<ValueParameterDescriptor> substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters(
|
||||
newSetter, setter.getValueParameters(), substitutor, /* dropOriginal = */ false,
|
||||
false
|
||||
false, null
|
||||
);
|
||||
if (substitutedValueParameters == null) {
|
||||
// The setter is projected out, e.g. in this case:
|
||||
|
||||
+2
-1
@@ -179,7 +179,8 @@ class TypeAliasConstructorDescriptorImpl private constructor(
|
||||
|
||||
val valueParameters =
|
||||
FunctionDescriptorImpl.getSubstitutedValueParameters(
|
||||
typeAliasConstructor, constructor.valueParameters, substitutorForUnderlyingClass, false, false
|
||||
typeAliasConstructor, constructor.valueParameters, substitutorForUnderlyingClass, false, false,
|
||||
null
|
||||
) ?: return null
|
||||
|
||||
val returnType = run {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types;
|
||||
|
||||
import org.jetbrains.annotations.Mutable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.ReadOnly;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement;
|
||||
@@ -38,6 +39,17 @@ public class DescriptorSubstitutor {
|
||||
@NotNull TypeSubstitution originalSubstitution,
|
||||
@NotNull DeclarationDescriptor newContainingDeclaration,
|
||||
@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>();
|
||||
|
||||
@@ -69,6 +81,11 @@ public class DescriptorSubstitutor {
|
||||
for (KotlinType upperBound : descriptor.getUpperBounds()) {
|
||||
KotlinType substitutedBound = substitutor.substitute(upperBound, Variance.IN_VARIANCE);
|
||||
assert substitutedBound != null : "Upper bound failed to substitute: " + descriptor;
|
||||
|
||||
if (substitutedBound != upperBound && wereChanges != null) {
|
||||
wereChanges[0] = true;
|
||||
}
|
||||
|
||||
substituted.addUpperBound(substitutedBound);
|
||||
}
|
||||
substituted.setInitialized();
|
||||
|
||||
Reference in New Issue
Block a user