mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'PY-16282'
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -31,12 +32,6 @@ import java.util.*;
|
||||
*/
|
||||
public class TypeEvalContext {
|
||||
|
||||
/**
|
||||
* Contexts are cached here to prevent useless recreations (and cache loss)
|
||||
*/
|
||||
@NotNull
|
||||
private static final TypeEvalContextCache CACHE = new TypeEvalContextCache();
|
||||
|
||||
public static class Key {
|
||||
private static final Key INSTANCE = new Key();
|
||||
|
||||
@@ -94,7 +89,7 @@ public class TypeEvalContext {
|
||||
* the analyzed code was called or may be called. Since this is basically guesswork, the results should be used only for code completion.
|
||||
*/
|
||||
public static TypeEvalContext codeCompletion(@NotNull final Project project, @Nullable final PsiFile origin) {
|
||||
return CACHE.getContext(project, new TypeEvalContext(true, true, true, origin));
|
||||
return getContextFromCache(project, new TypeEvalContext(true, true, true, origin));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +100,7 @@ public class TypeEvalContext {
|
||||
* For code completion see {@link TypeEvalContext#codeCompletion(Project, PsiFile)}.
|
||||
*/
|
||||
public static TypeEvalContext userInitiated(@NotNull final Project project, @Nullable final PsiFile origin) {
|
||||
return CACHE.getContext(project, new TypeEvalContext(true, true, false, origin));
|
||||
return getContextFromCache(project, new TypeEvalContext(true, true, false, origin));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +110,7 @@ public class TypeEvalContext {
|
||||
* Inspections should not create a new type evaluation context. They should re-use the context of the inspection session.
|
||||
*/
|
||||
public static TypeEvalContext codeAnalysis(@NotNull final Project project, @Nullable final PsiFile origin) {
|
||||
return CACHE.getContext(project, new TypeEvalContext(false, false, false, origin));
|
||||
return getContextFromCache(project, new TypeEvalContext(false, false, false, origin));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +123,7 @@ public class TypeEvalContext {
|
||||
public static TypeEvalContext codeInsightFallback(@Nullable final Project project) {
|
||||
final TypeEvalContext anchor = new TypeEvalContext(false, false, false, null);
|
||||
if (project != null) {
|
||||
return CACHE.getContext(project, anchor);
|
||||
return getContextFromCache(project, anchor);
|
||||
}
|
||||
return anchor;
|
||||
}
|
||||
@@ -139,7 +134,20 @@ public class TypeEvalContext {
|
||||
* Should be used only when normal code insight context is not enough for getting good results.
|
||||
*/
|
||||
public static TypeEvalContext deepCodeInsight(@NotNull final Project project) {
|
||||
return CACHE.getContext(project, new TypeEvalContext(false, true, false, null));
|
||||
return getContextFromCache(project, new TypeEvalContext(false, true, false, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves context through cache returning one from cache (if exists).
|
||||
*
|
||||
* @param project current project
|
||||
* @param context context to fetch from cache
|
||||
* @return context to use
|
||||
* @see TypeEvalContextCache#getContext(TypeEvalContext)
|
||||
*/
|
||||
@NotNull
|
||||
private static TypeEvalContext getContextFromCache(@NotNull final Project project, @NotNull final TypeEvalContext context) {
|
||||
return ServiceManager.getService(project, TypeEvalContextCache.class).getContext(context);
|
||||
}
|
||||
|
||||
public TypeEvalContext withTracing() {
|
||||
@@ -191,7 +199,7 @@ public class TypeEvalContext {
|
||||
}
|
||||
}
|
||||
final PyType type = element.getType(this, Key.INSTANCE);
|
||||
assertValid(type, element);
|
||||
assertValid(type, element);
|
||||
synchronized (myEvaluated) {
|
||||
myEvaluated.put(element, type);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -15,72 +15,25 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.psi.util.PsiModificationTracker.SERVICE;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Caches context by their constraints (to prevent context cache loss). Flushes cache every PSI change.
|
||||
* Class is thread safe.
|
||||
* See {@link #getContext(com.intellij.openapi.project.Project, TypeEvalContext)}
|
||||
*
|
||||
* Project service to cache {@link TypeEvalContext}
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class TypeEvalContextCache {
|
||||
/**
|
||||
* Cache itself.
|
||||
*/
|
||||
@NotNull
|
||||
private final Map<TypeEvalConstraints, TypeEvalContext> myCache = new HashMap<TypeEvalConstraints, TypeEvalContext>();
|
||||
/**
|
||||
* Current PSI modification count
|
||||
*/
|
||||
private long myModificationCount = -1;
|
||||
/**
|
||||
* Lock to sync
|
||||
*/
|
||||
@NotNull
|
||||
private final Object myLock = new Object();
|
||||
|
||||
|
||||
public interface TypeEvalContextCache {
|
||||
/**
|
||||
* Returns context from cache (if exist) or returns the one you provided (and puts it into cache).
|
||||
* To use this method, do the following:
|
||||
* <ol>
|
||||
* <li>Instantiate {@link com.jetbrains.python.psi.types.TypeEvalContext} you want to use</li>
|
||||
* <li>Instantiate {@link TypeEvalContext} you want to use</li>
|
||||
* <li>Pass its instance here as argument</li>
|
||||
* <li>Use result</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param project project is required for caching engine
|
||||
* @param standard context you want to use. Just instantiate it and pass here.
|
||||
* @return context from cache (the one equals by constraints to yours or the one you provided)
|
||||
*/
|
||||
@NotNull
|
||||
TypeEvalContext getContext(@NotNull final Project project, @NotNull final TypeEvalContext standard) {
|
||||
final PsiModificationTracker tracker = SERVICE.getInstance(project);
|
||||
synchronized (myLock) {
|
||||
final long currentCount = tracker.getModificationCount();
|
||||
if (currentCount == myModificationCount) {
|
||||
// Cache is valid, use it
|
||||
final TypeEvalContext valueFromCache = myCache.get(standard.getConstraints());
|
||||
if (valueFromCache != null) {
|
||||
// We have element in cache, return it
|
||||
return valueFromCache;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Cache is invalid, flush it and store current count
|
||||
myCache.clear();
|
||||
myModificationCount = currentCount;
|
||||
}
|
||||
// We do not have value in cache (or cache is invalid), put it
|
||||
myCache.put(standard.getConstraints(), standard);
|
||||
return standard;
|
||||
}
|
||||
}
|
||||
TypeEvalContext getContext(@NotNull TypeEvalContext standard);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.util.CachedValue;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Caches context by their constraints (to prevent context cache loss). Flushes cache every PSI change or low memory conditions.
|
||||
* Class is thread safe.
|
||||
* See {@link #getContext(TypeEvalContext)}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class TypeEvalContextCacheImpl implements TypeEvalContextCache {
|
||||
|
||||
/**
|
||||
* Producer to create map to store cache
|
||||
*/
|
||||
@NotNull
|
||||
private static final MapCreator MAP_CREATOR = new MapCreator();
|
||||
|
||||
/**
|
||||
* Lock to sync
|
||||
*/
|
||||
@NotNull
|
||||
private final Object myLock = new Object();
|
||||
|
||||
/**
|
||||
* {@link CachedValue} to store/create map [constraints, context]
|
||||
*/
|
||||
private final CachedValue<Map<TypeEvalConstraints, TypeEvalContext>> myCachedMapStorage;
|
||||
|
||||
|
||||
TypeEvalContextCacheImpl(@NotNull final Project project) {
|
||||
final CachedValuesManager manager = CachedValuesManager.getManager(project);
|
||||
myCachedMapStorage = manager.createCachedValue(MAP_CREATOR, false);
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TypeEvalContext getContext(@NotNull final TypeEvalContext standard) {
|
||||
|
||||
// Map is not thread safe, and "getValue" is not atomic. I do not want several maps to be created.
|
||||
synchronized (myLock) {
|
||||
final Map<TypeEvalConstraints, TypeEvalContext> map = myCachedMapStorage.getValue();
|
||||
final TypeEvalContext context = map.get(standard.getConstraints());
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
map.put(standard.getConstraints(), standard);
|
||||
return standard;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider that creates map to store cache. Map depends on PSI modification
|
||||
*/
|
||||
private static final class MapCreator implements CachedValueProvider<Map<TypeEvalConstraints, TypeEvalContext>> {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result<Map<TypeEvalConstraints, TypeEvalContext>> compute() {
|
||||
// This method is called if cache is empty. Create new map for it.
|
||||
final HashMap<TypeEvalConstraints, TypeEvalContext> map = new HashMap<TypeEvalConstraints, TypeEvalContext>();
|
||||
return new Result<Map<TypeEvalConstraints, TypeEvalContext>>(map, PsiModificationTracker.MODIFICATION_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,10 @@
|
||||
<projectService serviceInterface="com.jetbrains.python.psi.PyElementGenerator"
|
||||
serviceImplementation="com.jetbrains.python.psi.impl.PyElementGeneratorImpl"/>
|
||||
|
||||
<projectService serviceInterface="com.jetbrains.python.psi.types.TypeEvalContextCache"
|
||||
serviceImplementation="com.jetbrains.python.psi.types.TypeEvalContextCacheImpl"
|
||||
/>
|
||||
|
||||
<applicationService serviceInterface="com.jetbrains.python.packaging.PyPackageService"
|
||||
serviceImplementation="com.jetbrains.python.packaging.PyPackageService"/>
|
||||
<applicationService serviceInterface="com.jetbrains.python.module.PyModuleService"
|
||||
|
||||
Reference in New Issue
Block a user