Skip to content

Commit

Permalink
cache type resolve result while Psi evaluation is running to avoid un…
Browse files Browse the repository at this point in the history
…necessary re-resolves.
  • Loading branch information
m0rkeulv committed Apr 3, 2024
1 parent 9fae427 commit cccf365
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.intellij.plugins.haxe.lang.psi;

import com.intellij.plugins.haxe.lang.psi.impl.AnonymousHaxeTypeImpl;
import com.intellij.plugins.haxe.model.type.*;
import com.intellij.plugins.haxe.model.type.resolver.ResolveSource;
import com.intellij.plugins.haxe.util.HaxeDebugUtil;
Expand Down Expand Up @@ -163,6 +164,11 @@ public static HaxeGenericSpecialization fromGenericResolver(@Nullable PsiElement
continue;
}

//NOTE: AnonymousHaxeTypeImpl can be manually created and wont necessarily share the same instance
// (see, HaxeTypeParameterMultiType, HaxeClassWrapperForTypeParameter)
// we can however check if their from the same node
if (element != null && element.getNode() == context.getNode()) continue;

SpecificHaxeClassReference classType = holder.getClassType();
if (context instanceof HaxeClass haxeClass && classType != null) {
HaxeGenericResolver genericResolver = classType.getGenericResolver();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1177,13 +1177,23 @@ public Object[] getVariants() {

// if not first in chain
// foo.bar.baz
final HaxeReference leftReference = HaxeResolveUtil.getLeftReference(this);
HaxeResolveResult result = null;
final HaxeReference leftReference = HaxeResolveUtil.getLeftReference(this);
if (leftReference != null) {
HaxeGenericResolver resolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(leftReference);
ResultHolder leftResult = HaxeTypeResolver.getPsiElementType(leftReference, resolver);
if (leftResult.getClassType() != null) {
SpecificTypeReference reference = leftResult.getClassType().fullyResolveTypeDefAndUnwrapNullTypeReference();
result = reference.asResolveResult();
}else {
result = leftResult.getType().asResolveResult();
}
}

HaxeClass haxeClass = null;
String name = null;
HaxeGenericResolver resolver = null;
if (leftReference != null) {
result = leftReference.resolveHaxeClass();
if (result != null) {
if (result != HaxeResolveResult.EMPTY) {
haxeClass = result.getHaxeClass();
if (haxeClass != null) {
Expand All @@ -1207,7 +1217,7 @@ public Object[] getVariants() {

addChildClassVariants(suggestedVariants, haxeClass);
}
else if (leftReference != null && !result.isFunctionType()) {
else if (result != null && !result.isFunctionType()) {
if (null == haxeClass) {
// TODO: fix haxeClass by type inference. Use compiler code assist?!
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ static public HaxeExpressionEvaluatorContext evaluate(PsiElement element, HaxeGe
// evaluation of complex expressions can in some cases result in needing the type for a psiElement multiple times
// untyped parameters and variables can cause a lot of unnecessary computation if we have to re-evaluate them
// in order to avoid this we put any useful results in a thread-local map that we clear once we are done with the evaluation
private static final ThreadLocal<Map<PsiElement, ResultHolder>> resultCache = ThreadLocal.withInitial(HashMap::new);
record CacheRecord(ResultHolder holder, String resolverAsString){}
private static final ThreadLocal<Map<PsiElement, CacheRecord>> resultCache = ThreadLocal.withInitial(HashMap::new);
private static final ThreadLocal<Map<PsiElement, AtomicInteger>> resultCacheHits = ThreadLocal.withInitial(HashMap::new);
private static final ThreadLocal<Stack<PsiElement>> processingStack = ThreadLocal.withInitial(Stack::new);
@NotNull
Expand Down Expand Up @@ -206,11 +207,11 @@ static private ResultHolder _handle(final PsiElement element,
return handleValueIterator(context, resolver, valueIterator);
}
if (element instanceof HaxeIteratorkey || element instanceof HaxeIteratorValue) {
return resolveWithCache(element, () -> findIteratorType(element));
return resolveWithCache(element, resolver, () -> findIteratorType(element));
}

if (element instanceof HaxeEnumExtractedValue extractedValue) {
return resolveWithCache(extractedValue, () -> handleEnumExtractedValue(extractedValue));
return resolveWithCache(extractedValue, resolver, () -> handleEnumExtractedValue(extractedValue));
}

//NOTE: must be before HaxeParameter as HaxeRestParameter extends HaxeParameter
Expand All @@ -221,7 +222,7 @@ static private ResultHolder _handle(final PsiElement element,
if (element instanceof HaxeParameter parameter) {
boolean isUntyped = parameter.getTypeTag() == null && parameter.getVarInit() == null;
if (isUntyped) {
return resolveWithCache(element, () -> handleParameter(context, resolver, parameter));
return resolveWithCache(element, resolver, () -> handleParameter(context, resolver, parameter));
}
return handleParameter(context, resolver, parameter);
}
Expand All @@ -243,11 +244,11 @@ static private ResultHolder _handle(final PsiElement element,
}

if (element instanceof HaxeCallExpression callExpression) {
return resolveWithCache(callExpression, () -> handleCallExpression(context, resolver, callExpression));
return resolveWithCache(callExpression, resolver, () -> handleCallExpression(context, resolver, callExpression));
}

if (element instanceof HaxeReferenceExpression referenceExpression) {
return resolveWithCache(referenceExpression, () -> handleReferenceExpression(context, resolver, referenceExpression));
return resolveWithCache(referenceExpression, resolver, () -> handleReferenceExpression(context, resolver, referenceExpression));
}

if (element instanceof HaxeCastExpression castExpression) {
Expand Down Expand Up @@ -314,7 +315,7 @@ static private ResultHolder _handle(final PsiElement element,
}

if (element instanceof HaxeFunctionLiteral function) {
return resolveWithCache(function, () -> handleFunctionLiteral(context, resolver, function));
return resolveWithCache(function, resolver, () -> handleFunctionLiteral(context, resolver, function));
}

if (element instanceof HaxePsiToken primitive) {
Expand Down Expand Up @@ -370,24 +371,26 @@ static private ResultHolder _handle(final PsiElement element,
return createUnknown(element);
}

private static ResultHolder resolveWithCache(PsiElement element, Supplier<ResultHolder> resolveLogic) {
Map<PsiElement, ResultHolder> map = resultCache.get();
private static ResultHolder resolveWithCache(@NotNull PsiElement element, @NotNull HaxeGenericResolver resolver, Supplier<ResultHolder> resolveLogic) {
Map<PsiElement, CacheRecord> map = resultCache.get();
Map<PsiElement, AtomicInteger> hitCounter = resultCacheHits.get();
if (map.containsKey(element)) {
String resolverAsString = resolver.toCacheString();
if (map.containsKey(element) && map.get(element).resolverAsString().equals(resolverAsString)) {
CacheRecord cacheRecord = map.get(element);
hitCounter.get(element).incrementAndGet();
return map.get(element);
return cacheRecord.holder();
}else {
ResultHolder result = resolveLogic.get();
if (!result.isUnknown()) {

if (result.getClassType() != null) {
if (!result.isTypeParameter() && result.getClassType().getGenericResolver().isEmpty()) {
hitCounter.put(element, new AtomicInteger(0));
map.put(element, result);
map.put(element, new CacheRecord(result, resolverAsString));
}
}else if (result.isFunctionType()) {
hitCounter.put(element, new AtomicInteger(0));
map.put(element, result);
map.put(element, new CacheRecord(result, resolverAsString));
}
}
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import java.util.*;

import static com.intellij.plugins.haxe.ide.annotator.semantics.HaxeCallExpressionUtil.tryGetCallieType;
import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets.ONLY_COMMENTS;
import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes.KUNTYPED;
import static com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl.getLiteralClassName;
Expand Down Expand Up @@ -1094,7 +1095,18 @@ static ResultHolder handleCallExpression(
HaxeGenericResolver resolver,
HaxeCallExpression callExpression) {
HaxeExpression callLeft = callExpression.getExpression();
SpecificTypeReference functionType = handle(callLeft, context, resolver).getType();

HaxeGenericResolver localResolver = new HaxeGenericResolver();
localResolver.addAll(resolver);
ResultHolder callie = tryGetCallieType(callExpression);
if (callie.isClassType()){
SpecificTypeReference reference = callie.getClassType().fullyResolveTypeDefAndUnwrapNullTypeReference();
if (reference instanceof SpecificHaxeClassReference classReference) {
localResolver.addAll(classReference.getGenericResolver());
}
}

SpecificTypeReference functionType = handle(callLeft, context, localResolver).getType();

// @TODO: this should be innecessary when code is working right!
if (functionType.isUnknown()) {
Expand Down Expand Up @@ -1264,6 +1276,8 @@ static ResultHolder handleLocalVarDeclaration(

if (typeTag != null) {
result = HaxeTypeResolver.getTypeFromTypeTag(typeTag, varDeclaration);
ResultHolder resolve = resolver.resolve(result);
if (!resolve.isUnknown()) result = resolve;
}

if (result == null && init != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.stream.Collectors;

import static com.intellij.plugins.haxe.model.type.resolver.ResolveSource.ARGUMENT_TYPE;

Expand Down Expand Up @@ -186,6 +187,11 @@ public ResultHolder resolve(String name) {
.map(ResolverEntry::type)
.orElse(null);
}
// continue resolving until no longer typeParameter or no match
if (holder!= null && holder.isTypeParameter()){
ResultHolder resolve = this.without(name).resolve(holder);
if (resolve != null && !resolve.isUnknown()) holder = resolve;
}
return holder;
}
@Nullable
Expand Down Expand Up @@ -590,4 +596,12 @@ public SpecificFunctionReference substituteTypeParamsWithAssignHintTypes(Specifi


}

public String toCacheString() {
String resolversAsString = resolvers.stream().map(entry -> entry.name() + ":" + entry.type().toPresentationString() + ":" + entry.resolveSource())
.collect(Collectors.joining(","));
String constrainsAsString = constaints.stream().map(entry -> entry.name() + ":" + entry.type().toPresentationString() + ":" + entry.resolveSource())
.collect(Collectors.joining(","));
return "resolvers:["+resolversAsString + "], constraints: [" + constrainsAsString+"]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,9 @@ public class HaxeGenericResolverUtil {
public static HaxeGenericResolver generateResolverFromScopeParents(PsiElement element) {
HaxeGenericResolver resolver = new HaxeGenericResolver();

HaxeMethod method = element instanceof HaxeMethod
? (HaxeMethod)element
: UsefulPsiTreeUtil.getParentOfType(element, HaxeMethod.class);
boolean isStatic = null != method && method.isStatic();
if (!isStatic) {
appendClassGenericResolver(element, resolver);
}

appendClassGenericResolver(element, resolver);

appendMethodGenericResolver(element, resolver);
appendStatementGenericResolver(HaxeResolveUtil.getLeftReference(element), resolver);
appendCallExpressionGenericResolver(element, resolver);
Expand Down

0 comments on commit cccf365

Please sign in to comment.