From 3860ac7ea2f6bc1255b4538aa4af0cd307c3529a Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Tue, 11 Jan 2022 11:26:21 +0100 Subject: [PATCH] MULTIPLE COMMITS 4 EXPERIMENTS ObjectTree: make fields into arrays of Strings. - The previous approach used a single string, with fields separated by dots. - The inclusion of packages in class names thwarts the previous approach. --- .../java/es/upv/mist/slicing/cli/Slicer.java | 39 ++-- .../slicing/arcs/pdg/FlowDependencyArc.java | 5 + .../es/upv/mist/slicing/graphs/CallGraph.java | 2 +- .../upv/mist/slicing/graphs/ClassGraph.java | 25 ++- .../es/upv/mist/slicing/graphs/cfg/CFG.java | 7 +- .../mist/slicing/graphs/cfg/CFGBuilder.java | 2 + .../mist/slicing/graphs/jsysdg/JSysCFG.java | 10 +- .../mist/slicing/graphs/jsysdg/JSysDG.java | 11 + .../mist/slicing/graphs/jsysdg/JSysPDG.java | 33 +-- .../es/upv/mist/slicing/graphs/sdg/SDG.java | 26 +++ .../es/upv/mist/slicing/nodes/ObjectTree.java | 142 ++++++++++++- .../mist/slicing/nodes/VariableAction.java | 31 ++- .../mist/slicing/nodes/VariableVisitor.java | 190 ++++++++---------- .../java/es/upv/mist/slicing/utils/Utils.java | 20 ++ sdg-core/src/main/java/module-info.java | 1 + 15 files changed, 374 insertions(+), 170 deletions(-) diff --git a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java index 1303a59..f79dfeb 100644 --- a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java +++ b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java @@ -1,17 +1,12 @@ package es.upv.mist.slicing.cli; -import com.github.javaparser.JavaParser; -import com.github.javaparser.ParseResult; -import com.github.javaparser.ParserConfiguration; import com.github.javaparser.Problem; +import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.comments.BlockComment; import com.github.javaparser.ast.nodeTypes.NodeWithName; -import com.github.javaparser.symbolsolver.JavaSymbolSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; import es.upv.mist.slicing.graphs.augmented.ASDG; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; @@ -21,12 +16,15 @@ import es.upv.mist.slicing.slicing.FileLineSlicingCriterion; import es.upv.mist.slicing.slicing.Slice; import es.upv.mist.slicing.slicing.SlicingCriterion; import es.upv.mist.slicing.utils.NodeHashSet; +import es.upv.mist.slicing.utils.StaticTypeSolver; import org.apache.commons.cli.*; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -175,24 +173,21 @@ public class Slicer { public void slice() throws ParseException { // Configure JavaParser - ParserConfiguration parserConfig = new ParserConfiguration(); - parserConfig.setAttributeComments(false); - CombinedTypeSolver cts = new CombinedTypeSolver(); - cts.add(new ReflectionTypeSolver(true)); + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Configuring JavaParser"); + StaticTypeSolver.addTypeSolverJRE(); for (File directory : dirIncludeSet) - if (directory.isDirectory()) - cts.add(new JavaParserTypeSolver(directory)); - parserConfig.setSymbolResolver(new JavaSymbolSolver(cts)); - JavaParser parser = new JavaParser(parserConfig); + StaticTypeSolver.addTypeSolver(new JavaParserTypeSolver(directory)); + StaticJavaParser.getConfiguration().setAttributeComments(false); // Build the SDG + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Parsing files"); Set units = new NodeHashSet<>(); List problems = new LinkedList<>(); boolean scFileFound = false; for (File file : (Iterable) findAllJavaFiles(dirIncludeSet)::iterator) - scFileFound |= parse(parser, file, units, problems); + scFileFound |= parse(file, units, problems); if (!scFileFound) - parse(parser, scFile, units, problems); + parse(scFile, units, problems); if (!problems.isEmpty()) { for (Problem p : problems) System.out.println(" * " + p.getVerboseMessage()); @@ -209,16 +204,20 @@ public class Slicer { default: throw new IllegalArgumentException("Unknown type of graph. Available graphs are SDG, ASDG, PSDG, ESSDG"); } + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Building the SDG"); sdg.build(new NodeList<>(units)); // Slice the SDG + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Searching for criterion and slicing"); SlicingCriterion sc = new FileLineSlicingCriterion(scFile, scLine, scVar); Slice slice = sdg.slice(sc); // Convert the slice to code and output the result to `outputDir` + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Printing slice to files"); for (CompilationUnit cu : slice.toAst()) { if (cu.getStorage().isEmpty()) throw new IllegalStateException("A synthetic CompilationUnit was discovered, with no file associated to it."); + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Printing slice for " + cu.getStorage().get().getFileName()); String packagePath = cu.getPackageDeclaration().map(NodeWithName::getNameAsString).orElse("").replace(".", "/"); File packageDir = new File(outputDir, packagePath); packageDir.mkdirs(); @@ -232,13 +231,9 @@ public class Slicer { } } - private boolean parse(JavaParser parser, File file, Set units, List problems) { + private boolean parse(File file, Set units, List problems) { try { - ParseResult result = parser.parse(file); - if (result.isSuccessful()) - result.ifSuccessful(units::add); - else - problems.addAll(result.getProblems()); + units.add(StaticJavaParser.parse(file)); } catch (FileNotFoundException e) { problems.add(new Problem(e.getLocalizedMessage(), null, e)); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java index e56b5ae..126d886 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java @@ -1,6 +1,7 @@ package es.upv.mist.slicing.arcs.pdg; import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.utils.Utils; /** Represents a data dependency in an object-oriented SDG or PDG. */ public class FlowDependencyArc extends Arc { @@ -12,4 +13,8 @@ public class FlowDependencyArc extends Arc { super(variable); } + public FlowDependencyArc(String[] member) { + super(Utils.arrayJoin(member, ".")); + } + } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java index e061adb..8d8062e 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java @@ -271,7 +271,7 @@ public class CallGraph extends DirectedPseudograph, ClassGraph.ClassArc> implements Buildable> { private static ClassGraph instance = null; @@ -191,10 +194,10 @@ public class ClassGraph extends DirectedPseudograph, ClassG protected ObjectTree generateObjectTreeFor(Vertex> classVertex) { if (classVertex == null) return new ObjectTree(); - return generatePolyObjectTreeFor(classVertex, new ObjectTree(), ObjectTree.ROOT_NAME, 0); + return generatePolyObjectTreeFor(classVertex, new ObjectTree(), ROOT_NODE, 0); } - protected ObjectTree generatePolyObjectTreeFor(Vertex> classVertex, ObjectTree tree, String level, int depth) { + protected ObjectTree generatePolyObjectTreeFor(Vertex> classVertex, ObjectTree tree, String[] level, int depth) { if (depth >= StaticConfig.K_LIMIT) return tree; Set> types = subclassesOf(classVertex); @@ -205,19 +208,25 @@ public class ClassGraph extends DirectedPseudograph, ClassG Vertex> subclassVertex = classDeclarationMap.get(mapKey(type)); if (!findAllFieldsOf(subclassVertex).isEmpty()) { ObjectTree newType = tree.addType(ASTUtils.resolvedTypeDeclarationToResolvedType(type.resolve()), level); - generateObjectTreeFor(subclassVertex, tree, level + '.' + newType.getMemberNode().getLabel(), depth + 1); + String[] newLevel = new String[level.length + 1]; + System.arraycopy(level, 0, newLevel, 0, level.length); + newLevel[level.length] = newType.getMemberNode().getLabel(); + generateObjectTreeFor(subclassVertex, tree, newLevel, depth + 1); } } } return tree; } - protected void generateObjectTreeFor(Vertex> classVertex, ObjectTree tree, String level, int depth) { + protected void generateObjectTreeFor(Vertex> classVertex, ObjectTree tree, String[] level, int depth) { Map>> classFields = findAllFieldsOf(classVertex); for (var entry : classFields.entrySet()) { - tree.addField(level + '.' + entry.getKey()); + String[] newLevel = new String[level.length + 1]; + System.arraycopy(level, 0, newLevel, 0, level.length); + newLevel[level.length] = entry.getKey(); + tree.addField(newLevel); if (entry.getValue() != null) - generatePolyObjectTreeFor(entry.getValue(), tree, level + '.' + entry.getKey(), depth); + generatePolyObjectTreeFor(entry.getValue(), tree, newLevel, depth); } } @@ -441,7 +450,9 @@ public class ClassGraph extends DirectedPseudograph, ClassG @Override public int hashCode() { - return Objects.hash(declaration, declaration.getRange()); + if (declaration instanceof NodeWithName) + return Objects.hash(((NodeWithName) declaration).getNameAsString(), declaration.getRange()); + return Objects.hash(String.valueOf(declaration), declaration.getRange()); } @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java index 347fbea..3516912 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java @@ -98,14 +98,11 @@ public class CFG extends GraphWithRootNode> { stream = stream.takeWhile(va -> va != var); List list = stream.filter(var::matches).filter(filter).collect(Collectors.toList()); if (!list.isEmpty()) { - boolean found = false; - for (int i = list.size() - 1; i >= 0 && !found; i--) { + for (int i = list.size() - 1; i >= 0; i--) { result.add(list.get(i)); if (!list.get(i).isOptional()) - found = true; + return true; } - if (found) - return true; } // Not found: traverse backwards! diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java index f34565a..029f58b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java @@ -15,6 +15,7 @@ import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.MethodExitNode; import es.upv.mist.slicing.nodes.io.OutputNode; import es.upv.mist.slicing.utils.ASTUtils; +import es.upv.mist.slicing.utils.Logger; import java.util.*; @@ -80,6 +81,7 @@ public class CFGBuilder extends VoidVisitorAdapter { * @see #connectTo(GraphNode) */ protected GraphNode connectTo(T n, String text) { + Logger.log("Connecting new node: " + text); GraphNode dest = graph.addVertex(text, n); connectTo(dest); return dest; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index ce426ec..5be9828 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -21,6 +21,7 @@ import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.MethodExitNode; import es.upv.mist.slicing.utils.ASTUtils; +import es.upv.mist.slicing.utils.Logger; import es.upv.mist.slicing.utils.NodeHashSet; import es.upv.mist.slicing.utils.NodeNotFoundException; @@ -65,7 +66,7 @@ public class JSysCFG extends ESCFG { /** Given a usage of an object member, find the last definitions of that member. * This method returns a list of variable actions, where the caller can find the member. */ - public List findLastDefinitionOfObjectMember(VariableAction usage, String member) { + public List findLastDefinitionOfObjectMember(VariableAction usage, String[] member) { return findLastVarActionsFrom(usage, def -> def.isDefinition() && def.hasTreeMember(member)); } @@ -111,7 +112,7 @@ public class JSysCFG extends ESCFG { /** Given an action that defines a member, locates the previous total definition that gave * it value. */ - public List findLastTotalDefinitionOf(VariableAction action, String member) { + public List findLastTotalDefinitionOf(VariableAction action, String[] member) { return findLastVarActionsFrom(action, def -> (def.isDeclaration() && def.hasTreeMember(member)) || (def.isDefinition() && def.asDefinition().isTotallyDefinedMember(member))); @@ -120,7 +121,7 @@ public class JSysCFG extends ESCFG { /** Given a definition of a given member, locate all definitions of the same object until a definition * containing the given member is found (not including that last one). If the member is found in the * given definition, it will return a list with only the given definition. */ - public List findNextObjectDefinitionsFor(VariableAction definition, String member) { + public List findNextObjectDefinitionsFor(VariableAction definition, String[] member) { if (!this.containsVertex(definition.getGraphNode())) throw new NodeNotFoundException(definition.getGraphNode(), this); if (definition.hasTreeMember(member)) @@ -135,7 +136,7 @@ public class JSysCFG extends ESCFG { * the given argument. This search stops after finding a matching action in each branch. */ protected boolean findNextVarActionsFor(Set> visited, List result, GraphNode currentNode, VariableAction var, - Predicate filter, String memberName) { + Predicate filter, String[] memberName) { // Base case if (visited.contains(currentNode)) return true; @@ -181,6 +182,7 @@ public class JSysCFG extends ESCFG { @Override protected GraphNode connectTo(T n, String text) { + Logger.log("Connecting new node: " + text); GraphNode dest; dest = new GraphNode<>(text, n); if (methodInsertedInstructions.contains(n) || diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java index 0fd0819..c12ae78 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java @@ -21,6 +21,10 @@ import es.upv.mist.slicing.slicing.JSysDGSlicingAlgorithm; import es.upv.mist.slicing.slicing.SlicingAlgorithm; import es.upv.mist.slicing.utils.NodeHashSet; +import java.util.NoSuchElementException; +import java.util.logging.Level; +import java.util.logging.Logger; + public class JSysDG extends ESSDG { @Override protected SlicingAlgorithm createSlicingAlgorithm() { @@ -71,6 +75,13 @@ public class JSysDG extends ESSDG { @Override protected void buildCFG(CallableDeclaration declaration, CFG cfg) { + String origin; + try { + origin = " from " + declaration.findCompilationUnit().get().getStorage().get().getFileName() + "."; + } catch (NoSuchElementException ignore) { + origin = " (location unknown, may be synthetic)."; + } + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Building CFG for method " + declaration.getSignature() + origin); ((JSysCFG) cfg).build(declaration, newlyInsertedConstructors, ClassGraph.getInstance()); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index bb75c9a..fa1df02 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -18,6 +18,7 @@ import java.util.LinkedList; import java.util.List; import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NAME; +import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NODE; public class JSysPDG extends ESPDG { public JSysPDG() { @@ -43,8 +44,8 @@ public class JSysPDG extends ESPDG { } // definicion de miembro --object-flow--> definicion de raiz - protected void addObjectFlowDependencyArc(VariableAction nextDefinitionRoot, String memberDefined, VariableAction definition) { - MemberNode defMember = definition.getObjectTree().getNodeFor(memberDefined); + protected void addObjectFlowDependencyArc(VariableAction nextDefinitionRoot, String[] memberDefined, VariableAction definition) { + MemberNode defMember = definition.getObjectTree().getNodeFor(true, memberDefined); addEdge(defMember, graphNodeOf(nextDefinitionRoot), new ObjectFlowDependencyArc()); } @@ -59,22 +60,22 @@ public class JSysPDG extends ESPDG { } // definicion de miembro --flow--> uso de miembro - protected void addFlowDependencyArc(VariableAction definition, VariableAction usage, String objMember) { - GraphNode defMember = definition.getObjectTree().getNodeFor(objMember); - GraphNode useMember = usage.getObjectTree().getNodeFor(objMember); + protected void addFlowDependencyArc(VariableAction definition, VariableAction usage, String[] objMember) { + GraphNode defMember = definition.getObjectTree().getNodeFor(true, objMember); + GraphNode useMember = usage.getObjectTree().getNodeFor(true, objMember); addEdge(defMember, useMember, new FlowDependencyArc(objMember)); } - protected void addValueDependencyArc(VariableAction usage, String member, GraphNode statement) { - addEdge(usage.getObjectTree().getNodeFor(member), statement, new FlowDependencyArc(member)); + protected void addValueDependencyArc(VariableAction usage, GraphNode statement) { + addEdge(usage.getObjectTree().getMemberNode(), statement, new FlowDependencyArc(ROOT_NAME)); } - protected void addTotalDefinitionDependencyArc(VariableAction totalDefinition, VariableAction target, String member) { - if (member.equals(ROOT_NAME)) + protected void addTotalDefinitionDependencyArc(VariableAction totalDefinition, VariableAction target, String[] member) { + if (member.length == 1 && member[0].equals(ROOT_NAME)) addEdge(graphNodeOf(totalDefinition), graphNodeOf(target), new TotalDefinitionDependenceArc()); else - addEdge(totalDefinition.getObjectTree().getNodeFor(member), - target.getObjectTree().getNodeFor(member), + addEdge(totalDefinition.getObjectTree().getNodeFor(true, member), + target.getObjectTree().getNodeFor(true, member), new TotalDefinitionDependenceArc()); } @@ -122,10 +123,10 @@ public class JSysPDG extends ESPDG { * non-synthetic definitions. Connects each member to its previous total definition. */ private void buildTotalDefinitionDependence(JSysCFG jSysCFG, VariableAction varAct) { if (!varAct.isPrimitive() && (varAct.isUsage() || (varAct.isDefinition() && !varAct.isSynthetic()))) { - jSysCFG.findLastTotalDefinitionOf(varAct, ROOT_NAME).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, ROOT_NAME)); + jSysCFG.findLastTotalDefinitionOf(varAct, ROOT_NODE).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, ROOT_NODE)); if (!varAct.hasObjectTree()) return; - for (String member : varAct.getObjectTree().nameIterable()) + for (String[] member : varAct.getObjectTree().nameAsArrayIterable()) jSysCFG.findLastTotalDefinitionOf(varAct, member).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, member)); } } @@ -139,7 +140,7 @@ public class JSysPDG extends ESPDG { jSysCFG.findLastDefinitionOfObjectRoot(varAct).forEach(def -> addObjectFlowDependencyArc(def, varAct)); if (!varAct.hasObjectTree()) return; - for (String member : varAct.getObjectTree().nameIterable()) + for (String[] member : varAct.getObjectTree().nameAsArrayIterable()) jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); } } @@ -152,7 +153,7 @@ public class JSysPDG extends ESPDG { // Object flow definition --> definition if (varAct.isPrimitive() || !varAct.hasObjectTree()) return; - for (String member : varAct.getObjectTree().nameIterable()) + for (String[] member : varAct.getObjectTree().nameAsArrayIterable()) jSysCFG.findNextObjectDefinitionsFor(varAct, member).forEach(def -> addObjectFlowDependencyArc(varAct, member, def)); } @@ -248,7 +249,7 @@ public class JSysPDG extends ESPDG { if (action.isDefinition() && action.hasObjectTree() && action.getName().equals(ESCFG.ACTIVE_EXCEPTION_VARIABLE)) - addValueDependencyArc(action, ROOT_NAME, node); + addValueDependencyArc(action, node); } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java index 101838b..c39ff32 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java @@ -3,6 +3,7 @@ package es.upv.mist.slicing.graphs.sdg; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; @@ -27,6 +28,8 @@ import java.util.Collection; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; /** * The System Dependence Graph represents the statements of a program in @@ -121,13 +124,21 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList) { // See creation strategy at http://kaz2.dsic.upv.es:3000/Fzg46cQvT1GzHQG9hFnP1g#Using-data-flow-in-the-SDG // This ordering cannot be altered, as each step requires elements from the previous one. + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Creating class graph"); createClassGraph(nodeList); // 0 + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Building CFGs"); buildCFGs(nodeList); // 1 + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Creating call graph"); createCallGraph(nodeList); // 2 + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Performing dataflow analysis"); dataFlowAnalysis(); // 3 + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Building PDGs and copying nodes to SDG"); buildAndCopyPDGs(); // 4 + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Connecting call sites to method declarations"); connectCalls(); // 5 + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Computing summary arcs"); createSummaryArcs(); // 6 + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "SDG completed!"); } /** Build a CFG per declaration found in the list of compilation units. */ @@ -135,6 +146,10 @@ public class SDG extends Graph implements Sliceable, Buildable() { @Override public void visit(MethodDeclaration n, Void arg) { + boolean isInInterface = n.findAncestor(ClassOrInterfaceDeclaration.class) + .map(ClassOrInterfaceDeclaration::isInterface).orElse(false); + if (n.isAbstract() || isInInterface) + return; // Allow abstract methods CFG cfg = createCFG(); buildCFG(n, cfg); cfgMap.put(n, cfg); @@ -142,6 +157,10 @@ public class SDG extends Graph implements Sliceable, Buildable declaration, CFG cfg) { + String origin; + try { + origin = " from " + declaration.findCompilationUnit().get().getStorage().get().getFileName() + "."; + } catch (NoSuchElementException ignore) { + origin = " (location unknown, may be synthetic)."; + } + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Building CFG for method " + declaration.getSignature() + origin); cfg.build(declaration); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java index dcd21dc..e52427d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java @@ -12,6 +12,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; +import static es.upv.mist.slicing.utils.Utils.arrayJoin; + /** * A tree data structure that mimics the tree found in an object's fields. * Each tree contains a MemberNode that represents its, including a name. @@ -25,6 +27,8 @@ import java.util.stream.Stream; public class ObjectTree implements Cloneable { /** The default name of a tree's root. */ public static final String ROOT_NAME = "-root-"; + /** The representation of the root node in member form. */ + public static final String[] ROOT_NODE = new String[] { ROOT_NAME }; /** Regex pattern to split the root from the fields of a field access expression. */ private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|(?(-?))([_0-9A-Za-z]+\\k)+)(\\.(?.+))?$"); @@ -126,14 +130,14 @@ public class ObjectTree implements Cloneable { return childrenMap.computeIfAbsent(rt.describe(), n -> new ObjectTree(rt, this)); } - public ObjectTree addType(ResolvedType rt, String prefix) { - String members = removeRoot(prefix); + public ObjectTree addType(ResolvedType rt, String[] prefix) { + String[] members = removeRoot(prefix); Collection trees = findObjectTreeOfPolyMember(members); if (trees.size() > 1) throw new IllegalArgumentException("This method accepts only prefixes with all the necessary types"); for (ObjectTree tree : trees) return tree.addType(rt); - throw new IllegalArgumentException("Could not locate any tree for the given prefix " + prefix); + throw new IllegalArgumentException("Could not locate any tree for the given prefix " + arrayJoin(prefix, ".")); } /** @@ -149,6 +153,11 @@ public class ObjectTree implements Cloneable { return addNonRootField(members); } + public ObjectTree addField(String[] fieldName) { + String[] members = removeRoot(fieldName); + return addNonRootField(members, 0); + } + /** Insert a field in the current level of object tree. The field should be a variable name, * and not contain dots or be blank. */ public ObjectTree addImmediateField(String fieldName) { @@ -177,6 +186,15 @@ public class ObjectTree implements Cloneable { } } + private ObjectTree addNonRootField(String[] members, int index) { + assert index < members.length; + ObjectTree tree = childrenMap.computeIfAbsent(members[index], f -> new ObjectTree(f, this)); + if (members.length - 1 == index) + return tree; + else + return tree.addNonRootField(members, index + 1); + } + /** Copies the structure of another object tree into this object tree. * All elements inserted in the current tree are a copy of the argument's children and members. */ public void addAll(ObjectTree tree) { @@ -252,12 +270,41 @@ public class ObjectTree implements Cloneable { return result; } + Collection findObjectTreeOfPolyMember(String[] member) { + Collection result = List.of(this); + for (String field : member) { + result = result.stream().flatMap(res -> { + ObjectTree ot = res.childrenMap.get(field); + if (ot == null && res.childrenMap.size() > 0) { + Collection collection = new LinkedList<>(); + for (ObjectTree child : childrenMap.values()) { + if (!(child.getMemberNode() instanceof PolyMemberNode) || !child.childrenMap.containsKey(field)) + throw new IllegalArgumentException("Could not locate member in object tree"); + collection.add(child.childrenMap.get(field)); + } + return collection.stream(); + } else if (ot == null) { + throw new IllegalArgumentException("Could not locate member in object tree"); + } else { + return Stream.of(ot); + } + }).collect(Collectors.toList()); + } + return result; + } + /** Whether this object tree contains the given member. The argument should contain the root variable name. */ public boolean hasMember(String member) { String field = removeRoot(member); return hasNonRootMember(field, false); } + public boolean hasMember(String[] member) { + if (member.length < 2) + return true; + return hasMemberIndexed(member, 1, false); + } + /** Whether this object tree contains the given member. The argument may omit typing * information (i.e., 'a.x' will find 'a.A.x', where A is a polymorphic node). */ public boolean hasPolyMember(String member) { @@ -265,6 +312,10 @@ public class ObjectTree implements Cloneable { return hasNonRootMember(field, true); } + public boolean hasPolyMember(String[] member) { + return hasMemberIndexed(member, 1, true); + } + /** Similar to hasMember, but valid at any level of the tree and the argument should not contain * the root variable's name. * @see #hasMember(String) */ @@ -287,12 +338,31 @@ public class ObjectTree implements Cloneable { } } + private boolean hasMemberIndexed(String[] member, int index, boolean polymorphic) { + String first = member[index]; + if (polymorphic && !childrenMap.containsKey(first) && !childrenMap.isEmpty()) + return childrenMap.values().stream() + .filter(ot -> ot.getMemberNode() instanceof PolyMemberNode) + .anyMatch(ot -> ot.hasMemberIndexed(member, index, polymorphic)); + if (index + 1 < member.length) + return childrenMap.containsKey(first) && childrenMap.get(first).hasMemberIndexed(member, index + 1, polymorphic); + else return childrenMap.containsKey(first); + } + + public MemberNode getRootNode() { + return memberNode; + } + /** Obtain the member node that corresponds to the given field name (with root). */ public MemberNode getNodeFor(String member) { String field = removeRoot(member); return getNodeForNonRoot(field); } + public MemberNode getNodeFor(boolean withRoot, String... members) { + return getNodeForIndex(members, withRoot ? 1 : 0); + } + /** Similar to getNodeFor, but valid at any level of the tree, and the argument must be the field only. * @see #getNodeFor(String) */ MemberNode getNodeForNonRoot(String members) { @@ -310,6 +380,16 @@ public class ObjectTree implements Cloneable { } } + MemberNode getNodeForIndex(String[] members, int index) { + if (members.length <= index) + return memberNode; + assert childrenMap.containsKey(members[index]); + if (members.length == index + 1) + return childrenMap.get(members[index]).memberNode; + else + return childrenMap.get(members[index]).getNodeForIndex(members, index + 1); + } + /** Similar to {@link #getNodeFor(String)}, but if the argument does not contain * types, it will obtain all member nodes that represent a given field (in multiple * types). For example, the argument 'a.x' may produce 'a.A.x' and 'a.B.x'; whereas @@ -352,6 +432,35 @@ public class ObjectTree implements Cloneable { }; } + public Iterable nameAsArrayIterable() { + return () -> new Iterator<>() { + final Iterator it = treeIterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public String[] next() { + ObjectTree element = it.next(); + List builder = new ArrayList<>(); + MemberNode node = element.memberNode; + if (node == null) + return new String[] {ROOT_NAME}; + else if (node instanceof PolyMemberNode) + return next(); + else + builder.add(node.getLabel()); + while (node.getParent() instanceof MemberNode) { + node = (MemberNode) node.getParent(); + builder.add(0, node.getLabel()); + } + return builder.toArray(new String[0]); + } + }; + } + /** @return An iterable through the nodes of all members of this tree, excluding the root. */ public Iterable nodeIterable() { return () -> new Iterator<>() { @@ -425,6 +534,19 @@ public class ObjectTree implements Cloneable { throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); } + public static String[] removeRoot(String[] field) { + int newStart = 1; + for (int i = 0; i < field.length; i++) { + if (field[i].equals("this")) { + newStart = i + 1; + break; + } + } + String[] res = new String[field.length - newStart]; + System.arraycopy(field, newStart, res, 0, res.length); + return res; + } + /** * Utility method to remove the fields a string, retaining just the root. The root element or root of * the object tree should be either "-root-", a valid variable name or an optionally type-prefixed @@ -438,6 +560,20 @@ public class ObjectTree implements Cloneable { throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); } + public static String[] removeFields(String[] fields) { + Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|(?(-?))([_0-9A-Za-z]+\\k)+)(\\.(?.+))?$"); + int length = 1; + for (int i = 0; i < fields.length; i++) { + if (fields[i].equals("this")) { + length = i + 1; + break; + } + } + String[] res = new String[length]; + System.arraycopy(fields, 0, res, 0, length); + return res; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 33ae3d0..1a81fb8 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -16,6 +16,7 @@ import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.NodeHashSet; +import es.upv.mist.slicing.utils.Utils; import java.util.*; import java.util.stream.Collectors; @@ -208,6 +209,14 @@ public abstract class VariableAction { return getObjectTree().hasMember(member); } + public boolean hasTreeMember(String[] member) { + if (member.length == 0) + return hasObjectTree(); + if (!hasObjectTree()) + return false; + return getObjectTree().hasMember(member); + } + /** Whether there is an object tree and it contains the given member. * The search will skip polymorphic nodes if they haven't been specified in the argument. */ public boolean hasPolyTreeMember(String member) { @@ -218,6 +227,14 @@ public abstract class VariableAction { return getObjectTree().hasPolyMember(member); } + public boolean hasPolyTreeMember(String[] member) { + if (member.length == 0) + return hasObjectTree(); + if (!hasObjectTree()) + return false; + return getObjectTree().hasPolyMember(member); + } + public boolean hasObjectTree() { return objectTree != null; } @@ -434,7 +451,7 @@ public abstract class VariableAction { /** The value to which the variable has been defined. */ protected final Expression expression; /** The members of the object tree that are total definitions. */ - protected String totallyDefinedMember; + protected String[] totallyDefinedMember; public Definition(DeclarationType declarationType, String name, GraphNode graphNode) { this(declarationType, name, graphNode, (Expression) null); @@ -454,18 +471,18 @@ public abstract class VariableAction { this.expression = expression; } - public void setTotallyDefinedMember(String totallyDefinedMember) { + public void setTotallyDefinedMember(String[] totallyDefinedMember) { this.totallyDefinedMember = Objects.requireNonNull(totallyDefinedMember); } - public boolean isTotallyDefinedMember(String member) { + public boolean isTotallyDefinedMember(String[] member) { if (totallyDefinedMember == null) return false; - if (totallyDefinedMember.equals(member)) + if (Arrays.equals(totallyDefinedMember, member)) return true; - if (member.startsWith(totallyDefinedMember) - || ObjectTree.removeRoot(member).startsWith(ObjectTree.removeRoot(totallyDefinedMember))) - return ObjectTree.removeRoot(member).isEmpty() || hasTreeMember(member); + if (Utils.arrayPrefix(totallyDefinedMember, member) + || Utils.arrayPrefix(ObjectTree.removeRoot(member), ObjectTree.removeRoot(totallyDefinedMember))) + return ObjectTree.removeRoot(member).length == 0 || hasTreeMember(member); return false; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java index 30f61c2..2fd9cc6 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java @@ -28,9 +28,10 @@ import java.util.stream.Collectors; import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; import static es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG.ACTIVE_EXCEPTION_VARIABLE; -import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NAME; +import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NODE; import static es.upv.mist.slicing.nodes.VariableAction.DeclarationType.*; import static es.upv.mist.slicing.nodes.VariableVisitor.Action.*; +import static es.upv.mist.slicing.utils.Utils.arrayJoin; /** A graph node visitor that extracts the actions performed in a given GraphNode. An initial action mode can * be set, to consider variables found a declaration, definition or usage (default). @@ -111,14 +112,13 @@ public class VariableVisitor extends GraphNodeContentVisitor node) { startVisit(node, USE); - groupActionsByRoot(node); generatePolyTrees(node); } @Override public void visit(NameExpr n, Action action) { - String realName = getRealName(n); - if (realName.equals(n.toString())) { + String[] realName = getRealName(n); + if (realName.length == 1 && realName[0].equals(n.toString())) { acceptAction(n, action); } else { VariableAction va = acceptAction(DeclarationType.valueOf(n), realName, action); @@ -155,9 +155,9 @@ public class VariableVisitor extends GraphNodeContentVisitor nodeVAs = graphNode.getVariableActions(); + VariableAction prev = nodeVAs.isEmpty() ? null : nodeVAs.get(nodeVAs.size() - 1); + String nameRoot = arrayJoin(ObjectTree.removeFields(realName), "."); + + boolean matches = prev != null && // must have a previous VA + !(prev instanceof VariableAction.CallMarker) && // CallMarkers can't be combined + !prev.isDeclaration() && // declarations are never combined + !prev.isRootAction() && // Can't combine with root actions + Objects.equals(nameRoot, prev.getName()) && // Name must match + // Type must match: DEF-DEF or USE-USE (DEC never combines) + ((prev.isDefinition() && action.isDefinition()) || + (prev.isUsage() && action.isUse())); + + if (matches) { + va = prev; + } else if (action.isDeclaration()) { + va = new VariableAction.Declaration(declarationType, nameRoot, graphNode); } else if (action.isDefinition()) { assert !definitionStack.isEmpty() || canDefBeNull; - va = new VariableAction.Definition(declarationType, realName, graphNode, definitionStack.peek()); + va = new VariableAction.Definition(declarationType, nameRoot, graphNode, definitionStack.peek()); } else if (action.isUse()) { - va = new VariableAction.Usage(declarationType, realName, graphNode); + va = new VariableAction.Usage(declarationType, nameRoot, graphNode); } else { throw new UnsupportedOperationException(); } + + if (ObjectTree.removeRoot(realName).length > 0) + va.getObjectTree().addField(realName); + va.setOptional(action.isOptional()); if (!realNodeStack.isEmpty()) { va = new VariableAction.Movable(va, realNodeStack.peek()); @@ -238,10 +259,10 @@ public class VariableVisitor extends GraphNodeContentVisitor() { @Override public void visit(NameExpr nameExpr, Void arg) { - String realName = getRealName(nameExpr); + String[] realName = getRealName(nameExpr); definitionStack.push(n.getValue()); - if (!realName.contains(".")) { + if (realName.length < 2) { VariableAction va = acceptAction(nameExpr, realName, action.or(DEFINITION)); va.asDefinition().setTotallyDefinedMember(realName); realNameWithoutRootList.add(""); } else { - String root = ObjectTree.removeFields(realName); + String[] root = ObjectTree.removeFields(realName); VariableAction va = acceptAction(DeclarationType.valueOf(nameExpr), root, action.or(DEFINITION)); va.setStaticType(ASTUtils.resolvedTypeOfCurrentClass(nameExpr)); va.getObjectTree().addField(realName); va.asDefinition().setTotallyDefinedMember(realName); va.addExpression(nameExpr); - realNameWithoutRootList.add(ObjectTree.removeRoot(realName)); + realNameWithoutRootList.add(arrayJoin(ObjectTree.removeRoot(realName), ".")); } definitionStack.pop(); } @@ -336,11 +357,11 @@ public class VariableVisitor extends GraphNodeContentVisitor]+ =, and [.]+"); - String realName = getRealName(fieldAccessExpr); - String root = ObjectTree.removeFields(realName); + String[] realName = getRealName(fieldAccessExpr); + String[] root = ObjectTree.removeFields(realName); definitionStack.push(n.getValue()); VariableAction va; - if (root.equals(scope.toString())) + if (root.length == 1 && root[0].equals(scope.toString())) va = acceptAction(scope, root, action.or(DEFINITION)); else { va = acceptAction(FIELD, root, action.or(DEFINITION)); @@ -352,7 +373,7 @@ public class VariableVisitor extends GraphNodeContentVisitor { init.accept(this, action); definitionStack.push(init); - VariableAction vaDef = acceptAction(LOCAL_VARIABLE, v.getNameAsString(), DEFINITION); + VariableAction vaDef = acceptAction(LOCAL_VARIABLE, vName, DEFINITION); vaDef.addExpression(n); vaDef.setStaticType(v.getType().resolve()); definitionStack.pop(); if (v.getType().isClassOrInterfaceType()) - vaDef.asDefinition().setTotallyDefinedMember(v.getNameAsString()); + vaDef.asDefinition().setTotallyDefinedMember(vName); v.accept(this, action); }); } @@ -416,7 +437,7 @@ public class VariableVisitor extends GraphNodeContentVisitor ASTUtils.initializerForField(n)); @@ -434,7 +455,6 @@ public class VariableVisitor extends GraphNodeContentVisitor vaList = graphNode.getVariableActions(); @@ -485,7 +506,7 @@ public class VariableVisitor extends GraphNodeContentVisitor= 5; VariableAction useOutput = vaList.get(vaList.size() - 3); assert useOutput.isUsage() && useOutput.getName().equals(VARIABLE_NAME_OUTPUT); - defThis.asDefinition().setTotallyDefinedMember("this"); + defThis.asDefinition().setTotallyDefinedMember(new String[]{ "this" }); ObjectTree.copyTargetTreeToSource(defThis.getObjectTree(), useOutput.getObjectTree(), "", ""); useOutput.setPDGTreeConnectionTo(defThis, "", ""); } @@ -507,7 +528,7 @@ public class VariableVisitor extends GraphNodeContentVisitor scope.accept(this, action), () -> { - VariableAction va = acceptAction(FIELD, "this", action); + VariableAction va = acceptAction(FIELD, new String[]{ "this" }, action); va.setStaticType(ASTUtils.resolvedTypeOfCurrentClass((MethodCallExpr) call)); }); // Generate -scope-in- action, so that InterproceduralUsageFinder does not need to do so. @@ -554,7 +575,7 @@ public class VariableVisitor extends GraphNodeContentVisitor (ObjectTree) tree.clone()).orElse(null)); - def.setTotallyDefinedMember(ROOT_NAME); + def.setTotallyDefinedMember(ROOT_NODE); var defMov = new VariableAction.Movable(def, CallNode.Return.create(call)); defMov.setStaticType(ASTUtils.getCallResolvedType(call)); graphNode.addVariableAction(defMov); @@ -579,89 +600,43 @@ public class VariableVisitor extends GraphNodeContentVisitor result = new LinkedList<>(); Expression scope = n.asFieldAccessExpr().getScope(); - StringBuilder builder = new StringBuilder(n.asFieldAccessExpr().getNameAsString()); + result.add(n.asFieldAccessExpr().getNameAsString()); while (scope instanceof FieldAccessExpr) { - builder.insert(0, '.'); - builder.insert(0, scope.asFieldAccessExpr().getNameAsString()); + result.add(0, scope.asFieldAccessExpr().getNameAsString()); scope = scope.asFieldAccessExpr().getScope(); } - builder.insert(0, '.'); - builder.insert(0, getRealName(scope)); - return builder.toString(); + String[] realName = getRealName(scope); + for (int i = realName.length - 1; i >= 0; i--) + result.add(0, realName[i]); + return result.toArray(new String[0]); } - return n.toString(); + return new String[] { n.toString() }; } /** Generate the correct prefix for a NameExpr. Only works for non-static fields. */ protected String getNamePrefix(NameExpr n) { // We only care about non-static fields ResolvedValueDeclaration resolved = n.resolve(); - if (!resolved.isField() || resolved.asField().isStatic()) - return ""; - return "this."; - } - - /** Extracts the parent elements affected by each variable action (e.g. an action on a.x affects a). - * When multiple compatible actions (same root and action) are found, only one parent element is generated. */ - protected void groupActionsByRoot(GraphNode graphNode) { - VariableAction lastRootAction = null; - for (int i = 0; i < graphNode.variableActions.size(); i++) { - VariableAction action = graphNode.variableActions.get(i); - if (action instanceof VariableAction.CallMarker || - action.isDeclaration() || action.isRootAction()) { - if (lastRootAction != null) { - graphNode.variableActions.add(i, lastRootAction); - i++; - lastRootAction = null; - } - continue; - } - if (lastRootAction == null) { - // generate our own root action - lastRootAction = action.getRootAction(); - // It can be representing a fieldAccessExpr or a fieldDeclaration - // in the first, we can use the expression to obtain the 'type.this' or 'object_name' - // in the second, the expression is null but we can extract 'type.this' from realName - } else if (!action.rootMatches(lastRootAction) - || !actionTypeMatches(action, lastRootAction)) { - // No match: add the root before the current element and update counter - graphNode.variableActions.add(i, lastRootAction); - i++; - // generate our own root action - lastRootAction = action.getRootAction(); - } - lastRootAction.getObjectTree().addField(action.getName()); - lastRootAction.copyExpressions(action); - graphNode.variableActions.remove(action); - i--; - } - // Append the last root action if there is any! - if (lastRootAction != null) - graphNode.variableActions.add(lastRootAction); - } - - /** Whether two variable actions perform the same action. */ - private static boolean actionTypeMatches(VariableAction a, VariableAction b) { - return (a.isDeclaration() && b.isDeclaration()) || - (a.isDefinition() && b.isDefinition()) || - (a.isUsage() && b.isUsage()); + return !resolved.isField() || resolved.asField().isStatic() ? null : "this"; } /** Given a graph node, modify the existing object trees in each variable action @@ -686,7 +661,7 @@ public class VariableVisitor extends GraphNodeContentVisitor types, ClassGraph classGraph) { + protected static void polyUnit(ObjectTree oldOT, ObjectTree newOT, Set types, ClassGraph classGraph) { boolean skipPolyNodes = types.stream().noneMatch(classGraph::containsType) || !oldOT.hasChildren() || oldOT.hasPoly(); if (skipPolyNodes) { // Copy as-is @@ -708,11 +683,16 @@ public class VariableVisitor extends GraphNodeContentVisitor dynamicTypesOf(ResolvedType rt, String fieldName, ClassGraph classGraph) { + protected static Set dynamicTypesOf(ResolvedType rt, String fieldName, ClassGraph classGraph) { Optional field = classGraph.findClassField(rt, fieldName); if (field.isEmpty()) return Collections.emptySet(); - ResolvedType fieldType = field.get().getVariable(0).getType().resolve(); + ResolvedType fieldType; + try { + fieldType = field.get().getVariable(0).getType().resolve(); + } catch (UnsupportedOperationException e) { + return Set.of(); + } if (!fieldType.isReferenceType() || !classGraph.containsType(fieldType)) return Set.of(fieldType); return classGraph.subclassesOf(fieldType.asReferenceType()).stream() diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java index fe6f642..64681a8 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java @@ -27,4 +27,24 @@ public class Utils { return element; throw new NoSuchElementException("Could not locate " + object + " in set."); } + + public static String arrayJoin(String[] array, String separator) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < array.length; i++) { + if (i > 0) builder.append(separator); + builder.append(array[i]); + } + return builder.toString(); + } + + public static boolean arrayPrefix(E[] array, E[] prefix) { + if (prefix.length == 0) + return true; + if (array.length < prefix.length) + return false; + for (int i = 0; i < prefix.length; i++) + if (!Objects.equals(array[i], prefix[i])) + return false; + return true; + } } diff --git a/sdg-core/src/main/java/module-info.java b/sdg-core/src/main/java/module-info.java index b00c1ea..c14b7df 100644 --- a/sdg-core/src/main/java/module-info.java +++ b/sdg-core/src/main/java/module-info.java @@ -2,6 +2,7 @@ module sdg.core { requires com.github.javaparser.core; requires com.github.javaparser.symbolsolver.core; requires org.jgrapht.core; + requires java.logging; exports es.upv.mist.slicing.slicing; exports es.upv.mist.slicing.graphs; -- GitLab