wrap def-use structure into a bit more friendly DefinitionMap

This commit is contained in:
peter
2012-08-30 20:33:15 +02:00
parent dd550e9feb
commit e65b55b2da
6 changed files with 130 additions and 85 deletions
@@ -27,7 +27,6 @@ import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.Processor;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntProcedure;
import gnu.trove.TObjectProcedure;
import org.jetbrains.annotations.Nls;
@@ -48,6 +47,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.DefinitionMap;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsDfaInstance;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsSemilattice;
@@ -82,8 +82,8 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase {
final Instruction[] flow = owner.getControlFlow();
final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow);
final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice();
final DFAEngine<TIntObjectHashMap<TIntHashSet>> engine = new DFAEngine<TIntObjectHashMap<TIntHashSet>>(flow, dfaInstance, lattice);
final List<TIntObjectHashMap<TIntHashSet>> dfaResult = engine.performDFAWithTimeout();
final DFAEngine<DefinitionMap> engine = new DFAEngine<DefinitionMap>(flow, dfaInstance, lattice);
final List<DefinitionMap> dfaResult = engine.performDFAWithTimeout();
if (dfaResult == null) {
return;
}
@@ -101,7 +101,7 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase {
final ReadWriteVariableInstruction varInst = (ReadWriteVariableInstruction) instruction;
if (!varInst.isWrite()) {
final String varName = varInst.getVariableName();
TIntObjectHashMap<TIntHashSet> e = dfaResult.get(i);
DefinitionMap e = dfaResult.get(i);
e.forEachValue(new TObjectProcedure<TIntHashSet>() {
public boolean execute(TIntHashSet reaching) {
reaching.forEach(new TIntProcedure() {
@@ -0,0 +1,84 @@
/*
* Copyright 2000-2012 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 org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntObjectProcedure;
import gnu.trove.TObjectProcedure;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
/**
* @author peter
*/
public class DefinitionMap {
private final TIntObjectHashMap<TIntHashSet> myMap = new TIntObjectHashMap<TIntHashSet>();
public void registerDef(Instruction varInsn, int num) {
TIntHashSet defs = myMap.get(num);
if (defs == null) {
myMap.put(num, defs = new TIntHashSet());
} else {
defs.clear();
}
defs.add(varInsn.num());
}
public void merge(DefinitionMap map2) {
map2.myMap.forEachEntry(new TIntObjectProcedure<TIntHashSet>() {
public boolean execute(int num, TIntHashSet defs) {
TIntHashSet defs2 = myMap.get(num);
if (defs2 == null) {
defs2 = new TIntHashSet(defs.toArray());
myMap.put(num, defs2);
}
else {
defs2.addAll(defs.toArray());
}
return true;
}
});
}
public boolean eq(final DefinitionMap m2) {
if (myMap.size() != m2.myMap.size()) return false;
return myMap.forEachEntry(new TIntObjectProcedure<TIntHashSet>() {
public boolean execute(int num, TIntHashSet defs1) {
final TIntHashSet defs2 = m2.myMap.get(num);
return defs2 != null && defs2.equals(defs1);
}
});
}
public void copyFrom(DefinitionMap map, int fromIndex, int toIndex) {
TIntHashSet defs = map.myMap.get(fromIndex);
if (defs == null) defs = new TIntHashSet();
myMap.put(toIndex, defs);
}
@Nullable
public int[] getDefinitions(int atInstruction) {
TIntHashSet set = myMap.get(atInstruction);
return set == null ? null : set.toArray();
}
public void forEachValue(TObjectProcedure<TIntHashSet> procedure) {
myMap.forEachValue(procedure);
}
}
@@ -66,8 +66,8 @@ public class ReachingDefinitionsCollector {
final Instruction[] flow = flowOwner.getControlFlow();
final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow);
final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice();
final DFAEngine<TIntObjectHashMap<TIntHashSet>> engine = new DFAEngine<TIntObjectHashMap<TIntHashSet>>(flow, dfaInstance, lattice);
final TIntObjectHashMap<TIntHashSet> dfaResult = postprocess(engine.performForceDFA(), flow, dfaInstance);
final DFAEngine<DefinitionMap> engine = new DFAEngine<DefinitionMap>(flow, dfaInstance, lattice);
final DefinitionMap dfaResult = postprocess(engine.performForceDFA(), flow, dfaInstance);
final LinkedHashSet<Integer> fragmentInstructions = getFragmentInstructions(first, last, flow);
final int[] postorder = ControlFlowBuilderUtil.postorder(flow);
@@ -82,7 +82,7 @@ public class ReachingDefinitionsCollector {
for (final Integer ref : fragmentReads) {
ReadWriteVariableInstruction rwInstruction = (ReadWriteVariableInstruction) flow[ref];
String name = rwInstruction.getVariableName();
final int[] defs = dfaResult.get(ref).toArray();
final int[] defs = dfaResult.getDefinitions(ref);
if (!allDefsInFragment(defs, fragmentInstructions)) {
addVariable(name, imap, manager, getType(rwInstruction.getElement()));
}
@@ -91,7 +91,7 @@ public class ReachingDefinitionsCollector {
for (final Integer ref : reachableFromFragmentReads) {
ReadWriteVariableInstruction rwInstruction = (ReadWriteVariableInstruction) flow[ref];
String name = rwInstruction.getVariableName();
final int[] defs = dfaResult.get(ref).toArray();
final int[] defs = dfaResult.getDefinitions(ref);
if (anyDefInFragment(defs, fragmentInstructions)) {
for (int def : defs) {
if (fragmentInstructions.contains(def)) {
@@ -277,24 +277,21 @@ public class ReachingDefinitionsCollector {
return true;
}
private static LinkedHashSet<Integer> getReachable(final LinkedHashSet<Integer> fragmentInsns, final Instruction[] flow, TIntObjectHashMap<TIntHashSet> dfaResult, final int[] postorder) {
private static LinkedHashSet<Integer> getReachable(final LinkedHashSet<Integer> fragmentInsns, final Instruction[] flow, DefinitionMap dfaResult, final int[] postorder) {
final LinkedHashSet<Integer> result = new LinkedHashSet<Integer>();
for (Instruction insn : flow) {
if (insn instanceof ReadWriteVariableInstruction &&
!((ReadWriteVariableInstruction) insn).isWrite()) {
final int ref = insn.num();
TIntHashSet defs = dfaResult.get(ref);
defs.forEach(new TIntProcedure() {
public boolean execute(int def) {
if (fragmentInsns.contains(def)) {
if (!fragmentInsns.contains(ref) || postorder[ref] < postorder[def]) {
result.add(ref);
return false;
}
for (int def : dfaResult.getDefinitions(ref)) {
if (fragmentInsns.contains(def)) {
if (!fragmentInsns.contains(ref) || postorder[ref] < postorder[def]) {
result.add(ref);
break;
}
return true;
}
});
}
}
}
@@ -367,19 +364,17 @@ public class ReachingDefinitionsCollector {
}
@NotNull
private static TIntObjectHashMap<TIntHashSet> postprocess(@NotNull final ArrayList<TIntObjectHashMap<TIntHashSet>> dfaResult,
private static DefinitionMap postprocess(@NotNull final ArrayList<DefinitionMap> dfaResult,
@NotNull Instruction[] flow,
@NotNull ReachingDefinitionsDfaInstance dfaInstance) {
TIntObjectHashMap<TIntHashSet> result = new TIntObjectHashMap<TIntHashSet>();
DefinitionMap result = new DefinitionMap();
for (int i = 0; i < flow.length; i++) {
Instruction insn = flow[i];
if (insn instanceof ReadWriteVariableInstruction) {
ReadWriteVariableInstruction rwInsn = (ReadWriteVariableInstruction) insn;
if (!rwInsn.isWrite()) {
int idx = dfaInstance.getVarIndex(rwInsn.getVariableName());
TIntHashSet defs = dfaResult.get(i).get(idx);
if (defs == null) defs = new TIntHashSet();
result.put(i, defs);
result.copyFrom(dfaResult.get(i), idx, i);
}
}
}
@@ -26,7 +26,7 @@ import java.util.Arrays;
/**
* @author ven
*/
public class ReachingDefinitionsDfaInstance implements DfaInstance<TIntObjectHashMap<TIntHashSet>> {
public class ReachingDefinitionsDfaInstance implements DfaInstance<DefinitionMap> {
private final TObjectIntHashMap<String> myVarToIndexMap = new TObjectIntHashMap<String>();
private final Instruction[] myFlow;
@@ -48,30 +48,21 @@ public class ReachingDefinitionsDfaInstance implements DfaInstance<TIntObjectHas
}
public void fun(TIntObjectHashMap<TIntHashSet> m, Instruction instruction) {
public void fun(DefinitionMap m, Instruction instruction) {
if (instruction instanceof ReadWriteVariableInstruction) {
final ReadWriteVariableInstruction varInsn = (ReadWriteVariableInstruction) instruction;
final String name = varInsn.getVariableName();
assert myVarToIndexMap.containsKey(name) : name + "; " + Arrays.asList(myFlow).contains(instruction);
final int num = myVarToIndexMap.get(name);
if (varInsn.isWrite()) {
registerDef(m, varInsn, num);
m.registerDef(varInsn, num);
}
}
}
protected static void registerDef(TIntObjectHashMap<TIntHashSet> m, Instruction varInsn, int num) {
TIntHashSet defs = m.get(num);
if (defs == null) {
defs = new TIntHashSet();
m.put(num, defs);
} else defs.clear();
defs.add(varInsn.num());
}
@NotNull
public TIntObjectHashMap<TIntHashSet> initial() {
return new TIntObjectHashMap<TIntHashSet>();
public DefinitionMap initial() {
return new DefinitionMap();
}
public boolean isForward() {
@@ -15,7 +15,6 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs;
import gnu.trove.*;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice;
import java.util.ArrayList;
@@ -23,42 +22,19 @@ import java.util.ArrayList;
/**
* @author ven
*/
public class ReachingDefinitionsSemilattice implements Semilattice<TIntObjectHashMap<TIntHashSet>> {
public TIntObjectHashMap<TIntHashSet> join(ArrayList<TIntObjectHashMap<TIntHashSet>> ins) {
if (ins.isEmpty()) return new TIntObjectHashMap<TIntHashSet>();
public class ReachingDefinitionsSemilattice implements Semilattice<DefinitionMap> {
public DefinitionMap join(ArrayList<DefinitionMap> ins) {
if (ins.isEmpty()) return new DefinitionMap();
TIntObjectHashMap<TIntHashSet> result = new TIntObjectHashMap<TIntHashSet>();
for (TIntObjectHashMap<TIntHashSet> map : ins) {
merge(result, map);
DefinitionMap result = new DefinitionMap();
for (DefinitionMap map : ins) {
result.merge(map);
}
return result;
}
private void merge(final TIntObjectHashMap<TIntHashSet> result, TIntObjectHashMap<TIntHashSet> map2) {
map2.forEachEntry(new TIntObjectProcedure<TIntHashSet>() {
public boolean execute(int num, TIntHashSet defs) {
TIntHashSet defs2 = result.get(num);
if (defs2 == null) {
defs2 = new TIntHashSet(defs.toArray());
result.put(num, defs2);
} else {
defs2.addAll(defs.toArray());
}
return true;
}
});
}
public boolean eq(final TIntObjectHashMap<TIntHashSet> m1, final TIntObjectHashMap<TIntHashSet> m2) {
if (m1.size() != m2.size()) return false;
return m1.forEachEntry(new TIntObjectProcedure<TIntHashSet>() {
public boolean execute(int num, TIntHashSet defs1) {
final TIntHashSet defs2 = m2.get(num);
return defs2 != null && defs2.equals(defs1);
}
});
public boolean eq(final DefinitionMap m1, final DefinitionMap m2) {
return m1.eq(m2);
}
}
@@ -23,8 +23,6 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.*;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
@@ -40,6 +38,7 @@ import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstru
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;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.DefinitionMap;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsDfaInstance;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsSemilattice;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -137,19 +136,19 @@ public class TypeInferenceHelper {
@Nullable
private static DFAType getInferredType(@NotNull String varName, @NotNull Instruction instruction, @NotNull Instruction[] flow, @NotNull GrControlFlowOwner scope) {
final Pair<ReachingDefinitionsDfaInstance, List<TIntObjectHashMap<TIntHashSet>>> pair = getDefUseMaps(scope);
final Pair<ReachingDefinitionsDfaInstance, List<DefinitionMap>> pair = getDefUseMaps(scope);
List<TIntObjectHashMap<TIntHashSet>> dfaResult = pair.second;
List<DefinitionMap> dfaResult = pair.second;
if (dfaResult == null) return null;
final int varIndex = pair.first.getVarIndex(varName);
final TIntObjectHashMap<TIntHashSet> allDefs = dfaResult.get(instruction.num());
final TIntHashSet varDefs = allDefs.get(varIndex);
final DefinitionMap allDefs = dfaResult.get(instruction.num());
final int[] varDefs = allDefs.getDefinitions(varIndex);
if (varDefs == null) return null;
DFAType result = null;
for (int defIndex : varDefs.toArray()) {
for (int defIndex : varDefs) {
DFAType defType = getDefinitionType(flow[defIndex], flow, scope);
if (defType != null) {
@@ -163,27 +162,27 @@ public class TypeInferenceHelper {
return result;
}
private static Pair<ReachingDefinitionsDfaInstance, List<TIntObjectHashMap<TIntHashSet>>> getDefUseMaps(final GrControlFlowOwner scope) {
return CachedValuesManager.getManager(scope.getProject()).getCachedValue(scope, new CachedValueProvider<Pair<ReachingDefinitionsDfaInstance, List<TIntObjectHashMap<TIntHashSet>>>>() {
private static Pair<ReachingDefinitionsDfaInstance, List<DefinitionMap>> getDefUseMaps(final GrControlFlowOwner scope) {
return CachedValuesManager.getManager(scope.getProject()).getCachedValue(scope, new CachedValueProvider<Pair<ReachingDefinitionsDfaInstance, List<DefinitionMap>>>() {
@Override
public Result<Pair<ReachingDefinitionsDfaInstance, List<TIntObjectHashMap<TIntHashSet>>>> compute() {
public Result<Pair<ReachingDefinitionsDfaInstance, List<DefinitionMap>>> compute() {
final Instruction[] flow = scope.getControlFlow();
final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow) {
@Override
public void fun(TIntObjectHashMap<TIntHashSet> m, Instruction instruction) {
public void fun(DefinitionMap m, Instruction instruction) {
if (instruction instanceof InstanceOfInstruction) {
final InstanceOfInstruction instanceOfInstruction = (InstanceOfInstruction)instruction;
ReadWriteVariableInstruction i = instanceOfInstruction.getInstructionToMixin(flow);
if (i != null) {
int varIndex = getVarIndex(i.getVariableName());
if (varIndex >= 0) {
registerDef(m, instruction, varIndex);
m.registerDef(instruction, varIndex);
}
}
}
else if (instruction instanceof ArgumentInstruction) {
final int varIndex = getVarIndex(((ArgumentInstruction)instruction).getVariableName());
registerDef(m, instruction, varIndex);
m.registerDef(instruction, varIndex);
}
else {
super.fun(m, instruction);
@@ -191,8 +190,8 @@ public class TypeInferenceHelper {
}
};
final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice();
final DFAEngine<TIntObjectHashMap<TIntHashSet>> engine = new DFAEngine<TIntObjectHashMap<TIntHashSet>>(flow, dfaInstance, lattice);
final List<TIntObjectHashMap<TIntHashSet>> dfaResult = engine.performDFAWithTimeout();
final DFAEngine<DefinitionMap> engine = new DFAEngine<DefinitionMap>(flow, dfaInstance, lattice);
final List<DefinitionMap> dfaResult = engine.performDFAWithTimeout();
return Result.create(Pair.create(dfaInstance, dfaResult), PsiModificationTracker.MODIFICATION_COUNT);
}
});