Skip to content

Commit 20bfed4

Browse files
Merge pull request #132 from UniVE-SSV/type-inference-config
Configurability of the `AbstractState` used for type inference
2 parents 6fb519d + 120df1c commit 20bfed4

File tree

3 files changed

+150
-48
lines changed

3 files changed

+150
-48
lines changed

lisa/lisa-core/src/main/java/it/unive/lisa/LiSA.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,24 @@
22

33
import static it.unive.lisa.LiSAFactory.getDefaultFor;
44

5+
import it.unive.lisa.analysis.AbstractState;
6+
import it.unive.lisa.analysis.heap.HeapDomain;
7+
import it.unive.lisa.analysis.nonrelational.inference.InferenceSystem;
8+
import it.unive.lisa.analysis.types.InferredTypes;
59
import it.unive.lisa.checks.warnings.Warning;
610
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
711
import it.unive.lisa.interprocedural.callgraph.CallGraph;
812
import it.unive.lisa.logging.TimerLogger;
913
import it.unive.lisa.outputs.JsonReport;
1014
import it.unive.lisa.program.Program;
15+
import it.unive.lisa.type.Type;
16+
import it.unive.lisa.util.collections.externalSet.ExternalSet;
1117
import it.unive.lisa.util.file.FileManager;
1218
import java.io.IOException;
1319
import java.util.ArrayList;
1420
import java.util.Collection;
1521
import java.util.Collections;
22+
import java.util.function.Function;
1623
import org.apache.logging.log4j.LogManager;
1724
import org.apache.logging.log4j.Logger;
1825

@@ -90,7 +97,19 @@ public void run(Program program) throws AnalysisException {
9097
throw new AnalysisExecutionException("Unable to create default interprocedural analysis", e);
9198
}
9299

93-
LiSARunner runner = new LiSARunner(conf, interproc, callGraph, conf.getState());
100+
AbstractState inferenceState = conf.getTypeInferenceState();
101+
Function<AbstractState<?, ?, ?>, ExternalSet<Type>> typeExtractor = conf.getTypeExtractor();
102+
if (conf.isInferTypes() && inferenceState == null) {
103+
// these are used only if type inference is requested
104+
// we can skip configuration otherwise
105+
inferenceState = getDefaultFor(AbstractState.class, getDefaultFor(HeapDomain.class),
106+
new InferenceSystem<>(new InferredTypes()));
107+
typeExtractor = s -> ((InferenceSystem<InferredTypes>) s.getValueState()).getInferredValue()
108+
.getRuntimeTypes();
109+
}
110+
111+
LiSARunner runner = new LiSARunner(conf, interproc, callGraph, conf.getAbstractState(),
112+
inferenceState, typeExtractor);
94113

