diff --git a/README.md b/README.md index 0bd663e..f439d73 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,98 @@ +[![Join the chat at https://gitter.im/JavaDataFlow/community](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/JavaDataFlow/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![maven-central](https://img.shields.io/maven-central/v/com.github.daanvdh.javadataflow/JavaDataFlow.svg)](https://search.maven.org/search?q=g:com.github.daanvdh.javadataflow) + # JavaDataFlow Creating Data Flow Graphs from java input classes + +With JavaDataFlow you can create data flow graphs. +This is a directed graph where a node represents data (e.g. fields, inputParameters, etc.) and the edges represent a node influencing the state of another node. +Nodes are always defined within a specific method, constructor, codeBlock. +We refer to such a code block as owner. +Every Node has an owner. +Every owner can also have an owner, for instance a method is owned by a class. +To make it possible to only parse a part of an application we model calls to a method and the dataflow inside the method itself separately. +At any moment the flow through a method can be linked to a call to that method to follow the flow of data though multiple methods or classes. + + +## Example + +Let's say we have a class as given as below: + + public class JavaDataFlowExampleInputClass { + int a; + public int getA() { + return a; + } + public void setA(int inputA) { + this.a = inputA; + } + } + +Then we can execute the following commands to create a data flow graph from it. +First define the path to your project and setup the path where JavaDataFlow will look for class files. +If you have multiple projects where JavaDataFlow needs to look for class files, you can enter multiple project paths there. +Then create the data flow graph using the absolute input path to the java class. +A DataFlowGraph represents a single class. + + String projectPath = "projectPath"; + String input = "/relativePath/Example.java"; + StaticJavaDataFlow.getConfig().setProjectPaths(projectPath); + DataFlowGraph dfg = JavaDataFlow.create(projectPath + input); + +Now if we want to gather all input nodes to this class that can influence the output of the method "getA", we can do that as given below. +First get the given method. +Now we need to walk back until we reach a node that is an input parameter of a method, for this we can use the method DataFlowNode::isInputParameter. +For this example we don't want to go outside of this class so we add dfg::owns as scope to the method walkBackUntil. +The scope determines when to stop walking over the nodes, this can become important if multiple data flow graphs are connected to each other. +However, this is currently not supported yet. + + DataFlowMethod getA = dfg.getMethods().stream().filter(m -> m.getName().equals("getA")).findFirst().get(); + List inputNodes = getA.getReturnNode().get().walkBackUntil(DataFlowNode::isInputParameter, dfg::owns); + System.out.println(inputNodes.get(0).getName()); + +The above code will output the name "inputA". All code above is also given in an [example project](https://github.com/daanvdh/JavaDataFlowExample). + +## Setup +Add the dependency below to the pom of your project. + + + com.github.daanvdh.javadataflow + JavaDataFlow + 0.0.5 + +## Definitions + +- Any **object or primitive** is modelled as a DataFlowNode. + A DataFlowNode can have 0 or more input or output DataFlowEdge's. + Such an edge represents a data flow from one node to another. +- The **owner** of a DataFlowNode represents structure in which the node is defined. + For the method setA, the parameter inputA has a ParameterList as input. + The ParameterList has the DataFlowMethod for "setA" as owner. + The owner of the method is the DataFlowGraph representing Example.java +- Each **usage of an object** is modelled as a separate DataFlowNode. + A DataFlowNode representing the usage of an earlier defined object will contain an edge as input from either the last usage before that or the definition of the object. + This way the order in which a node was used is maintained. +- A NodeCall represents a **call to another code block**, for instance a method call. + If a NodeCall was called directly on an object, that object will be modelled as a DataFlowNode and the NodeCall will have a reference to that DataFlowNode via NodeCall::getInstance. + That same DataFlowNode will then have a reference to the NodeCall via DataFlowNode::getNodeCall. + A DataFlowNode can only have a single NodeCall, since every object usage is modelled as a separate node. + If a NodeCall was not directly called on an object, it can be found via DataFlowMethod::getNodeCalls on the method in which the NodeCall was done. + + +## Features +- JavaDataFlow uses [JavaParser](https://github.com/javaparser/javaparser/) for parsing the input classes. + Each DataFlowNode has a representedNode which is the JavaParser Node that it represents. + If you have a given JavaParser Node you can get the JavaDataFlowNode via DataFlowGraph::getNode. +- Collect all methods that where called on a given object by executing DataFlowNode::collectNodeCalls. + A scope can be added to this method to only find calls within a certain method or graph, you can for example use DataFlowMethod::owns. + +## Roadmap +- Include Constructors in the JavaDataFlow graph. +- Model if statements. +- Model primitive functions like: + - * / < >. +- Model for and while loops. +- Connect multiple JavaDataFlow graphs to each other so that we can walk from class to class. + +## License + +JavaDataFlow is available under the terms of the Apache License. http://www.apache.org/licenses/LICENSE-2.0 diff --git a/pom.xml b/pom.xml index d28c589..97d0b23 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.github.daanvdh.javadataflow JavaDataFlow - 0.0.2-SNAPSHOT + 0.0.5 jar JavaDataFlow @@ -44,28 +44,28 @@ org.apache.maven.plugins - maven-source-plugin - 2.2.1 + maven-javadoc-plugin + 2.9.1 - attach-sources + attach-javadocs deploy - jar-no-fork + jar org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 + maven-source-plugin + 2.2.1 - attach-javadocs + attach-sources deploy - jar + jar-no-fork @@ -88,6 +88,32 @@ + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + default-deploy + deploy + + deploy + + + true + + + + release + deploy + + deploy + + + + + @@ -114,7 +140,7 @@ junit junit - 4.12 + 4.13.1 test @@ -128,7 +154,7 @@ com.github.javaparser javaparser-symbol-solver-core - 3.15.4 + 3.24.0 diff --git a/src/main/java/dataflow/DataFlowException.java b/src/main/java/common/DataFlowException.java similarity index 95% rename from src/main/java/dataflow/DataFlowException.java rename to src/main/java/common/DataFlowException.java index 214950a..7f3e92d 100644 --- a/src/main/java/dataflow/DataFlowException.java +++ b/src/main/java/common/DataFlowException.java @@ -15,9 +15,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package common; -import dataflow.model.DataFlowGraph; +import model.DataFlowGraph; /** * Exception that will be thrown for unexpected behavior or unsupported types while constructing a {@link DataFlowGraph}. diff --git a/src/main/java/facade/JavaDataFlow.java b/src/main/java/facade/JavaDataFlow.java new file mode 100644 index 0000000..d20b068 --- /dev/null +++ b/src/main/java/facade/JavaDataFlow.java @@ -0,0 +1,53 @@ +/* + * Copyright 2018 by Daan van den Heuvel. + * + * This file is part of JavaDataFlow. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package facade; + +import com.github.javaparser.ast.CompilationUnit; + +import factory.DataFlowGraphFactory; +import model.DataFlowGraph; +import util.ParserUtil; + +/** + * Facade class to create {@link DataFlowGraph}s. + * + * @author Daan + */ +public class JavaDataFlow { + + /** + * Creates a {@link DataFlowGraph} from the class located at the given classPath. + * + * @param classPath The path to the input class. + * @return A {@link DataFlowGraph} representing the input class. + */ + public static DataFlowGraph create(String classPath) { + return create(new ParserUtil().createCompilationUnit(classPath)); + } + + /** + * Creates a {@link DataFlowGraph} from the given {@link CompilationUnit}. + * + * @param cu The input {@link CompilationUnit}. + * @return A {@link DataFlowGraph} representing the input class. + */ + public static DataFlowGraph create(CompilationUnit cu) { + return new DataFlowGraphFactory().create(cu); + } + +} diff --git a/src/main/java/facade/StaticJavaDataFlow.java b/src/main/java/facade/StaticJavaDataFlow.java new file mode 100644 index 0000000..7b50ea6 --- /dev/null +++ b/src/main/java/facade/StaticJavaDataFlow.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 by Eyefreight BV (www.eyefreight.com). All rights reserved. + * + * This software is provided by the copyright holder and contributors "as is" and any express or implied warranties, including, but + * not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall + * Eyefreight BV or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages + * (including, but not limited to, procurement of substitute goods or services; * loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including + * negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. + */ +package facade; + +import java.io.File; +import java.nio.file.Files; +import java.util.stream.Stream; + +import org.apache.commons.lang3.ArrayUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.symbolsolver.JavaSymbolSolver; +import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; + +/** + * Contains all static setting for {@link JavaDataFlow}. + * + * @author Daan + */ +public class StaticJavaDataFlow { + private static final Logger LOG = LoggerFactory.getLogger(StaticJavaDataFlow.class); + + /** Used to gather more data about a parsed class, such as resolving imports or super classes. */ + private JavaSymbolSolver symbolSolver; + + private static StaticJavaDataFlow config; + + private StaticJavaDataFlow() { + // don't create it via any constructor + setupSymbolSolver(); + } + + public static synchronized StaticJavaDataFlow getConfig() { + if (config == null) { + config = new StaticJavaDataFlow(); + } + return config; + } + + public final void setSymbolSolver(JavaSymbolSolver symbolSolver) { + this.symbolSolver = symbolSolver; + StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver); + } + + public JavaSymbolSolver getSymbolSolver() { + return symbolSolver; + } + + /** + * Sets the project paths to be used to find classes. Note that these paths should be the full path to the source folder, typically ending with + * ".../src/main/java" for maven projects. This method will override anything set by the method {@link StaticJavaDataFlow#setSymbolSolver(JavaSymbolSolver)}. + * + * @param paths The full paths to source folders where {@link JavaDataFlow} needs to look for classes that any input class depends on. + */ + public void setProjectPaths(String... paths) { + Stream.of(paths).filter(p -> !Files.exists(new File(p).toPath())).forEach(p -> LOG.error("Could not find the folder located at: " + p)); + JavaParserTypeSolver[] solvers = + Stream.of(paths).filter(p -> Files.exists(new File(p).toPath())).map(JavaParserTypeSolver::new).toArray(JavaParserTypeSolver[]::new); + TypeSolver[] reflTypeSolver = {new ReflectionTypeSolver()}; + TypeSolver typeSolver = new CombinedTypeSolver(ArrayUtils.addAll(reflTypeSolver, solvers)); + JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver); + setSymbolSolver(symbolSolver); + } + + private final void setupSymbolSolver() { + TypeSolver reflTypeSolver = new ReflectionTypeSolver(); + JavaSymbolSolver symbolSolver = new JavaSymbolSolver(reflTypeSolver); + this.setSymbolSolver(symbolSolver); + } + +} diff --git a/src/main/java/dataflow/DataFlowGraphFactory.java b/src/main/java/factory/DataFlowGraphFactory.java similarity index 97% rename from src/main/java/dataflow/DataFlowGraphFactory.java rename to src/main/java/factory/DataFlowGraphFactory.java index 60cf8de..c2b1e88 100644 --- a/src/main/java/dataflow/DataFlowGraphFactory.java +++ b/src/main/java/factory/DataFlowGraphFactory.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package factory; import java.util.HashMap; import java.util.List; @@ -41,12 +41,12 @@ import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserMethodDeclaration; -import dataflow.model.DataFlowGraph; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; -import dataflow.model.NodeCall; -import dataflow.model.OwnedNode; -import dataflow.model.ParameterList; +import model.DataFlowGraph; +import model.DataFlowMethod; +import model.DataFlowNode; +import model.NodeCall; +import model.OwnedNode; +import model.ParameterList; /** * Factory for creating a {@link DataFlowGraph} from a {@link JavaParser} {@link CompilationUnit}. diff --git a/src/main/java/dataflow/DataFlowNodeFactory.java b/src/main/java/factory/DataFlowNodeFactory.java similarity index 83% rename from src/main/java/dataflow/DataFlowNodeFactory.java rename to src/main/java/factory/DataFlowNodeFactory.java index 5b249be..613d66e 100644 --- a/src/main/java/dataflow/DataFlowNodeFactory.java +++ b/src/main/java/factory/DataFlowNodeFactory.java @@ -15,18 +15,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.expr.SimpleName; import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName; import com.github.javaparser.ast.stmt.ReturnStmt; -import com.github.javaparser.printer.Printable; +import com.github.javaparser.printer.Stringable; -import dataflow.model.DataFlowNode; -import dataflow.model.OwnedNode; +import model.DataFlowNode; +import model.OwnedNode; /** * Factory for {@link DataFlowNode}s. @@ -45,8 +46,10 @@ public DataFlowNode create(Node n, OwnedNode owner) { if (nodeWithName instanceof NodeWithSimpleName) { builder.name(((NodeWithSimpleName) nodeWithName).getNameAsString()); - } else if (nodeWithName instanceof Printable) { - builder.name(((Printable) nodeWithName).asString()); + } else if (nodeWithName instanceof Stringable) { + builder.name(((Stringable) nodeWithName).asString()); + } else if (nodeWithName instanceof SimpleName) { + builder.name(((SimpleName) nodeWithName).asString()); } else { LOG.warn("Not supported to add a name to a created DataFlowNode for node of type {}, input node is {}", n.getClass(), n); } diff --git a/src/main/java/dataflow/MethodNodeHandler.java b/src/main/java/factory/MethodNodeHandler.java similarity index 90% rename from src/main/java/dataflow/MethodNodeHandler.java rename to src/main/java/factory/MethodNodeHandler.java index 8ac6294..c6127a4 100644 --- a/src/main/java/dataflow/MethodNodeHandler.java +++ b/src/main/java/factory/MethodNodeHandler.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package factory; import java.util.List; import java.util.Map; @@ -43,13 +43,15 @@ import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.stmt.ReturnStmt; -import dataflow.model.DataFlowEdge; -import dataflow.model.DataFlowGraph; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; -import dataflow.model.NodeCall; -import dataflow.model.OwnedNode; -import dataflow.model.ParameterList; +import common.DataFlowException; +import model.DataFlowEdge; +import model.DataFlowGraph; +import model.DataFlowMethod; +import model.DataFlowNode; +import model.NodeCall; +import model.OwnedNode; +import model.ParameterList; +import util.ParserUtil; /** * Class for handling {@link JavaParser} {@link Node}s while filling a {@link DataFlowMethod}. @@ -72,6 +74,8 @@ public class MethodNodeHandler { * @param method {@link DataFlowMethod} to add {@link DataFlowNode} to * @param overriddenValues The values that have been overridden in previous iterations. * @param n The {@link Node} to handle. ChildNodes will recursively be handled if needed. + * @param owner The owner for the node to be created. This variable might be removed later, giving the caller of this method the responsibility to set the + * owner. * @return An optional of the {@link DataFlowNode} of the input node. If multiple head nodes are created, (In case of a {@link BlockStmt}) the optional will * be empty. */ @@ -162,7 +166,11 @@ private Optional handleMethodCallExpr(DataFlowGraph graph, DataFlo } calledMethod.setIn(params.build()); + if (instance != null) { + instance.setNodeCall(calledMethod); + } method.addMethodCall(calledMethod); + calledMethod.getReturnNode().ifPresent(method::addNode); // Return the return node of the called method so that the return value can be assigned to the caller. return calledMethod.getReturnNode(); @@ -260,30 +268,30 @@ private Optional handleAssignExpr(DataFlowGraph graph, DataFlowMet return Optional.of(flowNode); } - /** - * TODO javadoc - * - * @param graph - * @param method - * @param overwriddenValues - * @param node - * @return - */ private Optional getDataFlowNode(DataFlowGraph graph, DataFlowMethod method, Map overwriddenValues, Node node) { Optional optionalResolvedNode = parserUtil.getJavaParserNode(method, node); DataFlowNode flowNode = null; if (optionalResolvedNode.isPresent()) { Node resolvedNode = optionalResolvedNode.get(); - flowNode = overwriddenValues.get(resolvedNode); - flowNode = flowNode != null ? flowNode : graph.getNode(resolvedNode); - flowNode = flowNode != null ? flowNode : method.getNode(resolvedNode); + flowNode = getLastFlowNode(graph, method, overwriddenValues, resolvedNode); + flowNode = (flowNode != null || !(resolvedNode instanceof VariableDeclarationExpr)) ? flowNode + : ((VariableDeclarationExpr) resolvedNode).getVariables().stream().map(child -> getLastFlowNode(graph, method, overwriddenValues, child)) + .filter(n -> n != null).findFirst().orElse(null); } if (flowNode == null) { - LOG.warn("In method {} did not resolve the type of node {} of type {}", method.getName(), node, node.getClass()); + LOG.warn("In method {} did not resolve the type of node {} of type {}, resolvedNode was {}", method.getName(), node, node.getClass(), + optionalResolvedNode); } return Optional.ofNullable(flowNode); } + private DataFlowNode getLastFlowNode(DataFlowGraph graph, DataFlowMethod method, Map overwriddenValues, Node resolvedNode) { + DataFlowNode flowNode = overwriddenValues.get(resolvedNode); + flowNode = flowNode != null ? flowNode : method.getNode(resolvedNode); + flowNode = flowNode != null ? flowNode : graph.getNode(resolvedNode); + return flowNode; + } + private String nameForInBetweenNode(DataFlowMethod method, Map overriddenValues, Node realAssignedJP, NodeWithSimpleName nodeWithName) { String namePostFix = ""; diff --git a/src/main/java/dataflow/NodeCallFactory.java b/src/main/java/factory/NodeCallFactory.java similarity index 88% rename from src/main/java/dataflow/NodeCallFactory.java rename to src/main/java/factory/NodeCallFactory.java index 54efb04..3732ad8 100644 --- a/src/main/java/dataflow/NodeCallFactory.java +++ b/src/main/java/factory/NodeCallFactory.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package factory; import java.util.Optional; @@ -29,12 +29,13 @@ import com.github.javaparser.resolution.types.ResolvedPrimitiveType; import com.github.javaparser.resolution.types.ResolvedType; -import dataflow.model.DataFlowGraph; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; -import dataflow.model.NodeCall; -import dataflow.model.OwnedNode; -import dataflow.model.OwnerNode; +import model.DataFlowGraph; +import model.DataFlowMethod; +import model.DataFlowNode; +import model.NodeCall; +import model.OwnedNode; +import model.OwnerNode; +import util.ParserUtil; /** * Class for resolving {@link DataFlowMethod}s and {@link DataFlowNode}s from {@link Node}s and {@link DataFlowGraph}s. @@ -67,9 +68,8 @@ public Optional create(OwnedNode owner, MethodCallExpr node, DataFl } private NodeCall createMethodCall(OwnedNode owner, ResolvedMethodLikeDeclaration resolved, MethodCallExpr node, DataFlowNode instance) { - NodeCall methodCall = - NodeCall.builder().name(resolved.getName()).claz(resolved.getClassName()).peckage(resolved.getPackageName()).owner(owner).representedNode(node).build(); - methodCall.setInstance(instance); + NodeCall methodCall = NodeCall.builder().name(resolved.getName()).claz(resolved.getClassName()).peckage(resolved.getPackageName()).owner(owner) + .representedNode(node).instance(instance).build(); setReturn(methodCall, owner, node, resolved); return methodCall; } diff --git a/src/main/java/dataflow/model/DataFlowEdge.java b/src/main/java/model/DataFlowEdge.java similarity index 98% rename from src/main/java/dataflow/model/DataFlowEdge.java rename to src/main/java/model/DataFlowEdge.java index f24a809..b16bff2 100644 --- a/src/main/java/dataflow/model/DataFlowEdge.java +++ b/src/main/java/model/DataFlowEdge.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; /** * An edge inside a {@link DataFlowGraph} representing a {@link DataFlowNode} influencing the state of another {@link DataFlowNode}. diff --git a/src/main/java/dataflow/model/DataFlowGraph.java b/src/main/java/model/DataFlowGraph.java similarity index 99% rename from src/main/java/dataflow/model/DataFlowGraph.java rename to src/main/java/model/DataFlowGraph.java index d325ab0..948777c 100644 --- a/src/main/java/dataflow/model/DataFlowGraph.java +++ b/src/main/java/model/DataFlowGraph.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/dataflow/model/DataFlowMethod.java b/src/main/java/model/DataFlowMethod.java similarity index 97% rename from src/main/java/dataflow/model/DataFlowMethod.java rename to src/main/java/model/DataFlowMethod.java index 48d842a..261d914 100644 --- a/src/main/java/dataflow/model/DataFlowMethod.java +++ b/src/main/java/model/DataFlowMethod.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.ArrayList; import java.util.Arrays; @@ -35,7 +35,7 @@ import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.CallableDeclaration; -import dataflow.util.HashCodeWrapper; +import util.HashCodeWrapper; /** * DataFlow class representing a method inside a {@link DataFlowGraph}. @@ -111,7 +111,7 @@ public final void setReturnNode(DataFlowNode returnNode) { this.addNode(returnNode); } - public ParameterList getInputParameters() { + public ParameterList getParameters() { return inputParameters; } @@ -180,7 +180,8 @@ public void addChangedFields(DataFlowNode... fields) { } /** - * @return List of {@link DataFlowMethod}s containing both the input and output methods. + * @return List of {@link NodeCall}s representing the method calls that where not called directly on an object, for instance static or methods from within the + * same class. */ public List getNodeCalls() { return this.nodeCalls; diff --git a/src/main/java/dataflow/model/DataFlowNode.java b/src/main/java/model/DataFlowNode.java similarity index 79% rename from src/main/java/dataflow/model/DataFlowNode.java rename to src/main/java/model/DataFlowNode.java index 1f8a0c1..9adbfab 100644 --- a/src/main/java/dataflow/model/DataFlowNode.java +++ b/src/main/java/model/DataFlowNode.java @@ -15,13 +15,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; +import java.util.stream.Collectors; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -31,7 +33,7 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; -import dataflow.GraphUtil; +import util.GraphUtil; /** * A node inside the {@link DataFlowGraph} containing a {@link JavaParser} {@link Node}. The incoming {@link DataFlowEdge}s are {@link DataFlowNode}s that @@ -56,6 +58,13 @@ public class DataFlowNode extends OwnedNode { */ private OwnedNode owner; + /** + * If a method was called on the current {@link DataFlowNode} this {@link NodeCall} will represent that NodeCall. Each {@link DataFlowNode} can only have a + * single nodeCall, since each usage of a single variable is modeled to be a separate dataFlowNode. All method called on a given dataFlowNode can be collected + * by walking through the graph. This value will be null if no method was called. + */ + private NodeCall nodeCall; + public DataFlowNode(Node representedNode) { super(representedNode); } @@ -72,6 +81,7 @@ private DataFlowNode(Builder builder) { this.out.addAll(builder.out); this.setType(builder.type); this.owner = builder.owner; + this.nodeCall = builder.nodeCall; } public List getIn() { @@ -104,19 +114,26 @@ public void setType(String type) { this.type = type; } + public Optional getNodeCall() { + return Optional.ofNullable(nodeCall); + } + + public void setNodeCall(NodeCall nodeCall) { + this.nodeCall = nodeCall; + } + public boolean isField() { return this.getOwner().map(DataFlowGraph.class::isInstance).orElse(false); } public boolean isInputParameter() { - return this.getOwner().filter(DataFlowMethod.class::isInstance).map(DataFlowMethod.class::cast).map(dfm -> dfm.getInputParameters().contains(this)) - .orElse(false); + return this.getOwner().filter(ParameterList.class::isInstance).map(ParameterList.class::cast).filter(ParameterList::isInputParametersForMethod).isPresent(); } /** * Walks back over incoming edges until predicate is met or no incoming edges are present. * - * @see GraphUtil#walkBackUntil(DataFlowNode, Predicate) + * @see GraphUtil#walkBackUntil(DataFlowNode, Predicate, Predicate) * @param predicate The {@link Predicate} to meet * @param scope The scope for the variable, the search is stopped as soon as the scope does not hold and an empty list is returned. * @return {@link List} of {@link DataFlowNode} @@ -125,6 +142,25 @@ public List walkBackUntil(Predicate predicate, Predi return GraphUtil.walkBackUntil(this, predicate, scope); } + /** + * Returns all {@link NodeCall} that are called directly on this {@link DataFlowNode} or on any other {@link DataFlowNode} that has an {@link DataFlowEdge} + * resulting from this node. Only nodes within the defined scope are considered. + * + * @param scope The scope for searching for {@link NodeCall}s. + * @return List of {@link NodeCall}. + */ + public List collectNodeCalls(Predicate scope) { + if (!scope.test(this)) { + return new ArrayList<>(); + } + List collect = + this.getOut().stream().map(DataFlowEdge::getTo).map(dfn -> dfn.collectNodeCalls(scope)).flatMap(List::stream).collect(Collectors.toList()); + if (this.nodeCall != null) { + collect.add(nodeCall); + } + return collect; + } + /** * @param node The {@link DataFlowNode} to check. * @return True if this node is equal to the given node or has a direct incoming edge from the input node, false otherwise. @@ -242,6 +278,7 @@ public static final class Builder extends NodeRepresenter.Builder in = new ArrayList<>(); private List out = new ArrayList<>(); private String type; + private NodeCall nodeCall; private Builder() { // Builder should only be constructed via the parent class @@ -259,6 +296,12 @@ public Builder out(List out) { return this; } + public Builder out(DataFlowEdge... out) { + this.out.clear(); + this.out.addAll(Arrays.asList(out)); + return this; + } + public Builder type(String type) { this.type = type; return this; @@ -269,6 +312,11 @@ public Builder owner(OwnedNode owner) { return this; } + public Builder nodeCall(NodeCall nodeCall) { + this.nodeCall = nodeCall; + return this; + } + public DataFlowNode build() { return new DataFlowNode(this); } diff --git a/src/main/java/dataflow/model/NodeCall.java b/src/main/java/model/NodeCall.java similarity index 96% rename from src/main/java/dataflow/model/NodeCall.java rename to src/main/java/model/NodeCall.java index 97fc0a7..015bf17 100644 --- a/src/main/java/dataflow/model/NodeCall.java +++ b/src/main/java/model/NodeCall.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Objects; import java.util.Optional; @@ -76,6 +76,7 @@ private NodeCall(Builder builder) { this.claz = builder.claz == null ? this.claz : builder.claz; this.peckage = builder.peckage == null ? this.peckage : builder.peckage; this.returnNode = builder.returnNode == null ? this.returnNode : builder.returnNode; + this.instance = builder.instance == null ? this.instance : builder.instance; } @Override @@ -98,7 +99,7 @@ public Optional getCalledMethod() { public void setCalledMethod(DataFlowMethod calledMethod) { this.calledMethod = calledMethod; - this.in.connectTo(calledMethod.getInputParameters()); + this.in.connectTo(calledMethod.getParameters()); if (this.returnNode != null) { if (calledMethod.getReturnNode().isPresent()) { calledMethod.getReturnNode().get().addEdgeTo(returnNode); @@ -199,6 +200,7 @@ public static final class Builder extends NodeRepresenter.Builder The type of the {@link JavaParser} {@link Node} to represent. * @return created builder */ public static Builder> builder() { diff --git a/src/main/java/dataflow/model/OwnedNode.java b/src/main/java/model/OwnedNode.java similarity index 98% rename from src/main/java/dataflow/model/OwnedNode.java rename to src/main/java/model/OwnedNode.java index f66caad..59f19a8 100644 --- a/src/main/java/dataflow/model/OwnedNode.java +++ b/src/main/java/model/OwnedNode.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Optional; diff --git a/src/main/java/dataflow/model/OwnerNode.java b/src/main/java/model/OwnerNode.java similarity index 98% rename from src/main/java/dataflow/model/OwnerNode.java rename to src/main/java/model/OwnerNode.java index 9b4b7ef..7bc3ed1 100644 --- a/src/main/java/dataflow/model/OwnerNode.java +++ b/src/main/java/model/OwnerNode.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Collection; import java.util.HashSet; diff --git a/src/main/java/dataflow/model/ParameterList.java b/src/main/java/model/ParameterList.java similarity index 94% rename from src/main/java/dataflow/model/ParameterList.java rename to src/main/java/model/ParameterList.java index 73bb4a0..1a55a95 100644 --- a/src/main/java/dataflow/model/ParameterList.java +++ b/src/main/java/model/ParameterList.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.ArrayList; import java.util.Arrays; @@ -29,7 +29,7 @@ import com.github.javaparser.ast.Node; -import dataflow.DataFlowException; +import common.DataFlowException; /** * Represents the set of parameters from a {@link DataFlowMethod}. Every method has at most one {@link ParameterList}. A parameter list can also exist outside @@ -117,6 +117,14 @@ public void connectTo(ParameterList otherParams) { } } + public boolean isInputParametersForMethod() { + boolean isInputParam = false; + if (this.owner != null && this.owner instanceof DataFlowMethod && ((DataFlowMethod) this.owner).getParameters() == this) { + isInputParam = true; + } + return isInputParam; + } + @Override public int hashCode() { return Objects.hash(super.hashCode(), nodes); diff --git a/src/main/java/dataflow/GraphUtil.java b/src/main/java/util/GraphUtil.java similarity index 96% rename from src/main/java/dataflow/GraphUtil.java rename to src/main/java/util/GraphUtil.java index d40ac44..1112525 100644 --- a/src/main/java/dataflow/GraphUtil.java +++ b/src/main/java/util/GraphUtil.java @@ -15,16 +15,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package util; import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; -import dataflow.model.DataFlowEdge; -import dataflow.model.DataFlowGraph; -import dataflow.model.DataFlowNode; +import model.DataFlowEdge; +import model.DataFlowGraph; +import model.DataFlowNode; /** * Service for methods to be executed on a {@link DataFlowGraph}. diff --git a/src/main/java/dataflow/util/HashCodeWrapper.java b/src/main/java/util/HashCodeWrapper.java similarity index 98% rename from src/main/java/dataflow/util/HashCodeWrapper.java rename to src/main/java/util/HashCodeWrapper.java index 04b3c44..b689a8d 100644 --- a/src/main/java/dataflow/util/HashCodeWrapper.java +++ b/src/main/java/util/HashCodeWrapper.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.util; +package util; import com.github.javaparser.JavaParser; import com.github.javaparser.ast.Node; diff --git a/src/main/java/dataflow/util/HashMapWrapper.java b/src/main/java/util/HashMapWrapper.java similarity index 98% rename from src/main/java/dataflow/util/HashMapWrapper.java rename to src/main/java/util/HashMapWrapper.java index 7943556..e5ac251 100644 --- a/src/main/java/dataflow/util/HashMapWrapper.java +++ b/src/main/java/util/HashMapWrapper.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.util; +package util; import java.util.Collection; import java.util.HashMap; diff --git a/src/main/java/dataflow/ParserUtil.java b/src/main/java/util/ParserUtil.java similarity index 75% rename from src/main/java/dataflow/ParserUtil.java rename to src/main/java/util/ParserUtil.java index ae9d1cf..fe5afeb 100644 --- a/src/main/java/dataflow/ParserUtil.java +++ b/src/main/java/util/ParserUtil.java @@ -15,8 +15,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package util; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Optional; import java.util.stream.Stream; @@ -25,14 +28,17 @@ import org.slf4j.LoggerFactory; import com.github.javaparser.JavaParser; +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserFieldDeclaration; import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserParameterDeclaration; -import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration; +import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserVariableDeclaration; +import com.google.common.util.concurrent.UncheckedExecutionException; -import dataflow.model.DataFlowMethod; -import dataflow.model.OwnedNode; +import model.DataFlowMethod; +import model.OwnedNode; public class ParserUtil { private static final Logger LOG = LoggerFactory.getLogger(ParserUtil.class); @@ -52,8 +58,8 @@ public Optional getJavaParserNode(DataFlowMethod method, Node node) { resolvedNode = ((JavaParserFieldDeclaration) resolved).getVariableDeclarator(); } else if (resolved instanceof JavaParserParameterDeclaration) { resolvedNode = ((JavaParserParameterDeclaration) resolved).getWrappedNode(); - } else if (resolved instanceof JavaParserSymbolDeclaration) { - resolvedNode = ((JavaParserSymbolDeclaration) resolved).getWrappedNode(); + } else if (resolved instanceof JavaParserVariableDeclaration) { + resolvedNode = ((JavaParserVariableDeclaration) resolved).getWrappedNode(); } else { LOG.warn("In method {}, resolving is not supported for node {} of type {}", method.getName(), node, resolved == null ? null : resolved.getClass()); } @@ -78,4 +84,17 @@ public Object resolve(OwnedNode method, Node node) { return resolved; } + public CompilationUnit createCompilationUnit(String inputClass) { + CompilationUnit cu = null; + try (FileInputStream in = new FileInputStream(inputClass)) { + cu = StaticJavaParser.parse(in); + in.close(); + } catch (FileNotFoundException e) { + throw new UncheckedExecutionException("Could not parse class at location: " + inputClass, e); + } catch (IOException e) { + throw new UncheckedExecutionException("Unable to close input stream for: " + inputClass, e); + } + return cu; + } + } diff --git a/src/test/java/dataflow/DataFlowMethodBuilder.java b/src/test/java/common/DataFlowMethodBuilder.java similarity index 95% rename from src/test/java/dataflow/DataFlowMethodBuilder.java rename to src/test/java/common/DataFlowMethodBuilder.java index adb3249..31e9570 100644 --- a/src/test/java/dataflow/DataFlowMethodBuilder.java +++ b/src/test/java/common/DataFlowMethodBuilder.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package common; import java.util.HashMap; import java.util.Map; @@ -24,8 +24,8 @@ import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.type.VarType; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; +import model.DataFlowMethod; +import model.DataFlowNode; /** * Builder for {@link DataFlowMethod} with some build method only ment for testing. diff --git a/src/test/java/dataflow/GraphBuilder.java b/src/test/java/common/GraphBuilder.java similarity index 96% rename from src/test/java/dataflow/GraphBuilder.java rename to src/test/java/common/GraphBuilder.java index daf4a5c..9cfb965 100644 --- a/src/test/java/dataflow/GraphBuilder.java +++ b/src/test/java/common/GraphBuilder.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package common; import java.util.ArrayList; import java.util.Arrays; @@ -29,10 +29,10 @@ import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.type.ClassOrInterfaceType; -import dataflow.model.DataFlowGraph; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; -import dataflow.model.ParameterList; +import model.DataFlowGraph; +import model.DataFlowMethod; +import model.DataFlowNode; +import model.ParameterList; /** * Builder for {@link DataFlowGraph}, only to be used for test purposes. @@ -100,7 +100,7 @@ private void addNode(DataFlowGraph graph, NodeBuilder nodeBuilder, Map 0) { method.inputParameters(ParameterList.builder().name(name + "Parameters").nodes(params).build()); } @@ -381,7 +380,7 @@ private Optional assertMethodsEqual(Collection exp, Coll } private Optional assertMethodEqual(DataFlowMethod expMethod, DataFlowMethod equalMethod) { - Optional parametersEqual = dfnTest.assertNodesEqual(expMethod.getInputParameters().getNodes(), equalMethod.getInputParameters().getNodes()) + Optional parametersEqual = dfnTest.assertNodesEqual(expMethod.getParameters().getNodes(), equalMethod.getParameters().getNodes()) .map(s -> "for " + expMethod.getName() + " parameters not equal: " + s); Optional nodesEqual = parametersEqual.isPresent() ? parametersEqual : dfnTest.assertNodesEqual(expMethod.getNodes(), equalMethod.getNodes()).map(s -> "for " + expMethod.getName() + ": " + s); @@ -399,8 +398,8 @@ private Matcher createMatcher(DataFlowMethod method) { EqualFeatureMatcher methodNameMatcher = new EqualFeatureMatcher<>(DataFlowMethod::getName, method.getName(), "methodName"); EqualFeatureMatcher> parameterMatcher = - new EqualFeatureMatcher<>((m) -> m.getInputParameters().getNodes().stream().map(DataFlowNode::getName).collect(Collectors.toList()), - method.getInputParameters().getNodes().stream().map(DataFlowNode::getName).collect(Collectors.toList()), "methodParameters"); + new EqualFeatureMatcher<>((m) -> m.getParameters().getNodes().stream().map(DataFlowNode::getName).collect(Collectors.toList()), + method.getParameters().getNodes().stream().map(DataFlowNode::getName).collect(Collectors.toList()), "methodParameters"); return Matchers.allOf(methodNameMatcher, parameterMatcher); } diff --git a/src/test/java/dataflow/MethodNodeHandlerTest.java b/src/test/java/factory/MethodNodeHandlerTest.java similarity index 86% rename from src/test/java/dataflow/MethodNodeHandlerTest.java rename to src/test/java/factory/MethodNodeHandlerTest.java index 2657f80..dd3826f 100644 --- a/src/test/java/dataflow/MethodNodeHandlerTest.java +++ b/src/test/java/factory/MethodNodeHandlerTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package factory; import java.util.Arrays; import java.util.HashMap; @@ -36,11 +36,11 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; -import dataflow.model.DataFlowGraph; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; -import dataflow.model.NodeCall; -import dataflow.model.ParameterList; +import model.DataFlowGraph; +import model.DataFlowMethod; +import model.DataFlowNode; +import model.NodeCall; +import model.ParameterList; import util.MethodMatcher; /** @@ -70,11 +70,14 @@ public void testHandleMethodCallExpr_inputMethod() { NodeCall methodCall = NodeCall.builder().in(ParameterList.builder().nodes(Arrays.asList(DataFlowNode.builder().name("param1").build())).build()) .returnNode(returnNode).build(); DataFlowMethod method = DataFlowMethod.builder().build(); - mockNodeCallFactory(method, node, cu.findAll(NameExpr.class).get(0), methodCall); + NameExpr sbUsage = cu.findAll(NameExpr.class).get(0); + mockNodeCallFactory(method, node, sbUsage, methodCall); Optional resultNode = execute(node, method); Assert.assertTrue(resultNode.isPresent()); + Assert.assertEquals(returnNode, resultNode.get()); + Assert.assertEquals(methodCall, method.getNode(sbUsage).getNodeCall().get()); Assert.assertEquals(Arrays.asList(methodCall), method.getNodeCalls()); } @@ -91,11 +94,14 @@ public void testHandleMethodCallExpr_outputMethod() { NodeCall methodCall = NodeCall.builder().in(ParameterList.builder().nodes(Arrays.asList(DataFlowNode.builder().name("param1").build())).build()) .returnNode(returnNode).build(); DataFlowMethod method = DataFlowMethod.builder().build(); - mockNodeCallFactory(method, node, cu.findAll(NameExpr.class).get(0), methodCall); + NameExpr sbUsage = cu.findAll(NameExpr.class).get(0); + mockNodeCallFactory(method, node, sbUsage, methodCall); Optional resultNode = execute(node, method); Assert.assertTrue(resultNode.isPresent()); + Assert.assertEquals(returnNode, resultNode.get()); + Assert.assertEquals(methodCall, method.getNode(sbUsage).getNodeCall().get()); Assert.assertEquals(Arrays.asList(methodCall), method.getNodeCalls()); } @@ -122,6 +128,9 @@ public void testHandleMethodCallExpr_methodConcatenation() { Optional resultNode = execute(charrAt, method); Assert.assertTrue(resultNode.isPresent()); + Assert.assertEquals(dfnCharrAt, resultNode.get()); + Assert.assertEquals(appendCall, method.getNode(instance).getNodeCall().get()); + Assert.assertEquals(charrAtCall, method.getNode(append).getNodeCall().get()); Assert.assertEquals(Arrays.asList(appendCall, charrAtCall), method.getNodeCalls()); } diff --git a/src/test/java/dataflow/NodeCallFactoryTest.java b/src/test/java/factory/NodeCallFactoryTest.java similarity index 94% rename from src/test/java/dataflow/NodeCallFactoryTest.java rename to src/test/java/factory/NodeCallFactoryTest.java index 99871fb..12fb7af 100644 --- a/src/test/java/dataflow/NodeCallFactoryTest.java +++ b/src/test/java/factory/NodeCallFactoryTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package factory; import java.util.List; import java.util.Optional; @@ -28,10 +28,11 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.expr.MethodCallExpr; -import dataflow.common.SymbolSolverSetup; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; -import dataflow.model.NodeCall; +import common.SymbolSolverSetup; +import factory.NodeCallFactory; +import model.DataFlowMethod; +import model.DataFlowNode; +import model.NodeCall; /** * Unit test for {@link NodeCallFactory}. diff --git a/src/test/java/dataflow/model/DataFlowEdgeTest.java b/src/test/java/model/DataFlowEdgeTest.java similarity index 98% rename from src/test/java/dataflow/model/DataFlowEdgeTest.java rename to src/test/java/model/DataFlowEdgeTest.java index 507d8e4..a88ba09 100644 --- a/src/test/java/dataflow/model/DataFlowEdgeTest.java +++ b/src/test/java/model/DataFlowEdgeTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/dataflow/model/DataFlowGraphTest.java b/src/test/java/model/DataFlowGraphTest.java similarity index 99% rename from src/test/java/dataflow/model/DataFlowGraphTest.java rename to src/test/java/model/DataFlowGraphTest.java index 84d90d0..c4ce8c7 100644 --- a/src/test/java/dataflow/model/DataFlowGraphTest.java +++ b/src/test/java/model/DataFlowGraphTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Collections; import java.util.List; diff --git a/src/test/java/dataflow/model/DataFlowMethodTest.java b/src/test/java/model/DataFlowMethodTest.java similarity index 98% rename from src/test/java/dataflow/model/DataFlowMethodTest.java rename to src/test/java/model/DataFlowMethodTest.java index 877a5ad..f791533 100644 --- a/src/test/java/dataflow/model/DataFlowMethodTest.java +++ b/src/test/java/model/DataFlowMethodTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Collections; import java.util.List; @@ -45,7 +45,7 @@ public class DataFlowMethodTest { public void testDataFlowMethod_minimum() { DataFlowMethod dataFlowMethod = DataFlowMethod.builder().build(); - Assert.assertNull("Unexpected inputParameters", dataFlowMethod.getInputParameters()); + Assert.assertNull("Unexpected inputParameters", dataFlowMethod.getParameters()); Assert.assertTrue("Unexpected inputFields", dataFlowMethod.getInputFields().isEmpty()); Assert.assertTrue("Unexpected changedFields", dataFlowMethod.getChangedFields().isEmpty()); } @@ -54,7 +54,7 @@ public void testDataFlowMethod_minimum() { public void testDataFlowMethod_maximum() { DataFlowMethod dataFlowMethod = createAndFillBuilder().build(); - Assert.assertEquals("Unexpected inputParameters", INPUT_PARAMETERS, dataFlowMethod.getInputParameters()); + Assert.assertEquals("Unexpected inputParameters", INPUT_PARAMETERS, dataFlowMethod.getParameters()); Assert.assertEquals("Unexpected inputFields", INPUT_FIELDS, dataFlowMethod.getInputFields()); Assert.assertEquals("Unexpected changedFields", CHANGED_FIELDS, dataFlowMethod.getChangedFields()); } diff --git a/src/test/java/dataflow/model/DataFlowNodeTest.java b/src/test/java/model/DataFlowNodeTest.java similarity index 87% rename from src/test/java/dataflow/model/DataFlowNodeTest.java rename to src/test/java/model/DataFlowNodeTest.java index 411b425..6a5f459 100644 --- a/src/test/java/dataflow/model/DataFlowNodeTest.java +++ b/src/test/java/model/DataFlowNodeTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Collection; import java.util.Collections; @@ -40,8 +40,8 @@ import com.github.javaparser.ast.stmt.ReturnStmt; import com.google.common.base.Objects; -import dataflow.util.HashCodeWrapper; -import dataflow.util.HashMapWrapper; +import util.HashCodeWrapper; +import util.HashMapWrapper; /** * Unit test for {@link DataFlowNode}. @@ -106,6 +106,38 @@ public void testHashCode_Different() { verifyHashCode_Different(DataFlowNode.Builder::out, Collections.singletonList(DataFlowEdge.builder().build())); } + @Test + public void testCollectNodeCalls_empty() { + Assert.assertTrue(DataFlowNode.builder().build().collectNodeCalls(x -> true).isEmpty()); + } + + @Test + public void testCollectNodeCalls() { + DataFlowNode n3 = DataFlowNode.builder().nodeCall(NodeCall.builder().build()).build(); + DataFlowNode n2 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n3).build()).build(); + DataFlowNode n1 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n2).build()).nodeCall(NodeCall.builder().build()).build(); + + Assert.assertEquals(2, n1.collectNodeCalls(x -> true).size()); + } + + @Test + public void testCollectNodeCalls_predicate() { + DataFlowNode n3 = DataFlowNode.builder().nodeCall(NodeCall.builder().build()).build(); + DataFlowNode n2 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n3).build()).build(); + DataFlowNode n1 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n2).build()).nodeCall(NodeCall.builder().build()).build(); + + Assert.assertEquals(1, n1.collectNodeCalls(x -> !x.equals(n3)).size()); + } + + @Test + public void testCollectNodeCalls_predicateFalseForFirst() { + DataFlowNode n3 = DataFlowNode.builder().nodeCall(NodeCall.builder().build()).build(); + DataFlowNode n2 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n3).build()).build(); + DataFlowNode n1 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n2).build()).nodeCall(NodeCall.builder().build()).build(); + + Assert.assertEquals(0, n1.collectNodeCalls(x -> !x.equals(n1)).size()); + } + /** * Assert that the names and all incoming and outgoing edges are equal, regardless of the order. * diff --git a/src/test/java/dataflow/model/NodeCallTest.java b/src/test/java/model/NodeCallTest.java similarity index 99% rename from src/test/java/dataflow/model/NodeCallTest.java rename to src/test/java/model/NodeCallTest.java index 999dffd..1176961 100644 --- a/src/test/java/dataflow/model/NodeCallTest.java +++ b/src/test/java/model/NodeCallTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Optional; import java.util.function.BiFunction; diff --git a/src/test/java/dataflow/model/ParameterListTest.java b/src/test/java/model/ParameterListTest.java similarity index 99% rename from src/test/java/dataflow/model/ParameterListTest.java rename to src/test/java/model/ParameterListTest.java index 0fee2f5..afed19f 100644 --- a/src/test/java/dataflow/model/ParameterListTest.java +++ b/src/test/java/model/ParameterListTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow.model; +package model; import java.util.Collections; import java.util.List; diff --git a/src/test/java/dataflow/GraphUtilTest.java b/src/test/java/util/GraphUtilTest.java similarity index 88% rename from src/test/java/dataflow/GraphUtilTest.java rename to src/test/java/util/GraphUtilTest.java index 4d07b06..8d1405d 100644 --- a/src/test/java/dataflow/GraphUtilTest.java +++ b/src/test/java/util/GraphUtilTest.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dataflow; +package util; import java.util.Arrays; import java.util.Collections; @@ -24,9 +24,11 @@ import org.junit.Assert; import org.junit.Test; -import dataflow.model.DataFlowGraph; -import dataflow.model.DataFlowMethod; -import dataflow.model.DataFlowNode; +import common.GraphBuilder; +import common.NodeBuilder; +import model.DataFlowGraph; +import model.DataFlowMethod; +import model.DataFlowNode; /** * Unit test for {@link GraphUtil}. @@ -40,7 +42,7 @@ public void testWalkBackUntil_simple() { DataFlowGraph graph = GraphBuilder.withStartingNodes(NodeBuilder.ofParameter("setS", "a").to("setS.a").to("setS.b").to(NodeBuilder.ofField("s"))).build(); DataFlowMethod m = graph.getMethods().iterator().next(); DataFlowNode node = m.getChangedFields().get(0); - DataFlowNode expected = m.getInputParameters().getNodes().get(0); + DataFlowNode expected = m.getParameters().getNodes().get(0); List result = GraphUtil.walkBackUntil(node, m::isInputBoundary, graph::owns); Assert.assertEquals(Collections.singletonList(expected), result); @@ -50,7 +52,7 @@ public void testWalkBackUntil_simple() { public void testWalkBackUntil_inputIsBoundaryNode() { DataFlowGraph graph = GraphBuilder.withStartingNodes(NodeBuilder.ofParameter("setS", "a").to("setS.a").to("setS.b").to(NodeBuilder.ofField("s"))).build(); DataFlowMethod m = graph.getMethods().iterator().next(); - DataFlowNode expected = m.getInputParameters().getNodes().get(0); + DataFlowNode expected = m.getParameters().getNodes().get(0); List result = GraphUtil.walkBackUntil(expected, m::isInputBoundary, graph::owns); Assert.assertEquals(Collections.singletonList(expected), result); @@ -67,7 +69,7 @@ public void testWalkBackUntil_multipleOutput() { DataFlowGraph graph = withStartingNodes.build(); DataFlowMethod m = graph.getMethods().iterator().next(); DataFlowNode node = m.getChangedFields().get(0); - List parameters = m.getInputParameters().getNodes(); + List parameters = m.getParameters().getNodes(); List result = GraphUtil.walkBackUntil(node, m::isInputBoundary, graph::owns); Assert.assertEquals(Arrays.asList(parameters.get(0), parameters.get(1)), result);