From aac72871e7a5f5e243216ce303b08bb9dc16a846 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 14 Feb 2020 15:42:07 +0300 Subject: [PATCH] Fix UI freezes caused by calls with a huge number overloads While we have an overload resolution algorithm working for O(n^2), call resolution for the single particular call may work more then just a second. Thus, we need to call ProgressManager.checkCanceled() with more granularity ^KT-35135 Fixed --- .../org/jetbrains/kotlin/frontend/di/injection.kt | 2 ++ .../calls/results/FlatSignatureForResolvedCall.kt | 5 ++++- .../calls/results/ResolutionResultsHandler.java | 6 ++++-- .../ProgressManagerBasedCancellationChecker.kt | 14 ++++++++++++++ .../calls/components/CallableReferenceResolver.kt | 3 +++ .../components/NewOverloadingConflictResolver.kt | 3 +++ .../calls/results/OverloadingConflictResolver.kt | 7 ++++++- .../jetbrains/kotlin/resolve/OverridingUtil.java | 7 ++++++- .../jetbrains/kotlin/util/CancellationChecker.kt | 10 ++++++++++ 9 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/util/ProgressManagerBasedCancellationChecker.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/util/CancellationChecker.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt index 2785b5e5a70..cce536c590c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.types.checker.NewKotlinTypeCheckerImpl import org.jetbrains.kotlin.types.expressions.DeclarationScopeProviderForLocalClassifierAnalyzer import org.jetbrains.kotlin.types.expressions.LocalClassDescriptorHolder import org.jetbrains.kotlin.types.expressions.LocalLazyDeclarationResolver +import org.jetbrains.kotlin.util.ProgressManagerBasedCancellationChecker fun StorageComponentContainer.configureModule( moduleContext: ModuleContext, @@ -97,6 +98,7 @@ private fun StorageComponentContainer.configurePlatformIndependentComponents() { useImpl() useImpl() + useInstance(ProgressManagerBasedCancellationChecker) } /** diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureForResolvedCall.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureForResolvedCall.kt index 5c21cfdabcb..1a765cba68c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureForResolvedCall.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureForResolvedCall.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCallImpl import org.jetbrains.kotlin.resolve.calls.results.FlatSignature.Companion.argumentValueType import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.util.CancellationChecker import java.util.* @@ -55,12 +56,14 @@ fun createOverloadingConflictResolver( builtIns: KotlinBuiltIns, module: ModuleDescriptor, specificityComparator: TypeSpecificityComparator, - platformOverloadsSpecificityComparator: PlatformOverloadsSpecificityComparator + platformOverloadsSpecificityComparator: PlatformOverloadsSpecificityComparator, + cancellationChecker: CancellationChecker, ) = OverloadingConflictResolver( builtIns, module, specificityComparator, platformOverloadsSpecificityComparator, + cancellationChecker, MutableResolvedCall<*>::getResultingDescriptor, ConstraintSystemBuilderImpl.Companion::forSpecificity, MutableResolvedCall<*>::createFlatSignature, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java index 257f31560c9..9ed0af58e2c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode; import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall; import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy; import org.jetbrains.kotlin.resolve.calls.tower.TowerUtilsKt; +import org.jetbrains.kotlin.util.CancellationChecker; import java.util.*; @@ -42,10 +43,11 @@ public class ResolutionResultsHandler { @NotNull KotlinBuiltIns builtIns, @NotNull ModuleDescriptor module, @NotNull TypeSpecificityComparator specificityComparator, - @NotNull PlatformOverloadsSpecificityComparator platformOverloadsSpecificityComparator + @NotNull PlatformOverloadsSpecificityComparator platformOverloadsSpecificityComparator, + @NotNull CancellationChecker cancellationChecker ) { overloadingConflictResolver = FlatSignatureForResolvedCallKt.createOverloadingConflictResolver( - builtIns, module, specificityComparator, platformOverloadsSpecificityComparator + builtIns, module, specificityComparator, platformOverloadsSpecificityComparator, cancellationChecker ); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/ProgressManagerBasedCancellationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/util/ProgressManagerBasedCancellationChecker.kt new file mode 100644 index 00000000000..64a13cadb76 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/ProgressManagerBasedCancellationChecker.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.util + +import com.intellij.openapi.progress.ProgressManager + +object ProgressManagerBasedCancellationChecker : CancellationChecker { + override fun check() { + ProgressManager.checkCanceled() + } +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt index 3b54d9e90c6..ecf44976b81 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.ImplicitScopeTower import org.jetbrains.kotlin.resolve.calls.tower.TowerResolver import org.jetbrains.kotlin.resolve.calls.tower.isInapplicable import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.util.CancellationChecker class CallableReferenceOverloadConflictResolver( @@ -39,6 +40,7 @@ class CallableReferenceOverloadConflictResolver( module: ModuleDescriptor, specificityComparator: TypeSpecificityComparator, platformOverloadsSpecificityComparator: PlatformOverloadsSpecificityComparator, + cancellationChecker: CancellationChecker, statelessCallbacks: KotlinResolutionStatelessCallbacks, constraintInjector: ConstraintInjector ) : OverloadingConflictResolver( @@ -46,6 +48,7 @@ class CallableReferenceOverloadConflictResolver( module, specificityComparator, platformOverloadsSpecificityComparator, + cancellationChecker, { it.candidate }, { statelessCallbacks.createConstraintSystemForOverloadResolution(constraintInjector, builtIns) }, Companion::createFlatSignature, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/NewOverloadingConflictResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/NewOverloadingConflictResolver.kt index 03ee92566c3..932df889cf5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/NewOverloadingConflictResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/NewOverloadingConflictResolver.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver import org.jetbrains.kotlin.resolve.calls.results.PlatformOverloadsSpecificityComparator import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.util.CancellationChecker import java.util.* class NewOverloadingConflictResolver( @@ -34,6 +35,7 @@ class NewOverloadingConflictResolver( module: ModuleDescriptor, specificityComparator: TypeSpecificityComparator, platformOverloadsSpecificityComparator: PlatformOverloadsSpecificityComparator, + cancellationChecker: CancellationChecker, statelessCallbacks: KotlinResolutionStatelessCallbacks, constraintInjector: ConstraintInjector ) : OverloadingConflictResolver( @@ -41,6 +43,7 @@ class NewOverloadingConflictResolver( module, specificityComparator, platformOverloadsSpecificityComparator, + cancellationChecker, { // todo investigate it.resolvedCall.candidateDescriptor diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt index 8c4ef9988a8..946ba11042b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.checker.requireOrDescribe import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.util.CancellationChecker import java.util.* open class OverloadingConflictResolver( @@ -38,6 +39,7 @@ open class OverloadingConflictResolver( private val module: ModuleDescriptor, private val specificityComparator: TypeSpecificityComparator, private val platformOverloadsSpecificityComparator: PlatformOverloadsSpecificityComparator, + private val cancellationChecker: CancellationChecker, private val getResultingDescriptor: (C) -> CallableDescriptor, private val createEmptyConstraintSystem: () -> SimpleConstraintSystem, private val createFlatSignature: (C) -> FlatSignature, @@ -78,7 +80,8 @@ open class OverloadingConflictResolver( val noEquivalentCalls = filterOutEquivalentCalls(fixedCandidates) val noOverrides = OverridingUtil.filterOverrides( noEquivalentCalls, - isTypeRefinementEnabled + isTypeRefinementEnabled, + cancellationChecker::check ) { a, b -> val aDescriptor = a.resultingDescriptor val bDescriptor = b.resultingDescriptor @@ -124,6 +127,7 @@ open class OverloadingConflictResolver( val result = LinkedHashSet() outerLoop@ for (meD in fromSourcesGoesFirst) { + cancellationChecker.check() for (otherD in result) { val me = meD.resultingDescriptor.originalIfTypeRefinementEnabled val other = otherD.resultingDescriptor.originalIfTypeRefinementEnabled @@ -209,6 +213,7 @@ open class OverloadingConflictResolver( } val bestCandidatesByParameterTypes = conflictingCandidates.filter { candidate -> + cancellationChecker.check() isMostSpecific(candidate, conflictingCandidates) { call1, call2 -> isNotLessSpecificCallWithArgumentMapping(call1, call2, discriminateGenerics) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java index d0e87ec94bd..7a7f797cd69 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java @@ -20,6 +20,7 @@ import kotlin.Pair; import kotlin.Unit; import kotlin.annotations.jvm.Mutable; import kotlin.collections.CollectionsKt; +import kotlin.jvm.functions.Function0; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; import org.jetbrains.annotations.NotNull; @@ -97,7 +98,7 @@ public class OverridingUtil { DescriptorUtilsKt .isTypeRefinementEnabled(DescriptorUtilsKt.getModule(candidateSet.iterator().next())); - return filterOverrides(candidateSet, allowDescriptorCopies, new Function2>() { + return filterOverrides(candidateSet, allowDescriptorCopies, null, new Function2>() { @Override public Pair invoke(D a, D b) { return new Pair(a, b); @@ -109,6 +110,7 @@ public class OverridingUtil { public static Set filterOverrides( @NotNull Set candidateSet, boolean allowDescriptorCopies, + @Nullable Function0 cancellationCallback, @NotNull Function2> transformFirst ) { if (candidateSet.size() <= 1) return candidateSet; @@ -116,6 +118,9 @@ public class OverridingUtil { Set result = new LinkedHashSet(); outerLoop: for (D meD : candidateSet) { + if (cancellationCallback != null) { + cancellationCallback.invoke(); + } for (Iterator iterator = result.iterator(); iterator.hasNext(); ) { D otherD = iterator.next(); Pair meAndOther = transformFirst.invoke(meD, otherD); diff --git a/core/descriptors/src/org/jetbrains/kotlin/util/CancellationChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/util/CancellationChecker.kt new file mode 100644 index 00000000000..c8f85c5dc96 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/util/CancellationChecker.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.util + +interface CancellationChecker { + fun check() +}