95114
try {
96115
warnings.addAll(TimerLogger.execSupplier(LOG, "Analysis time", () -> runner.run(program, fileManager)));

lisa/lisa-core/src/main/java/it/unive/lisa/LiSAConfiguration.java

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@
99
import it.unive.lisa.interprocedural.callgraph.CallGraph;
1010
import it.unive.lisa.program.cfg.CFG;
1111
import it.unive.lisa.program.cfg.statement.Statement;
12+
import it.unive.lisa.symbolic.SymbolicExpression;
13+
import it.unive.lisa.type.Type;
14+
import it.unive.lisa.util.collections.externalSet.ExternalSet;
1215
import it.unive.lisa.util.collections.workset.FIFOWorkingSet;
1316
import it.unive.lisa.util.collections.workset.WorkingSet;
1417
import java.nio.file.Paths;
1518
import java.util.Collection;
1619
import java.util.Collections;
20+
import java.util.Objects;
1721
import java.util.concurrent.ConcurrentHashMap;
22+
import java.util.function.Function;
1823

1924
/**
2025
* A holder for the configuration of a {@link LiSA} analysis.
@@ -50,10 +55,23 @@ public class LiSAConfiguration {
5055
*/
5156
private InterproceduralAnalysis<?, ?, ?> interproceduralAnalysis;
5257

58+
/**
59+
* The abstract state to run during the type inference
60+
*/
61+
private AbstractState<?, ?, ?> typeInferenceState;
62+
63+
/**
64+
* The function that, given a state computed during type inference, yield
65+
* the set of types computed for the processed {@link SymbolicExpression}.
66+
* This value will be considered as the set of runtime types for the
67+
* {@link Statement} that generated such expression.
68+
*/
69+
private Function<AbstractState<?, ?, ?>, ExternalSet<Type>> typeExtractor;
70+
5371
/**
5472
* The abstract state to run during the analysis
5573
*/
56-
private AbstractState<?, ?, ?> state;
74+
private AbstractState<?, ?, ?> abstractState;
5775

5876
/**
5977
* Whether or not type inference should be executed before the analysis
@@ -205,6 +223,31 @@ public LiSAConfiguration setInterproceduralAnalysis(InterproceduralAnalysis<?, ?
205223
return this;
206224
}
207225

226+
/**
227+
* Sets the {@link AbstractState} to use for the type inference. Any
228+
* existing value is overwritten.
229+
*
230+
* @param state the abstract state to use
231+
* @param typeExtractor the function that, given a state computed during
232+
* type inference, yield the set of types computed
233+
* for the processed {@link SymbolicExpression}.
234+
* This value will be considered as the set of
235+
* runtime types for the {@link Statement} that
236+
* generated such expression.
237+
*
238+
* @return the current (modified) configuration
239+
*/
240+
public LiSAConfiguration setTypeInferenceState(AbstractState<?, ?, ?> state,
241+
Function<AbstractState<?, ?, ?>, ExternalSet<Type>> typeExtractor) {
242+
this.typeInferenceState = state;
243+
this.typeExtractor = typeExtractor;
244+
245+
if (state != null)
246+
Objects.requireNonNull(typeExtractor, "A type extractor is needed for a custom type inference state");
247+
248+
return this;
249+
}
250+
208251
/**
209252
* Sets the {@link AbstractState} to use for the analysis. Any existing
210253
* value is overwritten.
@@ -214,7 +257,7 @@ public LiSAConfiguration setInterproceduralAnalysis(InterproceduralAnalysis<?, ?
214257
* @return the current (modified) configuration
215258
*/
216259
public LiSAConfiguration setAbstractState(AbstractState<?, ?, ?> state) {
217-
this.state = state;
260+
this.abstractState = state;
218261
return this;
219262
}
220263

@@ -377,14 +420,37 @@ public CallGraph getCallGraph() {
377420
return interproceduralAnalysis;
378421
}
379422

423+
/**
424+
* Yields the {@link AbstractState} for the type inference. Might be
425+
* {@code null} if none was set. If this is not {@code null}, then
426+
* {@link #getTypeExtractor()} is not {@code null} either.
427+
*
428+
* @return the abstract state for the type inference
429+
*/
430+
public AbstractState<?, ?, ?> getTypeInferenceState() {
431+
return typeInferenceState;
432+
}
433+
434+
/**
435+
* Yields the function that, given a state computed during type inference,
436+
* yield the set of types computed for the processed
437+
* {@link SymbolicExpression}. This value will be considered as the set of
438+
* runtime types for the {@link Statement} that generated such expression.
439+
*
440+
* @return the function that is used to extract types
441+
*/
442+
public Function<AbstractState<?, ?, ?>, ExternalSet<Type>> getTypeExtractor() {
443+
return typeExtractor;
444+
}
445+
380446
/**
381447
* Yields the {@link AbstractState} for the analysis. Might be {@code null}
382448
* if none was set.
383449
*
384450
* @return the abstract state for the analysis
385451
*/
386-
public AbstractState<?, ?, ?> getState() {
387-
return state;
452+
public AbstractState<?, ?, ?> getAbstractState() {
453+
return abstractState;
388454
}
389455

390456
/**
@@ -500,7 +566,9 @@ public int hashCode() {
500566
result = prime * result + ((interproceduralAnalysis == null) ? 0 : interproceduralAnalysis.hashCode());
501567
result = prime * result + (jsonOutput ? 1231 : 1237);
502568
result = prime * result + ((semanticChecks == null) ? 0 : semanticChecks.hashCode());
503-
result = prime * result + ((state == null) ? 0 : state.hashCode());
569+
result = prime * result + ((typeInferenceState == null) ? 0 : typeInferenceState.hashCode());
570+
result = prime * result + ((typeExtractor == null) ? 0 : typeExtractor.hashCode());
571+
result = prime * result + ((abstractState == null) ? 0 : abstractState.hashCode());
504572
result = prime * result + ((syntacticChecks == null) ? 0 : syntacticChecks.hashCode());
505573
result = prime * result + wideningThreshold;
506574
result = prime * result + ((workdir == null) ? 0 : workdir.hashCode());
@@ -546,10 +614,20 @@ public boolean equals(Object obj) {
546614
return false;
547615
} else if (!semanticChecks.equals(other.semanticChecks))
548616
return false;
549-
if (state == null) {
550-
if (other.state != null)
617+
if (typeInferenceState == null) {
618+
if (other.typeInferenceState != null)
619+
return false;
620+
} else if (!typeInferenceState.equals(other.typeInferenceState))
621+
return false;
622+
if (typeExtractor == null) {
623+
if (other.typeExtractor != null)
624+
return false;
625+
} else if (!typeExtractor.equals(other.typeExtractor))
626+
return false;
627+
if (abstractState == null) {
628+
if (other.abstractState != null)
551629
return false;
552-
} else if (!state.equals(other.state))
630+
} else if (!abstractState.equals(other.abstractState))
553631
return false;
554632
if (syntacticChecks == null) {
555633
if (other.syntacticChecks != null)

lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
import it.unive.lisa.analysis.AnalysisState;
77
import it.unive.lisa.analysis.CFGWithAnalysisResults;
88
import it.unive.lisa.analysis.SemanticException;
9-
import it.unive.lisa.analysis.SimpleAbstractState;
109
import it.unive.lisa.analysis.heap.HeapDomain;
11-
import it.unive.lisa.analysis.nonrelational.inference.InferenceSystem;
12-
import it.unive.lisa.analysis.types.InferredTypes;
1310
import it.unive.lisa.analysis.value.ValueDomain;
1411
import it.unive.lisa.caches.Caches;
1512
import it.unive.lisa.checks.ChecksExecutor;
@@ -48,16 +45,25 @@
4845
*
4946
* @author <a href="mailto:[email protected]">Luca Negrini</a>
5047
*
51-
* @param <A> the type of {@link AbstractState} contained into the analysis
52-
* state that will be used in the fixpoints
53-
* @param <H> the type of {@link HeapDomain} contained into the computed
54-
* abstract state that will be used in the fixpoints
55-
* @param <V> the type of {@link ValueDomain} contained into the computed
56-
* abstract state that will be used in the fixpoints
48+
* @param <A> the type of {@link AbstractState} contained into the analysis
49+
* state that will be used in the analysis fixpoint
50+
* @param <H> the type of {@link HeapDomain} contained into the abstract state
51+
* that will be used in the analysis fixpoint
52+
* @param <V> the type of {@link ValueDomain} contained into the abstract state
53+
* that will be used in the analysis fixpoint
54+
* @param <T> the type of {@link AbstractState} contained into the analysis
55+
* state that will be used in the type inference fixpoint
56+
* @param <HT> the type of {@link HeapDomain} contained into the abstract state
57+
* that will be used in the type inference fixpoint
58+
* @param <VT> the type of {@link ValueDomain} contained into the abstract state
59+
* that will be used in the type inference fixpoint
5760
*/
5861
public class LiSARunner<A extends AbstractState<A, H, V>,
5962
H extends HeapDomain<H>,
60-
V extends ValueDomain<V>> {
63+
V extends ValueDomain<V>,
64+
T extends AbstractState<T, HT, VT>,
65+
HT extends HeapDomain<HT>,
66+
VT extends ValueDomain<VT>> {
6167

6268
private static final String FIXPOINT_EXCEPTION_MESSAGE = "Exception during fixpoint computation";
6369

@@ -71,20 +77,30 @@ public class LiSARunner<A extends AbstractState<A, H, V>,
7177

7278
private final A state;
7379

80+
private final T typeState;
81+
82+
private final Function<T, ExternalSet<Type>> typeExtractor;
83+
7484
/**
7585
* Builds the runner.
7686
*
77-
* @param conf the configuration of the analysis
78-
* @param interproc the interprocedural analysis to use
79-
* @param callGraph the call graph to use
80-
* @param state the abstract state to use
87+
* @param conf the configuration of the analysis
88+
* @param interproc the interprocedural analysis to use
89+
* @param callGraph the call graph to use
90+
* @param state the abstract state to use for the analysis
91+
* @param typeState the abstract state to use for type inference
92+
* @param typeExtractor the abstract state to use the function that can
93+
* extract runtime types from {@code typeState}
94+
* instances
8195
*/
8296
LiSARunner(LiSAConfiguration conf, InterproceduralAnalysis<A, H, V> interproc, CallGraph callGraph,
83-
A state) {
97+
A state, T typeState, Function<T, ExternalSet<Type>> typeExtractor) {
8498
this.conf = conf;
8599
this.interproc = interproc;
86100
this.callGraph = callGraph;
87101
this.state = state;
102+
this.typeState = typeState;
103+
this.typeExtractor = typeExtractor;
88104
}
89105

90106
/**
@@ -173,14 +189,9 @@ private void analyze(Collection<CFG> allCFGs, FileManager fileManager) {
173189

174190
@SuppressWarnings("unchecked")
175191
private void inferTypes(FileManager fileManager, Program program, Collection<CFG> allCFGs) {
176-
SimpleAbstractState<H, InferenceSystem<InferredTypes>> typesState;
177-
InterproceduralAnalysis<SimpleAbstractState<H, InferenceSystem<InferredTypes>>, H,
178-
InferenceSystem<InferredTypes>> typesInterproc;
192+
T typesState = this.typeState.top();
193+
InterproceduralAnalysis<T, HT, VT> typesInterproc;
179194
try {
180-
// type inference is executed with the simplest abstract state
181-
InferenceSystem<InferredTypes> types = new InferenceSystem<>(new InferredTypes());
182-
HeapDomain<?> heap = state == null ? LiSAFactory.getDefaultFor(HeapDomain.class) : state.getHeapState();
183-
typesState = getInstance(SimpleAbstractState.class, heap, types).top();
184195
typesInterproc = getInstance(interproc.getClass());
185196
typesInterproc.init(program, callGraph);
186197
} catch (AnalysisSetupException | InterproceduralAnalysisException e) {
@@ -202,18 +213,15 @@ private void inferTypes(FileManager fileManager, Program program, Collection<CFG
202213
? "Dumping type analysis and propagating it to cfgs"
203214
: "Propagating type information to cfgs";
204215
for (CFG cfg : IterationLogger.iterate(LOG, allCFGs, message, "cfgs")) {
205-
Collection<CFGWithAnalysisResults<SimpleAbstractState<H, InferenceSystem<InferredTypes>>, H,
206-
InferenceSystem<InferredTypes>>> results = typesInterproc.getAnalysisResultsOf(cfg);
216+
Collection<CFGWithAnalysisResults<T, HT, VT>> results = typesInterproc.getAnalysisResultsOf(cfg);
207217
if (results.isEmpty()) {
208218
LOG.warn("No type information computed for '{}': it is unreachable", cfg);
209219
continue;
210220
}
211221

212-
CFGWithAnalysisResults<SimpleAbstractState<H, InferenceSystem<InferredTypes>>, H,
213-
InferenceSystem<InferredTypes>> result = null;
222+
CFGWithAnalysisResults<T, HT, VT> result = null;
214223
try {
215-
for (CFGWithAnalysisResults<SimpleAbstractState<H, InferenceSystem<InferredTypes>>, H,
216-
InferenceSystem<InferredTypes>> res : results)
224+
for (CFGWithAnalysisResults<T, HT, VT> res : results)
217225
if (result == null)
218226
result = res;
219227
else
@@ -222,36 +230,33 @@ private void inferTypes(FileManager fileManager, Program program, Collection<CFG
222230
throw new AnalysisExecutionException("Unable to compute type information for " + cfg, e);
223231
}
224232

225-
cfg.accept(new TypesPropagator<SimpleAbstractState<H, InferenceSystem<InferredTypes>>, H>(), result);
233+
cfg.accept(new TypesPropagator(), result);
226234

227-
CFGWithAnalysisResults<SimpleAbstractState<H, InferenceSystem<InferredTypes>>, H,
228-
InferenceSystem<InferredTypes>> r = result;
235+
CFGWithAnalysisResults<T, HT, VT> r = result;
229236
if (conf.isDumpTypeInference())
230237
dumpCFG(fileManager, "typing___", r, st -> r.getAnalysisStateAfter(st).toString());
231238
}
232239
}
233240

234-
private static class TypesPropagator<A extends AbstractState<A, H, InferenceSystem<InferredTypes>>,
235-
H extends HeapDomain<H>>
241+
private class TypesPropagator
236242
implements
237-
GraphVisitor<CFG, Statement, Edge, CFGWithAnalysisResults<A, H, InferenceSystem<InferredTypes>>> {
243+
GraphVisitor<CFG, Statement, Edge, CFGWithAnalysisResults<T, HT, VT>> {
238244

239245
@Override
240-
public boolean visit(CFGWithAnalysisResults<A, H, InferenceSystem<InferredTypes>> tool, CFG graph) {
246+
public boolean visit(CFGWithAnalysisResults<T, HT, VT> tool, CFG graph) {
241247
return true;
242248
}
243249

244250
@Override
245-
public boolean visit(CFGWithAnalysisResults<A, H, InferenceSystem<InferredTypes>> tool, CFG graph, Edge edge) {
251+
public boolean visit(CFGWithAnalysisResults<T, HT, VT> tool, CFG graph, Edge edge) {
246252
return true;
247253
}
248254

249255
@Override
250-
public boolean visit(CFGWithAnalysisResults<A, H, InferenceSystem<InferredTypes>> tool, CFG graph,
256+
public boolean visit(CFGWithAnalysisResults<T, HT, VT> tool, CFG graph,
251257
Statement node) {
252258
if (tool != null && node instanceof Expression)
253-
((Expression) node).setRuntimeTypes(tool.getAnalysisStateAfter(node).getState().getValueState()
254-
.getInferredValue().getRuntimeTypes());
259+
((Expression) node).setRuntimeTypes(typeExtractor.apply(tool.getAnalysisStateAfter(node).getState()));
255260
return true;
256261
}
257262
}

0 commit comments

Comments
 (0)