Do not store instances of Application in strong static references
Introduce ExtensionProvider which takes care of caching application using weak reference Fixes a memory leak for scenarios with application being substituted (Jps build does that when compiling multiple modules)
This commit is contained in:
+23
-53
@@ -16,13 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.diagnostics.rendering;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import kotlin.Function1;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
@@ -38,9 +33,11 @@ import org.jetbrains.kotlin.renderer.MultiRenderer;
|
||||
import org.jetbrains.kotlin.renderer.Renderer;
|
||||
import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.util.MappedExtensionProvider;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -58,61 +55,36 @@ public class DefaultErrorMessages {
|
||||
}
|
||||
|
||||
private static final DiagnosticFactoryToRendererMap MAP = new DiagnosticFactoryToRendererMap();
|
||||
private static List<DiagnosticFactoryToRendererMap> maps = null;
|
||||
private static Application application = ApplicationManager.getApplication();
|
||||
private static DispatchingDiagnosticRenderer renderer = null;
|
||||
private static final MappedExtensionProvider<Extension, List<DiagnosticFactoryToRendererMap>> RENDERER_MAPS = MappedExtensionProvider.create(
|
||||
Extension.EP_NAME,
|
||||
new Function1<List<? extends Extension>, List<DiagnosticFactoryToRendererMap>>() {
|
||||
@Override
|
||||
public List<DiagnosticFactoryToRendererMap> invoke(List<? extends Extension> extensions) {
|
||||
List<DiagnosticFactoryToRendererMap> result = new ArrayList<DiagnosticFactoryToRendererMap>(extensions.size() + 1);
|
||||
for (Extension extension : extensions) {
|
||||
result.add(extension.getMap());
|
||||
}
|
||||
result.add(MAP);
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
@NotNull
|
||||
public static String render(@NotNull Diagnostic diagnostic) {
|
||||
return getRenderer().render(diagnostic);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DiagnosticRenderer<Diagnostic> getRenderer() {
|
||||
boolean mapsChanged = resetMapsIfNeeded();
|
||||
|
||||
// Renderer is changed in tests only
|
||||
if (renderer == null || mapsChanged) {
|
||||
renderer = new DispatchingDiagnosticRenderer(maps);
|
||||
for (DiagnosticFactoryToRendererMap map : RENDERER_MAPS.get()) {
|
||||
DiagnosticRenderer renderer = map.get(diagnostic.getFactory());
|
||||
if (renderer != null) {
|
||||
//noinspection unchecked
|
||||
return renderer.render(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private static boolean resetMapsIfNeeded() {
|
||||
boolean needToResetMaps = maps == null;
|
||||
Application newApp = ApplicationManager.getApplication();
|
||||
|
||||
if (application != newApp) {
|
||||
assert newApp.isUnitTestMode(): "Expected application switch only in tests";
|
||||
application = newApp;
|
||||
needToResetMaps = true;
|
||||
}
|
||||
|
||||
if (!needToResetMaps) return false;
|
||||
|
||||
maps = ImmutableList.<DiagnosticFactoryToRendererMap>builder()
|
||||
.addAll(
|
||||
KotlinPackage.map(
|
||||
Extensions.getExtensions(Extension.EP_NAME),
|
||||
new Function1<Extension, DiagnosticFactoryToRendererMap>() {
|
||||
@Override
|
||||
public DiagnosticFactoryToRendererMap invoke(Extension extension) {
|
||||
return extension.getMap();
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
.add(MAP)
|
||||
.build();
|
||||
|
||||
return true;
|
||||
throw new IllegalArgumentException("Don't know how to render diagnostic of type " + diagnostic.getFactory().getName());
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@Nullable
|
||||
public static DiagnosticRenderer getRendererForDiagnostic(@NotNull Diagnostic diagnostic) {
|
||||
for (DiagnosticFactoryToRendererMap map : maps) {
|
||||
for (DiagnosticFactoryToRendererMap map : RENDERER_MAPS.get()) {
|
||||
DiagnosticRenderer renderer = map.get(diagnostic.getFactory());
|
||||
|
||||
if (renderer != null) return renderer;
|
||||
@@ -642,8 +614,6 @@ public class DefaultErrorMessages {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetMapsIfNeeded();
|
||||
}
|
||||
|
||||
private DefaultErrorMessages() {
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.diagnostics.rendering;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DispatchingDiagnosticRenderer implements DiagnosticRenderer<Diagnostic> {
|
||||
@NotNull
|
||||
private final List<DiagnosticFactoryToRendererMap> maps;
|
||||
|
||||
public DispatchingDiagnosticRenderer(@NotNull List<DiagnosticFactoryToRendererMap> maps) {
|
||||
this.maps = maps;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(@NotNull Diagnostic diagnostic) {
|
||||
for (DiagnosticFactoryToRendererMap map : maps) {
|
||||
DiagnosticRenderer renderer = map.get(diagnostic.getFactory());
|
||||
if (renderer != null) {
|
||||
//noinspection unchecked
|
||||
return renderer.render(diagnostic);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Don't know how to render diagnostic of type " + diagnostic.getFactory().getName());
|
||||
}
|
||||
}
|
||||
+8
-40
@@ -17,11 +17,8 @@
|
||||
package org.jetbrains.kotlin.resolve.diagnostics;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.ModificationTracker;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -35,15 +32,12 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.diagnostics.Severity;
|
||||
import org.jetbrains.kotlin.psi.JetAnnotated;
|
||||
import org.jetbrains.kotlin.psi.JetAnnotationEntry;
|
||||
import org.jetbrains.kotlin.psi.JetFile;
|
||||
import org.jetbrains.kotlin.psi.JetStubbedPsiUtil;
|
||||
import org.jetbrains.kotlin.psi.PsiPackage;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue;
|
||||
import org.jetbrains.kotlin.util.ExtensionProvider;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -64,36 +58,10 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(DiagnosticsWithSuppression.class);
|
||||
|
||||
private static Application app = null;
|
||||
private static SuppressStringProvider[] ADDITIONAL_SUPPRESS_STRING_PROVIDERS = null;
|
||||
private static DiagnosticSuppressor[] DIAGNOSTIC_SUPPRESSORS = null;
|
||||
|
||||
static {
|
||||
resetExtensionsIfNeed();
|
||||
}
|
||||
|
||||
private static SuppressStringProvider[] getAdditionalSuppressStringProviders() {
|
||||
resetExtensionsIfNeed();
|
||||
return ADDITIONAL_SUPPRESS_STRING_PROVIDERS;
|
||||
}
|
||||
|
||||
private static DiagnosticSuppressor[] getDiagnosticSuppressors() {
|
||||
resetExtensionsIfNeed();
|
||||
return DIAGNOSTIC_SUPPRESSORS;
|
||||
}
|
||||
|
||||
// We need to update extensions in tests because they may be different for different tests.
|
||||
private static void resetExtensionsIfNeed() {
|
||||
if (app != null && !app.isUnitTestMode()) return;
|
||||
|
||||
Application newApp = ApplicationManager.getApplication();
|
||||
if (app == newApp) return;
|
||||
|
||||
app = newApp;
|
||||
|
||||
ADDITIONAL_SUPPRESS_STRING_PROVIDERS = Extensions.getExtensions(SuppressStringProvider.EP_NAME);
|
||||
DIAGNOSTIC_SUPPRESSORS = Extensions.getExtensions(DiagnosticSuppressor.EP_NAME);
|
||||
}
|
||||
private static final ExtensionProvider<SuppressStringProvider> ADDITIONAL_SUPPRESS_STRING_PROVIDERS
|
||||
= ExtensionProvider.create(SuppressStringProvider.EP_NAME);
|
||||
private static final ExtensionProvider<DiagnosticSuppressor> DIAGNOSTIC_SUPPRESSORS
|
||||
= ExtensionProvider.create(DiagnosticSuppressor.EP_NAME);
|
||||
|
||||
private final BindingContext context;
|
||||
private final Collection<Diagnostic> diagnostics;
|
||||
@@ -153,7 +121,7 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
if (PsiPackage.getDoNotAnalyze((JetFile) file) != null) return true;
|
||||
}
|
||||
|
||||
for (DiagnosticSuppressor suppressor : getDiagnosticSuppressors()) {
|
||||
for (DiagnosticSuppressor suppressor : DIAGNOSTIC_SUPPRESSORS.get()) {
|
||||
if (suppressor.isSuppressed(diagnostic)) return true;
|
||||
}
|
||||
|
||||
@@ -239,7 +207,7 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
AnnotationDescriptor annotationDescriptor = context.get(BindingContext.ANNOTATION, annotationEntry);
|
||||
if (annotationDescriptor == null) continue;
|
||||
|
||||
for (SuppressStringProvider suppressStringProvider : getAdditionalSuppressStringProviders()) {
|
||||
for (SuppressStringProvider suppressStringProvider : ADDITIONAL_SUPPRESS_STRING_PROVIDERS.get()) {
|
||||
builder.addAll(suppressStringProvider.get(annotationDescriptor));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.util
|
||||
|
||||
import com.intellij.openapi.application.Application
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.platform.platformStatic
|
||||
|
||||
public open class MappedExtensionProvider<T, R> protected (private val epName: ExtensionPointName<T>, private val map: (List<T>) -> R) {
|
||||
private var cached = WeakReference<Pair<Application, R>>(null)
|
||||
|
||||
public fun get(): R {
|
||||
val cached = cached.get() ?: return update()
|
||||
val (app, extensions) = cached
|
||||
if (app == ApplicationManager.getApplication()) {
|
||||
return extensions
|
||||
}
|
||||
else {
|
||||
return update()
|
||||
}
|
||||
}
|
||||
|
||||
private fun update(): R {
|
||||
val newVal = ApplicationManager.getApplication().let { app ->
|
||||
Pair(app, map(app.getExtensions(epName).toList()))
|
||||
}
|
||||
cached = WeakReference(newVal)
|
||||
return newVal.second
|
||||
}
|
||||
|
||||
companion object {
|
||||
platformStatic public fun <T, R> create(epName: ExtensionPointName<T>, map: (List<T>) -> R): MappedExtensionProvider<T, R>
|
||||
= MappedExtensionProvider(epName, map)
|
||||
}
|
||||
}
|
||||
|
||||
public class ExtensionProvider<T>(epName: ExtensionPointName<T>) : MappedExtensionProvider<T, List<T>>(epName, { it }) {
|
||||
companion object {
|
||||
platformStatic public fun <T> create(epName: ExtensionPointName<T>): ExtensionProvider<T> = ExtensionProvider(epName)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user