diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java index e625314ffe14..2bb36b1df599 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.groovy.lang.psi.dataFlow; +import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiIntersectionType; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiType; @@ -57,6 +58,16 @@ public class DFAType { Mixin negate() { return new Mixin(ID, myType, myCondition, !myNegated); } + + @Override + public String toString() { + return "Mixin{" + + "ID=" + ID + + ", myType=" + myType + + ", myCondition=" + myCondition + + ", myNegated=" + myNegated + + '}'; + } } private final PsiType primary; @@ -68,6 +79,10 @@ public class DFAType { } public void addMixin(@Nullable PsiType mixin, ConditionInstruction instruction) { + if (mixin == null) { + return; + } + mixins.add(new Mixin(mixin, instruction, false)); } @@ -136,7 +151,7 @@ public class DFAType { } private static boolean eq(PsiType t1, PsiType t2) { - return !TypeConversionUtil.erasure(t1).equals(TypeConversionUtil.erasure(t2)); + return Comparing.equal(TypeConversionUtil.erasure(t1), TypeConversionUtil.erasure(t2)); } @Nullable @@ -153,4 +168,12 @@ public class DFAType { } return type; } + + @Override + public String toString() { + return "DFAType{" + + "primary=" + primary + + ", mixins=" + mixins + + '}'; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeInferenceHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeInferenceHelper.java index 7603016e4d15..7c9ba1a79933 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeInferenceHelper.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeInferenceHelper.java @@ -18,10 +18,8 @@ package org.jetbrains.plugins.groovy.lang.psi.dataFlow.types; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.*; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiIntersectionType; import com.intellij.psi.PsiType; import com.intellij.psi.util.*; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,7 +29,10 @@ import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; -import org.jetbrains.plugins.groovy.lang.psi.controlFlow.*; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.InstanceOfInstruction; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.MixinTypeInstruction; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ArgumentInstruction; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAType; @@ -53,9 +54,9 @@ public class TypeInferenceHelper { private static final Logger LOG = Logger.getInstance(TypeInferenceHelper.class); private static final ThreadLocal ourInferenceContext = new ThreadLocal(); - private static T doInference(TypeDfaState bindings, Computable computation) { + private static T doInference(Map bindings, Computable computation) { InferenceContext old = ourInferenceContext.get(); - ourInferenceContext.set(new InferenceContext.PartialContext(bindings.getBindings())); + ourInferenceContext.set(new InferenceContext.PartialContext(bindings)); try { return computation.compute(); } @@ -70,7 +71,7 @@ public class TypeInferenceHelper { } @Nullable - public static PsiType getInferredType(@NotNull final GrReferenceExpression refExpr) { + public static PsiType getInferredTypeOld(@NotNull final GrReferenceExpression refExpr) { return RecursionManager.doPreventingRecursion(refExpr, true, new NullableComputable() { @Override public PsiType compute() { @@ -92,7 +93,7 @@ public class TypeInferenceHelper { } @Nullable - public static PsiType getInferredType(@NotNull PsiElement place, String variableName) { + public static PsiType getInferredTypeOld(@NotNull PsiElement place, String variableName) { final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(place); if (scope == null) return null; @@ -106,7 +107,7 @@ public class TypeInferenceHelper { @Nullable - public static PsiType getInferredTypeNew(@NotNull final GrReferenceExpression refExpr) { + public static PsiType getInferredType(@NotNull final GrReferenceExpression refExpr) { final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(refExpr); if (scope == null) return null; @@ -114,7 +115,7 @@ public class TypeInferenceHelper { } @Nullable - public static PsiType getInferredTypeNew(@NotNull PsiElement place, String variableName) { + public static PsiType getInferredType(@NotNull PsiElement place, String variableName) { final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(place); if (scope == null) return null; @@ -344,22 +345,25 @@ public class TypeInferenceHelper { @Nullable private static List> performTypeDfa(GrControlFlowOwner owner, Instruction[] flow) { - final TypeDfaInstance dfaInstance = new TypeDfaInstance(owner); + final TypeDfaInstance dfaInstance = new TypeDfaInstance(owner, flow); final TypesSemilattice semilattice = new TypesSemilattice(owner.getManager()); List states = new DFAEngine(flow, dfaInstance, semilattice).performDFAWithTimeout(); - return states == null ? null : ContainerUtil.map(states, new Function>() { - @Override - public Map fun(TypeDfaState state) { - return state.getBindings(); - } - }); + if (states == null) return null; + + List> result = ContainerUtil.newArrayList(); + for (int i = 0; i < states.size(); i++) { + result.add(states.get(i).getBindings(flow[i])); + } + return result; } static class TypeDfaInstance implements DfaInstance { - private final PsiElement myScope; + private final GrControlFlowOwner myScope; + private final Instruction[] myFlow; - TypeDfaInstance(PsiElement scope) { + TypeDfaInstance(GrControlFlowOwner scope, Instruction[] flow) { myScope = scope; + myFlow = flow; } public void fun(final TypeDfaState state, final Instruction instruction) { @@ -371,16 +375,19 @@ public class TypeInferenceHelper { } } - private static void handleMixin(final TypeDfaState state, final MixinTypeInstruction instruction) { + private void handleMixin(final TypeDfaState state, final MixinTypeInstruction instruction) { final String varName = instruction.getVariableName(); - state.putType(varName, doInference(state, new Computable() { + if (varName == null) return; + + state.putType(varName, doInference(state.getBindings(instruction), new NullableComputable() { @Override - public PsiType compute() { - PsiType original = state.getBindings().get(varName); - final PsiType mixin = instruction.inferMixinType(); - if (mixin == null) return original; - if (original == null) return mixin; - return PsiIntersectionType.createIntersection(original, mixin); + public DFAType compute() { + ReadWriteVariableInstruction originalInstr = instruction.getInstructionToMixin(myFlow); + assert originalInstr != null && !originalInstr.isWrite(); + + DFAType original = state.getVariableType(varName).negate(originalInstr); + original.addMixin(instruction.inferMixinType(), instruction.getConditionInstruction()); + return original; } })); } @@ -388,10 +395,10 @@ public class TypeInferenceHelper { private void handleVariableWrite(TypeDfaState state, ReadWriteVariableInstruction instruction) { final PsiElement element = instruction.getElement(); if (element != null && instruction.isWrite()) { - state.putType(instruction.getVariableName(), doInference(state, new Computable() { + state.putType(instruction.getVariableName(), doInference(state.getBindings(instruction), new Computable() { @Override - public PsiType compute() { - return TypesUtil.boxPrimitiveType(getInitializerType(element), myScope.getManager(), myScope.getResolveScope()); + public DFAType compute() { + return DFAType.create(TypesUtil.boxPrimitiveType(getInitializerType(element), myScope.getManager(), myScope.getResolveScope())); } })); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java index c6c6073266ad..d5d49e79e810 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java @@ -17,12 +17,14 @@ package org.jetbrains.plugins.groovy.lang.psi.dataFlow.types; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiType; -import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; +import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAType; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import java.util.ArrayList; +import java.util.HashMap; import java.util.Map; /** @@ -51,7 +53,7 @@ public class TypesSemilattice implements Semilattice { } class TypeDfaState { - private final Map myVarTypes; + private final Map myVarTypes; TypeDfaState() { myVarTypes = ContainerUtil.newHashMap(); @@ -62,13 +64,13 @@ class TypeDfaState { } void joinState(TypeDfaState another, PsiManager manager) { - for (Map.Entry entry : another.myVarTypes.entrySet()) { + for (Map.Entry entry : another.myVarTypes.entrySet()) { final String name = entry.getKey(); - final PsiType t1 = entry.getValue(); + final DFAType t1 = entry.getValue(); if (myVarTypes.containsKey(name)) { - final PsiType t2 = myVarTypes.get(name); + final DFAType t2 = myVarTypes.get(name); if (t1 != null && t2 != null) { - myVarTypes.put(name, TypesUtil.getLeastUpperBound(t1, t2, manager)); + myVarTypes.put(name, DFAType.create(t1, t2, manager)); } else { myVarTypes.put(name, null); @@ -78,36 +80,30 @@ class TypeDfaState { } boolean contentsEqual(TypeDfaState another) { - if (myVarTypes.size() != another.myVarTypes.size()) { - return false; - } - - for (String name : myVarTypes.keySet()) { - if (!areTypesErasureEqual(another.myVarTypes, name)) { - return false; - } - } - return true; + return myVarTypes.equals(another.myVarTypes); } - private boolean areTypesErasureEqual(Map another, String name) { - if (!another.containsKey(name)) return false; - final PsiType t1 = myVarTypes.get(name); - final PsiType t2 = another.get(name); - if (t1 == null || t2 == null) { - if (t1 != null || t2 != null) return false; - } - else { - if (!TypeConversionUtil.erasure(t1).equals(TypeConversionUtil.erasure(t2))) return false; - } - return true; + @NotNull + DFAType getVariableType(String variableName) { + DFAType type = myVarTypes.get(variableName); + return type == null ? DFAType.create(null) : type; } - Map getBindings() { - return myVarTypes; + Map getBindings(Instruction instruction) { + HashMap map = ContainerUtil.newHashMap(); + for (Map.Entry entry : myVarTypes.entrySet()) { + DFAType value = entry.getValue(); + map.put(entry.getKey(), value == null ? null : value.negate(instruction).getResultType()); + } + return map; } - void putType(String variableName, PsiType type) { + void putType(String variableName, DFAType type) { myVarTypes.put(variableName, type); } + + @Override + public String toString() { + return "TypeDfaState{" + myVarTypes + '}'; + } } \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy index 8ff6a9911833..06a078328c4b 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy @@ -179,7 +179,6 @@ def bar(File file) { file.path } } public void "test using SSA variables in a for loop"() { - //todo RecursionManager.assertOnRecursionPrevention(testRootDisposable) def text = """ def foo(List list, SomeClass sc) { List result @@ -190,7 +189,7 @@ ${ bar2(s, result, sc) bar3(foo:s, bar:result, sc) sc.someMethod(s) -''' * 2 +''' * 20 } } }