Skip to content

Commit 4f6abc2

Browse files
committed
Experimental caching of CallExpression evaluations
1 parent c2e9399 commit 4f6abc2

24 files changed

+358
-230
lines changed

src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeCompletionPriorityUtil.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import com.intellij.plugins.haxe.ide.lookup.*;
99
import com.intellij.plugins.haxe.lang.psi.*;
1010
import com.intellij.plugins.haxe.model.*;
11+
import com.intellij.plugins.haxe.model.evaluator.HaxeCallExpressionEvaluatorCacheService;
1112
import com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluator;
12-
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContext;
1313
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContextContainer;
1414
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionEvaluation;
1515
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionUtil;
@@ -294,8 +294,7 @@ private static HaxeCallExpressionEvaluation getValidationForMethod(HaxeCallExpre
294294
if (callExpression.getExpression() instanceof HaxeReferenceExpression referenceExpression) {
295295
PsiElement resolve = referenceExpression.resolve();
296296
if (resolve instanceof HaxeMethod method) {
297-
HaxeCallExpressionContextContainer contextContainer = HaxeCallExpressionUtil.createContextForMethodCall(callExpression, method);
298-
return contextContainer.evaluateContexts();
297+
return HaxeCallExpressionEvaluatorCacheService.cachedHaxeCallExpressionEvaluation(method, callExpression);
299298
}
300299
}
301300
return null;

src/main/java/com/intellij/plugins/haxe/ide/hint/parameters/HaxeInlayParameterHintsProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.intellij.plugins.haxe.HaxeHintBundle;
77
import com.intellij.plugins.haxe.lang.psi.*;
88
import com.intellij.plugins.haxe.model.*;
9+
import com.intellij.plugins.haxe.model.evaluator.HaxeCallExpressionEvaluatorCacheService;
910
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContext;
1011
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContextContainer;
1112
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionEvaluation;
@@ -94,8 +95,7 @@ private void handleEnumValueConstructor(HaxeCallExpression callExpression, HaxeE
9495
List<HaxeExpression> expressions = expressionList == null ? List.of() : expressionList.getExpressionList();
9596

9697
if (enumValueModel instanceof HaxeEnumValueConstructorModel constructorModel && constructorModel.getConstructorParameters() != null) {
97-
HaxeCallExpressionContextContainer contextContainer = HaxeCallExpressionUtil.createContextForMethodCall(callExpression, constructorModel.getMethod());
98-
HaxeCallExpressionEvaluation validation = contextContainer.evaluateContexts();
98+
HaxeCallExpressionEvaluation validation = HaxeCallExpressionEvaluatorCacheService.cachedHaxeCallExpressionEvaluation(constructorModel.getMethod(), callExpression);
9999
if(validation != null) {
100100
List<HaxeParameterModel> parameters = MapParametersToModel(constructorModel.getConstructorParameters());
101101
processArguments(validation.getArgumentToParameterMapping(), expressions, parameters, infoList, false);

src/main/java/com/intellij/plugins/haxe/ide/hint/types/HaxeSharedBypassCollector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private static void buildTypeTexts(ResultHolder type, PresentationTreeBuilder bu
6161
builder.text(className, inlayActionData);
6262
}
6363
// hide Unknown generics if Dynamic
64-
if(type.isDynamic() && type.containsUnknownTypeParameters()) return;;
64+
if(type.isDynamic() && type.containsUnknownOrUnresolvedTypeParameters()) return;;
6565

6666
@NotNull ResultHolder[] specifics = classReference.getSpecifics();
6767
if(specifics.length > 0 ) {

src/main/java/com/intellij/plugins/haxe/ide/inspections/intentions/HaxeUnresolvedSymbolIntentionBase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.intellij.plugins.haxe.model.HaxeBaseMemberModel;
1212
import com.intellij.plugins.haxe.model.HaxeClassModel;
1313
import com.intellij.plugins.haxe.model.HaxeFieldModel;
14+
import com.intellij.plugins.haxe.model.evaluator.HaxeCallExpressionEvaluatorCacheService;
1415
import com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluator;
1516
import com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluatorContext;
1617
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContextContainer;
@@ -324,8 +325,7 @@ private static ResultHolder findTypeFromCallExpression(HaxeCallExpressionList li
324325
if (callExpression.getExpression() instanceof HaxeReference reference) {
325326
PsiElement resolved = reference.resolve();
326327
if (resolved instanceof HaxeMethod method) {
327-
HaxeCallExpressionContextContainer contextContainer = HaxeCallExpressionUtil.createContextForMethodCall(callExpression, method);
328-
HaxeCallExpressionEvaluation validation = contextContainer.evaluateContexts();
328+
HaxeCallExpressionEvaluation validation = HaxeCallExpressionEvaluatorCacheService.cachedHaxeCallExpressionEvaluation(method, callExpression);
329329
if (validation != null) {
330330
Integer parameterIndex = validation.getArgumentToParameterMapping().get(index);
331331
if (parameterIndex != null) {

src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.intellij.plugins.haxe.metadata.psi.HaxeMetadataCompileTimeMeta;
3838
import com.intellij.plugins.haxe.metadata.util.HaxeMetadataUtils;
3939
import com.intellij.plugins.haxe.model.*;
40+
import com.intellij.plugins.haxe.model.evaluator.HaxeCallExpressionEvaluatorCacheService;
4041
import com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluator;
4142
import com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluatorContext;
4243
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContextContainer;
@@ -259,7 +260,7 @@ private List<? extends PsiElement> doResolveInner(@NotNull HaxeReference referen
259260
}
260261
return matchesInImport.isEmpty() ? null : matchesInImport;
261262
}
262-
boolean expectedEnumIsConstructor = parent instanceof HaxeCallExpression;
263+
boolean expectedEnumIsConstructor = parent instanceof HaxeCallExpression|| parent.getParent() instanceof HaxeEnumArgumentExtractor;
263264
PsiElement target = HaxeResolveUtil.searchInSamePackage(fileModel, referenceText, true, expectedEnumIsConstructor);
264265

265266
if (target != null) {
@@ -432,8 +433,7 @@ private ResultHolder findParentAssignType(@NotNull PsiElement reference, boolean
432433
PsiElement resolve = referenceExpression.resolve();
433434
if (resolve instanceof HaxeMethod method) {
434435
int index = expressionList.getExpressionList().indexOf(reference);
435-
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(callExpression, method);
436-
HaxeCallExpressionEvaluation evaluate = contextContainer.evaluateContexts();
436+
HaxeCallExpressionEvaluation evaluate = cachedHaxeCallExpressionEvaluation(method, callExpression);
437437
return evaluate == null ? null : evaluate.getParameterType(index);
438438
}
439439
}
@@ -477,8 +477,7 @@ private ResultHolder findParentAssignType(@NotNull PsiElement reference, boolean
477477
PsiElement resolve = referenceExpression.resolve();
478478
if (resolve instanceof HaxeMethod method) {
479479
int index = expressionList.getExpressionList().indexOf(reference);
480-
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(callExpression, method);
481-
HaxeCallExpressionEvaluation evaluate = contextContainer.evaluateContexts();
480+
HaxeCallExpressionEvaluation evaluate = cachedHaxeCallExpressionEvaluation(method, callExpression);
482481
return evaluate == null ? null : evaluate.getParameterType(index);
483482
}
484483
}
@@ -538,11 +537,17 @@ private ResultHolder findParentAssignType(@NotNull PsiElement reference, boolean
538537
return findParentAssignType(parent, isValueExpression);
539538
}
540539

540+
private static @Nullable HaxeCallExpressionEvaluation cachedHaxeCallExpressionEvaluation(HaxeMethod method, HaxeCallExpression callExpression) {
541+
542+
HaxeCallExpressionEvaluatorCacheService service = method.getProject().getService(HaxeCallExpressionEvaluatorCacheService.class);
543+
HaxeCallExpressionEvaluation evaluation = service.callExpressionCachedEvaluation(method, callExpression);
544+
return evaluation;
545+
}
546+
541547
private static boolean testAsEnumValueConstructor(@NotNull HaxeEnumValueDeclarationConstructor enumValueDeclaration, @NotNull HaxeReference reference) {
542548
if (reference.getParent() instanceof HaxeCallExpression haxeCallExpression) {
543549
HaxeMethod method = enumValueDeclaration.getModel().getMethod();
544-
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(haxeCallExpression, method);
545-
HaxeCallExpressionEvaluation validation = contextContainer.evaluateContexts();
550+
HaxeCallExpressionEvaluation validation = cachedHaxeCallExpressionEvaluation(method, haxeCallExpression);
546551
return validation != null && validation.isCompleted() && validation.isValid();
547552
}
548553
return false;
@@ -909,8 +914,7 @@ private List<? extends PsiElement> checkEnumMemberHints(HaxeReference reference)
909914
if (argumentList != null) {
910915
int argumentIndex = argumentList.getExpressionList().indexOf(argument);
911916
if (argumentIndex > -1) {
912-
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(methodCallCall, haxeMethod);
913-
HaxeCallExpressionEvaluation validation = contextContainer.evaluateContexts();
917+
HaxeCallExpressionEvaluation validation = cachedHaxeCallExpressionEvaluation(haxeMethod, methodCallCall);
914918
if(validation != null) {
915919
int parameterIndex = validation.getParameterForArgument(argumentIndex);
916920
ResultHolder parameterType = validation.getParameterType(parameterIndex);
@@ -1915,8 +1919,7 @@ private List<? extends PsiElement> checkByTreeWalk(HaxeReference reference, @Nu
19151919
// the code might be incomplete and validation might fail so we keep track of the element
19161920
// as a method match is more correct than a field match
19171921
bestGuess = psiElement;
1918-
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(callExpression, method);
1919-
HaxeCallExpressionEvaluation evaluation = contextContainer.evaluateContexts();
1922+
HaxeCallExpressionEvaluation evaluation = cachedHaxeCallExpressionEvaluation(method, callExpression);
19201923
if (evaluation != null && evaluation.isValid()) {
19211924
LogResolution(reference, "via tree walk. (method filtered)");
19221925
return List.of(psiElement);
@@ -2445,8 +2448,7 @@ private static void clearFakePsi(HaxeReference reference) {
24452448
for (HaxeBaseMemberModel member : members) {
24462449
if (member instanceof HaxeMethodModel methodModel) {
24472450
if (reference.getParent() instanceof HaxeCallExpression callExpression) {
2448-
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(callExpression, methodModel.getMethod());
2449-
HaxeCallExpressionEvaluation evaluate = contextContainer.evaluateContexts();
2451+
HaxeCallExpressionEvaluation evaluate = cachedHaxeCallExpressionEvaluation(methodModel.getMethod(), callExpression);
24502452
if (evaluate != null && evaluate.isValid()) {
24512453
return Collections.singletonList(member.getNamedComponentPsi());
24522454
}
@@ -2455,8 +2457,7 @@ private static void clearFakePsi(HaxeReference reference) {
24552457
if (argumentList.getParent() instanceof HaxeCallExpression callExpression) {
24562458
if (callExpression.getExpression() instanceof HaxeReferenceExpression referenceExpression) {
24572459
if (referenceExpression.resolve() instanceof HaxeMethod haxeMethod) {
2458-
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(callExpression, haxeMethod);
2459-
HaxeCallExpressionEvaluation evaluate = contextContainer.evaluateContexts();
2460+
HaxeCallExpressionEvaluation evaluate = cachedHaxeCallExpressionEvaluation(haxeMethod, callExpression);
24602461
if(evaluate != null) {
24612462
int paramIndex = evaluate.getParameterForArgument(argIndex);
24622463
if (paramIndex != -1) {

src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.intellij.plugins.haxe.metadata.psi.HaxeMeta;
3535
import com.intellij.plugins.haxe.metadata.util.HaxeMetadataUtils;
3636
import com.intellij.plugins.haxe.model.*;
37+
import com.intellij.plugins.haxe.model.evaluator.HaxeCallExpressionEvaluatorCacheService;
3738
import com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluator;
3839
import com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluatorContext;
3940
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContextContainer;
@@ -928,8 +929,7 @@ public static ResultHolder tryToFindTypeFromCallExpression(@NotNull HaxeFunction
928929
if (expressionListPsi != null) {
929930
expressionList = expressionListPsi.getExpressionList();
930931
methodModel = haxeMethod.getModel();
931-
HaxeCallExpressionContextContainer contextContainer = HaxeCallExpressionUtil.createContextForMethodCall(callExpression, haxeMethod);
932-
validation = contextContainer.evaluateContexts();
932+
validation = HaxeCallExpressionEvaluatorCacheService.cachedHaxeCallExpressionEvaluation(haxeMethod, callExpression);
933933
}
934934
}
935935
}

src/main/java/com/intellij/plugins/haxe/model/HaxeMethodModel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public ResultHolder getReturnType(@Nullable HaxeGenericResolver resolver) {
194194
ResultHolder result = CachedValuesManager.getProjectPsiDependentCache(haxeMethod, HaxeMethodModel::getReturnTypeCacheProvider);
195195
if (resolver != null) {
196196
ResultHolder resolve = resolver.resolve(result);
197-
if(resolve != null && resolve.containsUnknownTypeParameters()){
197+
if(resolve != null && resolve.containsUnknownOrUnresolvedTypeParameters()){
198198
// Special corner-case, might be only for multi-type abstracts ?
199199
// if we dont have any typeTag the return type is resolved come from an expression, and for abstracts that can be underlying type
200200
// and in the case of abstract Map(IMap) methods like the "get" method that use underlying type that is an interface so we need to translate
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.intellij.plugins.haxe.model.evaluator;
2+
3+
import com.intellij.plugins.haxe.lang.psi.HaxeCallExpression;
4+
import com.intellij.plugins.haxe.lang.psi.HaxeMethod;
5+
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContextContainer;
6+
import com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionEvaluation;
7+
import com.intellij.plugins.haxe.model.type.ResultHolder;
8+
import lombok.CustomLog;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
import java.util.Map;
12+
import java.util.concurrent.ConcurrentHashMap;
13+
14+
import static com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionUtil.createContextForMethodCall;
15+
16+
/**
17+
* Avoids unnecessary re-evaluation of callExpressions
18+
*/
19+
@CustomLog
20+
public class HaxeCallExpressionEvaluatorCacheService {
21+
22+
private volatile Map<CallExpressionEvaluationKey, HaxeCallExpressionEvaluation> cacheMap = new ConcurrentHashMap<>();
23+
public static boolean skipCaching = false;// just convenience flag for debugging
24+
25+
public static @Nullable HaxeCallExpressionEvaluation cachedHaxeCallExpressionEvaluation(HaxeMethod method, HaxeCallExpression callExpression) {
26+
27+
HaxeCallExpressionEvaluatorCacheService service = method.getProject().getService(HaxeCallExpressionEvaluatorCacheService.class);
28+
return service.callExpressionCachedEvaluation(method, callExpression);
29+
30+
}
31+
32+
33+
public @Nullable HaxeCallExpressionEvaluation callExpressionCachedEvaluation(HaxeMethod method, HaxeCallExpression callExpression) {
34+
35+
if(skipCaching){
36+
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(callExpression, method);
37+
return contextContainer.evaluateContexts();
38+
}
39+
40+
CallExpressionEvaluationKey key = new CallExpressionEvaluationKey(method, callExpression);
41+
if (cacheMap.containsKey(key)) {
42+
return cacheMap.get(key);
43+
}
44+
45+
HaxeCallExpressionContextContainer contextContainer = createContextForMethodCall(callExpression, method);
46+
HaxeCallExpressionEvaluation evaluate = contextContainer.evaluateContexts();
47+
if(evaluate == null) return null;
48+
49+
if(evaluate.isValid() && evaluate.isCompleted()) {
50+
if(noUnknownResolvedValues(evaluate)) {
51+
cacheMap.put(key, evaluate);
52+
}
53+
}
54+
55+
return evaluate;
56+
}
57+
58+
private boolean noUnknownResolvedValues(HaxeCallExpressionEvaluation evaluate) {
59+
if(evaluate.getReturnTypeWithoutResolve().containsUnknownTypes()) {
60+
return false;
61+
}
62+
for (ResultHolder argumentType : evaluate.getParameterTypes()) {
63+
if(argumentType.containsUnknownTypes()) {
64+
return false;
65+
}
66+
}
67+
return true;
68+
}
69+
70+
71+
public void clearCaches() {
72+
synchronized(this) {
73+
cacheMap.clear();
74+
}
75+
}
76+
}
77+
78+
record CallExpressionEvaluationKey(HaxeMethod method, HaxeCallExpression callExpression) {
79+
}

src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluator.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,7 @@ private static ResultHolder checkSearchResult(HaxeExpressionEvaluatorContext con
743743
final HaxeReference leftReference = PsiTreeUtil.getChildOfType(callExpression.getExpression(), HaxeReference.class);
744744
if (hint != null && leftReference == reference) {
745745
if (resolved instanceof HaxeMethod method ) {
746-
HaxeCallExpressionContextContainer contextContainer = HaxeCallExpressionUtil.createContextForMethodCall(callExpression, method);
747-
HaxeCallExpressionEvaluation validation = contextContainer.evaluateContexts();
746+
HaxeCallExpressionEvaluation validation = HaxeCallExpressionEvaluatorCacheService.cachedHaxeCallExpressionEvaluation(method, callExpression);
748747
if(validation != null) {
749748
ResultHolder hintResolved = validation.getCallExpressionResolver().resolve(hint);
750749
if (hintResolved != null) return hintResolved;

src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorCacheChangeListener.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public HaxeExpressionEvaluatorCacheChangeListener(Project project) {
1818

1919
public void modificationCountChanged() {
2020
myProject.getService(HaxeExpressionEvaluatorCacheService.class).clearCaches();
21+
myProject.getService(HaxeCallExpressionEvaluatorCacheService.class).clearCaches();
2122
}
2223
}
2324

0 commit comments

Comments
 (0)