IDEA-CR-62371 use client class as cache id because it's dangerous to rely on identity of functional expressions.

GitOrigin-RevId: 41d38ae5da583af0ed4520cb2e06b9def7fb871b
This commit is contained in:
Vladimir Krivosheev
2020-06-01 22:00:09 +03:00
committed by intellij-monorepo-bot
parent 9c22edb681
commit cab62c6ccb
15 changed files with 70 additions and 47 deletions
@@ -44,7 +44,7 @@ public final class JvmMetaLanguage extends MetaLanguage {
}
private static boolean matchesRegisteredLanguage(@NotNull Language language, @NotNull ExtensionPointImpl<LanguageExtensionPoint<JvmDeclarationSearcher>> point) {
return ExtensionProcessingHelper.getByKey(point, language.getID(), LanguageExtensionPoint::getKey) != null;
return ExtensionProcessingHelper.getByKey(point, language.getID(), JvmMetaLanguage.class, LanguageExtensionPoint::getKey) != null;
}
private @Nullable static ExtensionPointImpl<LanguageExtensionPoint<JvmDeclarationSearcher>> getPoint() {
@@ -16,7 +16,7 @@ public class CompletionIgnoreDumbnessEP extends LazyExtensionInstance<Object> {
ExtensionPointName.create("com.intellij.completion.ignoringDumbnessAllowed");
public static boolean isIgnoringDumbnessAllowed(@NotNull Language language) {
return EP_NAME.getByKey(language.getID(), ep -> ep.language) != null;
return EP_NAME.getByKey(language.getID(), CompletionIgnoreDumbnessEP.class, ep -> ep.language) != null;
}
/**
@@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
public class LocalInspectionToolWrapper extends InspectionToolWrapper<LocalInspectionTool, LocalInspectionEP> {
/** This should be used in tests primarily */
public LocalInspectionToolWrapper(@NotNull LocalInspectionTool tool) {
super(tool, LocalInspectionEP.LOCAL_INSPECTION.getByKey(tool.getShortName(), InspectionEP::getShortName));
super(tool, LocalInspectionEP.LOCAL_INSPECTION.getByKey(tool.getShortName(), LocalInspectionToolWrapper.class, InspectionEP::getShortName));
}
public LocalInspectionToolWrapper(@NotNull LocalInspectionEP ep) {
@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.io.jsonRpc
import com.google.gson.Gson
@@ -151,7 +151,7 @@ class JsonRpcServer(private val clientManager: ClientManager) : MessageServer {
private fun findDomain(domainName: String): Any? {
val testDomain = this.testDomain
if (testDomain != null && testDomain.first == domainName) return testDomain.second
return JsonRpcDomainBean.EP_NAME.getByKey(domainName, JsonRpcDomainBean::name)?.instance
return JsonRpcDomainBean.EP_NAME.getByKey(domainName, JsonRpcServer::class.java, JsonRpcDomainBean::name)?.instance
}
private fun processClientError(client: Client, error: String, messageId: Int) {
@@ -11,17 +11,20 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@ApiStatus.Internal
public final class ReferenceProviders {
private static final ExtensionPointName<PsiSymbolReferenceProviderBean> EP_NAME =
new ExtensionPointName<>("com.intellij.psi.symbolReferenceProvider");
private static final @NotNull Function<Language, LanguageReferenceProviders> VALUE_MAPPER = ReferenceProviders::byLanguageInner;
/**
* Given language of a host element returns list of providers that could provide references from this language.
*/
static @NotNull LanguageReferenceProviders byLanguage(@NotNull Language language) {
return EP_NAME.computeIfAbsent(language, ReferenceProviders::byLanguageInner);
return EP_NAME.computeIfAbsent(language, VALUE_MAPPER);
}
private static @NotNull LanguageReferenceProviders byLanguageInner(@NotNull Language language) {
@@ -41,6 +44,6 @@ public final class ReferenceProviders {
* Given class of target returns list of providers that could provide references to this target.
*/
public static @NotNull List<PsiSymbolReferenceProviderBean> byTargetClass(@NotNull Class<? extends Symbol> targetClass) {
return EP_NAME.getByGroupingKey(targetClass, bean -> bean.getResolveTargetClass().isAssignableFrom(targetClass) ? targetClass : null);
return EP_NAME.getByGroupingKey(targetClass, ReferenceProviders.class, bean -> bean.getResolveTargetClass().isAssignableFrom(targetClass) ? targetClass : null);
}
}
@@ -178,10 +178,13 @@ public final class ExtensionPointName<T> extends BaseExtensionPointName<T> {
* Build cache by arbitrary key using provided key to value mapper. Values with the same key merge into list. Return values by key.
* <p>
* To exclude extension from cache, return null key.
*
* {@code cacheId} is required because it's dangerous to rely on identity of functional expressions.
* JLS doesn't specify whether a new instance is produced or some common instance is reused for lambda expressions (see 15.27.4).
*/
@ApiStatus.Experimental
public final <@NotNull K> @NotNull List<T> getByGroupingKey(@NotNull K key, @NotNull Function<@NotNull T, @Nullable K> keyMapper) {
return ExtensionProcessingHelper.getByGroupingKey(getPointImpl(null), key, keyMapper);
public final <@NotNull K> @NotNull List<T> getByGroupingKey(@NotNull K key, @NotNull Class<?> cacheId, @NotNull Function<@NotNull T, @Nullable K> keyMapper) {
return ExtensionProcessingHelper.getByGroupingKey(getPointImpl(null), cacheId, key, keyMapper);
}
/**
@@ -190,8 +193,8 @@ public final class ExtensionPointName<T> extends BaseExtensionPointName<T> {
* To exclude extension from cache, return null key.
*/
@ApiStatus.Experimental
public final <@NotNull K> @Nullable T getByKey(@NotNull K key, @NotNull Function<@NotNull T, @Nullable K> keyMapper) {
return ExtensionProcessingHelper.getByKey(getPointImpl(null), key, keyMapper);
public final <@NotNull K> @Nullable T getByKey(@NotNull K key, @NotNull Class<?> cacheId, @NotNull Function<@NotNull T, @Nullable K> keyMapper) {
return ExtensionProcessingHelper.getByKey(getPointImpl(null), key, cacheId, keyMapper);
}
/**
@@ -201,11 +204,15 @@ public final class ExtensionPointName<T> extends BaseExtensionPointName<T> {
*/
@ApiStatus.Experimental
public final <@NotNull K, @NotNull V> @Nullable V getByKey(@NotNull K key,
@NotNull Class<?> cacheId,
@NotNull Function<@NotNull T, @Nullable K> keyMapper,
@NotNull Function<@NotNull T, @Nullable V> valueMapper) {
return ExtensionProcessingHelper.getByKey(getPointImpl(null), key, keyMapper, valueMapper);
return ExtensionProcessingHelper.getByKey(getPointImpl(null), key, cacheId, keyMapper, valueMapper);
}
/**
* {@code valueMapper} used as cache id and therefore must be extracted to a static final field.
*/
@ApiStatus.Experimental
public final <@NotNull K, @NotNull V> @NotNull V computeIfAbsent(@NotNull K key,
@NotNull Function<@NotNull K, @NotNull V> valueMapper) {
@@ -1,6 +1,7 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.extensions.impl;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.progress.ProcessCanceledException;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.jetbrains.annotations.ApiStatus;
@@ -71,13 +72,14 @@ public final class ExtensionProcessingHelper {
* See {@link com.intellij.openapi.extensions.ExtensionPointName#getByGroupingKey}.
*/
public static <@NotNull K, @NotNull T> @NotNull List<T> getByGroupingKey(@NotNull ExtensionPointImpl<T> point,
@NotNull Class<?> cacheId,
@NotNull K key,
@NotNull Function<@NotNull T, @Nullable K> keyMapper) {
ConcurrentMap<Function<T, K>, Map<K, List<T>>> keyMapperToCache = point.getCacheMap();
Map<K, List<T>> cache = keyMapperToCache.get(keyMapper);
ConcurrentMap<Class<?>, Map<K, List<T>>> keyMapperToCache = point.getCacheMap();
Map<K, List<T>> cache = keyMapperToCache.get(cacheId);
if (cache == null) {
cache = buildCacheForGroupingKeyMapper(keyMapper, point);
Map<K, List<T>> prev = keyMapperToCache.putIfAbsent(keyMapper, cache);
Map<K, List<T>> prev = keyMapperToCache.putIfAbsent(cacheId, cache);
if (prev != null) {
cache = prev;
}
@@ -93,10 +95,10 @@ public final class ExtensionProcessingHelper {
@ApiStatus.Internal
public static <@NotNull K, @NotNull T, @NotNull V> @Nullable V getByKey(@NotNull ExtensionPointImpl<T> point,
@NotNull K key,
@NotNull Class<?> cacheId,
@NotNull Function<@NotNull T, @Nullable K> keyMapper,
@NotNull Function<@NotNull T, @Nullable V> valueMapper) {
SimpleImmutableEntry<Function<T, K>, Function<T, V>> cacheKey = new SimpleImmutableEntry<>(keyMapper, valueMapper);
return doGetByKey(point, cacheKey, key, keyMapper, valueMapper, point.getCacheMap());
return doGetByKey(point, cacheId, key, keyMapper, valueMapper, point.getCacheMap());
}
/**
@@ -105,8 +107,9 @@ public final class ExtensionProcessingHelper {
@ApiStatus.Internal
public static <@NotNull K, @NotNull T> @Nullable T getByKey(@NotNull ExtensionPointImpl<T> point,
@NotNull K key,
@NotNull Class<?> cacheId,
@NotNull Function<@NotNull T, @Nullable K> keyMapper) {
return doGetByKey(point, keyMapper, key, keyMapper, Function.identity(), point.getCacheMap());
return doGetByKey(point, cacheId, key, keyMapper, Function.identity(), point.getCacheMap());
}
/**
@@ -121,16 +124,16 @@ public final class ExtensionProcessingHelper {
return cache.computeIfAbsent(new SimpleImmutableEntry<>(key, valueProducer), entry -> valueProducer.apply(entry.getKey()));
}
private static <CACHE_KEY, K, T, V> @Nullable V doGetByKey(@NotNull ExtensionPointImpl<T> point,
@NotNull CACHE_KEY cacheKey,
private static <CACHE_KEY, K, T, V> @Nullable V doGetByKey(@NotNull ExtensionPoint<T> point,
@NotNull CACHE_KEY cacheId,
@NotNull K key,
@NotNull Function<T, K> keyMapper,
@NotNull Function<@NotNull T, @Nullable V> valueMapper,
@NotNull ConcurrentMap<CACHE_KEY, Map<K, V>> keyMapperToCache) {
Map<K, V> cache = keyMapperToCache.get(cacheKey);
Map<K, V> cache = keyMapperToCache.get(cacheId);
if (cache == null) {
cache = buildCacheForKeyMapper(keyMapper, valueMapper, point);
Map<K, V> prev = keyMapperToCache.putIfAbsent(cacheKey, cache);
Map<K, V> prev = keyMapperToCache.putIfAbsent(cacheId, cache);
if (prev != null) {
cache = prev;
}
@@ -139,7 +142,7 @@ public final class ExtensionProcessingHelper {
}
private static <K, T> @NotNull Map<K, List<T>> buildCacheForGroupingKeyMapper(@NotNull Function<T, K> keyMapper,
@NotNull ExtensionPointImpl<T> point) {
@NotNull ExtensionPoint<T> point) {
// use HashMap instead of THashMap - a lot of keys not expected, nowadays HashMap is a more optimized (e.g. computeIfAbsent implemented in an efficient manner)
Map<K, List<T>> cache = new HashMap<>();
for (T extension : point.getExtensionList()) {
@@ -154,7 +157,7 @@ public final class ExtensionProcessingHelper {
private static @NotNull <K, T, V> Map<K, V> buildCacheForKeyMapper(@NotNull Function<T, K> keyMapper,
@NotNull Function<@NotNull T, @Nullable V> valueMapper,
@NotNull ExtensionPointImpl<T> point) {
@NotNull ExtensionPoint<T> point) {
List<T> extensions = point.getExtensionList();
Map<K, V> cache = new Object2ObjectOpenHashMap<>(extensions.size());
for (T extension : extensions) {
@@ -290,14 +290,17 @@ public class ExtensionPointImplTest {
assertThat(extensionPoint.getExtensionList()).containsExactly(4, 2);
assertThat(ExtensionProcessingHelper.getByGroupingKey(extensionPoint, "foo", it -> "foo")).isEqualTo(extensionPoint.getExtensionList());
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 2, Function.identity(), Function.identity())).isEqualTo(2);
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 2, Function.identity(), (Integer it) -> it * 2)).isEqualTo(4);
Function<Integer, String> f = it -> "foo";
assertThat(ExtensionProcessingHelper.getByGroupingKey(extensionPoint, f.getClass(), "foo", f)).isEqualTo(extensionPoint.getExtensionList());
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 2, ExtensionPointImplTest.class, Function.identity(), Function.identity())).isEqualTo(2);
Function<Integer, Integer> f2 = (Integer it) -> it * 2;
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 2, f2.getClass(), Function.identity(), f2)).isEqualTo(4);
Function<@NotNull Integer, @Nullable Integer> filteringKeyMapper = it -> it < 3 ? it : null;
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 2, filteringKeyMapper, Function.identity())).isEqualTo(2);
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 4, filteringKeyMapper, Function.identity())).isNull();
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 4, Function.identity(), (Integer it) -> (Integer)null)).isNull();
Function<Integer, Integer> filteringKeyMapper = it -> it < 3 ? it : null;
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 2, filteringKeyMapper.getClass(), filteringKeyMapper, Function.identity())).isEqualTo(2);
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 4, filteringKeyMapper.getClass(), filteringKeyMapper, Function.identity())).isNull();
Function<@NotNull Integer, @Nullable Integer> f3 = (Integer it) -> (Integer)null;
assertThat(ExtensionProcessingHelper.getByKey(extensionPoint, 4, f3.getClass(), Function.identity(), f3)).isNull();
}
@Test
@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.externalSystem.service.project.manage;
import com.intellij.openapi.application.ApplicationManager;
@@ -48,7 +48,7 @@ public class ProjectDataManagerImpl implements ProjectDataManager {
@Override
@NotNull
public List<ProjectDataService<?, ?>> findService(@NotNull Key<?> key) {
List<ProjectDataService<?, ?>> result = ProjectDataService.EP_NAME.getByGroupingKey(key, KEY_MAPPER);
List<ProjectDataService<?, ?>> result = ProjectDataService.EP_NAME.getByGroupingKey(key, ProjectDataManagerImpl.class, KEY_MAPPER);
ExternalSystemApiUtil.orderAwareSort(result);
return result;
}
@@ -1079,7 +1079,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
startLine > 0 ? myEditor.getFoldingModel().getCollapsedRegionAtOffset(document.getLineStartOffset(startLine - 1)) : null;
String lastFoldingFqn = USED_FOLDING_FQN_KEY.get(existingRegion);
ConsoleFolding lastFolding = lastFoldingFqn != null
? ConsoleFolding.EP_NAME.getByKey(lastFoldingFqn, consoleFolding -> consoleFolding.getClass().getName())
? ConsoleFolding.EP_NAME.getByKey(lastFoldingFqn, ConsoleViewImpl.class, consoleFolding -> consoleFolding.getClass().getName())
: null;
int lastStartLine = lastFolding == null ? Integer.MAX_VALUE :
existingRegion.getStartOffset() == 0 ? 0 :
@@ -175,7 +175,7 @@ class FacetEventsPublisher(private val project: Project) {
@Suppress("UNCHECKED_CAST")
private inline fun <F : Facet<*>> processListeners(facetType: FacetType<F, *>, action: (ProjectFacetListener<F>) -> Unit) {
for (listenerEP in LISTENER_EP.getByGroupingKey<String>(facetType.stringId, LISTENER_EP_CACHE_KEY)) {
for (listenerEP in LISTENER_EP.getByGroupingKey<String>(facetType.stringId, LISTENER_EP_CACHE_KEY::class.java, LISTENER_EP_CACHE_KEY)) {
action(listenerEP.listenerInstance as ProjectFacetListener<F>)
}
manuallyRegisteredListeners.filter { it.first == facetType.id }.forEach {
@@ -185,7 +185,7 @@ class FacetEventsPublisher(private val project: Project) {
@Suppress("UNCHECKED_CAST")
private inline fun processListeners(action: (ProjectFacetListener<Facet<*>>) -> Unit) {
for (listenerEP in LISTENER_EP.getByGroupingKey<String>(ANY_TYPE, LISTENER_EP_CACHE_KEY)) {
for (listenerEP in LISTENER_EP.getByGroupingKey<String>(ANY_TYPE, LISTENER_EP_CACHE_KEY::class.java, LISTENER_EP_CACHE_KEY)) {
action(listenerEP.listenerInstance as ProjectFacetListener<Facet<*>>)
}
manuallyRegisteredListeners.filter { it.first == null }.forEach {
@@ -90,7 +90,7 @@ public final class StructureViewFactoryImpl extends StructureViewFactoryEx imple
for (StructureViewExtension extension : point.getExtensionList()) {
Class<? extends PsiElement> registeredType = extension.getType();
if (ReflectionUtil.isAssignable(registeredType, type) && visitedTypes.add(registeredType)) {
result.addAll(ExtensionProcessingHelper.getByGroupingKey(point, registeredType, StructureViewExtension::getType));
result.addAll(ExtensionProcessingHelper.getByGroupingKey(point, StructureViewExtension.class, registeredType, StructureViewExtension::getType));
}
}
@@ -75,7 +75,7 @@ public class ModuleTypeManagerImpl extends ModuleTypeManager {
}
}
ModuleTypeEP result = EP_NAME.getByKey(moduleTypeId, it -> it.id);
ModuleTypeEP result = EP_NAME.getByKey(moduleTypeId, ModuleTypeManagerImpl.class, it -> it.id);
if (result != null) {
return result.getModuleType();
}
@@ -80,8 +80,8 @@ class ChainResolver {
return chains;
}
private static List<LibrarySupportProvider> forLanguage(@NotNull Language language) {
return LibrarySupportProvider.EP_NAME.getByGroupingKey(language.getID(), LibrarySupportProvider::getLanguageId);
private static @NotNull List<LibrarySupportProvider> forLanguage(@NotNull Language language) {
return LibrarySupportProvider.EP_NAME.getByGroupingKey(language.getID(), ChainResolver.class, LibrarySupportProvider::getLanguageId);
}
enum ChainStatus {
@@ -11,6 +11,8 @@ import com.intellij.xml.util.XmlTagUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
public interface XmlChildRole {
RoleFinder START_TAG_NAME_FINDER = new RoleFinder() {
@@ -36,14 +38,9 @@ public interface XmlChildRole {
RoleFinder ATTRIBUTE_NAME_FINDER = new DefaultRoleFinder(XmlTokenType.XML_NAME);
RoleFinder ATTRIBUTE_VALUE_VALUE_FINDER = new DefaultRoleFinder(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN);
RoleFinder START_TAG_END_FINDER = new DefaultRoleFinder(() -> {
return StartTagEndTokenProvider.EP_NAME.computeIfAbsent("the key", s -> {
IElementType[] elementTypes = new IElementType[]{XmlTokenType.XML_TAG_END};
for (StartTagEndTokenProvider tokenProvider : StartTagEndTokenProvider.EP_NAME.getExtensionList()) {
elementTypes = ArrayUtil.mergeArrays(elementTypes, tokenProvider.getTypes());
}
return elementTypes;
});
return StartTagEndTokenProvider.EP_NAME.computeIfAbsent("the key", Helper.START_TAG_END_FINDER);
});
RoleFinder START_TAG_START_FINDER = new DefaultRoleFinder(XmlTokenType.XML_START_TAG_START);
@@ -69,3 +66,13 @@ public interface XmlChildRole {
int XML_ATTRIBUTE_VALUE = 243;
int HTML_DOCUMENT = 252;
}
final class Helper {
static final Function<String, IElementType[]> START_TAG_END_FINDER = s -> {
IElementType[] elementTypes = new IElementType[]{XmlTokenType.XML_TAG_END};
for (StartTagEndTokenProvider tokenProvider : StartTagEndTokenProvider.EP_NAME.getExtensionList()) {
elementTypes = ArrayUtil.mergeArrays(elementTypes, tokenProvider.getTypes());
}
return elementTypes;
};
}