matching meta-languages: make thread safe, memory optimization: do not store Application reference/empty collections in the majority case of a language without matching meta-languages

GitOrigin-RevId: e2b2c7735649556ef1becfef26f111477c4d9d71
This commit is contained in:
Alexey Kudravtsev
2023-09-01 17:25:57 +00:00
committed by intellij-monorepo-bot
parent 8a8aceb216
commit 2113578e90
3 changed files with 33 additions and 21 deletions
@@ -1,14 +1,13 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.lang;
import com.intellij.ReviseWhenPortedToJDK;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
@@ -19,7 +18,6 @@ import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.function.Predicate;
@@ -157,14 +155,13 @@ public final class LanguageUtil {
return provider.getBaseLanguage();
}
private static final Key<Pair<SoftReference<Application>,Set<MetaLanguage>>> MATCHING_LANGUAGES = Key.create("language.matching");
private static final Key<Collection<MetaLanguage>> MATCHING_META_LANGUAGES = Key.create("MATCHING_META_LANGUAGES");
@ReviseWhenPortedToJDK(value = "9", description = "use List.of() instead of deprecated immutableList()")
static @NotNull Collection<MetaLanguage> matchingMetaLanguages(@NotNull Language language) {
// A mock application may cause incorrect caching during tests. It does not fire extension point removed events.
// Ensure that we have cached against correct application.
Pair<SoftReference<Application>, Set<MetaLanguage>> cached = language.getUserData(MATCHING_LANGUAGES);
if (cached != null && cached.first.get() == ApplicationManager.getApplication()) {
return cached.second;
Collection<MetaLanguage> cached = language.getUserData(MATCHING_META_LANGUAGES);
if (cached != null) {
return cached;
}
if (!ApplicationManager.getApplication().getExtensionArea().hasExtensionPoint(MetaLanguage.EP_NAME)) {
@@ -172,26 +169,25 @@ public final class LanguageUtil {
return Collections.emptyList();
}
Set<MetaLanguage> result;
Collection<MetaLanguage> toCache;
if (language instanceof MetaLanguage) {
result = Collections.emptySet();
toCache = Collections.emptySet();
}
else {
result = new HashSet<>();
Set<MetaLanguage> result = new HashSet<>();
MetaLanguage.EP_NAME.forEachExtensionSafe(metaLanguage -> {
if (metaLanguage.matchesLanguage(language)) {
result.add(metaLanguage);
}
});
toCache = result.isEmpty() ? Collections.emptySet() : ContainerUtil.immutableList(result.toArray(new MetaLanguage[0]));
}
Pair<SoftReference<Application>, Set<MetaLanguage>> toCache =
Pair.create(new SoftReference<>(ApplicationManager.getApplication()), result);
language.putUserData(MATCHING_LANGUAGES, toCache);
return result;
language.putUserData(MATCHING_META_LANGUAGES, toCache);
return toCache;
}
static void clearMatchingMetaLanguages(@NotNull Language language) {
language.putUserData(MATCHING_LANGUAGES, null);
static void clearMatchingMetaLanguagesCache(@NotNull Language language) {
language.putUserData(MATCHING_META_LANGUAGES, null);
}
public static @NotNull JBIterable<Language> getBaseLanguages(@NotNull Language language) {
@@ -202,7 +198,6 @@ public final class LanguageUtil {
final Language language = Language.findLanguageByID(langValueText);
if (language != null) return language;
return ContainerUtil.find(Language.getRegisteredLanguages(),
e -> e.getID().equalsIgnoreCase(langValueText));
return ContainerUtil.find(Language.getRegisteredLanguages(), e -> e.getID().equalsIgnoreCase(langValueText));
}
}
@@ -5,6 +5,7 @@ import com.intellij.openapi.extensions.ExtensionPointListener;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -25,7 +26,7 @@ public abstract class MetaLanguage extends Language {
public void extensionRemoved(@NotNull MetaLanguage metaLanguage, @NotNull PluginDescriptor pluginDescriptor) {
if (MetaLanguage.this == metaLanguage) {
for (Language matchingLanguage : metaLanguage.getMatchingLanguages()) {
LanguageUtil.clearMatchingMetaLanguages(matchingLanguage);
LanguageUtil.clearMatchingMetaLanguagesCache(matchingLanguage);
}
metaLanguage.unregisterLanguage(pluginDescriptor);
}
@@ -48,4 +49,11 @@ public abstract class MetaLanguage extends Language {
public @NotNull Collection<Language> getMatchingLanguages() {
return ContainerUtil.filter(Language.getRegisteredLanguages(), language -> matchesLanguage(language));
}
@ApiStatus.Internal
public static void clearAllMatchingMetaLanguagesCache() {
for (Language language : Language.getRegisteredLanguages()) {
LanguageUtil.clearMatchingMetaLanguagesCache(language);
}
}
}
@@ -1,6 +1,7 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.mock;
import com.intellij.lang.MetaLanguage;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.*;
import com.intellij.openapi.application.ex.ApplicationEx;
@@ -382,4 +383,12 @@ public class MockApplication extends MockComponentManager implements Application
@Override
public void setSaveAllowed(boolean value) {
}
@Override
public void dispose() {
// A mock application may cause incorrect caching during tests. It does not fire extension point removed events.
// Ensure that we have cached against correct application.
MetaLanguage.clearAllMatchingMetaLanguagesCache();
super.dispose();
}
}