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 460161a1ae61950a05618dfb28e765e00a3e7dcc..1ad24528c5bd8c0760b85b986438d803df985b9c 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 @@ -7,6 +7,7 @@ import com.github.javaparser.ast.comments.BlockComment; import com.github.javaparser.ast.nodeTypes.NodeWithName; import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; import es.upv.mist.slicing.graphs.augmented.ASDG; +import es.upv.mist.slicing.graphs.jsysdg.JSysDG; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; import es.upv.mist.slicing.graphs.sdg.SDG; @@ -232,11 +233,12 @@ public class Slicer { } SDG sdg; - switch (cliOpts.getOptionValue("type", "ESSDG")) { + switch (cliOpts.getOptionValue("type", "JSysDG")) { case "SDG": sdg = new SDG(); break; case "ASDG": sdg = new ASDG(); break; case "PSDG": sdg = new PSDG(); break; case "ESSDG": sdg = new ESSDG(); break; + case "JSysDG": sdg = new JSysDG(); break; default: throw new IllegalArgumentException("Unknown type of graph. Available graphs are SDG, ASDG, PSDG, ESSDG"); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java index bd11813025c76dd863e51abf65bb97aca5d5c4c9..5dc4c886ebc664fe7c9dca5d72c6480e5d106f79 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -1,6 +1,7 @@ package es.upv.mist.slicing.graphs; import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; @@ -202,7 +203,6 @@ public class ClassGraph extends DirectedPseudograph, CallGraph.Edge> getDOTExporter() { DOTExporter, CallGraph.Edge> dot = new DOTExporter<>(); @@ -311,6 +310,38 @@ public class ClassGraph extends DirectedPseudograph> getStaticInit(String className){ + return getClassInit(className,true); + } + + /** Returns a List with the dynamic FieldDeclarations and InitializerDeclarations of the given class */ + public List> getDynInit(String className){ + return getClassInit(className,false); + } + + /** Returns a List with FieldDeclarations and InitializerDeclarations static/dynamic items of the given class */ + private List> getClassInit(String className, Boolean isStatic){ + Vertex classNode = vertexDeclarationMap.get(className); + List> members = classNode.declaration.asClassOrInterfaceDeclaration().getMembers(); + List> classInit = new LinkedList<>(); + for (BodyDeclaration member : members) { + if (member instanceof CallableDeclaration) + continue; + + if (member.isFieldDeclaration()) { + if (isStatic == member.asFieldDeclaration().hasModifier(Modifier.Keyword.STATIC)) + classInit.add(member); + continue; + } + + if (member.isInitializerDeclaration()) + if (isStatic == member.asInitializerDeclaration().isStatic()) + classInit.add(member); + } + return classInit; + } + protected static class ClassArc extends Arc { /** An arc that connects a class with another one that inherits from it. */ protected static class Extends extends ClassArc {} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/GraphNodeContentVisitor.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/GraphNodeContentVisitor.java index fa0e68564c361b923ba2e984d171c28a14a78961..282f73384f7cd82a3975ef9231ef0cf190d5aeb3 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/GraphNodeContentVisitor.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/GraphNodeContentVisitor.java @@ -87,9 +87,7 @@ public class GraphNodeContentVisitor extends VoidVisitorAdapter { } @Override - public void visit(FieldDeclaration n, A arg) { - throw new UnsupportedOperationException(); - } + public void visit(FieldDeclaration n, A arg) { super.visit(n, arg); } @Override public void visit(ForEachStmt n, A arg) { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java index debd87e1f319c01ed2683bc49f69b7ec4a537e75..d0aa7cf350c8750280dbf67df67659072ab88b28 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java @@ -1,7 +1,6 @@ package es.upv.mist.slicing.graphs.augmented; import com.github.javaparser.ast.body.CallableDeclaration; -import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.stmt.*; import es.upv.mist.slicing.graphs.cfg.CFGBuilder; import es.upv.mist.slicing.nodes.GraphNode; @@ -134,7 +133,7 @@ public class ACFGBuilder extends CFGBuilder { GraphNode node = connectTo(returnStmt); returnStmt.getExpression().ifPresent(n -> { n.accept(this, arg); - node.addDefinedVariable(new NameExpr(VARIABLE_NAME_OUTPUT), n); + node.addDefinedVariable(null, VARIABLE_NAME_OUTPUT, n); }); returnList.add(node); clearHanging(); 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 79a14d1a49b6f8073e682dd29f88e985cebf7cbe..0f8278eea8e7942ebdcd3db337357836475e9ab8 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 @@ -6,7 +6,6 @@ import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.BooleanLiteralExpr; import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.SimpleName; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitor; @@ -331,7 +330,7 @@ public class CFGBuilder extends VoidVisitorAdapter { GraphNode node = connectTo(returnStmt); returnStmt.getExpression().ifPresent(n -> { n.accept(this, arg); - node.addDefinedVariable(new NameExpr(VARIABLE_NAME_OUTPUT), n); + node.addDefinedVariable(null, VARIABLE_NAME_OUTPUT, n); }); returnList.add(node); clearHanging(); @@ -379,7 +378,7 @@ public class CFGBuilder extends VoidVisitorAdapter { * @see #VARIABLE_NAME_OUTPUT */ protected void addMethodOutput(CallableDeclaration callableDeclaration, GraphNode exit) { if (!(callableDeclaration instanceof MethodDeclaration) || !((MethodDeclaration) callableDeclaration).getType().isVoidType()) { - VariableAction usage = new VariableAction.Usage(new NameExpr(VARIABLE_NAME_OUTPUT), exit); + VariableAction usage = new VariableAction.Usage(null, VARIABLE_NAME_OUTPUT, exit); exit.addMovableVariable(new VariableAction.Movable(usage, OutputNode.create(callableDeclaration))); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java index d4bf2600cd8c9d841e0b626d71395b7919519717..cbde73c183e1cc47c6631f3382b01fd32907bdfd 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java @@ -2,7 +2,6 @@ package es.upv.mist.slicing.graphs.exceptionsensitive; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.Type; @@ -35,7 +34,7 @@ public class ESCFG extends ACFG { @Override protected CFGBuilder newCFGBuilder() { - return new Builder(); + return new Builder(this); } protected ExceptionExitNode addExceptionExitNode(CallableDeclaration method, ResolvedType type) { @@ -104,8 +103,9 @@ public class ESCFG extends ACFG { /** Map of return nodes from each method call, mapped by the normal return node of said call. */ protected Map> pendingNormalReturnNodes = new HashMap<>(); - protected Builder() { + protected Builder(ESCFG escfg) { super(ESCFG.this); + assert escfg == ESCFG.this; } @Override @@ -127,7 +127,7 @@ public class ESCFG extends ACFG { // Exception exit Collection exceptionExits = processExceptionSources(callableDeclaration); for (ExceptionExitNode node : exceptionExits) { - node.addUsedVariable(new NameExpr(ACTIVE_EXCEPTION_VARIABLE)); + node.addUsedVariable(null, ACTIVE_EXCEPTION_VARIABLE); hangingNodes.add(node); lastNodes.addAll(hangingNodes); clearHanging(); @@ -240,7 +240,7 @@ public class ESCFG extends ACFG { stmtStack.push(n); GraphNode stmt = connectTo(n); n.getExpression().accept(this, arg); - stmt.addDefinedVariable(new NameExpr(ACTIVE_EXCEPTION_VARIABLE), n.getExpression()); + stmt.addDefinedVariable(null, ACTIVE_EXCEPTION_VARIABLE, n.getExpression()); populateExceptionSourceMap(new ExceptionSource(stmt, n.getExpression().calculateResolvedType())); clearHanging(); nonExecHangingNodes.add(stmt); @@ -249,23 +249,23 @@ public class ESCFG extends ACFG { @Override public void visit(MethodCallExpr n, Void arg) { - visitCall(n); + visitCallForExceptions(n); } @Override public void visit(ObjectCreationExpr n, Void arg) { - visitCall(n); + visitCallForExceptions(n); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { connectTo(n); - visitCall(n); + visitCallForExceptions(n); } /** Process a call that may throw exceptions. Generates normal and return nodes, and * registers the appropriate exception source. */ - protected void visitCall(Resolvable call) { + protected void visitCallForExceptions(Resolvable call) { ResolvedMethodLikeDeclaration resolved = call.resolve(); if (resolved.getNumberOfSpecifiedExceptions() == 0) return; @@ -286,7 +286,7 @@ public class ESCFG extends ACFG { for (ResolvedType type : resolved.getSpecifiedExceptions()) { hangingNodes.add(stmtNode); ExceptionReturnNode exceptionReturn = addExceptionReturnNode(call, type); - exceptionReturn.addDefinedVariable(new NameExpr(ACTIVE_EXCEPTION_VARIABLE), null); // TODO: improve initializer + exceptionReturn.addDefinedVariable(null, ACTIVE_EXCEPTION_VARIABLE, null); // TODO: improve initializer populateExceptionSourceMap(new ExceptionSource(exceptionReturn, type)); returnNodes.add(exceptionReturn); connectTo(exceptionReturn); @@ -313,7 +313,7 @@ public class ESCFG extends ACFG { for (ExceptionSource src : sources) (src.isActive() ? hangingNodes : nonExecHangingNodes).add(src.source); GraphNode node = connectTo(n, "catch (" + n.getParameter().toString() + ")"); - node.addUsedVariable(new NameExpr(ACTIVE_EXCEPTION_VARIABLE)); + node.addUsedVariable(null, ACTIVE_EXCEPTION_VARIABLE); exceptionSourceMap.clear(); // 2. Set up as exception source ExceptionSource catchES = ExceptionSource.merge(node, sources); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESSDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESSDG.java index 02d1214cdd1ad280a4f0698e8395d45078d47f24..2e57468df523736a7fcd0f619a4f647389a0dfc0 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESSDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESSDG.java @@ -1,7 +1,6 @@ package es.upv.mist.slicing.graphs.exceptionsensitive; import es.upv.mist.slicing.arcs.sdg.ReturnArc; -import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.augmented.PPDG; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.graphs.cfg.CFG; @@ -32,7 +31,7 @@ public class ESSDG extends PSDG { /** Populates an ESSDG, using ESPDG and ESCFG as default graphs. * @see PSDG.Builder * @see ExceptionSensitiveCallConnector */ - class Builder extends PSDG.Builder { + protected class Builder extends PSDG.Builder { @Override protected CFG createCFG() { return new ESCFG(); @@ -45,7 +44,7 @@ public class ESSDG extends PSDG { } @Override - protected void connectCalls(CallGraph callGraph) { + protected void connectCalls() { new ExceptionSensitiveCallConnector(ESSDG.this).connectAllCalls(callGraph); } } 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 new file mode 100644 index 0000000000000000000000000000000000000000..f43e32adc04d21340d191fa7491c2c0f26db1681 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -0,0 +1,116 @@ +package es.upv.mist.slicing.graphs.jsysdg; + +import com.github.javaparser.ast.Node; +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.FieldDeclaration; +import com.github.javaparser.ast.expr.ThisExpr; +import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; +import com.github.javaparser.ast.stmt.ReturnStmt; +import es.upv.mist.slicing.graphs.ClassGraph; +import es.upv.mist.slicing.graphs.cfg.CFGBuilder; +import es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG; +import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.io.MethodExitNode; +import es.upv.mist.slicing.utils.ASTUtils; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * An SDG that is tailored for Java, including a class graph, inheritance, + * polymorphism and other features. + */ +public class JSysCFG extends ESCFG { + /** ClassGraph associated to the Method represented by the CFG */ + protected ClassGraph classGraph; + /** Set of constructors that must be built with implicit nodes. */ + protected Set implicitConstructors; + + public JSysCFG(ClassGraph classGraph, Set implicitConstructors) { + super(); + this.classGraph = classGraph; + this.implicitConstructors = implicitConstructors; + } + + @Override + public void buildRootNode(CallableDeclaration rootNodeAst) { + super.buildRootNode(rootNodeAst); + if (implicitConstructors.contains(rootNodeAst)) + rootNode.markAsImplicit(); + } + + @Override + protected CFGBuilder newCFGBuilder() { + return new Builder(this); + } + + public class Builder extends ESCFG.Builder { + /** List of implicit instructions inserted explicitly in this CFG. + * They should be included in the graph as ImplicitNodes. */ + protected List methodInsertedInstructions = new LinkedList<>(); + /** Whether we are building a CFG for an implicit method or not. */ + protected boolean implicitDeclaration = false; + + protected Builder(JSysCFG jSysCFG) { + super(JSysCFG.this); + assert jSysCFG == JSysCFG.this; + } + + @Override + protected GraphNode connectTo(T n, String text) { + GraphNode dest; + dest = new GraphNode<>(text, n); + if (methodInsertedInstructions.contains(n) || + (implicitDeclaration && !(n instanceof FieldDeclaration))) + dest.markAsImplicit(); + addVertex(dest); + connectTo(dest); + return dest; + } + + @Override + public void visit(ExplicitConstructorInvocationStmt n, Void arg) { + // 1. Connect to the following statements + connectTo(n); + // 2. Insert dynamic class code (only for super()) + if (!n.isThis()) { + ClassOrInterfaceDeclaration containerClass = ASTUtils.getClassNode(rootNode.getAstNode()); + classGraph.getDynInit(containerClass.getNameAsString()).forEach(node -> node.accept(this, arg)); + } + // 3. Handle exceptions + super.visitCallForExceptions(n); + } + + @Override + public void visit(FieldDeclaration n, Void arg){ + connectTo(n); + super.visit(n,arg); + } + + @Override + public void visit(ConstructorDeclaration n, Void arg) { + if (implicitConstructors.contains(n)) + implicitDeclaration = true; + // Insert call to super() if it is implicit. + if (!ASTUtils.constructorHasExplicitConstructorInvocation(n)){ + var superCall = new ExplicitConstructorInvocationStmt(null, null, false, null, new NodeList<>()); + var returnThis = new ReturnStmt(new ThisExpr()); + methodInsertedInstructions.add(superCall); + methodInsertedInstructions.add(returnThis); + n.getBody().addStatement(0, superCall); + n.getBody().addStatement(returnThis); + } + // Perform the same task as previous graphs. + super.visit(n, arg); + // Convert the exit nodes to implicit if appropriate + if (implicitDeclaration) + vertexSet().stream() + .filter(MethodExitNode.class::isInstance) + .forEach(GraphNode::markAsImplicit); + } + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..eaf5a3652e7c5ea416b022ba78f10f54bc875919 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java @@ -0,0 +1,59 @@ +package es.upv.mist.slicing.graphs.jsysdg; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.ConstructorDeclaration; +import com.github.javaparser.ast.visitor.ModifierVisitor; +import com.github.javaparser.ast.visitor.Visitable; +import es.upv.mist.slicing.graphs.augmented.PSDG; +import es.upv.mist.slicing.graphs.cfg.CFG; +import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; +import es.upv.mist.slicing.graphs.exceptionsensitive.ExceptionSensitiveCallConnector; +import es.upv.mist.slicing.graphs.pdg.PDG; +import es.upv.mist.slicing.utils.NodeHashSet; + +public class JSysDG extends ESSDG { + + @Override + protected JSysDG.Builder createBuilder() { + return new JSysDG.Builder(); + } + + /** Populates an ESSDG, using ESPDG and ESCFG as default graphs. + * @see PSDG.Builder + * @see ExceptionSensitiveCallConnector */ + class Builder extends ESSDG.Builder { + protected NodeHashSet newlyInsertedConstructors = new NodeHashSet<>(); + + @Override + public void build(NodeList nodeList) { + insertImplicitConstructors(nodeList); + super.build(nodeList); + } + + /** Create implicit constructors, and store them in a set so that they may be built with implicit nodes. */ + protected void insertImplicitConstructors(NodeList nodeList) { + nodeList.accept(new ModifierVisitor<>() { + @Override + public Visitable visit(ClassOrInterfaceDeclaration n, Object arg) { + if (n.getConstructors().isEmpty()) + newlyInsertedConstructors.add(n.addConstructor(Modifier.Keyword.PUBLIC)); + return super.visit(n, arg); + } + }, null); + } + + @Override + protected CFG createCFG() { + return new JSysCFG(classGraph, newlyInsertedConstructors); + } + + @Override + protected PDG createPDG(CFG cfg) { + assert cfg instanceof JSysCFG; + return new JSysPDG((JSysCFG) cfg); + } + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..abd225e4b1a66d09243c5b88eeb8a560a75cb5a0 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -0,0 +1,17 @@ +package es.upv.mist.slicing.graphs.jsysdg; + +import com.github.javaparser.ast.body.ConstructorDeclaration; +import es.upv.mist.slicing.graphs.ClassGraph; +import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; + +import java.util.Set; + +public class JSysPDG extends ESPDG { + public JSysPDG(ClassGraph classGraph, Set implicitConstructors) { + this(new JSysCFG(classGraph, implicitConstructors)); + } + + public JSysPDG(JSysCFG cfg) { + super(cfg); + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java index 4692086f1921f09d3fbb3604f08a32b1823274d2..f6d561282b31b701250365a03759655ed26173c6 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java @@ -76,7 +76,7 @@ public class DynamicTypeResolver { /** Searches for the corresponding VariableAction object, then calls {@link #resolveVariableAction(VariableAction)}. */ protected Stream resolveVariable(Expression expression, GraphNode graphNode) { Optional va = graphNode.getVariableActions().stream() - .filter(action -> ASTUtils.equalsWithRange(action.getVariableExpression(), expression)) + .filter(action -> action.hasVariableExpression() && ASTUtils.equalsWithRange(action.getVariableExpression(), expression)) .findFirst(); if (va.isEmpty()) return anyTypeOf(expression); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/pdg/PDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/pdg/PDG.java index d361b90025ef79a79dae0eee6f3a71e2101a6a7e..101499867b6b7715e8ea50ec4bb6d162a532bb97 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/pdg/PDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/pdg/PDG.java @@ -122,6 +122,8 @@ public class PDG extends GraphWithRootNode> { callNodeStack.pop(); } else { CallNode callNode = CallNode.create(((VariableAction.CallMarker) action).getCall()); + if (graphNode.isImplicitInstruction()) + callNode.markAsImplicit(); addVertex(callNode); addControlDependencyArc(graphNode, callNode); callNodeStack.push(callNode); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java index 30e3aaeea4f401544ad3a48d6125cfcde7236b3a..92e8289eeb3fcecc168496465ca175e49acc53a4 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java @@ -2,7 +2,12 @@ package es.upv.mist.slicing.graphs.sdg; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.ThisExpr; +import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; +import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; @@ -15,6 +20,9 @@ import es.upv.mist.slicing.utils.Logger; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; // TODO: this approach of generating actual nodes may skip an argument; this is only a problem if there is a definition // TODO: update placement of actual and formal outputs for ESSDG (see if the definition/usage reaches all/any exits). @@ -71,24 +79,73 @@ public abstract class InterproceduralActionFinder exte /** Generate the actual node(s) related to this action and call. */ protected abstract void handleActualAction(CallGraph.Edge edge, A action); + // =========================================================== + // ============== AUXILIARY METHODS FOR CHILDREN ============= + // =========================================================== + + /** Given a call, obtains the scope. If none is present it may return null. + * ExpressionConstructorInvocations result in a this expression, as they + * may be seen as dynamic method calls that can modify 'this'. */ + protected static Expression obtainScope(Resolvable call) { + if (call instanceof MethodCallExpr) { + var methodCall = (MethodCallExpr) call; + return methodCall.getScope().orElse(null); + } else if (call instanceof ExplicitConstructorInvocationStmt) { + return new ThisExpr(); + } else { + throw new IllegalArgumentException("The given call is not of a valid type"); + } + } + /** Obtains the expression passed as argument for the given action at the given call. If {@code input} * is false, primitive parameters will be skipped, as their value cannot be redefined.*/ - protected Expression extractArgument(VariableAction action, CallGraph.Edge edge, boolean input) { - ResolvedValueDeclaration resolved = action.getResolvedValueDeclaration(); + protected Expression extractArgument(ResolvedParameterDeclaration p, CallGraph.Edge edge, boolean input) { CallableDeclaration callTarget = graph.getEdgeTarget(edge).getDeclaration(); - if (resolved.isParameter()) { - ResolvedParameterDeclaration p = resolved.asParameter(); - if (!input && p.getType().isPrimitive()) - return null; // primitives do not have actual-out! - int paramIndex = ASTUtils.getMatchingParameterIndex(callTarget, p); - return ASTUtils.getResolvableArgs(edge.getCall()).get(paramIndex); - } else if (resolved.isField()) { - return action.getVariableExpression(); + if (!input && p.getType().isPrimitive()) + return null; // primitives do not have actual-out! + int paramIndex = ASTUtils.getMatchingParameterIndex(callTarget, p); + return ASTUtils.getResolvableArgs(edge.getCall()).get(paramIndex); + } + + /** Generate the name that should be given to an object in a caller method, given an action + * in the callee method. This is used to transform a reference to 'this' into the scope + * of a method. */ + protected static String obtainAliasedFieldName(VariableAction action, CallGraph.Edge edge) { + if (edge.getCall() instanceof MethodCallExpr) { + Optional optScope = ((MethodCallExpr) edge.getCall()).getScope(); + return obtainAliasedFieldName(action, edge, optScope.isPresent() ? optScope.get().toString() : ""); + } else if (edge.getCall() instanceof ExplicitConstructorInvocationStmt) { + // The only possibility is 'this' or its fields, so we return empty scope and 'type.this.' is generated + return obtainAliasedFieldName(action, edge, ""); + } else { + throw new IllegalArgumentException("The given call is not of a valid type"); + } + } + + /** To be used by {@link #obtainAliasedFieldName(VariableAction, CallGraph.Edge)} exclusively.
+ * Given a scope, name inside a method and call, translates the name of a variable, such that 'this' becomes + * the scope of the method. */ + protected static String obtainAliasedFieldName(VariableAction action, CallGraph.Edge edge, String scope) { + if (scope.isEmpty()) { + return action.getVariable(); } else { - throw new IllegalArgumentException("Variable should be either param or field!"); + String newPrefix = scope; + newPrefix = newPrefix.replaceAll("((\\.)super|^super)(\\.)?", "$2this$3"); + if (newPrefix.equals("this")) { + String fqName = ASTUtils.getClassNode(edge.getGraphNode().getAstNode()).getFullyQualifiedName().orElseThrow(); + newPrefix = fqName + ".this"; + } + String withPrefix = action.getVariable(); + String withoutPrefix = withPrefix.replaceFirst("^((.*\\.)?this\\.?)", ""); + String result = newPrefix + withoutPrefix; + return result.replaceFirst("this(\\.this)+", "this"); } } + // =========================================================== + // =============== COMPUTE DATA FOR FIXED POINT ============== + // =========================================================== + @Override protected Set> compute(CallGraph.Vertex vertex, Set predecessors) { saveDeclaration(vertex); @@ -97,11 +154,32 @@ public abstract class InterproceduralActionFinder
exte return newValue; } - /** Wrap a variable action in a {@link StoredAction}, to track whether it has been applied to the graph or not. */ - protected StoredAction wrapAction(A action) { - return new StoredAction<>(action); + @Override + protected Set> initialValue(CallGraph.Vertex vertex) { + CFG cfg = cfgMap.get(vertex.getDeclaration()); + if (cfg == null) + return Collections.emptySet(); + Stream stream = cfg.vertexSet().stream() + // Ignore root node, it is literally the entrypoint for interprocedural actions. + .filter(n -> n != cfg.getRootNode()) + .flatMap(n -> n.getVariableActions().stream()) + // We never analyze synthetic variables (all intraprocedural) + .filter(Predicate.not(VariableAction::isSynthetic)) + // We skip over non-root variables (for each 'x.a' action we'll find 'x' later) + .filter(VariableAction::isRootAction); + return mapAndFilterActionStream(stream, cfg) + .map(StoredAction::new) + .collect(Collectors.toSet()); } + /** Given a stream of VariableAction objects, map it to the finders' type and + * filter unwanted items (only if the filter is specific to that type). */ + protected abstract Stream mapAndFilterActionStream(Stream stream, CFG cfg); + + // =========================================================== + // ========================= SUBCLASSES ====================== + // =========================================================== + /** A comparator to sort parameters and fields in the generation of actual nodes. It will sort * {@link StoredAction}s in the following order: fields, then parameters by descending index number. * The actual nodes will be generated in that order and inserted in reverse order in the graph node. */ @@ -128,7 +206,6 @@ public abstract class InterproceduralActionFinder exte else if (r1.isField() && r2.isParameter()) return 1; } catch (UnsolvedSymbolException e) { - Logger.log("Could not resolve a given name expression, it may be a type: " + e.getName()); if (r1 == null) return 1; else if (r2 == null) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java index d665dd526bd793d58467e490b3baed1fa7cd6aa8..7ad47b30b21c3b36c8db770a015021ddc6972a02 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java @@ -3,6 +3,7 @@ package es.upv.mist.slicing.graphs.sdg; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; @@ -11,12 +12,10 @@ import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; -import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; +import java.util.stream.Stream; /** An interprocedural definition finder, which adds the associated actions to formal and actual nodes in the CFGs. */ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder { @@ -41,34 +40,39 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder Set movables = new HashSet<>(); GraphNode graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); - Expression arg = extractArgument(def, edge, false); - if (arg == null) - return; - ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, arg); if (resolved.isParameter()) { - Set exprSet = new HashSet<>(); - arg.accept(new OutNodeVariableVisitor(), exprSet); - for (NameExpr nameExpr : exprSet) - movables.add(new VariableAction.Movable(new VariableAction.Definition(nameExpr, graphNode), actualOut)); + Expression arg = extractArgument(resolved.asParameter(), edge, false); + if (arg == null) + return; + ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, arg); + if (resolved.isParameter()) { + Set exprSet = new HashSet<>(); + arg.accept(new OutNodeVariableVisitor(), exprSet); + for (NameExpr nameExpr : exprSet) + movables.add(new VariableAction.Movable(new VariableAction.Definition(nameExpr, nameExpr.toString(), graphNode), actualOut)); + } else { + movables.add(new VariableAction.Movable(def.toDefinition(graphNode), actualOut)); + } + } else if (resolved.isField()) { + // Known limitation: static fields + // An object creation expression doesn't alter an existing object via actual-out + // it is returned and assigned via -output-. + if (edge.getCall() instanceof ObjectCreationExpr) + return; + String aliasedName = obtainAliasedFieldName(def, edge); + ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, null); + var movableDef = new VariableAction.Definition(obtainScope(edge.getCall()), aliasedName, graphNode, null); + movables.add(new VariableAction.Movable(movableDef, actualOut)); } else { - movables.add(new VariableAction.Movable(def.toDefinition(graphNode), actualOut)); + throw new IllegalStateException("Definition must be either from a parameter or a field!"); } graphNode.addActionsForCall(movables, edge.getCall(), false); } @Override - protected Set> initialValue(CallGraph.Vertex vertex) { - CFG cfg = cfgMap.get(vertex.getDeclaration()); - if (cfg == null) - return Collections.emptySet(); - return cfg.vertexSet().stream() - .filter(n -> n != cfg.getRootNode()) - .flatMap(n -> n.getVariableActions().stream()) - .filter(VariableAction::isDefinition) - .filter(Predicate.not(VariableAction::isSynthetic)) + protected Stream mapAndFilterActionStream(Stream stream, CFG cfg) { + return stream.filter(VariableAction::isDefinition) .map(VariableAction::asDefinition) - .filter(def -> cfg.findDeclarationFor(def).isEmpty()) - .map(this::wrapAction) - .collect(Collectors.toSet()); + .filter(def -> cfg.findDeclarationFor(def).isEmpty()); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java index 1e3ee0cd6342f7127d5d4fc1e6deeaacff87b144..20919e905722aeb1937a475fd915d9a53747c76a 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java @@ -2,6 +2,7 @@ package es.upv.mist.slicing.graphs.sdg; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; @@ -11,12 +12,11 @@ import es.upv.mist.slicing.nodes.VariableVisitor; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; -import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Predicate; -import java.util.stream.Collectors; +import java.util.stream.Stream; /** An interprocedural usage finder, which adds the associated actions to formal and actual nodes in the CFGs. */ public class InterproceduralUsageFinder extends InterproceduralActionFinder { @@ -37,29 +37,33 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder movables = new HashSet<>(); GraphNode graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); - Expression argument = extractArgument(use, edge, true); - ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, argument); - argument.accept(new VariableVisitor( - (n, name) -> movables.add(new VariableAction.Movable(new VariableAction.Declaration(name, graphNode), actualIn)), - (n, name, expression) -> movables.add(new VariableAction.Movable(new VariableAction.Definition(name, graphNode, expression), actualIn)), - (n, name) -> movables.add(new VariableAction.Movable(new VariableAction.Usage(name, graphNode), actualIn)) - ), VariableVisitor.Action.USE); + if (resolved.isParameter()) { + Expression argument = extractArgument(resolved.asParameter(), edge, true); + ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, argument); + argument.accept(new VariableVisitor( + (n, exp, name) -> movables.add(new VariableAction.Movable(new VariableAction.Declaration(exp, name, graphNode), actualIn)), + (n, exp, name, expression) -> movables.add(new VariableAction.Movable(new VariableAction.Definition(exp, name, graphNode, expression), actualIn)), + (n, exp, name) -> movables.add(new VariableAction.Movable(new VariableAction.Usage(exp, name, graphNode), actualIn)) + ), VariableVisitor.Action.USE); + } else if (resolved.isField()) { + // Known limitation: static fields + // An object creation expression input an existing object via actual-in because it creates it. + if (edge.getCall() instanceof ObjectCreationExpr) + return; + String aliasedName = obtainAliasedFieldName(use, edge); + ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, null); + var movableUse = new VariableAction.Usage(obtainScope(edge.getCall()), aliasedName, graphNode); + movables.add(new VariableAction.Movable(movableUse, actualIn)); + } else { + throw new IllegalStateException("Definition must be either from a parameter or a field!"); + } graphNode.addActionsForCall(movables, edge.getCall(), true); } @Override - protected Set> initialValue(CallGraph.Vertex vertex) { - CFG cfg = cfgMap.get(vertex.getDeclaration()); - if (cfg == null) - return Collections.emptySet(); - return cfg.vertexSet().stream() - .filter(n -> n != cfg.getRootNode()) - .flatMap(n -> n.getVariableActions().stream()) - .filter(VariableAction::isUsage) - .filter(Predicate.not(VariableAction::isSynthetic)) + protected Stream mapAndFilterActionStream(Stream stream, CFG cfg) { + return stream.filter(VariableAction::isUsage) .map(VariableAction::asUsage) - .filter(Predicate.not(cfg::isCompletelyDefined)) - .map(this::wrapAction) - .collect(Collectors.toSet()); + .filter(Predicate.not(cfg::isCompletelyDefined)); } } 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 35d6ceee61b4502fe425ca7854d3bcbe50c72579..4e6209b07d56d8aa64b4e83f8559f2e8c03b184a 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 @@ -5,7 +5,7 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import es.upv.mist.slicing.arcs.pdg.ControlDependencyArc; import es.upv.mist.slicing.arcs.pdg.DataDependencyArc; @@ -18,7 +18,6 @@ import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.Graph; import es.upv.mist.slicing.graphs.cfg.CFG; -import es.upv.mist.slicing.graphs.cfg.CFGBuilder; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.SyntheticNode; @@ -33,6 +32,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; + /** * The System Dependence Graph represents the statements of a program in * a graph, connecting statements according to their {@link ControlDependencyArc control}, @@ -107,15 +108,19 @@ 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 - buildCFGs(nodeList); // 1 - ClassGraph classGraph = createClassGraph(nodeList); // TODO: Update order and creation strategy - CallGraph callGraph = createCallGraph(nodeList, classGraph); // 2 - dataFlowAnalysis(callGraph); // 3 - buildAndCopyPDGs(); // 4 - connectCalls(callGraph); // 5 - createSummaryArcs(callGraph); // 6 + // This ordering cannot be altered, as each step requires elements from the previous one. + classGraph = createClassGraph(nodeList); // 0 + buildCFGs(nodeList); // 1 + callGraph = createCallGraph(nodeList); // 2 + dataFlowAnalysis(); // 3 + buildAndCopyPDGs(); // 4 + connectCalls(); // 5 + createSummaryArcs(); // 6 } /** Build a CFG per declaration found in the list of compilation units. */ @@ -138,7 +143,7 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList, ClassGraph classGraph) { + protected CallGraph createCallGraph(NodeList nodeList) { CallGraph callGraph = new CallGraph(cfgMap, classGraph); callGraph.build(nodeList); return callGraph; @@ -153,25 +158,28 @@ public class SDG extends Graph implements Sliceable, Buildable edge : callGraph.edgeSet()) { if (ASTUtils.resolvableIsVoid(edge.getCall())) continue; + // We handle super()/this() in VariableVisitor + if (edge.getCall() instanceof ExplicitConstructorInvocationStmt) + continue; GraphNode graphNode = edge.getGraphNode(); // A node defines -output- - var def = new VariableAction.Definition(new NameExpr(CFGBuilder.VARIABLE_NAME_OUTPUT), graphNode); + var def = new VariableAction.Definition(null, VARIABLE_NAME_OUTPUT, graphNode); var defMov = new VariableAction.Movable(def, CallNode.Return.create(edge.getCall())); graphNode.addActionsForCall(Set.of(defMov), edge.getCall(), false); // The container of the call uses -output- - var use = new VariableAction.Usage(new NameExpr(CFGBuilder.VARIABLE_NAME_OUTPUT), graphNode); - graphNode.addActionsAfterCall(Set.of(use), edge.getCall()); + var use = new VariableAction.Usage(null, VARIABLE_NAME_OUTPUT, graphNode); + graphNode.addActionsAfterCall(edge.getCall(), use); } } @@ -188,12 +196,12 @@ public class SDG extends Graph implements Sliceable, Buildable implements Comparable> { /** The method calls contained */ protected final List> methodCalls = new LinkedList<>(); + /** @see #isImplicitInstruction() */ + protected boolean isImplicit = false; + /** Create a graph node, with id and variable actions generated automatically. */ public GraphNode(String label, N astNode) { this(IdHelper.getInstance().getNextId(), label, astNode); @@ -86,13 +90,30 @@ public class GraphNode implements Comparable> { return label; } + /** Marks the current node as implicit. + * @see #isImplicitInstruction() */ + public void markAsImplicit() { + this.isImplicit = true; + variableActions.stream() + .filter(VariableAction.Movable.class::isInstance) + .map(VariableAction.Movable.class::cast) + .map(VariableAction.Movable::getRealNode) + .forEach(GraphNode::markAsImplicit); + } + + /** Whether this graph node represents an AST node that didn't exist explicitly, such as 'super()'. */ + public boolean isImplicitInstruction() { + return isImplicit; + } + // ============================================================= // =================== Variables and Calls =================== // ============================================================= /** Whether this node contains the given call AST node. */ public boolean containsCall(Resolvable call) { - return methodCalls.contains(call); + return methodCalls.stream() + .anyMatch(callInMethod -> ASTUtils.equalsWithRangeInCU((Node) callInMethod, (Node) call)); } /** Append or prepend the given set of actions to the actions of the given call. */ @@ -110,14 +131,14 @@ public class GraphNode implements Comparable> { throw new IllegalArgumentException("Could not find markers for " + call.resolve().getSignature() + " in " + this); } - /** Append the given set of actions to after the actions of the given call. */ - public void addActionsAfterCall(Set actions, Resolvable call) { + /** Append the given actions to after the actions of the given call. */ + public void addActionsAfterCall(Resolvable call, VariableAction... actions) { for (int i = 0; i < variableActions.size(); i++) { VariableAction var = variableActions.get(i); if (var instanceof VariableAction.CallMarker) { VariableAction.CallMarker marker = (VariableAction.CallMarker) var; if (marker.getCall().equals(call) && !marker.isEnter()) { - variableActions.addAll(i + 1, actions); + variableActions.addAll(i + 1, List.of(actions)); return; } } @@ -126,19 +147,19 @@ public class GraphNode implements Comparable> { } /** Create and append a declaration of a variable to the list of actions of this node. */ - public void addDeclaredVariable(Expression variable) { - variableActions.add(new VariableAction.Declaration(variable, this)); + public void addDeclaredVariable(Expression variable, String realName) { + variableActions.add(new VariableAction.Declaration(variable, realName, this)); } /** Create and append a definition of a variable to the list of actions of this node. */ - public void addDefinedVariable(Expression variable, Expression expression) { - VariableAction.Definition def = new VariableAction.Definition(variable, this, expression); + public void addDefinedVariable(Expression variable, String realName, Expression expression) { + VariableAction.Definition def = new VariableAction.Definition(variable, realName, this, expression); variableActions.add(def); } /** Create and append a usage of a variable to the list of actions of this node. */ - public void addUsedVariable(Expression variable) { - VariableAction.Usage use = new VariableAction.Usage(variable, this); + public void addUsedVariable(Expression variable, String realName) { + VariableAction.Usage use = new VariableAction.Usage(variable, realName, this); variableActions.add(use); } 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 385076e616bbf575844e0a8c887ca16c8d3cd77d..556b12f6d74e455b6985fa89e7e9c69435e67508 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 @@ -1,56 +1,165 @@ package es.upv.mist.slicing.nodes; import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.expr.FieldAccessExpr; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; +import com.github.javaparser.resolution.types.ResolvedType; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.arcs.pdg.DataDependencyArc; import es.upv.mist.slicing.graphs.Graph; import es.upv.mist.slicing.graphs.pdg.PDG; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.SimpleDirectedGraph; import java.lang.reflect.InvocationTargetException; import java.util.Objects; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; /** An action upon a variable (e.g. usage, definition, declaration) */ public abstract class VariableAction { - protected static final String VARIABLE_PATTERN = "^([a-zA-Z][a-zA-Z0-9_]*|[_a-zA-Z][a-zA-Z0-9_]+)$"; + protected static final String VARIABLE_PATTERN = "([a-zA-Z][a-zA-Z0-9_]*|_[a-zA-Z0-9_]+)"; + protected static final String FIELD_PATTERN = "^" + VARIABLE_PATTERN + "(\\." + VARIABLE_PATTERN + ")*" + "$"; protected final Expression variable; + protected final String realName; protected final GraphNode graphNode; + protected final SimpleDirectedGraph objectTree = new SimpleDirectedGraph<>(null, DefaultEdge::new, false); protected boolean optional = false; protected ResolvedValueDeclaration resolvedVariableCache; - public VariableAction(Expression variable, GraphNode graphNode) { - assert variable.isNameExpr() || variable.isFieldAccessExpr(); + public VariableAction(Expression variable, String realName, GraphNode graphNode) { + assert realName != null && !realName.isEmpty(); this.variable = variable; + this.realName = realName; this.graphNode = graphNode; + this.objectTree.addVertex(realName); + } + + /** Add a field of this object, such that the same action performed on the object + * is applied to this field too. Fields of fields may be specified separated by dots. */ + public void addObjectField(String fieldName) { + String parent = null; + for (String element : fieldName.split("\\.")) { + objectTree.addVertex(element); + if (parent != null) + objectTree.addEdge(parent, element); + parent = element; + } + } + + public VariableAction getRootAction() { + assert !isRootAction(); + assert variable == null || variable.isNameExpr() || variable.isFieldAccessExpr() || variable.isThisExpr(); + if (this instanceof Movable) { + Movable movable = (Movable) this; + return new Movable(movable.inner.getRootAction(), (SyntheticNode) graphNode); + } + Expression nVar; + String nRealName = getRootVariable(); + GraphNode nNode = graphNode; + Expression nExpr = isDefinition() ? asDefinition().expression : null; + if (variable == null || !(variable instanceof FieldAccessExpr)) { + // This appears only when generated from a field: just set the variable to null + assert realName.contains(".this."); + nVar = null; + } else { // We are in a FieldAccessExpr + nVar = variable; + while (nVar.isFieldAccessExpr()) + nVar = variable.asFieldAccessExpr().getScope(); + } + if (this instanceof Usage) + return new Usage(nVar, nRealName, nNode); + if (this instanceof Definition) + return new Definition(nVar, nRealName, nNode, nExpr); + if (this instanceof Declaration) + throw new UnsupportedOperationException("Can't create a root node for a declaration!"); + throw new IllegalStateException("Invalid action type"); + } + + public String getRootVariable() { + Pattern rootVariable = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)).*$"); + Matcher matcher = rootVariable.matcher(realName); + if (matcher.matches()) { + if (matcher.group("root") != null) + return matcher.group("root"); // [type.this] or [this] + else + throw new IllegalStateException("Invalid real name: " + realName); + } else { + return null; + } + } + + public boolean isRootAction() { + return isSynthetic() || Objects.equals(getRootVariable(), realName); + } + + public static boolean typeMatches(VariableAction a, VariableAction b) { + return (a.isDeclaration() && b.isDeclaration()) || + (a.isDefinition() && b.isDefinition()) || + (a.isUsage() && b.isUsage()); + } + + public static boolean rootMatches(VariableAction a, VariableAction b) { + return a.getRootVariable().equals(b.getRootVariable()); } /** Whether this action is performed upon an invented variable, * introduced by this library (e.g. the active exception or the returned value). */ public boolean isSynthetic() { - return !getVariable().matches(VARIABLE_PATTERN); + return !getVariable().matches(FIELD_PATTERN); } public String getVariable() { - return variable.toString(); + return realName; + } + + public boolean hasVariableExpression() { + return variable != null; } public Expression getVariableExpression() { return variable; } + /** + * Returns the resolved value declaration. When the action being performed + * is done so on a ThisExpr, the resulting declaration has the following properties: + *
    + *
  • Can return type and name
  • + *
  • Is not a parameter, it's a field.
  • + *
  • All other methods are left to their default implementations.
  • + *
+ */ public ResolvedValueDeclaration getResolvedValueDeclaration() { if (resolvedVariableCache == null) { - if (variable.isFieldAccessExpr()) - resolvedVariableCache = variable.asFieldAccessExpr().resolve(); - else if (variable.isNameExpr()) - resolvedVariableCache = variable.asNameExpr().resolve(); + if (variable instanceof Resolvable) { + var resolved = ((Resolvable) variable).resolve(); + if (resolved instanceof ResolvedValueDeclaration) + resolvedVariableCache = (ResolvedValueDeclaration) resolved; + } + if (resolvedVariableCache == null) + resolvedVariableCache = new ResolvedValueDeclaration() { + @Override + public ResolvedType getType() { + return null; + } + + @Override + public String getName() { + return realName; + } + + @Override + public boolean isField() { + return true; + } + }; } return resolvedVariableCache; } @@ -68,7 +177,7 @@ public abstract class VariableAction { /** Whether the argument is performed upon the same variable as this action. */ public boolean matches(VariableAction action) { - return Objects.equals(action.variable, variable); + return Objects.equals(action.realName, realName); } public boolean isUsage() { @@ -97,34 +206,35 @@ public abstract class VariableAction { /** Creates a new usage action with the same variable and the given node. */ public final Usage toUsage(GraphNode graphNode) { - return new Usage(variable, graphNode); + return new Usage(variable, realName, graphNode); } /** Creates a new definition action with the same variable and the given node. */ public final Definition toDefinition(GraphNode graphNode) { - return new Definition(variable, graphNode); + return new Definition(variable, realName, graphNode); } /** Creates a new declaration action with the same variable and the given node. */ public final Declaration toDeclaration(GraphNode graphNode) { - return new Declaration(variable, graphNode); + return new Declaration(variable, realName, graphNode); } @Override public boolean equals(Object obj) { return obj instanceof VariableAction && obj.getClass().equals(getClass()) && - variable.equals(((VariableAction) obj).variable); + Objects.equals(variable, ((VariableAction) obj).variable) && + realName.equals(((VariableAction) obj).realName); } @Override public int hashCode() { - return Objects.hash(getClass(), variable); + return Objects.hash(getClass(), variable, realName); } @Override public String toString() { - return "{" + variable + "}"; + return "{" + realName + "}"; } /** An invented action used to locate the relative position of the start and end of a call inside a list of actions. */ @@ -133,7 +243,7 @@ public abstract class VariableAction { protected final boolean enter; public CallMarker(Resolvable call, GraphNode graphNode, boolean enter) { - super(new NameExpr(String.format("-%s-%s-", enter ? "call" : "return", call.resolve().getSignature())), graphNode); + super(null, String.format("-%s-%s-", enter ? "call" : "return", call.resolve().getSignature()), graphNode); this.call = call; this.enter = enter; } @@ -162,8 +272,8 @@ public abstract class VariableAction { /** A usage of a variable. */ public static class Usage extends VariableAction { - public Usage(Expression variable, GraphNode graphNode) { - super(variable, graphNode); + public Usage(Expression variable, String realName, GraphNode graphNode) { + super(variable, realName, graphNode); } @Override @@ -177,12 +287,12 @@ public abstract class VariableAction { /** The value to which the variable has been defined. */ protected final Expression expression; - public Definition(Expression variable, GraphNode graphNode) { - this(variable, graphNode, null); + public Definition(Expression variable, String realName, GraphNode graphNode) { + this(variable, realName, graphNode, null); } - public Definition(Expression variable, GraphNode graphNode, Expression expression) { - super(variable, graphNode); + public Definition(Expression variable, String realName, GraphNode graphNode, Expression expression) { + super(variable, realName, graphNode); this.expression = expression; } @@ -199,8 +309,8 @@ public abstract class VariableAction { /** A declaration of a variable. */ public static class Declaration extends VariableAction { - public Declaration(Expression variable, GraphNode graphNode) { - super(variable, graphNode); + public Declaration(Expression variable, String realName, GraphNode graphNode) { + super(variable, realName, graphNode); } @Override @@ -222,7 +332,7 @@ public abstract class VariableAction { * to generate dependencies and a {@link PDG PDG} node that * is the final location of this action. */ public Movable(VariableAction inner, SyntheticNode pdgNode) { - super(inner.variable, inner.graphNode); + super(inner.variable, inner.realName, inner.graphNode); if (inner instanceof Movable) throw new IllegalArgumentException("'inner' must be an unmovable action"); this.realNode = pdgNode; @@ -243,11 +353,11 @@ public abstract class VariableAction { VariableAction newAction; try { if (inner instanceof Definition && inner.asDefinition().getExpression() != null) - newAction = inner.getClass().getConstructor(Expression.class, GraphNode.class, Expression.class) - .newInstance(variable, realNode, inner.asDefinition().expression); + newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class, Expression.class) + .newInstance(variable, realName, realNode, inner.asDefinition().expression); else - newAction = inner.getClass().getConstructor(Expression.class, GraphNode.class) - .newInstance(variable, realNode); + newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class) + .newInstance(variable, realName, realNode); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new UnsupportedOperationException("The VariableAction constructor has changed!", e); } 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 dd407e53d86db8f2b5dacf71fb38eaf19a20889a..fff957332d8278f2dc0b96d47c7ea18fe8bd94f9 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 @@ -1,6 +1,7 @@ package es.upv.mist.slicing.nodes; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.*; @@ -8,15 +9,21 @@ import com.github.javaparser.ast.stmt.CatchClause; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.stmt.ForEachStmt; import com.github.javaparser.resolution.Resolvable; +import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.GraphNodeContentVisitor; +import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.utils.ASTUtils; -import es.upv.mist.slicing.utils.TriConsumer; +import es.upv.mist.slicing.utils.Logger; import java.util.Deque; import java.util.LinkedList; import java.util.Objects; -import java.util.function.BiConsumer; +import java.util.Set; + +import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; /** 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). @@ -43,16 +50,12 @@ public class VariableVisitor extends GraphNodeContentVisitor, Expression> BLANK_BICONSUMER = (a, b) -> {}; - protected static final TriConsumer, Expression, Expression> BLANK_TRICONSUMER = (a, b, c) -> {}; - /** The action to perform when a declaration is found. */ - protected final BiConsumer, Expression> declConsumer; + protected final DeclarationConsumer declConsumer; /** The action to perform when a definition is found. */ - protected final TriConsumer, Expression, Expression> defConsumer; + protected final DefinitionConsumer defConsumer; /** The action to perform when a usage is found. */ - protected final BiConsumer, Expression> useConsumer; + protected final UsageConsumer useConsumer; /** A stack with the last definition expression, to provide it when a variable definition is found. */ protected final Deque definitionStack = new LinkedList<>(); @@ -65,14 +68,10 @@ public class VariableVisitor extends GraphNodeContentVisitor, Expression> declConsumer, TriConsumer, Expression, Expression> defConsumer, BiConsumer, Expression> useConsumer) { - this.declConsumer = Objects.requireNonNullElse(declConsumer, BLANK_BICONSUMER); - this.defConsumer = Objects.requireNonNullElse(defConsumer, BLANK_TRICONSUMER); - this.useConsumer = Objects.requireNonNullElse(useConsumer, BLANK_BICONSUMER); - } - - public void visitAsDefinition(Node node, Expression value) { - visitAsDefinition(node, value, Action.DEFINITION); + public VariableVisitor(DeclarationConsumer declConsumer, DefinitionConsumer defConsumer, UsageConsumer useConsumer) { + this.declConsumer = Objects.requireNonNullElse(declConsumer, DeclarationConsumer.defaultConsumer()); + this.defConsumer = Objects.requireNonNullElse(defConsumer, DefinitionConsumer.defaultConsumer()); + this.useConsumer = Objects.requireNonNullElse(useConsumer, UsageConsumer.defaultConsumer()); } public void visitAsDefinition(Node node, Expression value, Action action) { @@ -84,6 +83,7 @@ public class VariableVisitor extends GraphNodeContentVisitor node) { startVisit(node, Action.USE); + groupActionsByRoot(node); } @Override @@ -91,10 +91,13 @@ public class VariableVisitor extends GraphNodeContentVisitor l.accept(this, arg)); } @Override public void visit(AssignExpr n, Action action) { + // Value is always visited first since uses occur before definitions + n.getValue().accept(this, action); // Target will be used if operator is not '=' if (n.getOperator() != AssignExpr.Operator.ASSIGN) n.getTarget().accept(this, action); visitAsDefinition(n.getTarget(), n.getValue(), action); - n.getValue().accept(this, action); } @Override @@ -205,14 +213,28 @@ public class VariableVisitor extends GraphNodeContentVisitor { init.accept(this, action); - visitAsDefinition(v.getNameAsExpression(), init); + defConsumer.acceptDefinition(graphNode, null, realName, init); }); } } + @Override + public void visit(FieldDeclaration n, Action action) { + for (VariableDeclarator v : n.getVariables()) { + String realName; + realName = getRealNameForFieldDeclaration(v); + declConsumer.acceptDeclaration(graphNode, null, realName); + Expression init = v.getInitializer().orElseGet(() -> ASTUtils.initializerForField(n)); + init.accept(this, action); + defConsumer.acceptDefinition(graphNode, null, realName, init); + } + } + @Override public void visit(CatchClause n, Action arg) { n.getParameter().accept(this, arg.or(Action.DECLARATION)); @@ -221,8 +243,8 @@ public class VariableVisitor extends GraphNodeContentVisitor s.accept(this, arg)); graphNode.addCallMarker(call, false); return false; } + + /** Adds a declaration for the variable 'this'. */ + protected void declareThis(ExplicitConstructorInvocationStmt call) { + String variableName = getFQClassName(call) + ".this"; + declConsumer.acceptDeclaration(graphNode, null, variableName); + } + + /** Obtains the fully qualified class name of the class that contains an AST node. */ + protected String getFQClassName(Node node) { + // Known limitation: anonymous classes do not have a FQ class name. + return ASTUtils.getClassNode(node).getFullyQualifiedName().orElseThrow(); + } + + /** Prepends [declaring class name].this. to the name of the given variable declarator. */ + protected String getRealNameForFieldDeclaration(VariableDeclarator decl) { + return ASTUtils.getClassNode(decl).getFullyQualifiedName().orElseThrow() + ".this." + decl.getNameAsString(); + } + + /** Obtain the prefixed name of a variable, to improve matching of variables + * that point to the same object, such as 'x' and 'this.x'. */ + protected String getRealName(Expression n) { + if (n.isNameExpr()) { + try { + return getNamePrefix(n.asNameExpr()) + n.toString(); + } catch (UnsolvedSymbolException e) { + Logger.log("Unable to resolve symbol " + e.getName()); + } + } else if (n.isThisExpr()) { + var hasTypeName = n.asThisExpr().getTypeName().isPresent(); + return (hasTypeName ? n.asThisExpr().resolve().getQualifiedName() : getFQClassName(n)) + ".this"; + } + return 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 ""; + // Obtain the class where the field is declared + ResolvedTypeDeclaration declaringType = resolved.asField().declaringType(); + // Generate the full prefix + return declaringType.getQualifiedName() + ".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.isRootAction() || action.isDeclaration() || + action instanceof VariableAction.CallMarker) { + 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 { + // Check if action matches the previously generated root action + if (VariableAction.rootMatches(action, lastRootAction) + && VariableAction.typeMatches(action, lastRootAction)) { + lastRootAction.addObjectField(action.getVariable()); + } else { + // 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(); + } + } + } + // Append the last root action if there is any! + if (lastRootAction != null) + graphNode.variableActions.add(lastRootAction); + } + + @FunctionalInterface + public interface DeclarationConsumer { + void acceptDeclaration(GraphNode graphNode, Expression variable, String realName); + + static DeclarationConsumer defaultConsumer() { + return (a, b, c) -> {}; + } + } + + @FunctionalInterface + public interface DefinitionConsumer { + void acceptDefinition(GraphNode graphNode, Expression variable, String realName, Expression valueAssigned); + + static DefinitionConsumer defaultConsumer() { + return (a, b, c, d) -> {}; + } + } + + @FunctionalInterface + public interface UsageConsumer { + void acceptUsage(GraphNode graphNode, Expression variable, String realName); + + static UsageConsumer defaultConsumer() { + return (a, b, c) -> {}; + } + } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java index d7704a0a0122a978c0e478efc4ded190ea7c7647..e0745563653e14fd120539fbbf3ec8367656fbf2 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java @@ -8,7 +8,6 @@ import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclarat import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; -import com.github.javaparser.resolution.types.ResolvedType; import java.util.Objects; @@ -17,8 +16,8 @@ public class ActualIONode extends IONode { protected final Expression argument; protected ActualIONode(Resolvable astNode, ResolvedValueDeclaration variable, Expression argument, boolean isInput) { - super(createLabel(isInput, variable.getType(), variable.getName(), argument), (Node) astNode, variable.getType(), variable.getName(), isInput); - this.argument = Objects.requireNonNull(argument); + super(createLabel(isInput, variable.getName(), argument), (Node) astNode, variable.getName(), isInput); + this.argument = argument; } public Expression getArgument() { @@ -30,7 +29,6 @@ public class ActualIONode extends IONode { return getClass().equals(ActualIONode.class) && o.getClass().equals(FormalIONode.class) // 2. Our variables must match (type + name) && Objects.equals(variableName, o.variableName) - && Objects.equals(variableType, o.variableType) // 3. in matches in, out matches out && isInput() == o.isInput() // 4. The method call must resolve to the method declaration of the argument. @@ -58,9 +56,9 @@ public class ActualIONode extends IONode { return Objects.hash(super.hashCode(), argument); } - protected static String createLabel(boolean isInput, ResolvedType paramType, String paramName, Expression arg) { + protected static String createLabel(boolean isInput, String paramName, Expression arg) { if (isInput) - return String.format("%s %s_in = %s", paramType.describe(), paramName, arg.toString()); + return String.format("%s_in = %s", paramName, arg); else return String.format("%s = %s_out", arg, paramName); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java index fe4a0da0ff6f284031e90f67a2fa5858d4e9ca25..4b3864ea9a180ed78efc73d31b84024e6b4acdfd 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java @@ -2,36 +2,34 @@ package es.upv.mist.slicing.nodes.io; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; -import com.github.javaparser.resolution.types.ResolvedType; /** A formal-in or formal-out node, displaying interprocedural data dependencies. */ public class FormalIONode extends IONode> { - protected FormalIONode(CallableDeclaration astNode, ResolvedType varType, String varName, boolean isInput) { - this(createLabel(isInput, varType, varName), astNode, varType, varName, isInput); + protected FormalIONode(CallableDeclaration astNode, String varName, boolean isInput) { + this(createLabel(isInput, varName), astNode, varName, isInput); } - protected FormalIONode(String text, CallableDeclaration astNode, ResolvedType varType, String varName, boolean isInput) { - super(text, astNode, varType, varName, isInput); + protected FormalIONode(String text, CallableDeclaration astNode, String varName, boolean isInput) { + super(text, astNode, varName, isInput); } - protected static String createLabel(boolean isInput, ResolvedType varType, String varName) { + protected static String createLabel(boolean isInput, String varName) { if (isInput) - return String.format("%s %s = %2$s_in", varType.describe(), varName); + return String.format("%s = %1$s_in", varName); else - return String.format("%s %s_out = %2$s", varType.describe(), varName); + return String.format("%s_out = %1$s", varName); } public static FormalIONode createFormalIn(CallableDeclaration declaration, ResolvedValueDeclaration resolvedValue) { - return new FormalIONode(declaration, resolvedValue.getType(), resolvedValue.getName(), true); + return new FormalIONode(declaration, resolvedValue.getName(), true); } public static FormalIONode createFormalOut(CallableDeclaration declaration, ResolvedValueDeclaration resolvedValue) { - return new FormalIONode(declaration, resolvedValue.getType(), resolvedValue.getName(), false); + return new FormalIONode(declaration, resolvedValue.getName(), false); } public static FormalIONode createFormalInDecl(CallableDeclaration declaration, ResolvedValueDeclaration resolvedValue) { - ResolvedType type = resolvedValue.getType(); String name = resolvedValue.getName(); - return new FormalIONode(type.describe() + " " + name, declaration, type, name, true); + return new FormalIONode(name, declaration, name, true); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java index 09e0d33436bd988fbf844951d1384d0506fe2ee2..d6ee3847ff4f99ca956bce231e5f0f92d0395905 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java @@ -1,7 +1,6 @@ package es.upv.mist.slicing.nodes.io; import com.github.javaparser.ast.Node; -import com.github.javaparser.resolution.types.ResolvedType; import es.upv.mist.slicing.nodes.SyntheticNode; import java.util.LinkedList; @@ -10,12 +9,10 @@ import java.util.Objects; /** A node representing an input or output from a declaration or call (formal or actual). */ public abstract class IONode extends SyntheticNode { protected final boolean isInput; - protected final ResolvedType variableType; protected final String variableName; - protected IONode(String instruction, T astNode, ResolvedType variableType, String variableName, boolean isInput) { + protected IONode(String instruction, T astNode, String variableName, boolean isInput) { super(instruction, astNode, new LinkedList<>()); - this.variableType = variableType; this.variableName = variableName; this.isInput = isInput; } @@ -32,12 +29,11 @@ public abstract class IONode extends SyntheticNode { public boolean equals(Object o) { return super.equals(o) && o instanceof IONode && ((IONode) o).isInput == isInput - && Objects.equals(((IONode) o).variableType, variableType) && Objects.equals(((IONode) o).variableName, variableName); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), isInput, variableType, variableName); + return Objects.hash(super.hashCode(), isInput, variableName); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java index d888ce2bea1f849570b84c0136bd29b595b199e6..0aa3a3e4726c83399c53752f1a7c8142d315a9a4 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java @@ -115,6 +115,7 @@ public class ExceptionSensitiveSlicingAlgorithm implements SlicingAlgorithm { protected boolean ppdgIgnore(Arc arc) { GraphNode target = graph.getEdgeTarget(arc); return arc.isUnconditionalControlDependencyArc() && + graph.isPseudoPredicate(target) && reachedStream(target).allMatch(Arc::isUnconditionalControlDependencyArc) && !target.equals(slicingCriterion); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java index b79d4392e6d8cab5bd35d003d4d78ad4bcccadae..22ef03f2918daff1029e319f0a9cab3b5194e8a5 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java @@ -16,14 +16,11 @@ import java.util.*; public class Slice { /** Nodes contained in this slice, mapped by id. */ private final Map> map = new HashMap<>(); - /** The AST nodes contained in this slice. */ - private final List nodes = new LinkedList<>(); /** Add a node to this slice. */ public void add(GraphNode node) { assert !map.containsKey(node.getId()); map.put(node.getId(), node); - nodes.add(node.getAstNode()); } /** Add multiple nodes to this slice. */ @@ -38,7 +35,7 @@ public class Slice { /** Whether the slice contains the given AST node. */ public boolean contains(Node node) { - return nodes.stream().anyMatch(n -> n == node); + return map.values().stream().anyMatch(gn -> gn.getAstNode() == node); } @Override @@ -62,11 +59,13 @@ public class Slice { Map> cuMap = ASTUtils.newIdentityHashMap(); // Add each node to the corresponding bucket of the map // Nodes may not belong to a compilation unit (fictional nodes), and they are skipped for the slice. - for (Node node : nodes) { - Optional cu = node.findCompilationUnit(); + for (GraphNode graphNode : map.values()) { + if (graphNode.isImplicitInstruction()) + continue; + Optional cu = graphNode.getAstNode().findCompilationUnit(); if (cu.isEmpty()) continue; cuMap.computeIfAbsent(cu.get(), compilationUnit -> new NodeHashSet<>()); - cuMap.get(cu.get()).add(node); + cuMap.get(cu.get()).add(graphNode.getAstNode()); } // Traverse the AST of each compilation unit, creating a copy and // removing any element not present in the slice. diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java index 6381413573f9a89f66747c72cb85bcaa390f1ce8..f5569b099709900bfc2a5290131b5fb307bc5b33 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java @@ -3,10 +3,7 @@ package es.upv.mist.slicing.slicing; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; -import com.github.javaparser.ast.body.ConstructorDeclaration; -import com.github.javaparser.ast.body.FieldDeclaration; -import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.BooleanLiteralExpr; import com.github.javaparser.ast.nodeTypes.NodeWithBody; import com.github.javaparser.ast.stmt.*; @@ -44,7 +41,8 @@ public class SlicePruneVisitor extends ModifierVisitor> { public Visitable visit(ClassOrInterfaceDeclaration n, NodeHashSet arg) { boolean keep = arg.contains(n); Visitable v = super.visit(n, arg); - return keep || !((Node) v).getChildNodes().isEmpty() ? v : null; + boolean containsDeclarations = ((Node) v).getChildNodes().stream().anyMatch(BodyDeclaration.class::isInstance); + return keep || containsDeclarations ? v : null; } // ========== Class body visitors ========== @@ -65,9 +63,7 @@ public class SlicePruneVisitor extends ModifierVisitor> { @Override public Visitable visit(FieldDeclaration n, NodeHashSet arg) { - boolean keep = arg.contains(n); - Visitable v = super.visit(n, arg); - return keep ? v : null; + return arg.contains(n) ? n : null; } // ========== Method body visitors ========== diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java b/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java index 436742d879e8231983c9969ba7e50839623850df..0d8629c19af758633fa70833dbece41a1b49d837 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java @@ -1,17 +1,14 @@ package es.upv.mist.slicing.utils; import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.body.CallableDeclaration; -import com.github.javaparser.ast.body.ConstructorDeclaration; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.expr.CastExpr; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.ObjectCreationExpr; +import com.github.javaparser.ast.body.*; +import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.stmt.SwitchEntry; import com.github.javaparser.ast.stmt.SwitchStmt; +import com.github.javaparser.ast.type.PrimitiveType; +import com.github.javaparser.ast.type.Type; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.*; import com.github.javaparser.resolution.types.ResolvedType; @@ -125,6 +122,11 @@ public class ASTUtils { return shouldVisitArgumentsForMethodCalls(call) || graphNode == null; } + public static boolean constructorHasExplicitConstructorInvocation(ConstructorDeclaration declaration) { + return !getCallableBody(declaration).getStatements().isEmpty() && + getCallableBody(declaration).getStatements().getFirst().get() instanceof ExplicitConstructorInvocationStmt; + } + /** * Creates a new set that is suitable for JavaParser nodes. This * set behaves by comparing by identity (==) instead of equality (equals()). @@ -175,4 +177,27 @@ public class ASTUtils { throw new IllegalArgumentException("This operation is only valid for reference type cast operations."); } + /** Given an AST node, visit the parent until finding a ClassOrInterfaceDeclaration */ + public static ClassOrInterfaceDeclaration getClassNode(Node n){ + Node upperNode = n; + while (!(upperNode instanceof ClassOrInterfaceDeclaration)) + upperNode = upperNode.getParentNode().orElseThrow(); + return (ClassOrInterfaceDeclaration) upperNode; + } + + /** Generates the default initializer, given a field. In Java, reference types + * default to null, booleans to false and all other primitives to 0. */ + public static Expression initializerForField(FieldDeclaration field) { + Type type = field.getVariables().getFirst().orElseThrow().getType(); + if (type.isReferenceType()) + return new NullLiteralExpr(); + if (type.isPrimitiveType()) { + PrimitiveType primitive = type.asPrimitiveType(); + if (primitive.equals(PrimitiveType.booleanType())) + return new BooleanLiteralExpr(); + else + return new IntegerLiteralExpr(); + } + throw new IllegalArgumentException("Invalid typing for a field"); + } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/utils/TriConsumer.java b/sdg-core/src/main/java/es/upv/mist/slicing/utils/TriConsumer.java deleted file mode 100644 index fe933c27bbb5354d34823c597a4a3795a6645a3f..0000000000000000000000000000000000000000 --- a/sdg-core/src/main/java/es/upv/mist/slicing/utils/TriConsumer.java +++ /dev/null @@ -1,5 +0,0 @@ -package es.upv.mist.slicing.utils; - -public interface TriConsumer { - void accept(T arg1, U arg2, V arg3); -} 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 6e823b8e9deaa5813a20593fb591d883f4c91037..d5f2dc8899bfc2884994f0ff62d4fc9100c20336 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 @@ -25,7 +25,8 @@ public class Utils { public static Map dotLabel(String label) { Map map = new HashMap<>(); - map.put("label", DefaultAttribute.createAttribute(label)); + if (label != null) + map.put("label", DefaultAttribute.createAttribute(label)); return map; } } diff --git a/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java b/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java index 21f022482da8d58f66b67f94e5aa36ee73247661..f111440cb79d4e69a680e573fa80a4a2ccbb84c4 100644 --- a/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java +++ b/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java @@ -3,7 +3,7 @@ package es.upv.mist.slicing; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; -import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; +import es.upv.mist.slicing.graphs.jsysdg.JSysDG; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.slicing.FileLineSlicingCriterion; import es.upv.mist.slicing.slicing.Slice; @@ -127,7 +127,7 @@ public class SlicerTest { } private static Slice slice(File javaFile, SlicingCriterion sc) throws FileNotFoundException { - SDG sdg = new ESSDG(); + SDG sdg = new JSysDG(); sdg.build(new NodeList<>(StaticJavaParser.parse(javaFile))); return sdg.slice(sc); } diff --git a/sdg-core/src/test/res/regression/carlos/Problem2.java.sdg.sliced b/sdg-core/src/test/res/regression/carlos/Problem2.java.sdg.sliced index 369ed034b4f3e47808b556e09d3b4dcb3fb8009a..a27fe6711e290e2ad4029bf50883e731113488cb 100644 --- a/sdg-core/src/test/res/regression/carlos/Problem2.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/carlos/Problem2.java.sdg.sliced @@ -2,6 +2,11 @@ public class Problem2 { public static void main(String[] args) { int a = 0; + while (a >= 0) { + if (a > 10) + break; + a++; + } System.out.println(a); } } diff --git a/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced b/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced index 4fadd2bf812a24a1b020ba63b3697a03a04f6268..f181758b8ee19c4a838550ceff8028bcfb8cc363 100644 --- a/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced @@ -2,6 +2,11 @@ public class Problem3 { public static void main() throws Exception { x = 0; + try { + f(); + } catch (Exception e) { + } + x = 1; f(); } diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Carlos1.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Carlos1.java.sdg.sliced index 18cd2cc996843a7c6eb2007d97f2882b299af306..b284c03406421dd219f86b4a38ebf78319c006ea 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Carlos1.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Carlos1.java.sdg.sliced @@ -4,9 +4,3 @@ class A { int SC = 42; } } - -class Exception2 extends Exception { -} - -class Exception3 extends Exception2 { -} diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Carlos2.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Carlos2.java.sdg.sliced index 0df3d837d445ef88a28c59371f9df018404910c0..f6c39ff55ac1a84095b76cb386f49257c6f97401 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Carlos2.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Carlos2.java.sdg.sliced @@ -26,9 +26,3 @@ class A { return e1; } } - -class Exception2 extends Exception { -} - -class Exception3 extends Exception2 { -} diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced index 49c80edb67799d606ed51870e8d8e8edc6211080..074020810eecf70c9e2f14fb9ad8f93aaee625a4 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced @@ -21,6 +21,8 @@ public class Josep2 { class Numeros { + int noHaceFalta = 1; + int random() { return haceFalta; } diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced index bb7aeb23696f17072b5f8d4303c23d73bae01926..739b3a49a11a428c16e42f83cf547b9aad0da2cf 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced @@ -1,5 +1,7 @@ class A { + int x = 0; + A(int x) { } diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced index d9e3a797b0b47c5db764e0fb30b3d9ab1175b86c..6551a9e6dd436831a68a0deedfbe7402716b04f1 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced @@ -1,5 +1,7 @@ class A { + int x = 0; + A(int x) { } diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep5.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep5.java.sdg.sliced index 641233c9c9160b7af9210102a9c3926ed345f94e..7df4908575c9a6deb0be4371ea5c7fe5e4b4ad97 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep5.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep5.java.sdg.sliced @@ -1,16 +1,15 @@ class A { + int xA = 0; + A(int newx) { + xA = newx; } int getx() { return xA; } - void setx(int newx) { - xA = newx; - } - public static void main(String[] args) { B b1 = new B(5.6); int z = b1.getx(); diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced index 332b824b07be5aa6835e04c789d643569d19a887..90e514e8ac03b35ecec7e5f370d32438a357538a 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced @@ -1,10 +1,14 @@ class A { + int xA = 0; + A(int newx) { + xA = newx; } public static void main(String[] args) { B b1 = new B(5.6); + b1.modificarxB(b1); int z = b1.getxB(); System.out.println(z); } diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep7.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep7.java.sdg.sliced index 6bc5db64d13dfd49f16f36bf4580fce924217371..a42669b291abd987a854f74e0776f8fd424ad30c 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep7.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep7.java.sdg.sliced @@ -1,25 +1,18 @@ class A { + int xA = 0; + A(int newx) { + xA = newx; } int getx() { return xA; } - void siModificaxA(int v) { - xA++; - } - public static void main(String[] args) { A a1 = new A(1); int z = a1.getx(); System.out.println(z); } } - -class B extends A { - - void siModificaxA(int v) { - } -} diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java b/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java index 539cb1c0562bc71ecc85749fdab076f514e53b9a..8a6cef776b55e15f29f583e0f59c8a85d2a16276 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java @@ -43,4 +43,4 @@ class B extends A{ void noModificaxA() {System.out.println("useless");} void siModificaxA(int v) {super.siModificaxA(v);} -} \ No newline at end of file +} diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java.sdg.sliced index 6bc5db64d13dfd49f16f36bf4580fce924217371..a42669b291abd987a854f74e0776f8fd424ad30c 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep8.java.sdg.sliced @@ -1,25 +1,18 @@ class A { + int xA = 0; + A(int newx) { + xA = newx; } int getx() { return xA; } - void siModificaxA(int v) { - xA++; - } - public static void main(String[] args) { A a1 = new A(1); int z = a1.getx(); System.out.println(z); } } - -class B extends A { - - void siModificaxA(int v) { - } -} diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep9.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep9.java.sdg.sliced index 3a6b6c2029d859a08bbfc20fffd7964c75d4bec5..d20b0aa9faf1af7dcd035a475fc2d728eccfc978 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep9.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep9.java.sdg.sliced @@ -1,5 +1,7 @@ class A { + int xA = 0; + int getx() { return xA; } @@ -14,6 +16,8 @@ class A { public static void main(String[] args) { A a1 = new A(); + a1.setx(5); + a1.siModificaxA(5); int z = a1.getx(); System.out.println(z); } diff --git a/sdg-core/src/test/res/regression/ltd-samples/BasicBreak.java.sdg.sliced b/sdg-core/src/test/res/regression/ltd-samples/BasicBreak.java.sdg.sliced index 79eb72088376f57cd46d4f1ca3b5ebfa0c2059bf..403a692a76f7fcbc1706354ee0e2ad0d0bccb0ee 100644 --- a/sdg-core/src/test/res/regression/ltd-samples/BasicBreak.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/ltd-samples/BasicBreak.java.sdg.sliced @@ -6,6 +6,12 @@ public class BasicBreak { int x = 0; bucle: while (true) { x++; + for (int y = 0; y < 10; y++) { + if (y == x) + break; + if (y * 2 == x) + break bucle; + } if (x > 10) break; x++; diff --git a/sdg-core/src/test/res/regression/ltd-samples/BasicContinue.java.sdg.sliced b/sdg-core/src/test/res/regression/ltd-samples/BasicContinue.java.sdg.sliced index c00796b61a5be7f4f09138b37e74b23c064e3c84..f001b24f2ce8ce40ca9a3b52a6bb4218ec7027be 100644 --- a/sdg-core/src/test/res/regression/ltd-samples/BasicContinue.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/ltd-samples/BasicContinue.java.sdg.sliced @@ -6,6 +6,12 @@ public class BasicContinue { int x = 0; bucle: while (x < 20) { x++; + for (int y = 0; y < 10; y++) { + if (y == x) + continue; + if (y * 2 == x) + continue bucle; + } if (x > 10) continue; x++; diff --git a/sdg-core/src/test/res/regression/ltd-samples/Bucles_Josep.java.sdg.sliced b/sdg-core/src/test/res/regression/ltd-samples/Bucles_Josep.java.sdg.sliced index 7883a00d30d3626261269a2f7d14d165362952d6..3ac6ff8e22b8ca6d1cb360f646493dbe5dc84e5d 100644 --- a/sdg-core/src/test/res/regression/ltd-samples/Bucles_Josep.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/ltd-samples/Bucles_Josep.java.sdg.sliced @@ -6,5 +6,12 @@ public class Bucles_Josep { int x = 0; int y = 0, z = 0; z = x + y; + while (z == 0) { + if (z == 0) { + z++; + } else { + z--; + } + } } } diff --git a/sdg-core/src/test/res/regression/ltd-samples/Test_2.java.sdg.sliced b/sdg-core/src/test/res/regression/ltd-samples/Test_2.java.sdg.sliced index b61c5b7ccc5f6a97ded253274176eea495d87571..162179911150fce58e421c66e6c7379e31bd3f4a 100644 --- a/sdg-core/src/test/res/regression/ltd-samples/Test_2.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/ltd-samples/Test_2.java.sdg.sliced @@ -4,6 +4,8 @@ public class Test_2 { public static void main(String[] args) { int x = 1; + x++; + ++x; int y = 0; x = x + y; System.out.println(x); diff --git a/sdg-core/src/test/res/regression/programs/cfg/Eval_1.java.sdg.sliced b/sdg-core/src/test/res/regression/programs/cfg/Eval_1.java.sdg.sliced index e26a9e44fb280ff36fd60b813c667dc56a9e1e7c..a77113e5192fb8208d3b37b48427eef5c6202b44 100644 --- a/sdg-core/src/test/res/regression/programs/cfg/Eval_1.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/programs/cfg/Eval_1.java.sdg.sliced @@ -8,6 +8,12 @@ public class Eval_1 { int x = 1; one: while (x <= 10) { x++; + while (x <= 10) { + if (x > 0) { + continue one; + } else + x++; + } } do { x--; diff --git a/sdg-core/src/test/res/regression/programs/cfg/Eval_2.java.sdg.sliced b/sdg-core/src/test/res/regression/programs/cfg/Eval_2.java.sdg.sliced index ee318541c02a0fa5d19c90e8970bec66f5e7a5c0..1efef45be330143dc1d920e65e1064f0d3e2e5a4 100644 --- a/sdg-core/src/test/res/regression/programs/cfg/Eval_2.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/programs/cfg/Eval_2.java.sdg.sliced @@ -4,6 +4,15 @@ public class Eval_2 { public static void main(String[] args) { int x = 1; + if (x <= 1) { + if (x <= 2) { + if (x <= 3) { + x++; + } else + x--; + } else + x--; + } System.out.println(x); } } diff --git a/sdg-core/src/test/res/regression/programs/cfg/Eval_3.java.sdg.sliced b/sdg-core/src/test/res/regression/programs/cfg/Eval_3.java.sdg.sliced index 05636d5ca0a80639e2a092c03bfd21692d0c1643..87b30f4d5ac2ce31c0925e5f89746ba6bdf7bd89 100644 --- a/sdg-core/src/test/res/regression/programs/cfg/Eval_3.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/programs/cfg/Eval_3.java.sdg.sliced @@ -4,5 +4,19 @@ public class Eval_3 { public static void main(String[] args) { int x = 1; + while (x > 0) { + if (x <= 1) { + x--; + } else if (x <= 2) { + if (x <= 3) { + x++; + } else { + x--; + while (x > 1) { + x--; + } + } + } + } } } diff --git a/sdg-core/src/test/res/regression/programs/pdg/Test.java.sdg.sliced b/sdg-core/src/test/res/regression/programs/pdg/Test.java.sdg.sliced index 883158fe333145cc8a6aac61df2de49c8be07b00..84cf2b17a07b62c4799663f52c49c3441c89afea 100644 --- a/sdg-core/src/test/res/regression/programs/pdg/Test.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/programs/pdg/Test.java.sdg.sliced @@ -4,6 +4,11 @@ public class Test { public static void main(String[] args) { int a = 0; + while (a > 0) { + if (a > 100) + continue; + a++; + } System.out.println(a); } } diff --git a/sdg-core/src/test/res/regression/review-07-2020/P3.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P3.java.sdg.sliced index d72239364e8d11b6f75a446fb8b6b2d98584356b..29d44d23789fde99840aa6b80931b88787d07bcb 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P3.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P3.java.sdg.sliced @@ -2,11 +2,16 @@ public class Bucles { public static void main(String[] args) { int x = 2; + try { + if (x == 0) + throw new ExceptionA(); + if (x == 1) + throw new ExceptionB(); + if (x == 2) + throw new Exception(); + } catch (ExceptionA a) { + } catch (Exception a) { + System.out.println("Se lanza Exception"); + } } } - -class ExceptionA extends Exception { -} - -class ExceptionB extends ExceptionA { -} diff --git a/sdg-core/src/test/res/regression/review-07-2020/P4.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P4.java.sdg.sliced index 1c388f175912417300b44139a875105ea4ac40dc..6907e2ebf86398c318f580ffe37b4f6030d60616 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P4.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P4.java.sdg.sliced @@ -1,12 +1,27 @@ public class Bucles { public static void main(String[] args) { + try { + metodoGeneradorExcepciones(); + } catch (ExceptionB a) { + main(args); + } catch (ExceptionA a) { + main(args); + } catch (Exception a) { + main(args); + } System.out.println("No se lanza ninguna excepcion"); } -} - -class ExceptionA extends Exception { -} -class ExceptionB extends ExceptionA { + static void metodoGeneradorExcepciones() throws Exception { + if (x == 0) { + throw new ExceptionA(); + } + if (x == 1) { + throw new ExceptionB(); + } + if (x == 2) { + throw new Exception(); + } + } } diff --git a/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced index 49c80edb67799d606ed51870e8d8e8edc6211080..074020810eecf70c9e2f14fb9ad8f93aaee625a4 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced @@ -21,6 +21,8 @@ public class Josep2 { class Numeros { + int noHaceFalta = 1; + int random() { return haceFalta; } diff --git a/sdg-core/src/test/res/regression/review-07-2020/P6.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P6.java.sdg.sliced index 7e9046d64a3e9bfb040c463cab71ac8c5ce46b0d..b2595e26ff3f4e84312189d9878b8021efa37dfc 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P6.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P6.java.sdg.sliced @@ -1,5 +1,16 @@ public class Bucles { + public static void main(String[] args) { + try { + for (int i = 0; i <= 12; i++) { + metodoGeneradorExcepciones(x); + } + } catch (ExceptionA a) { + } catch (Exception a) { + System.out.println("Se lanza Exception"); + } + } + static void metodoGeneradorExcepciones(int x) throws Exception { if (x == 0) throw new ExceptionA(); @@ -9,9 +20,3 @@ public class Bucles { throw new Exception(); } } - -class ExceptionA extends Exception { -} - -class ExceptionB extends ExceptionA { -}