├── .gitignore
├── src
├── main
│ └── java
│ │ ├── common
│ │ └── DataFlowException.java
│ │ ├── util
│ │ ├── HashMapWrapper.java
│ │ ├── HashCodeWrapper.java
│ │ ├── GraphUtil.java
│ │ └── ParserUtil.java
│ │ ├── facade
│ │ ├── JavaDataFlow.java
│ │ └── StaticJavaDataFlow.java
│ │ ├── model
│ │ ├── OwnedNode.java
│ │ ├── OwnerNode.java
│ │ ├── DataFlowEdge.java
│ │ ├── NodeRepresenter.java
│ │ ├── ParameterList.java
│ │ ├── NodeCall.java
│ │ ├── DataFlowGraph.java
│ │ ├── DataFlowNode.java
│ │ └── DataFlowMethod.java
│ │ └── factory
│ │ ├── DataFlowNodeFactory.java
│ │ ├── NodeCallFactory.java
│ │ ├── DataFlowGraphFactory.java
│ │ └── MethodNodeHandler.java
└── test
│ └── java
│ ├── util
│ ├── MethodMatcher.java
│ └── GraphUtilTest.java
│ ├── model
│ ├── DataFlowEdgeTest.java
│ ├── DataFlowGraphTest.java
│ ├── ParameterListTest.java
│ ├── NodeCallTest.java
│ ├── DataFlowMethodTest.java
│ └── DataFlowNodeTest.java
│ ├── common
│ ├── SymbolSolverSetup.java
│ ├── DataFlowMethodBuilder.java
│ ├── NodeBuilder.java
│ └── GraphBuilder.java
│ └── factory
│ ├── NodeCallFactoryTest.java
│ ├── MethodNodeHandlerTest.java
│ └── DataFlowGraphFactoryTest.java
├── README.md
├── pom.xml
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | **/target
2 | **/.classpath
3 | **/.project
4 | **/.settings
5 | /bin/
6 |
7 | target/
8 | pom.xml.tag
9 | pom.xml.releaseBackup
10 | pom.xml.versionsBackup
11 | pom.xml.next
12 | release.properties
13 | dependency-reduced-pom.xml
14 | buildNumber.properties
15 | .mvn/timing.properties
16 |
17 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
18 | !/.mvn/wrapper/maven-wrapper.jar
19 |
--------------------------------------------------------------------------------
/src/main/java/common/DataFlowException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package common;
19 |
20 | import model.DataFlowGraph;
21 |
22 | /**
23 | * Exception that will be thrown for unexpected behavior or unsupported types while constructing a {@link DataFlowGraph}.
24 | *
25 | * @author Daan
26 | */
27 | public class DataFlowException extends RuntimeException {
28 | private static final long serialVersionUID = -1573995845694360640L;
29 |
30 | public DataFlowException(String message) {
31 | super(message);
32 | }
33 |
34 | public DataFlowException(String message, Object... args) {
35 | this(String.format(message, args));
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/util/MethodMatcher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package util;
19 |
20 | import java.util.function.Function;
21 |
22 | import org.hamcrest.FeatureMatcher;
23 | import org.hamcrest.Matchers;
24 |
25 | /**
26 | * Match method return values.
27 | *
28 | * @author Daan
29 | */
30 | public class MethodMatcher extends FeatureMatcher {
31 |
32 | private Function function;
33 |
34 | // hide constructor
35 | private MethodMatcher(Function function, B expected) {
36 | super(Matchers.equalTo(expected), null, null);
37 | this.function = function;
38 | }
39 |
40 | public static MethodMatcher of(Function f, B expected) {
41 | return new MethodMatcher<>(f, expected);
42 | }
43 |
44 | @Override
45 | protected B featureValueOf(A actual) {
46 | return function.apply(actual);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/util/HashMapWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package util;
19 |
20 | import java.util.Collection;
21 | import java.util.HashMap;
22 | import java.util.Map;
23 | import java.util.Set;
24 | import java.util.stream.Collectors;
25 |
26 | /**
27 | * HashMap where all keys are wrapped in a {@link HashCodeWrapper}.
28 | *
29 | * @author Daan
30 | */
31 | public class HashMapWrapper {
32 |
33 | private Map, V> map = new HashMap<>();
34 |
35 | public HashMapWrapper(Map, V> map) {
36 | this.map = map;
37 | }
38 |
39 | public Set keySet() {
40 | return map.keySet().stream().map(HashCodeWrapper::getValue).collect(Collectors.toSet());
41 | }
42 |
43 | public V get(T key) {
44 | return map.get(new HashCodeWrapper<>(key));
45 | }
46 |
47 | public Collection values() {
48 | return map.values();
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/model/DataFlowEdgeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import org.junit.Assert;
21 | import org.junit.Test;
22 |
23 | /**
24 | * Unit test for {@link DataFlowEdge}.
25 | *
26 | * @author Daan
27 | */
28 | public class DataFlowEdgeTest {
29 | private static final DataFlowNode FROM = DataFlowNode.builder().build();
30 | private static final DataFlowNode TO = DataFlowNode.builder().build();
31 |
32 | @Test
33 | public void testDataFlowEdge_minimum() {
34 | DataFlowEdge dataFlowEdge = DataFlowEdge.builder().build();
35 |
36 | Assert.assertNull("Unexpected from", dataFlowEdge.getFrom());
37 | Assert.assertNull("Unexpected to", dataFlowEdge.getTo());
38 | }
39 |
40 | @Test
41 | public void testDataFlowEdge_maximum() {
42 | DataFlowEdge dataFlowEdge = createAndFillBuilder().build();
43 |
44 | Assert.assertEquals("Unexpected from", FROM, dataFlowEdge.getFrom());
45 | Assert.assertEquals("Unexpected to", TO, dataFlowEdge.getTo());
46 | }
47 |
48 | private DataFlowEdge.Builder createAndFillBuilder() {
49 | return DataFlowEdge.builder().from(FROM).to(TO);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/facade/JavaDataFlow.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package facade;
19 |
20 | import com.github.javaparser.ast.CompilationUnit;
21 |
22 | import factory.DataFlowGraphFactory;
23 | import model.DataFlowGraph;
24 | import util.ParserUtil;
25 |
26 | /**
27 | * Facade class to create {@link DataFlowGraph}s.
28 | *
29 | * @author Daan
30 | */
31 | public class JavaDataFlow {
32 |
33 | /**
34 | * Creates a {@link DataFlowGraph} from the class located at the given classPath.
35 | *
36 | * @param classPath The path to the input class.
37 | * @return A {@link DataFlowGraph} representing the input class.
38 | */
39 | public static DataFlowGraph create(String classPath) {
40 | return create(new ParserUtil().createCompilationUnit(classPath));
41 | }
42 |
43 | /**
44 | * Creates a {@link DataFlowGraph} from the given {@link CompilationUnit}.
45 | *
46 | * @param cu The input {@link CompilationUnit}.
47 | * @return A {@link DataFlowGraph} representing the input class.
48 | */
49 | public static DataFlowGraph create(CompilationUnit cu) {
50 | return new DataFlowGraphFactory().create(cu);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/util/HashCodeWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package util;
19 |
20 | import com.github.javaparser.JavaParser;
21 | import com.github.javaparser.ast.Node;
22 |
23 | /**
24 | * A wrapper class to override the equals and hashCode methods to only be equal if 2 objects are the exact same instance. This class is typically used so that
25 | * {@link JavaParser} {@link Node} classes that have equals and hashCode methods that are not strict enough, can be used as key of a hashMap.
26 | *
27 | * @author Daan
28 | */
29 | public class HashCodeWrapper {
30 |
31 | private final T value;
32 |
33 | public HashCodeWrapper(T value) {
34 | this.value = value;
35 | }
36 |
37 | public T getValue() {
38 | return value;
39 | }
40 |
41 | @Override
42 | public String toString() {
43 | return "HashCodeWrapper[" + value.toString() + "]";
44 | }
45 |
46 | @Override
47 | public int hashCode() {
48 | return System.identityHashCode(value);
49 | }
50 |
51 | @Override
52 | public boolean equals(Object obj) {
53 | boolean equal = false;
54 | if (this.getClass().isInstance(obj)) {
55 | equal = value == ((HashCodeWrapper>) obj).getValue();
56 | }
57 | return equal;
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/common/SymbolSolverSetup.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package common;
19 |
20 | import com.github.javaparser.StaticJavaParser;
21 | import com.github.javaparser.symbolsolver.JavaSymbolSolver;
22 | import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
23 | import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
24 | import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
25 | import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
26 |
27 | /**
28 | * Class with common test methods for seting up the symbol solver.
29 | *
30 | * @author Daan
31 | */
32 | public class SymbolSolverSetup {
33 |
34 | public static void setup() {
35 | StaticJavaParser.getConfiguration().setSymbolResolver(getSymbolSolver());
36 | }
37 |
38 | private static JavaSymbolSolver getSymbolSolver() {
39 | JavaParserTypeSolver typeSolver_directory = new JavaParserTypeSolver("src/test/java/");
40 | ReflectionTypeSolver reflTypeSolver = new ReflectionTypeSolver();
41 | TypeSolver typeSolver = new CombinedTypeSolver(typeSolver_directory, reflTypeSolver);
42 | JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
43 | return symbolSolver;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/model/OwnedNode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Optional;
21 |
22 | import com.github.javaparser.ast.Node;
23 |
24 | /**
25 | * Interface for {@link DataFlowGraph} classes that own one or more {@link DataFlowNode}s. This interface contains a self reference since a node can be owned by
26 | * a {@link DataFlowMethod}, and that method can then be owned by a graph.
27 | *
28 | * @author Daan
29 | */
30 | public abstract class OwnedNode extends NodeRepresenter {
31 |
32 | protected OwnedNode() {
33 | super();
34 | }
35 |
36 | public OwnedNode(T representedNode) {
37 | super(representedNode);
38 | }
39 |
40 | public OwnedNode(String name, T representedNode) {
41 | super(name, representedNode);
42 | }
43 |
44 | public OwnedNode(NodeRepresenter.Builder builder) {
45 | super(builder);
46 | }
47 |
48 | public OwnedNode(String name) {
49 | super(name);
50 | }
51 |
52 | /**
53 | * @return An optional of the {@link OwnedNode} of this node. The optional will be empty in case of a {@link DataFlowGraph} representing a non inner class or
54 | * a method for which the rest of the graph was not parsed.
55 | */
56 | public abstract Optional> getOwner();
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/common/DataFlowMethodBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package common;
19 |
20 | import java.util.HashMap;
21 | import java.util.Map;
22 |
23 | import com.github.javaparser.ast.body.MethodDeclaration;
24 | import com.github.javaparser.ast.body.VariableDeclarator;
25 | import com.github.javaparser.ast.type.VarType;
26 |
27 | import model.DataFlowMethod;
28 | import model.DataFlowNode;
29 |
30 | /**
31 | * Builder for {@link DataFlowMethod} with some build method only ment for testing.
32 | *
33 | * @author Daan
34 | */
35 | public class DataFlowMethodBuilder extends DataFlowMethod.Builder {
36 |
37 | private Map currentNodes = new HashMap<>();
38 |
39 | private DataFlowMethodBuilder() {
40 | super.representedNode(new MethodDeclaration());
41 | }
42 |
43 | public static DataFlowMethodBuilder builder() {
44 | return new DataFlowMethodBuilder();
45 | }
46 |
47 | public DataFlowMethodBuilder withParameter(String name) {
48 | inputParameters.add(getNode(name));
49 | return this;
50 | }
51 |
52 | public DataFlowMethodBuilder withInputField(String name) {
53 | this.inputFields.add(new DataFlowNode(name, new VariableDeclarator(new VarType(), name)));
54 | return this;
55 | }
56 |
57 | public DataFlowMethodBuilder withChangedFieldEdge(String input, String changedField) {
58 | DataFlowNode a = getNode(input);
59 | DataFlowNode b = getNode("this." + changedField);
60 | a.addEdgeTo(b);
61 | this.changedFields.add(b);
62 | return this;
63 | }
64 |
65 | private DataFlowNode getNode(String name) {
66 | if (!this.currentNodes.containsKey(name)) {
67 | DataFlowNode node = new DataFlowNode(name, new VariableDeclarator(new VarType(), name));
68 | this.currentNodes.put(name, node);
69 | }
70 | return currentNodes.get(name);
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/model/OwnerNode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Collection;
21 | import java.util.HashSet;
22 | import java.util.Set;
23 |
24 | import com.github.javaparser.ast.Node;
25 |
26 | /**
27 | * A {@link OwnedNode} that owns other {@link OwnedNode}s. For instance a method owning a return node.
28 | *
29 | * @author Daan
30 | */
31 | public abstract class OwnerNode extends OwnedNode {
32 |
33 | public OwnerNode() {
34 | // empty constructor which would otherwise be invisible due to the constructor receiving the builder.
35 | }
36 |
37 | public OwnerNode(OwnedNode.Builder builder) {
38 | super(builder);
39 | }
40 |
41 | public OwnerNode(String name, T representedNode) {
42 | super(name, representedNode);
43 | }
44 |
45 | /**
46 | * True when this owner is either a direct owner or is an indirect owner of the input node.
47 | *
48 | * @param node The {@link OwnedNode} to check if it's owned by this.
49 | * @return true if this owns it, false otherwise.
50 | */
51 | public boolean owns(DataFlowNode node) {
52 | return getOwnedNodes().contains(node);
53 | }
54 |
55 | /**
56 | * Gets all direct or indirectly owned nodes.
57 | *
58 | * @return {@link Set} of {@link DataFlowNode}.
59 | */
60 | public final Set getOwnedNodes() {
61 | Set nodes = new HashSet<>(getDirectOwnedNodes());
62 | getOwnedOwners().stream().map(OwnerNode::getOwnedNodes).forEach(nodes::addAll);
63 | return nodes;
64 | }
65 |
66 | /**
67 | * @return all nodes directly owned by this {@link OwnerNode} for which it holds that
68 | */
69 | abstract Collection> getOwnedOwners();
70 |
71 | /**
72 | * @return all {@link DataFlowNode}s directly owned by this {@link OwnerNode}.
73 | */
74 | abstract Collection getDirectOwnedNodes();
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/factory/DataFlowNodeFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package factory;
19 |
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | import com.github.javaparser.ast.Node;
24 | import com.github.javaparser.ast.expr.SimpleName;
25 | import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName;
26 | import com.github.javaparser.ast.stmt.ReturnStmt;
27 | import com.github.javaparser.printer.Stringable;
28 |
29 | import model.DataFlowNode;
30 | import model.OwnedNode;
31 |
32 | /**
33 | * Factory for {@link DataFlowNode}s.
34 | *
35 | * @author Daan
36 | */
37 | public class DataFlowNodeFactory {
38 | private static final Logger LOG = LoggerFactory.getLogger(DataFlowNodeFactory.class);
39 |
40 | public DataFlowNode create(Node n, OwnedNode> owner) {
41 | DataFlowNode.Builder builder = DataFlowNode.builder().representedNode(n).owner(owner);
42 | Node nodeWithName = n;
43 | if (n instanceof ReturnStmt) {
44 | nodeWithName = ((ReturnStmt) n).getExpression().orElse(null);
45 | }
46 |
47 | if (nodeWithName instanceof NodeWithSimpleName) {
48 | builder.name(((NodeWithSimpleName>) nodeWithName).getNameAsString());
49 | } else if (nodeWithName instanceof Stringable) {
50 | builder.name(((Stringable) nodeWithName).asString());
51 | } else if (nodeWithName instanceof SimpleName) {
52 | builder.name(((SimpleName) nodeWithName).asString());
53 | } else {
54 | LOG.warn("Not supported to add a name to a created DataFlowNode for node of type {}, input node is {}", n.getClass(), n);
55 | }
56 |
57 | // TODO set the type
58 | // builder.type(null);
59 |
60 | // for (int i = 0; i < resolved.getNumberOfParams(); i++) {
61 | // ResolvedParameterDeclaration p = resolved.getParam(i);
62 | // DataFlowNode newNode =
63 | // DataFlowNode.builder().name(p.getName()).type(p.getType().describe().toString()).representedNode(node.getArgument(i)).owner(params).build();
64 | // params.add(newNode);
65 | // }
66 |
67 | return builder.build();
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/model/DataFlowEdge.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | /**
21 | * An edge inside a {@link DataFlowGraph} representing a {@link DataFlowNode} influencing the state of another {@link DataFlowNode}.
22 | *
23 | * @author Daan
24 | */
25 | public class DataFlowEdge {
26 |
27 | private DataFlowNode from;
28 | private DataFlowNode to;
29 |
30 | public DataFlowEdge() {
31 | // empty constructor which would otherwise be invisible due to the constructor receiving the builder.
32 | }
33 |
34 | private DataFlowEdge(Builder builder) {
35 | this.from = builder.from == null ? this.from : builder.from;
36 | this.to = builder.to == null ? this.to : builder.to;
37 | }
38 |
39 | public DataFlowEdge(DataFlowNode from, DataFlowNode to) {
40 | this.from = from;
41 | this.to = to;
42 | }
43 |
44 | public DataFlowNode getFrom() {
45 | return from;
46 | }
47 |
48 | public void setFrom(DataFlowNode from) {
49 | this.from = from;
50 | }
51 |
52 | public DataFlowNode getTo() {
53 | return to;
54 | }
55 |
56 | public void setTo(DataFlowNode to) {
57 | this.to = to;
58 | }
59 |
60 | @Override
61 | public String toString() {
62 | return from.getName() + "->" + to.getName();
63 | }
64 |
65 | /**
66 | * Creates builder to build {@link DataFlowEdge}.
67 | *
68 | * @return created builder
69 | */
70 | public static Builder builder() {
71 | return new Builder();
72 | }
73 |
74 | /**
75 | * Builder to build {@link DataFlowEdge}.
76 | */
77 | public static final class Builder {
78 | private DataFlowNode from;
79 | private DataFlowNode to;
80 |
81 | private Builder() {
82 | // Builder should only be constructed via the parent class
83 | }
84 |
85 | public Builder from(DataFlowNode from) {
86 | this.from = from;
87 | return this;
88 | }
89 |
90 | public Builder to(DataFlowNode to) {
91 | this.to = to;
92 | return this;
93 | }
94 |
95 | public DataFlowEdge build() {
96 | return new DataFlowEdge(this);
97 | }
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/test/java/factory/NodeCallFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package factory;
19 |
20 | import java.util.List;
21 | import java.util.Optional;
22 |
23 | import org.junit.Assert;
24 | import org.junit.Before;
25 | import org.junit.Test;
26 |
27 | import com.github.javaparser.StaticJavaParser;
28 | import com.github.javaparser.ast.CompilationUnit;
29 | import com.github.javaparser.ast.expr.MethodCallExpr;
30 |
31 | import common.SymbolSolverSetup;
32 | import factory.NodeCallFactory;
33 | import model.DataFlowMethod;
34 | import model.DataFlowNode;
35 | import model.NodeCall;
36 |
37 | /**
38 | * Unit test for {@link NodeCallFactory}.
39 | *
40 | * @author Daan
41 | */
42 | public class NodeCallFactoryTest {
43 |
44 | private NodeCallFactory sut = new NodeCallFactory();
45 |
46 | @Before
47 | public void setup() {
48 | SymbolSolverSetup.setup();
49 | }
50 |
51 | @Test
52 | public void testCreate() {
53 | CompilationUnit cu = StaticJavaParser.parse( //
54 | "public class Claz {\n" + //
55 | " StringBuilder sb = new StringBuilder(); \n" + //
56 | " public void met(int a) {\n" + //
57 | " sb.charAt(a);\n" + // returns a char
58 | " }\n" + //
59 | "}");
60 |
61 | List methodCalls = cu.findAll(MethodCallExpr.class);
62 | DataFlowMethod method = DataFlowMethod.builder().name("met").build();
63 | MethodCallExpr node = methodCalls.get(0);
64 | DataFlowNode instance = DataFlowNode.builder().name("inst").build();
65 |
66 | Optional resultMethod = sut.create(method, node, instance);
67 |
68 | MethodCallExpr expectedRepresentedNode = cu.findAll(MethodCallExpr.class).get(0);
69 | DataFlowNode expectedDfn = DataFlowNode.builder().name("nodeCall_charAt_return").type("char").representedNode(expectedRepresentedNode).build();
70 | NodeCall expectedDfm = NodeCall.builder().name("charAt").representedNode(expectedRepresentedNode).claz("AbstractStringBuilder").peckage("java.lang")
71 | .returnNode(expectedDfn).build();
72 |
73 | Assert.assertTrue(resultMethod.isPresent());
74 | Assert.assertEquals("charAt", resultMethod.get().getName());
75 | Assert.assertEquals(expectedDfn, resultMethod.get().getReturnNode().get());
76 | Assert.assertEquals("Unexpected instanceName", instance, resultMethod.get().getInstance().get());
77 | Assert.assertEquals(expectedDfm, resultMethod.get());
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/java/util/GraphUtilTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package util;
19 |
20 | import java.util.Arrays;
21 | import java.util.Collections;
22 | import java.util.List;
23 |
24 | import org.junit.Assert;
25 | import org.junit.Test;
26 |
27 | import common.GraphBuilder;
28 | import common.NodeBuilder;
29 | import model.DataFlowGraph;
30 | import model.DataFlowMethod;
31 | import model.DataFlowNode;
32 |
33 | /**
34 | * Unit test for {@link GraphUtil}.
35 | *
36 | * @author Daan
37 | */
38 | public class GraphUtilTest {
39 |
40 | @Test
41 | public void testWalkBackUntil_simple() {
42 | DataFlowGraph graph = GraphBuilder.withStartingNodes(NodeBuilder.ofParameter("setS", "a").to("setS.a").to("setS.b").to(NodeBuilder.ofField("s"))).build();
43 | DataFlowMethod m = graph.getMethods().iterator().next();
44 | DataFlowNode node = m.getChangedFields().get(0);
45 | DataFlowNode expected = m.getParameters().getNodes().get(0);
46 |
47 | List result = GraphUtil.walkBackUntil(node, m::isInputBoundary, graph::owns);
48 | Assert.assertEquals(Collections.singletonList(expected), result);
49 | }
50 |
51 | @Test
52 | public void testWalkBackUntil_inputIsBoundaryNode() {
53 | DataFlowGraph graph = GraphBuilder.withStartingNodes(NodeBuilder.ofParameter("setS", "a").to("setS.a").to("setS.b").to(NodeBuilder.ofField("s"))).build();
54 | DataFlowMethod m = graph.getMethods().iterator().next();
55 | DataFlowNode expected = m.getParameters().getNodes().get(0);
56 |
57 | List result = GraphUtil.walkBackUntil(expected, m::isInputBoundary, graph::owns);
58 | Assert.assertEquals(Collections.singletonList(expected), result);
59 | }
60 |
61 | @Test
62 | public void testWalkBackUntil_multipleOutput() {
63 | NodeBuilder field = NodeBuilder.ofField("x");
64 | GraphBuilder withStartingNodes = GraphBuilder.withStartingNodes( //
65 | NodeBuilder.ofParameter("setS", "a").to(field), //
66 | NodeBuilder.ofParameter("setS", "b").to(field), //
67 | NodeBuilder.ofParameter("setS", "c").to(NodeBuilder.ofField("y")) //
68 | );
69 | DataFlowGraph graph = withStartingNodes.build();
70 | DataFlowMethod m = graph.getMethods().iterator().next();
71 | DataFlowNode node = m.getChangedFields().get(0);
72 | List parameters = m.getParameters().getNodes();
73 |
74 | List result = GraphUtil.walkBackUntil(node, m::isInputBoundary, graph::owns);
75 | Assert.assertEquals(Arrays.asList(parameters.get(0), parameters.get(1)), result);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/util/GraphUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package util;
19 |
20 | import java.util.Collections;
21 | import java.util.List;
22 | import java.util.function.Predicate;
23 | import java.util.stream.Collectors;
24 |
25 | import model.DataFlowEdge;
26 | import model.DataFlowGraph;
27 | import model.DataFlowNode;
28 |
29 | /**
30 | * Service for methods to be executed on a {@link DataFlowGraph}.
31 | *
32 | * @author Daan
33 | */
34 | public class GraphUtil {
35 |
36 | public static List walkBackUntil(List nodes, Predicate predicate, Predicate scopePredicate) {
37 | return nodes.stream().map(n -> walkBackUntil(n, predicate, scopePredicate)).flatMap(List::stream).collect(Collectors.toList());
38 | }
39 |
40 | /**
41 | * Walks back via {@link DataFlowNode#getIn()} until for each node it holds that the predicate holds. The input {@link DataFlowNode} will be returned if the
42 | * {@link Predicate} holds for it. An empty list will be returned if the scopePredicate does not hold for the input node.
43 | *
44 | * @param dfn The input {@link DataFlowNode}
45 | * @param predicate The {@link Predicate} to check on the {@link DataFlowNode}
46 | * @param scopePredicate This predicate determines the scope for when to stop searching. If this predicate does not hold for the input node an empty list will
47 | * be returned.
48 | * @return Returns a list of nodes that either have no incoming edges, or for which the predicate holds.
49 | */
50 | public static List walkBackUntil(DataFlowNode dfn, Predicate predicate, Predicate scopePredicate) {
51 | if (!scopePredicate.test(dfn)) {
52 | return Collections.emptyList();
53 | }
54 | if (predicate.test(dfn)) {
55 | return Collections.singletonList(dfn);
56 | }
57 | return dfn.getIn().stream().map(DataFlowEdge::getFrom).map(node -> walkBackUntil(node, predicate, scopePredicate)).flatMap(List::stream)
58 | .collect(Collectors.toList());
59 | }
60 |
61 | /**
62 | * Walks forward via {@link DataFlowNode#getOut()} until for each node it holds that the predicate holds. The input {@link DataFlowNode} will be returned if
63 | * the {@link Predicate} holds for it. An empty list will be returned if the scopePredicate does not hold for the input node.
64 | *
65 | * @param dfn The input {@link DataFlowNode}
66 | * @param predicate The {@link Predicate} to check on the {@link DataFlowNode}
67 | * @param scopePredicate This predicate determines the scope for when to stop searching. If this predicate does not hold for the input node an empty list will
68 | * be returned.
69 | * @return Returns a list of nodes that either have no incoming edges, or for which the predicate holds.
70 | */
71 | public static List walkForwardUntil(DataFlowNode dfn, Predicate predicate, Predicate scopePredicate) {
72 | if (!scopePredicate.test(dfn)) {
73 | return Collections.emptyList();
74 | }
75 | if (predicate.test(dfn)) {
76 | return Collections.singletonList(dfn);
77 | }
78 | return dfn.getOut().stream().map(DataFlowEdge::getTo).map(node -> walkForwardUntil(node, predicate, scopePredicate)).flatMap(List::stream)
79 | .collect(Collectors.toList());
80 |
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/test/java/model/DataFlowGraphTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Collections;
21 | import java.util.List;
22 | import java.util.Map;
23 |
24 | import org.junit.Assert;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.mockito.runners.MockitoJUnitRunner;
28 |
29 | import com.github.javaparser.ast.Node;
30 | import com.github.javaparser.ast.body.FieldDeclaration;
31 | import com.github.javaparser.ast.body.MethodDeclaration;
32 | import com.github.javaparser.ast.expr.SimpleName;
33 |
34 | /**
35 | * Unit test for {@link DataFlowGraph}.
36 | *
37 | * @author Daan
38 | */
39 | @RunWith(MockitoJUnitRunner.class)
40 | public class DataFlowGraphTest {
41 | private static final String NAME = "a";
42 | private static final String CLASS_PACKAGE = "c";
43 | private static final List FIELDS = Collections.singletonList(DataFlowNode.builder().representedNode(new SimpleName("f")).build());
44 | private static final List CONSTRUCTORS = Collections.singletonList(DataFlowMethod.builder().build());
45 | private static final Map METHODS = Collections.singletonMap(new MethodDeclaration(), DataFlowMethod.builder().build());
46 | private static final Map NODES = Collections.singletonMap(new FieldDeclaration(), DataFlowNode.builder().build());
47 | private static final Map DEPENDED_GRAPHS = Collections.singletonMap("e", DataFlowGraph.builder().build());
48 |
49 | @Test
50 | public void testDataFlowGraph_minimum() {
51 | DataFlowGraph dataFlowGraph = DataFlowGraph.builder().build();
52 |
53 | Assert.assertNull("Unexpected name", dataFlowGraph.getName());
54 | Assert.assertNull("Unexpected classPackage", dataFlowGraph.getClassPackage());
55 | Assert.assertTrue("Unexpected fields", dataFlowGraph.getFields().isEmpty());
56 | Assert.assertTrue("Unexpected constructors", dataFlowGraph.getConstructors().isEmpty());
57 | Assert.assertTrue("Unexpected methods", dataFlowGraph.getMethods().isEmpty());
58 | Assert.assertEquals("Unexpected nodes", Collections.emptyMap(), dataFlowGraph.getNodes());
59 | Assert.assertEquals("Unexpected dependedGraphs", Collections.emptyMap(), dataFlowGraph.getDependedGraphs());
60 | }
61 |
62 | @Test
63 | public void testDataFlowGraph_maximum() {
64 | DataFlowGraph dataFlowGraph = createAndFillBuilder().build();
65 |
66 | Assert.assertEquals("Unexpected name", NAME, dataFlowGraph.getName());
67 | Assert.assertEquals("Unexpected classPackage", CLASS_PACKAGE, dataFlowGraph.getClassPackage());
68 | Assert.assertEquals("Unexpected fields", FIELDS, dataFlowGraph.getFields());
69 | Assert.assertEquals("Unexpected constructors", CONSTRUCTORS, dataFlowGraph.getConstructors());
70 | Assert.assertEquals("Unexpected methods", METHODS.values(), dataFlowGraph.getMethods());
71 | Assert.assertEquals("Unexpected nodes", NODES, dataFlowGraph.getNodes());
72 | Assert.assertEquals("Unexpected dependedGraphs", DEPENDED_GRAPHS, dataFlowGraph.getDependedGraphs());
73 | }
74 |
75 | private DataFlowGraph.Builder createAndFillBuilder() {
76 | return DataFlowGraph.builder().name(NAME).classPackage(CLASS_PACKAGE).fields(FIELDS).constructors(CONSTRUCTORS).methods(METHODS).nodes(NODES)
77 | .dependedGraphs(DEPENDED_GRAPHS);
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/java/model/ParameterListTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Collections;
21 | import java.util.List;
22 | import java.util.function.BiFunction;
23 |
24 | import org.junit.Assert;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.mockito.runners.MockitoJUnitRunner;
28 |
29 | /**
30 | * Unit test for {@link ParameterList}.
31 | *
32 | * @author Daan
33 | */
34 | @RunWith(MockitoJUnitRunner.class)
35 | public class ParameterListTest {
36 | private static final List NODES = Collections.singletonList(DataFlowNode.builder().build());
37 | private static final OwnedNode> OWNER = DataFlowNode.builder().build();
38 |
39 | @Test
40 | public void testParameterList_minimum() {
41 | ParameterList parameterList = ParameterList.builder().build();
42 |
43 | Assert.assertTrue("Unexpected nodes", parameterList.getNodes().isEmpty());
44 | Assert.assertFalse("Unexpected owner", parameterList.getOwner().isPresent());
45 | }
46 |
47 | @Test
48 | public void testParameterList_maximum() {
49 | ParameterList parameterList = createAndFillBuilder().build();
50 |
51 | Assert.assertEquals("Unexpected nodes", NODES, parameterList.getNodes());
52 | Assert.assertEquals("Unexpected owner", OWNER, parameterList.getOwner().get());
53 | }
54 |
55 | @Test
56 | public void testEquals_Same() {
57 | ParameterList.Builder builder = createAndFillBuilder();
58 | ParameterList a = builder.build();
59 | ParameterList b = builder.build();
60 | Assert.assertTrue("Expected a and b to be equal", a.equals(b));
61 | }
62 |
63 | @Test
64 | public void testEquals_Different() {
65 | verifyEqualsDifferent(ParameterList.Builder::nodes, Collections.singletonList(DataFlowNode.builder().name("a").build()));
66 | }
67 |
68 | @Test
69 | public void testHashCode_Same() {
70 | ParameterList.Builder builder = createAndFillBuilder();
71 | ParameterList a = builder.build();
72 | ParameterList b = builder.build();
73 | Assert.assertEquals("Expected hash code to be the same", a.hashCode(), b.hashCode());
74 | }
75 |
76 | @Test
77 | public void testHashCode_Different() {
78 | verifyHashCode_Different(ParameterList.Builder::nodes, Collections.singletonList(DataFlowNode.builder().name("a").build()));
79 | }
80 |
81 | private ParameterList.Builder createAndFillBuilder() {
82 | return ParameterList.builder().nodes(NODES).owner(OWNER);
83 | }
84 |
85 | private void verifyEqualsDifferent(BiFunction withMapper, T argument) {
86 | ParameterList.Builder builder = createAndFillBuilder();
87 | ParameterList a = builder.build();
88 | ParameterList b = withMapper.apply(builder, argument).build();
89 | Assert.assertFalse("Expected a and b not to be equal", a.equals(b));
90 | }
91 |
92 | private void verifyHashCode_Different(BiFunction withMapper, T argument) {
93 | ParameterList.Builder builder = createAndFillBuilder();
94 | ParameterList a = builder.build();
95 | ParameterList b = withMapper.apply(builder, argument).build();
96 | Assert.assertNotEquals("Expected hash code to be different", a.hashCode(), b.hashCode());
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/facade/StaticJavaDataFlow.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019 by Eyefreight BV (www.eyefreight.com). All rights reserved.
3 | *
4 | * This software is provided by the copyright holder and contributors "as is" and any express or implied warranties, including, but
5 | * not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall
6 | * Eyefreight BV or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages
7 | * (including, but not limited to, procurement of substitute goods or services; * loss of use, data, or profits; or business
8 | * interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including
9 | * negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
10 | */
11 | package facade;
12 |
13 | import java.io.File;
14 | import java.nio.file.Files;
15 | import java.util.stream.Stream;
16 |
17 | import org.apache.commons.lang3.ArrayUtils;
18 | import org.slf4j.Logger;
19 | import org.slf4j.LoggerFactory;
20 |
21 | import com.github.javaparser.StaticJavaParser;
22 | import com.github.javaparser.symbolsolver.JavaSymbolSolver;
23 | import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
24 | import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
25 | import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
26 | import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
27 |
28 | /**
29 | * Contains all static setting for {@link JavaDataFlow}.
30 | *
31 | * @author Daan
32 | */
33 | public class StaticJavaDataFlow {
34 | private static final Logger LOG = LoggerFactory.getLogger(StaticJavaDataFlow.class);
35 |
36 | /** Used to gather more data about a parsed class, such as resolving imports or super classes. */
37 | private JavaSymbolSolver symbolSolver;
38 |
39 | private static StaticJavaDataFlow config;
40 |
41 | private StaticJavaDataFlow() {
42 | // don't create it via any constructor
43 | setupSymbolSolver();
44 | }
45 |
46 | public static synchronized StaticJavaDataFlow getConfig() {
47 | if (config == null) {
48 | config = new StaticJavaDataFlow();
49 | }
50 | return config;
51 | }
52 |
53 | public final void setSymbolSolver(JavaSymbolSolver symbolSolver) {
54 | this.symbolSolver = symbolSolver;
55 | StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver);
56 | }
57 |
58 | public JavaSymbolSolver getSymbolSolver() {
59 | return symbolSolver;
60 | }
61 |
62 | /**
63 | * 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
64 | * ".../src/main/java" for maven projects. This method will override anything set by the method {@link StaticJavaDataFlow#setSymbolSolver(JavaSymbolSolver)}.
65 | *
66 | * @param paths The full paths to source folders where {@link JavaDataFlow} needs to look for classes that any input class depends on.
67 | */
68 | public void setProjectPaths(String... paths) {
69 | Stream.of(paths).filter(p -> !Files.exists(new File(p).toPath())).forEach(p -> LOG.error("Could not find the folder located at: " + p));
70 | JavaParserTypeSolver[] solvers =
71 | Stream.of(paths).filter(p -> Files.exists(new File(p).toPath())).map(JavaParserTypeSolver::new).toArray(JavaParserTypeSolver[]::new);
72 | TypeSolver[] reflTypeSolver = {new ReflectionTypeSolver()};
73 | TypeSolver typeSolver = new CombinedTypeSolver(ArrayUtils.addAll(reflTypeSolver, solvers));
74 | JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
75 | setSymbolSolver(symbolSolver);
76 | }
77 |
78 | private final void setupSymbolSolver() {
79 | TypeSolver reflTypeSolver = new ReflectionTypeSolver();
80 | JavaSymbolSolver symbolSolver = new JavaSymbolSolver(reflTypeSolver);
81 | this.setSymbolSolver(symbolSolver);
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/model/NodeRepresenter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Objects;
21 |
22 | import org.apache.commons.lang3.builder.EqualsBuilder;
23 | import org.apache.commons.lang3.builder.ToStringBuilder;
24 | import org.apache.commons.lang3.builder.ToStringStyle;
25 |
26 | import com.github.javaparser.JavaParser;
27 | import com.github.javaparser.ast.Node;
28 |
29 | /**
30 | * A class representing a {@link JavaParser} {@link Node}.
31 | *
32 | * @author Daan
33 | * @param The type of the {@link JavaParser} {@link Node} to represent.
34 | */
35 | public abstract class NodeRepresenter {
36 |
37 | /** The {@link JavaParser} {@link Node} */
38 | private T representedNode;
39 | /** The name of this node */
40 | protected String name;
41 |
42 | protected NodeRepresenter() {
43 | // empty constructor which would otherwise be invisible
44 | }
45 |
46 | public NodeRepresenter(T representedNode) {
47 | this.representedNode = representedNode;
48 | }
49 |
50 | public NodeRepresenter(String name, T representedNode) {
51 | this(representedNode);
52 | this.name = name;
53 | }
54 |
55 | protected NodeRepresenter(NodeRepresenter.Builder builder) {
56 | this.representedNode = builder.representedNode == null ? this.representedNode : builder.representedNode;
57 | this.name = builder.name == null ? this.name : builder.name;
58 | }
59 |
60 | public NodeRepresenter(String name) {
61 | this.name = name;
62 | }
63 |
64 | public T getRepresentedNode() {
65 | return representedNode;
66 | }
67 |
68 | public void setRepresentedNode(T representedNode) {
69 | this.representedNode = representedNode;
70 | }
71 |
72 | public String getName() {
73 | return name;
74 | }
75 |
76 | public void setName(String name) {
77 | this.name = name;
78 | }
79 |
80 | @Override
81 | public boolean equals(Object obj) {
82 | boolean equals = false;
83 | if (this == obj) {
84 | equals = true;
85 | } else if (obj != null && getClass() == obj.getClass()) {
86 | NodeRepresenter> other = (NodeRepresenter>) obj;
87 | equals = new EqualsBuilder().append(representedNode, other.representedNode).append(name, other.name).isEquals();
88 | }
89 | return equals;
90 | }
91 |
92 | @Override
93 | public int hashCode() {
94 | return Objects.hash(representedNode, name);
95 | }
96 |
97 | @Override
98 | public String toString() {
99 | return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("name", name).append("representedNode", representedNode).build();
100 | }
101 |
102 | /**
103 | * Creates builder to build {@link NodeRepresenter}.
104 | *
105 | * @param The type of the {@link JavaParser} {@link Node} to represent.
106 | * @return created builder
107 | */
108 | public static Builder> builder() {
109 | return new Builder<>();
110 | }
111 |
112 | /**
113 | * Builder to build {@link NodeRepresenter}.
114 | */
115 | @SuppressWarnings("unchecked")
116 | public static class Builder> {
117 | private T representedNode;
118 | private String name;
119 |
120 | protected Builder() {
121 | // Builder should only be used via the parent class or extending builder
122 | }
123 |
124 | public S representedNode(T representedNode) {
125 | this.representedNode = representedNode;
126 | return (S) this;
127 | }
128 |
129 | public S name(String name) {
130 | this.name = name;
131 | return (S) this;
132 | }
133 |
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/main/java/factory/NodeCallFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package factory;
19 |
20 | import java.util.Optional;
21 |
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 |
25 | import com.github.javaparser.ast.Node;
26 | import com.github.javaparser.ast.expr.MethodCallExpr;
27 | import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
28 | import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
29 | import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
30 | import com.github.javaparser.resolution.types.ResolvedType;
31 |
32 | import model.DataFlowGraph;
33 | import model.DataFlowMethod;
34 | import model.DataFlowNode;
35 | import model.NodeCall;
36 | import model.OwnedNode;
37 | import model.OwnerNode;
38 | import util.ParserUtil;
39 |
40 | /**
41 | * Class for resolving {@link DataFlowMethod}s and {@link DataFlowNode}s from {@link Node}s and {@link DataFlowGraph}s.
42 | *
43 | * @author Daan
44 | */
45 | public class NodeCallFactory {
46 | private static final Logger LOG = LoggerFactory.getLogger(NodeCallFactory.class);
47 |
48 | private ParserUtil parserUtil = new ParserUtil();
49 |
50 | /**
51 | * Creates a {@link NodeCall}.
52 | *
53 | * @param owner The direct {@link OwnerNode} for the {@link NodeCall} to be created.
54 | * @param node The {@link MethodCallExpr} that will be represented by the created {@link NodeCall}.
55 | * @param instance The {@link DataFlowNode} on which the call was executed.
56 | * @return created {@link NodeCall}.
57 | */
58 | public Optional create(OwnedNode> owner, MethodCallExpr node, DataFlowNode instance) {
59 | Object resolved = parserUtil.resolve(owner, node);
60 |
61 | NodeCall resolvedMethod = null;
62 | if (resolved instanceof ResolvedMethodLikeDeclaration) {
63 | resolvedMethod = createMethodCall(owner, (ResolvedMethodLikeDeclaration) resolved, node, instance);
64 | } else {
65 | LOG.warn("In method {}, resolving is not supported for node {} of type {}", owner.getName(), node, resolved == null ? null : resolved.getClass());
66 | }
67 | return Optional.ofNullable(resolvedMethod);
68 | }
69 |
70 | private NodeCall createMethodCall(OwnedNode> owner, ResolvedMethodLikeDeclaration resolved, MethodCallExpr node, DataFlowNode instance) {
71 | NodeCall methodCall = NodeCall.builder().name(resolved.getName()).claz(resolved.getClassName()).peckage(resolved.getPackageName()).owner(owner)
72 | .representedNode(node).instance(instance).build();
73 | setReturn(methodCall, owner, node, resolved);
74 | return methodCall;
75 | }
76 |
77 | private void setReturn(NodeCall methodCall, OwnedNode> method, MethodCallExpr node, ResolvedMethodLikeDeclaration rmd) {
78 | if (rmd instanceof ResolvedMethodDeclaration) {
79 | ResolvedType returnType = ((ResolvedMethodDeclaration) rmd).getReturnType();
80 | if (!returnType.isVoid()) {
81 | DataFlowNode returnNode =
82 | DataFlowNode.builder().name("nodeCall_" + methodCall.getName() + "_return").representedNode(node).type(getType(returnType)).build();
83 | methodCall.setReturnNode(returnNode);
84 | }
85 | } else {
86 | LOG.warn("Not supported to create return node in NodeCall from resolved node of type {} in method {}", rmd.getClass(), method.getName());
87 | }
88 | }
89 |
90 | private String getType(ResolvedType returnType) {
91 | String name;
92 | if (returnType instanceof ResolvedPrimitiveType) {
93 | name = ((ResolvedPrimitiveType) returnType).describe();
94 | } else {
95 | LOG.warn("Could not resolve the type of {}", returnType);
96 | name = "UNKNOWN";
97 | }
98 | return name;
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/util/ParserUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package util;
19 |
20 | import java.io.FileInputStream;
21 | import java.io.FileNotFoundException;
22 | import java.io.IOException;
23 | import java.util.Optional;
24 | import java.util.stream.Stream;
25 |
26 | import org.apache.commons.lang3.StringUtils;
27 | import org.slf4j.Logger;
28 | import org.slf4j.LoggerFactory;
29 |
30 | import com.github.javaparser.JavaParser;
31 | import com.github.javaparser.StaticJavaParser;
32 | import com.github.javaparser.ast.CompilationUnit;
33 | import com.github.javaparser.ast.Node;
34 | import com.github.javaparser.resolution.Resolvable;
35 | import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserFieldDeclaration;
36 | import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserParameterDeclaration;
37 | import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserVariableDeclaration;
38 | import com.google.common.util.concurrent.UncheckedExecutionException;
39 |
40 | import model.DataFlowMethod;
41 | import model.OwnedNode;
42 |
43 | public class ParserUtil {
44 | private static final Logger LOG = LoggerFactory.getLogger(ParserUtil.class);
45 |
46 | /**
47 | * Gets the node to which the input points to. For example for the input "this.s" which points to a field usage in a class, the {@link JavaParser}
48 | * {@link Node} that will be returned is the field "s".
49 | *
50 | * @param method Only needed for logging purposes
51 | * @param node The node to resolve.
52 | * @return An empty {@link Optional} if the node could not be resolved, an {@link Optional} with the pointed to node otherwise.
53 | */
54 | public Optional getJavaParserNode(DataFlowMethod method, Node node) {
55 | Object resolved = resolve(method, node);
56 | Node resolvedNode = null;
57 | if (resolved instanceof JavaParserFieldDeclaration) {
58 | resolvedNode = ((JavaParserFieldDeclaration) resolved).getVariableDeclarator();
59 | } else if (resolved instanceof JavaParserParameterDeclaration) {
60 | resolvedNode = ((JavaParserParameterDeclaration) resolved).getWrappedNode();
61 | } else if (resolved instanceof JavaParserVariableDeclaration) {
62 | resolvedNode = ((JavaParserVariableDeclaration) resolved).getWrappedNode();
63 | } else {
64 | LOG.warn("In method {}, resolving is not supported for node {} of type {}", method.getName(), node, resolved == null ? null : resolved.getClass());
65 | }
66 | return Optional.ofNullable(resolvedNode);
67 | }
68 |
69 | public Object resolve(OwnedNode> method, Node node) {
70 | if (!Resolvable.class.isAssignableFrom(node.getClass())) {
71 | LOG.warn("In method {}, node is not Resolvable for expression {} of type {}", method.getName(), node, node.getClass());
72 | return null;
73 | }
74 |
75 | Resolvable> resolvable = (Resolvable>) node;
76 | Object resolved = null;
77 | try {
78 | resolved = resolvable.resolve();
79 | } catch (Exception e) {
80 | LOG.warn(e.getMessage());
81 | LOG.trace(
82 | Stream.of(e.getStackTrace()).map(StackTraceElement::toString).reduce((str1, str2) -> StringUtils.join(new String[] {str1, str2}, '\n')).orElse(""));
83 | }
84 | return resolved;
85 | }
86 |
87 | public CompilationUnit createCompilationUnit(String inputClass) {
88 | CompilationUnit cu = null;
89 | try (FileInputStream in = new FileInputStream(inputClass)) {
90 | cu = StaticJavaParser.parse(in);
91 | in.close();
92 | } catch (FileNotFoundException e) {
93 | throw new UncheckedExecutionException("Could not parse class at location: " + inputClass, e);
94 | } catch (IOException e) {
95 | throw new UncheckedExecutionException("Unable to close input stream for: " + inputClass, e);
96 | }
97 | return cu;
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/test/java/common/NodeBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package common;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.Collections;
23 | import java.util.List;
24 |
25 | import com.github.javaparser.ast.expr.SimpleName;
26 |
27 | import model.DataFlowNode;
28 |
29 | /**
30 | * Builder for {@link DataFlowNode}, only to be used for test purposes.
31 | *
32 | * @author Daan
33 | * @deprecated This class can only build simple graphs, use the normal setters, constructors and builders of the JavaDataFlow classes.
34 | */
35 | @Deprecated
36 | public class NodeBuilder {
37 |
38 | protected enum NodeType {
39 | IN_BETWEEN,
40 | METHOD_PARAMETER,
41 | CLASS_FIELD,
42 | RETURN
43 | }
44 |
45 | private String method;
46 | private String name;
47 | private List out = new ArrayList<>();
48 | private final NodeType type;
49 | private List roots = new ArrayList<>();
50 | private DataFlowNode build;
51 |
52 | public NodeBuilder(String name, NodeType type) {
53 | this.name = name;
54 | this.type = type;
55 | }
56 |
57 | /**
58 | * Create a {@link NodeBuilder} for given method and parameter.
59 | *
60 | * @param method Name of the method
61 | * @param name Name of the parameter
62 | * @return {@link NodeBuilder}
63 | */
64 | public static NodeBuilder ofParameter(String method, String name) {
65 | NodeBuilder builder = new NodeBuilder(name, NodeType.METHOD_PARAMETER);
66 | builder.method = method;
67 | return builder;
68 | }
69 |
70 | public static NodeBuilder ofField(String name) {
71 | NodeBuilder builder = new NodeBuilder(name, NodeType.CLASS_FIELD);
72 | return builder;
73 | }
74 |
75 | public static NodeBuilder ofInBetween(String name) {
76 | NodeBuilder builder = new NodeBuilder(name, NodeType.IN_BETWEEN);
77 | return builder;
78 | }
79 |
80 | public static NodeBuilder ofReturn(String methodName, String line, String column) {
81 | NodeBuilder methodReturn = new NodeBuilder(methodName + "_return_" + line + "_" + column, NodeType.RETURN);
82 | methodReturn.method = methodName;
83 | return methodReturn;
84 | }
85 |
86 | public NodeBuilder to(String name) {
87 | NodeBuilder next = new NodeBuilder(name, NodeType.IN_BETWEEN);
88 | next.addRoots(this);
89 | out.add(next);
90 | return next;
91 | }
92 |
93 | public void to(String... names) {
94 | Arrays.stream(names).forEach(this::to);
95 | }
96 |
97 | public NodeBuilder to(NodeBuilder next) {
98 | out.add(next);
99 | // TODO don't think this is correct, it should be next.addRoots(this.roots)
100 | next.addRoots(this);
101 | return next;
102 | }
103 |
104 | public void to(NodeBuilder... names) {
105 | Arrays.stream(names).forEach(this::to);
106 | }
107 |
108 | private void addRoots(NodeBuilder root) {
109 | this.roots.addAll(root.getRoots());
110 | }
111 |
112 | public List getRoots() {
113 | return this.roots.isEmpty() ? Collections.singletonList(this) : roots;
114 | }
115 |
116 | public NodeBuilder getRoot() {
117 | return this.roots.isEmpty() ? this : roots.get(0);
118 | }
119 |
120 | public String getMethod() {
121 | return method;
122 | }
123 |
124 | public void setMethod(String method) {
125 | this.method = method;
126 | }
127 |
128 | public String getName() {
129 | return name;
130 | }
131 |
132 | public void setName(String name) {
133 | this.name = name;
134 | }
135 |
136 | public List getOut() {
137 | return out;
138 | }
139 |
140 | public void setOut(List out) {
141 | this.out = out;
142 | }
143 |
144 | public NodeType getType() {
145 | return type;
146 | }
147 |
148 | public DataFlowNode build() {
149 | return DataFlowNode.builder().name(name).representedNode(new SimpleName(name)).build();
150 | }
151 |
152 | public DataFlowNode getOrBuild() {
153 | if (build == null) {
154 | build = build();
155 | }
156 | return build;
157 | }
158 |
159 | @Override
160 | public String toString() {
161 | return getName();
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/src/test/java/model/NodeCallTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Optional;
21 | import java.util.function.BiFunction;
22 |
23 | import org.junit.Assert;
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 | import org.mockito.runners.MockitoJUnitRunner;
27 |
28 | import com.github.javaparser.ast.body.CallableDeclaration;
29 | import com.github.javaparser.ast.body.ConstructorDeclaration;
30 |
31 | /**
32 | * Unit test for {@link NodeCall}.
33 | *
34 | * @author Daan
35 | */
36 | @RunWith(MockitoJUnitRunner.class)
37 | public class NodeCallTest {
38 | private static final ParameterList IN = ParameterList.builder().name("n").build();
39 | private static final OwnedNode> OWNER = DataFlowNode.builder().build();
40 | private static final DataFlowMethod CALLED_METHOD = DataFlowMethod.builder().build();
41 | private static final String CLAZ = "a";
42 | private static final String PECKAGE = "c";
43 | private static final CallableDeclaration> REPRESENTED_NODE = new ConstructorDeclaration();
44 |
45 | @Test
46 | public void testNodeCall_minimum() {
47 | NodeCall nodeCall = NodeCall.builder().owner(OWNER).build();
48 |
49 | Assert.assertFalse("Unexpected in", nodeCall.getIn().isPresent());
50 | Assert.assertTrue("Unexpected owner", nodeCall.getOwner().isPresent());
51 | Assert.assertFalse("Unexpected calledMethod", nodeCall.getCalledMethod().isPresent());
52 | Assert.assertNull("Unexpected claz", nodeCall.getClaz());
53 | Assert.assertNull("Unexpected peckage", nodeCall.getPeckage());
54 | }
55 |
56 | @Test
57 | public void testNodeCall_maximum() {
58 | NodeCall nodeCall = createAndFillBuilder().build();
59 |
60 | Assert.assertEquals("Unexpected in", IN, nodeCall.getIn().get());
61 | Assert.assertEquals("Unexpected owner", OWNER, nodeCall.getOwner().get());
62 | Assert.assertEquals("Unexpected calledMethod", Optional.of(CALLED_METHOD), nodeCall.getCalledMethod());
63 | Assert.assertEquals("Unexpected claz", CLAZ, nodeCall.getClaz());
64 | Assert.assertEquals("Unexpected peckage", PECKAGE, nodeCall.getPeckage());
65 | }
66 |
67 | @Test
68 | public void testEquals_Same() {
69 | NodeCall.Builder builder = createAndFillBuilder();
70 | NodeCall a = builder.build();
71 | NodeCall b = builder.build();
72 | Assert.assertTrue("Expected a and b to be equal", a.equals(b));
73 | }
74 |
75 | @Test
76 | public void testEquals_Different() {
77 | verifyEqualsDifferent(NodeCall.Builder::in, ParameterList.builder().representedNode(REPRESENTED_NODE).build());
78 | verifyEqualsDifferent(NodeCall.Builder::calledMethod, DataFlowMethod.builder().name("x").build());
79 | verifyEqualsDifferent(NodeCall.Builder::claz, "b");
80 | verifyEqualsDifferent(NodeCall.Builder::peckage, "d");
81 | }
82 |
83 | @Test
84 | public void testHashCode_Same() {
85 | NodeCall.Builder builder = createAndFillBuilder();
86 | NodeCall a = builder.build();
87 | NodeCall b = builder.build();
88 | Assert.assertEquals("Expected hash code to be the same", a.hashCode(), b.hashCode());
89 | }
90 |
91 | @Test
92 | public void testHashCode_Different() {
93 | verifyHashCode_Different(NodeCall.Builder::in, ParameterList.builder().representedNode(REPRESENTED_NODE).build());
94 | verifyHashCode_Different(NodeCall.Builder::calledMethod, DataFlowMethod.builder().name("x").build());
95 | verifyHashCode_Different(NodeCall.Builder::claz, "b");
96 | verifyHashCode_Different(NodeCall.Builder::peckage, "d");
97 | }
98 |
99 | private NodeCall.Builder createAndFillBuilder() {
100 | return NodeCall.builder().in(IN).owner(OWNER).calledMethod(CALLED_METHOD).claz(CLAZ).peckage(PECKAGE);
101 | }
102 |
103 | private void verifyEqualsDifferent(BiFunction withMapper, T argument) {
104 | NodeCall.Builder builder = createAndFillBuilder();
105 | NodeCall a = builder.build();
106 | NodeCall b = withMapper.apply(builder, argument).build();
107 | Assert.assertFalse("Expected a and b not to be equal", a.equals(b));
108 | }
109 |
110 | private void verifyHashCode_Different(BiFunction withMapper, T argument) {
111 | NodeCall.Builder builder = createAndFillBuilder();
112 | NodeCall a = builder.build();
113 | NodeCall b = withMapper.apply(builder, argument).build();
114 | Assert.assertNotEquals("Expected hash code to be different", a.hashCode(), b.hashCode());
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/test/java/model/DataFlowMethodTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Collections;
21 | import java.util.List;
22 | import java.util.function.BiFunction;
23 |
24 | import org.junit.Assert;
25 | import org.junit.Test;
26 |
27 | import com.github.javaparser.ast.body.CallableDeclaration;
28 | import com.github.javaparser.ast.body.ConstructorDeclaration;
29 | import com.github.javaparser.ast.body.MethodDeclaration;
30 |
31 | /**
32 | * Unit test for {@link DataFlowMethod}.
33 | *
34 | * @author Daan
35 | */
36 | public class DataFlowMethodTest {
37 | private static final ParameterList INPUT_PARAMETERS = new ParameterList(DataFlowNode.builder().build());
38 | private static final List INPUT_FIELDS = Collections.singletonList(DataFlowNode.builder().name("b").build());
39 | private static final List CHANGED_FIELDS = Collections.singletonList(DataFlowNode.builder().name("a").build());
40 | private static final String NAME = "a";
41 | private static final CallableDeclaration> REPRESENTED_NODE = new ConstructorDeclaration();
42 | private static final DataFlowNode RETURN_NODE = DataFlowNode.builder().name("d").build();
43 |
44 | @Test
45 | public void testDataFlowMethod_minimum() {
46 | DataFlowMethod dataFlowMethod = DataFlowMethod.builder().build();
47 |
48 | Assert.assertNull("Unexpected inputParameters", dataFlowMethod.getParameters());
49 | Assert.assertTrue("Unexpected inputFields", dataFlowMethod.getInputFields().isEmpty());
50 | Assert.assertTrue("Unexpected changedFields", dataFlowMethod.getChangedFields().isEmpty());
51 | }
52 |
53 | @Test
54 | public void testDataFlowMethod_maximum() {
55 | DataFlowMethod dataFlowMethod = createAndFillBuilder().build();
56 |
57 | Assert.assertEquals("Unexpected inputParameters", INPUT_PARAMETERS, dataFlowMethod.getParameters());
58 | Assert.assertEquals("Unexpected inputFields", INPUT_FIELDS, dataFlowMethod.getInputFields());
59 | Assert.assertEquals("Unexpected changedFields", CHANGED_FIELDS, dataFlowMethod.getChangedFields());
60 | }
61 |
62 | @Test
63 | public void testHashCode_Same() {
64 | DataFlowMethod.Builder builder = createAndFillBuilder();
65 | DataFlowMethod a = builder.build();
66 | DataFlowMethod b = builder.build();
67 | Assert.assertEquals("Expected hash code to be the same", a.hashCode(), b.hashCode());
68 | }
69 |
70 | @Test
71 | public void testHashCode_Different() {
72 | verifyHashCode_Different(DataFlowMethod.Builder::name, "b");
73 | verifyHashCode_Different(DataFlowMethod.Builder::representedNode, new MethodDeclaration());
74 | verifyHashCode_Different(DataFlowMethod.Builder::returnNode, DataFlowNode.builder().build());
75 | verifyHashCode_Different(DataFlowMethod.Builder::inputParameters, ParameterList.builder().representedNode(REPRESENTED_NODE).build());
76 | verifyHashCode_Different(DataFlowMethod.Builder::inputFields, Collections.singletonList(DataFlowNode.builder().build()));
77 | verifyHashCode_Different(DataFlowMethod.Builder::changedFields, Collections.singletonList(DataFlowNode.builder().build()));
78 | }
79 |
80 | @Test
81 | public void testEquals_Same() {
82 | DataFlowMethod.Builder builder = createAndFillBuilder();
83 | DataFlowMethod a = builder.build();
84 | DataFlowMethod b = builder.build();
85 | Assert.assertTrue("Expected a and b to be equal", a.equals(b));
86 | }
87 |
88 | @Test
89 | public void testEquals_Different() {
90 | verifyEqualsDifferent(DataFlowMethod.Builder::name, "b");
91 | verifyEqualsDifferent(DataFlowMethod.Builder::representedNode, new MethodDeclaration());
92 | verifyEqualsDifferent(DataFlowMethod.Builder::returnNode, DataFlowNode.builder().build());
93 | verifyEqualsDifferent(DataFlowMethod.Builder::inputParameters, ParameterList.builder().representedNode(REPRESENTED_NODE).build());
94 | }
95 |
96 | private DataFlowMethod.Builder createAndFillBuilder() {
97 | return DataFlowMethod.builder().name(NAME).representedNode(REPRESENTED_NODE).returnNode(RETURN_NODE).inputParameters(INPUT_PARAMETERS)
98 | .inputFields(INPUT_FIELDS).changedFields(CHANGED_FIELDS);
99 | }
100 |
101 | private void verifyHashCode_Different(BiFunction withMapper, T argument) {
102 | DataFlowMethod.Builder builder = createAndFillBuilder();
103 | DataFlowMethod a = builder.build();
104 | DataFlowMethod b = withMapper.apply(builder, argument).build();
105 | Assert.assertNotEquals("Expected hash code to be different", a.hashCode(), b.hashCode());
106 | }
107 |
108 | private void verifyEqualsDifferent(BiFunction withMapper, T argument) {
109 | DataFlowMethod.Builder builder = createAndFillBuilder();
110 | DataFlowMethod a = builder.build();
111 | DataFlowMethod b = withMapper.apply(builder, argument).build();
112 | Assert.assertFalse("Expected a and b not to be equal", a.equals(b));
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://gitter.im/JavaDataFlow/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2 | [](https://search.maven.org/search?q=g:com.github.daanvdh.javadataflow)
3 |
4 | # JavaDataFlow
5 | Creating Data Flow Graphs from java input classes
6 |
7 | With JavaDataFlow you can create data flow graphs.
8 | 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.
9 | Nodes are always defined within a specific method, constructor, codeBlock.
10 | We refer to such a code block as owner.
11 | Every Node has an owner.
12 | Every owner can also have an owner, for instance a method is owned by a class.
13 | 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.
14 | 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.
15 |
16 |
17 | ## Example
18 |
19 | Let's say we have a class as given as below:
20 |
21 | public class JavaDataFlowExampleInputClass {
22 | int a;
23 | public int getA() {
24 | return a;
25 | }
26 | public void setA(int inputA) {
27 | this.a = inputA;
28 | }
29 | }
30 |
31 | Then we can execute the following commands to create a data flow graph from it.
32 | First define the path to your project and setup the path where JavaDataFlow will look for class files.
33 | If you have multiple projects where JavaDataFlow needs to look for class files, you can enter multiple project paths there.
34 | Then create the data flow graph using the absolute input path to the java class.
35 | A DataFlowGraph represents a single class.
36 |
37 | String projectPath = "projectPath";
38 | String input = "/relativePath/Example.java";
39 | StaticJavaDataFlow.getConfig().setProjectPaths(projectPath);
40 | DataFlowGraph dfg = JavaDataFlow.create(projectPath + input);
41 |
42 | 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.
43 | First get the given method.
44 | 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.
45 | For this example we don't want to go outside of this class so we add dfg::owns as scope to the method walkBackUntil.
46 | The scope determines when to stop walking over the nodes, this can become important if multiple data flow graphs are connected to each other.
47 | However, this is currently not supported yet.
48 |
49 | DataFlowMethod getA = dfg.getMethods().stream().filter(m -> m.getName().equals("getA")).findFirst().get();
50 | List inputNodes = getA.getReturnNode().get().walkBackUntil(DataFlowNode::isInputParameter, dfg::owns);
51 | System.out.println(inputNodes.get(0).getName());
52 |
53 | The above code will output the name "inputA". All code above is also given in an [example project](https://github.com/daanvdh/JavaDataFlowExample).
54 |
55 | ## Setup
56 | Add the dependency below to the pom of your project.
57 |
58 |
59 | com.github.daanvdh.javadataflow
60 | JavaDataFlow
61 | 0.0.5
62 |
63 | ## Definitions
64 |
65 | - Any **object or primitive** is modelled as a DataFlowNode.
66 | A DataFlowNode can have 0 or more input or output DataFlowEdge's.
67 | Such an edge represents a data flow from one node to another.
68 | - The **owner** of a DataFlowNode represents structure in which the node is defined.
69 | For the method setA, the parameter inputA has a ParameterList as input.
70 | The ParameterList has the DataFlowMethod for "setA" as owner.
71 | The owner of the method is the DataFlowGraph representing Example.java
72 | - Each **usage of an object** is modelled as a separate DataFlowNode.
73 | 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.
74 | This way the order in which a node was used is maintained.
75 | - A NodeCall represents a **call to another code block**, for instance a method call.
76 | 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.
77 | That same DataFlowNode will then have a reference to the NodeCall via DataFlowNode::getNodeCall.
78 | A DataFlowNode can only have a single NodeCall, since every object usage is modelled as a separate node.
79 | 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.
80 |
81 |
82 | ## Features
83 | - JavaDataFlow uses [JavaParser](https://github.com/javaparser/javaparser/) for parsing the input classes.
84 | Each DataFlowNode has a representedNode which is the JavaParser Node that it represents.
85 | If you have a given JavaParser Node you can get the JavaDataFlowNode via DataFlowGraph::getNode.
86 | - Collect all methods that where called on a given object by executing DataFlowNode::collectNodeCalls.
87 | 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.
88 |
89 | ## Roadmap
90 | - Include Constructors in the JavaDataFlow graph.
91 | - Model if statements.
92 | - Model primitive functions like: + - * / < >.
93 | - Model for and while loops.
94 | - Connect multiple JavaDataFlow graphs to each other so that we can walk from class to class.
95 |
96 | ## License
97 |
98 | JavaDataFlow is available under the terms of the Apache License. http://www.apache.org/licenses/LICENSE-2.0
99 |
--------------------------------------------------------------------------------
/src/test/java/common/GraphBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package common;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.HashMap;
23 | import java.util.List;
24 | import java.util.Map;
25 |
26 | import com.github.javaparser.ast.Modifier;
27 | import com.github.javaparser.ast.NodeList;
28 | import com.github.javaparser.ast.body.CallableDeclaration;
29 | import com.github.javaparser.ast.body.MethodDeclaration;
30 | import com.github.javaparser.ast.type.ClassOrInterfaceType;
31 |
32 | import model.DataFlowGraph;
33 | import model.DataFlowMethod;
34 | import model.DataFlowNode;
35 | import model.ParameterList;
36 |
37 | /**
38 | * Builder for {@link DataFlowGraph}, only to be used for test purposes.
39 | *
40 | * @author Daan
41 | * @deprecated This class can only build simple graphs, use the normal setters, constructors and builders of the JavaDataFlow classes.
42 | */
43 | @Deprecated
44 | public class GraphBuilder {
45 |
46 | private List startNodes = new ArrayList<>();
47 |
48 | public static GraphBuilder withStartingNodes(NodeBuilder... nodes) {
49 | GraphBuilder graphBuilder = new GraphBuilder();
50 | Arrays.stream(nodes).map(NodeBuilder::getRoots).flatMap(List::stream).distinct().forEach(graphBuilder.startNodes::add);
51 | return graphBuilder;
52 | }
53 |
54 | public DataFlowGraph build() {
55 | DataFlowGraph graph = new DataFlowGraph();
56 | Map nodes = new HashMap<>();
57 | Map methods = new HashMap<>();
58 | List addedNodes = new ArrayList<>();
59 | startNodes.forEach(node -> this.addNode(graph, node, nodes, methods, addedNodes));
60 | return graph;
61 | }
62 |
63 | private void addNode(DataFlowGraph graph, NodeBuilder nodeBuilder, Map nodes, Map methods,
64 | List addedNodes) {
65 | addNode(graph, nodeBuilder, nodes, methods, null, null, addedNodes);
66 | }
67 |
68 | /**
69 | * Recursively adds a new nodes to the given {@link DataFlowGraph}. This method should only be called for nodes not already added.
70 | *
71 | * @param graph The {@link DataFlowGraph} to add Nodes to.
72 | * @param nodeBuilder The next Node to add.
73 | * @param nodes Previously added nodes.
74 | * @param methods previously added methods.
75 | * @param previousNode The node that was added right before the given input node, can be null.
76 | * @param currentMethod The current method that is being added.
77 | * @param addNodes The nodes added in previous iterations. If a node is present, it will not be added to the graph again. Note that this has to be a list,
78 | * because the hash value might change in multiple iterations.
79 | */
80 | private void addNode(DataFlowGraph graph, NodeBuilder nodeBuilder, Map nodes, Map methods,
81 | DataFlowNode previousNode, DataFlowMethod currentMethod, List addedNodes) {
82 |
83 | DataFlowNode node = nodeBuilder.getOrBuild();
84 | if (previousNode != null) {
85 | // Always add the node to the previous node
86 | previousNode.addEdgeTo(node);
87 | }
88 | if (addedNodes.contains(node)) {
89 | // Don't add a node that was already added.
90 | return;
91 | }
92 |
93 | DataFlowMethod method = null;
94 | switch (nodeBuilder.getType()) {
95 | case CLASS_FIELD:
96 | graph.addField(node);
97 | if (currentMethod != null) {
98 | currentMethod.addChangedField(node);
99 | }
100 | break;
101 | case METHOD_PARAMETER:
102 | method = getOrCreateMethod(graph, methods, nodeBuilder.getMethod());
103 | if (method.getParameters() == null) {
104 | method.setInputParameters(ParameterList.builder().build());
105 | }
106 | // TODO if we want to influence the order of the parameters,
107 | // we need to create a NodeBuilder.ofParameter method with a parameter index as input and handle it here.
108 | method.addParameter(node);
109 | break;
110 | case RETURN:
111 | method = getOrCreateMethod(graph, methods, nodeBuilder.getMethod());
112 |
113 | method.addNode(node);
114 |
115 | DataFlowNode methodReturn = getOrCreateReturnNode(method);
116 | node.addEdgeTo(methodReturn);
117 |
118 | break;
119 | case IN_BETWEEN:
120 | currentMethod.addNode(node);
121 | break;
122 | default:
123 | // Do nothing
124 | }
125 |
126 | DataFlowMethod nextMethod = method == null ? currentMethod : method;
127 | addedNodes.add(node);
128 | nodeBuilder.getOut().forEach(nb -> addNode(graph, nb, nodes, methods, node, nextMethod, addedNodes));
129 | }
130 |
131 | private DataFlowNode getOrCreateReturnNode(DataFlowMethod method) {
132 | if (!method.getReturnNode().isPresent()) {
133 | method.setReturnNode(new DataFlowNode(method.getName() + "_return", method.getRepresentedNode()));
134 | }
135 | return method.getReturnNode().get();
136 | }
137 |
138 | private DataFlowMethod getOrCreateMethod(DataFlowGraph graph, Map methods, String methodName) {
139 | if (!methods.containsKey(methodName)) {
140 | CallableDeclaration> node = new MethodDeclaration(NodeList.nodeList(Modifier.publicModifier()), new ClassOrInterfaceType(), methodName);
141 | DataFlowMethod method = new DataFlowMethod(methodName, node);
142 | graph.addMethod(method);
143 | methods.put(methodName, method);
144 | }
145 | return methods.get(methodName);
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.github.daanvdh.javadataflow
6 | JavaDataFlow
7 | 0.0.5
8 | jar
9 |
10 | JavaDataFlow
11 | Creating Data Flow Graphs from java input classes
12 | https://github.com/daanvdh/JavaDataFlow
13 |
14 |
15 | scm:git:git://github.com/daanvdh/javadataflow.git
16 | scm:git:git@github.com:daanvdh/javadataflow.git
17 | https://github.com/daanvdh/javadataflow.git
18 | HEAD
19 |
20 |
21 |
22 |
23 | ossrh
24 | https://oss.sonatype.org/content/repositories/snapshots
25 |
26 |
27 | ossrh
28 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
29 |
30 |
31 |
32 |
33 |
34 |
35 | org.apache.maven.plugins
36 | maven-compiler-plugin
37 | 2.3.1
38 |
39 | 1.8
40 | 1.8
41 | true
42 | true
43 |
44 |
45 |
46 | org.apache.maven.plugins
47 | maven-javadoc-plugin
48 | 2.9.1
49 |
50 |
51 | attach-javadocs
52 | deploy
53 |
54 | jar
55 |
56 |
57 |
58 |
59 |
60 | org.apache.maven.plugins
61 | maven-source-plugin
62 | 2.2.1
63 |
64 |
65 | attach-sources
66 | deploy
67 |
68 | jar-no-fork
69 |
70 |
71 |
72 |
73 |
74 | org.apache.maven.plugins
75 | maven-gpg-plugin
76 | 1.5
77 |
78 |
79 | sign-artifacts
80 | deploy
81 |
82 | sign
83 |
84 |
85 | ${gpg.keyname}
86 | ${gpg.keyname}
87 |
88 |
89 |
90 |
91 |
92 |
93 | org.apache.maven.plugins
94 | maven-deploy-plugin
95 | 2.8.2
96 |
97 |
98 | default-deploy
99 | deploy
100 |
101 | deploy
102 |
103 |
104 | true
105 |
106 |
107 |
108 | release
109 | deploy
110 |
111 | deploy
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | Apache License, Version 2.0
123 | http://www.apache.org/licenses/LICENSE-2.0.txt
124 | repo
125 | A business-friendly OSS license
126 |
127 |
128 |
129 |
130 |
131 | Daan van den Heuvel
132 | javaforger@gmail.com
133 | https://github.com/daanvdh/JavaDataFlow
134 | daanvdh
135 |
136 |
137 |
138 |
139 |
140 |
141 | junit
142 | junit
143 | 4.13.1
144 | test
145 |
146 |
147 |
148 | org.mockito
149 | mockito-all
150 | 1.9.5
151 | test
152 |
153 |
154 |
155 | com.github.javaparser
156 | javaparser-symbol-solver-core
157 | 3.24.0
158 |
159 |
160 |
161 | org.hamcrest
162 | java-hamcrest
163 | 2.0.0.0
164 | test
165 |
166 |
167 |
168 | org.apache.commons
169 | commons-lang3
170 | 3.8.1
171 |
172 |
173 |
174 | ch.qos.logback
175 | logback-classic
176 | 1.3.0-alpha4
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/src/main/java/model/ParameterList.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.List;
23 | import java.util.Objects;
24 | import java.util.Optional;
25 |
26 | import org.apache.commons.lang3.builder.EqualsBuilder;
27 | import org.apache.commons.lang3.builder.ToStringBuilder;
28 | import org.apache.commons.lang3.builder.ToStringStyle;
29 |
30 | import com.github.javaparser.ast.Node;
31 |
32 | import common.DataFlowException;
33 |
34 | /**
35 | * Represents the set of parameters from a {@link DataFlowMethod}. Every method has at most one {@link ParameterList}. A parameter list can also exist outside
36 | * of a method when it is constructed to represent a call to another method. It can have input or output edges to other {@link ParameterList}s representing a
37 | * call from/to another location. With this class you can distinguish which variables where used as input for a method at a specific call.
38 | *
39 | * @author Daan
40 | */
41 | public class ParameterList extends OwnedNode {
42 |
43 | private List nodes = new ArrayList<>();
44 | /** The method/constructor/codeBlock that has this parameterList as input or the {@link NodeCall} for which this is the input. */
45 | private OwnedNode> owner;
46 |
47 | public ParameterList(OwnedNode> method) {
48 | this.owner = method;
49 | }
50 |
51 | public ParameterList(List inputParameters, DataFlowMethod method) {
52 | this(method);
53 | this.nodes.addAll(inputParameters);
54 | }
55 |
56 | private ParameterList(Builder builder) {
57 | super(builder);
58 | this.addAll(builder.nodes);
59 | if (builder.owner != null) {
60 | this.setOwnerAndName(builder.owner);
61 | }
62 | }
63 |
64 | @Override
65 | public Optional> getOwner() {
66 | return Optional.ofNullable(this.owner);
67 | }
68 |
69 | public final void setOwnerAndName(OwnedNode> owner) {
70 | this.owner = owner;
71 | String ownerName = owner.getName() == null ? "unkown" + owner.getClass() : owner.getName();
72 | this.name = ownerName + "Parameters";
73 | }
74 |
75 | public List getParameters() {
76 | return nodes;
77 | }
78 |
79 | public void setParameters(List parameters) {
80 | this.nodes.clear();
81 | this.addAll(parameters);
82 | }
83 |
84 | public final void add(DataFlowNode node) {
85 | this.nodes.add(node);
86 | // set the owner since this is the lowest possible owner
87 | node.setOwner(this);
88 | }
89 |
90 | public void clear() {
91 | this.nodes.clear();
92 | }
93 |
94 | public final void addAll(List inputParameters) {
95 | inputParameters.forEach(this::add);
96 | }
97 |
98 | public boolean contains(DataFlowNode dfn) {
99 | return this.nodes.contains(dfn);
100 | }
101 |
102 | public List getNodes() {
103 | return this.nodes;
104 | }
105 |
106 | public int nofNodes() {
107 | return this.nodes.size();
108 | }
109 |
110 | public void connectTo(ParameterList otherParams) {
111 | if (this.nodes.size() != otherParams.nodes.size()) {
112 | throw new DataFlowException("Number of parameters is not equal for ParameterList {} and {}, with owner nodes {} and {}", this, otherParams, owner,
113 | otherParams.owner);
114 | }
115 | for (int i = 0; i < this.nodes.size(); i++) {
116 | this.nodes.get(i).addEdgeTo(otherParams.nodes.get(i));
117 | }
118 | }
119 |
120 | public boolean isInputParametersForMethod() {
121 | boolean isInputParam = false;
122 | if (this.owner != null && this.owner instanceof DataFlowMethod && ((DataFlowMethod) this.owner).getParameters() == this) {
123 | isInputParam = true;
124 | }
125 | return isInputParam;
126 | }
127 |
128 | @Override
129 | public int hashCode() {
130 | return Objects.hash(super.hashCode(), nodes);
131 | }
132 |
133 | @Override
134 | public boolean equals(Object obj) {
135 | boolean equals = false;
136 | if (this == obj) {
137 | equals = true;
138 | } else if (obj != null && getClass() == obj.getClass()) {
139 | ParameterList other = (ParameterList) obj;
140 | equals = new EqualsBuilder().appendSuper(super.equals(obj)).append(nodes, other.nodes).append(name, other.name).isEquals();
141 | }
142 | return equals;
143 | }
144 |
145 | /**
146 | * Creates builder to build {@link ParameterList}.
147 | *
148 | * @return created builder
149 | */
150 | public static Builder builder() {
151 | return new Builder();
152 | }
153 |
154 | @Override
155 | public String toString() {
156 | return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).appendSuper(super.toString()).append("nodes", nodes)
157 | .append("owner", owner != null ? owner.getName() : "null").build();
158 | }
159 |
160 | /**
161 | * Builder to build {@link ParameterList}.
162 | */
163 | public static final class Builder extends NodeRepresenter.Builder {
164 | private List nodes = new ArrayList<>();
165 | private OwnedNode> owner;
166 |
167 | private Builder() {
168 | // Builder should only be constructed via the parent class
169 | }
170 |
171 | public Builder node(DataFlowNode node) {
172 | this.nodes.add(node);
173 | return this;
174 | }
175 |
176 | public Builder nodes(List nodes) {
177 | this.nodes.clear();
178 | this.nodes.addAll(nodes);
179 | return this;
180 | }
181 |
182 | public Builder nodes(DataFlowNode... nodes) {
183 | this.nodes.clear();
184 | this.nodes.addAll(Arrays.asList(nodes));
185 | return this;
186 | }
187 |
188 | public Builder owner(OwnedNode> owner) {
189 | this.owner = owner;
190 | return this;
191 | }
192 |
193 | public ParameterList build() {
194 | return new ParameterList(this);
195 | }
196 |
197 | }
198 |
199 | }
200 |
--------------------------------------------------------------------------------
/src/test/java/factory/MethodNodeHandlerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package factory;
19 |
20 | import java.util.Arrays;
21 | import java.util.HashMap;
22 | import java.util.Optional;
23 |
24 | import org.hamcrest.Matcher;
25 | import org.junit.Assert;
26 | import org.junit.Test;
27 | import org.junit.runner.RunWith;
28 | import org.mockito.InjectMocks;
29 | import org.mockito.Mock;
30 | import org.mockito.Mockito;
31 | import org.mockito.runners.MockitoJUnitRunner;
32 |
33 | import com.github.javaparser.StaticJavaParser;
34 | import com.github.javaparser.ast.CompilationUnit;
35 | import com.github.javaparser.ast.Node;
36 | import com.github.javaparser.ast.expr.MethodCallExpr;
37 | import com.github.javaparser.ast.expr.NameExpr;
38 |
39 | import model.DataFlowGraph;
40 | import model.DataFlowMethod;
41 | import model.DataFlowNode;
42 | import model.NodeCall;
43 | import model.ParameterList;
44 | import util.MethodMatcher;
45 |
46 | /**
47 | * Unit test for {@link MethodNodeHandler}.
48 | *
49 | * @author Daan
50 | */
51 | @RunWith(MockitoJUnitRunner.class)
52 | public class MethodNodeHandlerTest {
53 |
54 | @Mock
55 | private NodeCallFactory nodeCallFactory;
56 |
57 | @InjectMocks
58 | private MethodNodeHandler sut;
59 |
60 | @Test
61 | public void testHandleMethodCallExpr_inputMethod() {
62 | CompilationUnit cu = createCompilationUnit( //
63 | " StringBuilder sb = new StringBuilder(); \n" + //
64 | " public StringBuilder met(String a) {\n" + //
65 | " return sb.append(a);\n" + //
66 | " }\n");
67 |
68 | MethodCallExpr node = cu.findAll(MethodCallExpr.class).iterator().next();
69 | DataFlowNode returnNode = DataFlowNode.builder().name("niceName").build();
70 | NodeCall methodCall = NodeCall.builder().in(ParameterList.builder().nodes(Arrays.asList(DataFlowNode.builder().name("param1").build())).build())
71 | .returnNode(returnNode).build();
72 | DataFlowMethod method = DataFlowMethod.builder().build();
73 | NameExpr sbUsage = cu.findAll(NameExpr.class).get(0);
74 | mockNodeCallFactory(method, node, sbUsage, methodCall);
75 |
76 | Optional resultNode = execute(node, method);
77 |
78 | Assert.assertTrue(resultNode.isPresent());
79 | Assert.assertEquals(returnNode, resultNode.get());
80 | Assert.assertEquals(methodCall, method.getNode(sbUsage).getNodeCall().get());
81 | Assert.assertEquals(Arrays.asList(methodCall), method.getNodeCalls());
82 | }
83 |
84 | @Test
85 | public void testHandleMethodCallExpr_outputMethod() {
86 | CompilationUnit cu = createCompilationUnit( //
87 | " StringBuilder sb = new StringBuilder(); \n" + //
88 | " public void met(String a) {\n" + //
89 | " sb.append(a);\n" + //
90 | "}");
91 |
92 | MethodCallExpr node = cu.findAll(MethodCallExpr.class).iterator().next();
93 | DataFlowNode returnNode = DataFlowNode.builder().name("niceName").build();
94 | NodeCall methodCall = NodeCall.builder().in(ParameterList.builder().nodes(Arrays.asList(DataFlowNode.builder().name("param1").build())).build())
95 | .returnNode(returnNode).build();
96 | DataFlowMethod method = DataFlowMethod.builder().build();
97 | NameExpr sbUsage = cu.findAll(NameExpr.class).get(0);
98 | mockNodeCallFactory(method, node, sbUsage, methodCall);
99 |
100 | Optional resultNode = execute(node, method);
101 |
102 | Assert.assertTrue(resultNode.isPresent());
103 | Assert.assertEquals(returnNode, resultNode.get());
104 | Assert.assertEquals(methodCall, method.getNode(sbUsage).getNodeCall().get());
105 | Assert.assertEquals(Arrays.asList(methodCall), method.getNodeCalls());
106 | }
107 |
108 | @Test
109 | public void testHandleMethodCallExpr_methodConcatenation() {
110 | CompilationUnit cu = createCompilationUnit(//
111 | " StringBuilder sb = new StringBuilder(); \n" + //
112 | " public StringBuilder met(String a, int b) {\n" + //
113 | " return sb.append(a).charAt(b);\n" + //
114 | " }\n"); //
115 | DataFlowMethod method = DataFlowMethod.builder().build();
116 |
117 | MethodCallExpr append = cu.findAll(MethodCallExpr.class).get(1);
118 | DataFlowNode dfnSbAppend = DataFlowNode.builder().name("app").representedNode(append).build();
119 | NodeCall appendCall = NodeCall.builder().name("nc1").returnNode(dfnSbAppend).build();
120 | NameExpr instance = cu.findAll(NameExpr.class).get(0);
121 | mockNodeCallFactory(method, append, instance, appendCall);
122 |
123 | MethodCallExpr charrAt = cu.findAll(MethodCallExpr.class).get(0);
124 | DataFlowNode dfnCharrAt = DataFlowNode.builder().name("charrAt").representedNode(charrAt).build();
125 | NodeCall charrAtCall = NodeCall.builder().name("nc2").returnNode(dfnCharrAt).build();
126 | mockNodeCallFactory(method, charrAt, append, charrAtCall);
127 |
128 | Optional resultNode = execute(charrAt, method);
129 |
130 | Assert.assertTrue(resultNode.isPresent());
131 | Assert.assertEquals(dfnCharrAt, resultNode.get());
132 | Assert.assertEquals(appendCall, method.getNode(instance).getNodeCall().get());
133 | Assert.assertEquals(charrAtCall, method.getNode(append).getNodeCall().get());
134 | Assert.assertEquals(Arrays.asList(appendCall, charrAtCall), method.getNodeCalls());
135 | }
136 |
137 | private void mockNodeCallFactory(DataFlowMethod method, MethodCallExpr node, Node instance, NodeCall methodCall) {
138 | Matcher matchesInstance = MethodMatcher.of(DataFlowNode::getRepresentedNode, instance);
139 | Mockito.when(nodeCallFactory.create(Mockito.eq(method), Mockito.eq(node), Mockito.argThat(matchesInstance))).thenReturn(Optional.of(methodCall));
140 | }
141 |
142 | private CompilationUnit createCompilationUnit(String code) {
143 | CompilationUnit cu = StaticJavaParser.parse(//
144 | "public class Claz {\n" + //
145 | code + //
146 | "}");
147 | return cu;
148 | }
149 |
150 | private Optional execute(MethodCallExpr node, DataFlowMethod method) {
151 | DataFlowGraph graph = DataFlowGraph.builder().build();
152 | HashMap overriddenValues = new HashMap<>();
153 | Optional resultNode = sut.handleNode(graph, method, overriddenValues, node, method);
154 | return resultNode;
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/model/NodeCall.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Objects;
21 | import java.util.Optional;
22 |
23 | import org.apache.commons.lang3.builder.EqualsBuilder;
24 | import org.apache.commons.lang3.builder.ToStringBuilder;
25 | import org.apache.commons.lang3.builder.ToStringStyle;
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 |
29 | import com.github.javaparser.ast.Node;
30 |
31 | /**
32 | * Represents a call to a node (method, constructor or other code block). This node will be owned by the calling method. This class groups all in/output data
33 | * from one method to another.
34 | *
35 | * @author Daan
36 | */
37 | public class NodeCall extends OwnedNode {
38 |
39 | private static final Logger LOG = LoggerFactory.getLogger(NodeCall.class);
40 |
41 | /**
42 | * The {@link ParameterList}s that contain the {@link DataFlowNode}s that where used for a specific {@link DataFlowMethod} call to the owner
43 | * {@link DataFlowMethod}. Can be null if method does not have any input.
44 | */
45 | private ParameterList in;
46 | /** The method/constructor/codeBlock from which the method is called */
47 | private OwnedNode> owner;
48 | /** The called method, this can be null in case that the given method is not parsed. */
49 | private DataFlowMethod calledMethod;
50 | /**
51 | * The return Node of a node call, this will be null if method is void. If the return value is not read, the outgoing edges of this node will be empty. There
52 | * should only be a single incoming edge from the return node of the called method. This NodeCall is the owner of the returnNode.
53 | */
54 | private DataFlowNode returnNode;
55 |
56 | /**
57 | * The {@link DataFlowNode} on which this {@link NodeCall} was called. Can be null if this {@link NodeCall} was a static call. Will be "this" if the method
58 | * was called on the same instance as the method to which the call belongs to.
59 | */
60 | private DataFlowNode instance;
61 |
62 | private String claz;
63 | private String peckage;
64 |
65 | public NodeCall(OwnedNode> owner) {
66 | this.owner = owner;
67 | }
68 |
69 | private NodeCall(Builder builder) {
70 | super(builder);
71 | if (builder.in != null) {
72 | this.setIn(builder.in);
73 | }
74 | this.owner = builder.owner == null ? this.owner : builder.owner;
75 | this.calledMethod = builder.calledMethod == null ? this.calledMethod : builder.calledMethod;
76 | this.claz = builder.claz == null ? this.claz : builder.claz;
77 | this.peckage = builder.peckage == null ? this.peckage : builder.peckage;
78 | this.returnNode = builder.returnNode == null ? this.returnNode : builder.returnNode;
79 | this.instance = builder.instance == null ? this.instance : builder.instance;
80 | }
81 |
82 | @Override
83 | public Optional> getOwner() {
84 | return Optional.of(owner);
85 | }
86 |
87 | public Optional getIn() {
88 | return Optional.ofNullable(in);
89 | }
90 |
91 | public final void setIn(ParameterList in) {
92 | this.in = in;
93 | in.setOwnerAndName(this);
94 | }
95 |
96 | public Optional getCalledMethod() {
97 | return Optional.ofNullable(calledMethod);
98 | }
99 |
100 | public void setCalledMethod(DataFlowMethod calledMethod) {
101 | this.calledMethod = calledMethod;
102 | this.in.connectTo(calledMethod.getParameters());
103 | if (this.returnNode != null) {
104 | if (calledMethod.getReturnNode().isPresent()) {
105 | calledMethod.getReturnNode().get().addEdgeTo(returnNode);
106 | } else {
107 | LOG.warn("Could not connect method return node to NodeCall return Node because return node was not present in method {}", calledMethod);
108 | }
109 |
110 | }
111 | }
112 |
113 | public String getClaz() {
114 | return claz;
115 | }
116 |
117 | public void setClaz(String claz) {
118 | this.claz = claz;
119 | }
120 |
121 | public String getPeckage() {
122 | return peckage;
123 | }
124 |
125 | public void setPeckage(String peckage) {
126 | this.peckage = peckage;
127 | }
128 |
129 | public void setOwner(OwnedNode> owner) {
130 | this.owner = owner;
131 | }
132 |
133 | public Optional getReturnNode() {
134 | return Optional.ofNullable(returnNode);
135 | }
136 |
137 | public void setReturnNode(DataFlowNode returnNode) {
138 | this.returnNode = returnNode;
139 | }
140 |
141 | public Optional getInstance() {
142 | return Optional.ofNullable(instance);
143 | }
144 |
145 | public void setInstance(DataFlowNode instance) {
146 | this.instance = instance;
147 | }
148 |
149 | /**
150 | * @return True if the return value is used, false otherwise.
151 | */
152 | public boolean isReturnRead() {
153 | return returnNode != null && (
154 | // If it has outgoing edges, it's value is read
155 | !returnNode.getOut().isEmpty() ||
156 | // If the owner is a methodCall, that means that this nodeCall's return is the direct input to another method, example a(b());
157 | (owner != null && owner instanceof NodeCall));
158 | }
159 |
160 | /**
161 | * Creates builder to build {@link NodeCall}.
162 | *
163 | * @return created builder
164 | */
165 | public static Builder builder() {
166 | return new Builder();
167 | }
168 |
169 | @Override
170 | public boolean equals(Object obj) {
171 | boolean equals = false;
172 | if (this == obj) {
173 | equals = true;
174 | } else if (obj != null && getClass() == obj.getClass()) {
175 | NodeCall other = (NodeCall) obj;
176 | equals = new EqualsBuilder().appendSuper(super.equals(obj)).append(in, other.in).append(calledMethod, other.calledMethod).append(claz, other.claz)
177 | .append(peckage, other.peckage).isEquals();
178 | }
179 | return equals;
180 | }
181 |
182 | @Override
183 | public int hashCode() {
184 | return Objects.hash(in, calledMethod, claz, peckage);
185 | }
186 |
187 | @Override
188 | public String toString() {
189 | return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).appendSuper(super.toString()).append("in", in).append("calledMethod", calledMethod)
190 | .append("returnNode", returnNode).append("class", claz).append("package", peckage).build();
191 | }
192 |
193 | /**
194 | * Builder to build {@link NodeCall}.
195 | */
196 | public static final class Builder extends NodeRepresenter.Builder {
197 | private ParameterList in;
198 | private OwnedNode> owner;
199 | private DataFlowMethod calledMethod;
200 | private String claz;
201 | private String peckage;
202 | private DataFlowNode returnNode;
203 | private DataFlowNode instance;
204 |
205 | private Builder() {
206 | // Builder should only be constructed via the parent class
207 | }
208 |
209 | public Builder in(ParameterList in) {
210 | this.in = in;
211 | return this;
212 | }
213 |
214 | public Builder in(DataFlowNode... inputNodes) {
215 | this.in = ParameterList.builder().nodes(inputNodes).build();
216 | return this;
217 | }
218 |
219 | public Builder owner(OwnedNode> owner) {
220 | this.owner = owner;
221 | return this;
222 | }
223 |
224 | public Builder calledMethod(DataFlowMethod calledMethod) {
225 | this.calledMethod = calledMethod;
226 | return this;
227 | }
228 |
229 | public Builder claz(String claz) {
230 | this.claz = claz;
231 | return this;
232 | }
233 |
234 | public Builder peckage(String peckage) {
235 | this.peckage = peckage;
236 | return this;
237 | }
238 |
239 | public Builder instance(DataFlowNode instance) {
240 | this.instance = instance;
241 | return this;
242 | }
243 |
244 | public Builder returnNode(DataFlowNode node) {
245 | this.returnNode = node;
246 | return this;
247 | }
248 |
249 | public NodeCall build() {
250 | return new NodeCall(this);
251 | }
252 |
253 | }
254 |
255 | }
256 |
--------------------------------------------------------------------------------
/src/main/java/factory/DataFlowGraphFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package factory;
19 |
20 | import java.util.HashMap;
21 | import java.util.List;
22 | import java.util.Map;
23 | import java.util.Optional;
24 | import java.util.function.Consumer;
25 | import java.util.stream.Collectors;
26 |
27 | import org.slf4j.Logger;
28 | import org.slf4j.LoggerFactory;
29 |
30 | import com.github.javaparser.JavaParser;
31 | import com.github.javaparser.ast.CompilationUnit;
32 | import com.github.javaparser.ast.Node;
33 | import com.github.javaparser.ast.body.CallableDeclaration;
34 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
35 | import com.github.javaparser.ast.body.FieldDeclaration;
36 | import com.github.javaparser.ast.body.MethodDeclaration;
37 | import com.github.javaparser.ast.body.TypeDeclaration;
38 | import com.github.javaparser.ast.body.VariableDeclarator;
39 | import com.github.javaparser.ast.stmt.BlockStmt;
40 | import com.github.javaparser.ast.type.VoidType;
41 | import com.github.javaparser.resolution.Resolvable;
42 | import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserMethodDeclaration;
43 |
44 | import model.DataFlowGraph;
45 | import model.DataFlowMethod;
46 | import model.DataFlowNode;
47 | import model.NodeCall;
48 | import model.OwnedNode;
49 | import model.ParameterList;
50 |
51 | /**
52 | * Factory for creating a {@link DataFlowGraph} from a {@link JavaParser} {@link CompilationUnit}.
53 | *
54 | * @author Daan
55 | */
56 | public class DataFlowGraphFactory {
57 | private static final Logger LOG = LoggerFactory.getLogger(DataFlowGraphFactory.class);
58 |
59 | private MethodNodeHandler nodeHandler = new MethodNodeHandler();
60 | private DataFlowNodeFactory dfnFactory = new DataFlowNodeFactory();
61 |
62 | /**
63 | * Creates a {@link DataFlowGraph} for the given {@link CompilationUnit}.
64 | *
65 | * @param cu The {@link CompilationUnit} containing the parsed class.
66 | * @return A {@link DataFlowGraph}
67 | */
68 | public DataFlowGraph create(CompilationUnit cu) {
69 | DataFlowGraph graph = DataFlowGraph.builder().build();
70 | Optional representedNode = cu.findFirst(ClassOrInterfaceDeclaration.class);
71 | if (representedNode.isPresent()) {
72 | graph.setRepresentedNode(representedNode.get());
73 | representedNode.map(ClassOrInterfaceDeclaration::getNameAsString).ifPresent(graph::setName);
74 | }
75 | executeForEachChildNode(cu, (node) -> this.addField(graph, node));
76 | executeForEachChildNode(cu, (node) -> this.createMethod(graph, node));
77 | executeForEachChildNode(cu, (node) -> this.fillMethod(graph, node));
78 | connectMethods(graph);
79 | return graph;
80 | }
81 |
82 | private void executeForEachChildNode(CompilationUnit cu, Consumer consumer) {
83 | for (TypeDeclaration> type : cu.getTypes()) {
84 | List childNodes = type.getChildNodes();
85 | for (Node node : childNodes) {
86 | consumer.accept(node);
87 | }
88 | }
89 | }
90 |
91 | private void addField(DataFlowGraph graph, Node node) {
92 | if (node instanceof FieldDeclaration) {
93 | parseField((FieldDeclaration) node, graph).forEach(graph::addField);
94 | }
95 | }
96 |
97 | private DataFlowMethod createMethod(DataFlowGraph graph, Node node) {
98 | DataFlowMethod method = null;
99 | if (node instanceof CallableDeclaration) {
100 | CallableDeclaration> cd = (CallableDeclaration>) node;
101 | method = new DataFlowMethod(graph, cd, cd.getNameAsString());
102 | List dfnParameters = parseParameters(cd, method);
103 | ParameterList paramList = ParameterList.builder().nodes(dfnParameters).owner(method).build();
104 | method.setInputParameters(paramList);
105 | if (node instanceof MethodDeclaration) {
106 | MethodDeclaration md = (MethodDeclaration) node;
107 | if (!(md.getType() instanceof VoidType)) {
108 | method.setReturnNode(new DataFlowNode(cd.getNameAsString() + "_return", node));
109 | }
110 | } else {
111 | // Always add a return statement for a constructor.
112 | method.setReturnNode(new DataFlowNode(node));
113 | }
114 | }
115 | return method;
116 | }
117 |
118 | private void fillMethod(DataFlowGraph graph, Node node) {
119 | if (node instanceof MethodDeclaration) {
120 | MethodDeclaration md = (MethodDeclaration) node;
121 | parseCallable(graph, md);
122 | }
123 | }
124 |
125 | /**
126 | * Connects all method calls to methods inside this graph to each other.
127 | *
128 | * @param graph The graph to connect the methods from.
129 | */
130 | private void connectMethods(DataFlowGraph graph) {
131 | // TODO probably best to extract this to another class.
132 | for (DataFlowMethod method : graph.getMethods()) {
133 | for (NodeCall call : method.getNodeCalls()) {
134 | Node node = call.getRepresentedNode();
135 | Object resolved = resolve(method, node);
136 | if (resolved instanceof JavaParserMethodDeclaration) {
137 | MethodDeclaration resolvedNode = ((JavaParserMethodDeclaration) resolved).getWrappedNode();
138 | DataFlowMethod resolvedMethod = graph.getMethod(resolvedNode);
139 | if (resolvedMethod != null) {
140 | call.setCalledMethod(resolvedMethod);
141 | } else {
142 | // TODO handle connecting to other graphs
143 | }
144 | } else {
145 | LOG.warn("In method {}, Connecting methods of type {} is not supported, the node that was not connected is: {}", method.getName(),
146 | resolved == null ? null : resolved.getClass(), node);
147 | }
148 | }
149 | }
150 | }
151 |
152 | private Object resolve(DataFlowMethod method, Node node) {
153 | if (!Resolvable.class.isAssignableFrom(node.getClass())) {
154 | // LOG.warn("In method {}, node is not Resolvable for expression {} of type {}", method.getName(), node, node.getClass());
155 | return null;
156 | }
157 |
158 | Resolvable> resolvable = (Resolvable>) node;
159 | Object resolved = null;
160 | try {
161 | resolved = resolvable.resolve();
162 | } catch (Exception e) {
163 | LOG.warn(e.getMessage());
164 | }
165 | return resolved;
166 | }
167 |
168 | /**
169 | * Returns a list of DataFlowNodes that represent all fields defined with this fieldDeclaration. (With the syntax int i,j; one FieldDeclaration
170 | * can define multiple fields.
171 | *
172 | * @param node
173 | * @return
174 | */
175 | private List parseField(FieldDeclaration node, OwnedNode> owner) {
176 | return node.getVariables().stream().map(var -> dfnFactory.create(var, owner)).collect(Collectors.toList());
177 | }
178 |
179 | private void parseCallable(DataFlowGraph graph, CallableDeclaration> cd) {
180 | // TODO we need this method later to add outgoing and incoming nodes too.
181 | DataFlowMethod method = graph.getMethod(cd);
182 | // The values that are overwridden inside this method, for example assigning a field.
183 | Map overwriddenValues = new HashMap<>();
184 |
185 | Optional callableBody =
186 | cd.getChildNodes().stream().filter(n -> BlockStmt.class.isAssignableFrom(n.getClass())).findFirst().map(BlockStmt.class::cast);
187 |
188 | if (callableBody.isPresent()) {
189 | nodeHandler.handleNode(graph, method, overwriddenValues, callableBody.get(), method);
190 | }
191 |
192 | // Each overwridden value has to receive the value that it was overwridden with
193 | overwriddenValues.forEach((javaParserNode, dataFlowNode) -> dataFlowNode.addEdgeTo(graph.getNode(javaParserNode)));
194 |
195 | // Add changed fields
196 | overwriddenValues.keySet().stream().filter(VariableDeclarator.class::isInstance).map(graph::getNode).filter(n -> n != null).distinct()
197 | .forEach(method::addChangedField);
198 | }
199 |
200 | private List parseParameters(CallableDeclaration> cd, OwnedNode> owner) {
201 | return cd.getParameters().stream().map(n -> dfnFactory.create(n, owner)).collect(Collectors.toList());
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/src/main/java/model/DataFlowGraph.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.Collection;
23 | import java.util.HashMap;
24 | import java.util.List;
25 | import java.util.Map;
26 | import java.util.Optional;
27 | import java.util.stream.Collectors;
28 | import java.util.stream.Stream;
29 |
30 | import com.github.javaparser.ast.Node;
31 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
32 |
33 | /**
34 | * Graph representing the data flow within a single class. The {@link DataFlowNode}s represent variables. An {@link DataFlowEdge} goes from node a to b iff a
35 | * influences the state of b. Conditional statements are not supported in the current implementation.
36 | *
37 | * @author Daan
38 | */
39 | public class DataFlowGraph extends OwnerNode {
40 |
41 | /** The package of the class that this {@link DataFlowGraph} represents. */
42 | private String classPackage;
43 | /** This fields within the represented class */
44 | private List fields = new ArrayList<>();
45 | /** This Constructors within the represented class */
46 | private List constructors = new ArrayList<>();
47 | /** This Methods within the represented class */
48 | private Map methods = new HashMap<>();
49 | /**
50 | * All nodes defined within the class: fields and method/constructor parameters and return values. Does not contain method/constructor in-between variables.
51 | */
52 | private Map nodes = new HashMap<>();
53 | /**
54 | * List containing all external DataFlowGraphs that this {@link DataFlowGraph} depends on. The contained dfg's are not complete graphs they only contain the
55 | * signatures of methods called from this class. The keys are the package and class name concatenated with a dot.
56 | */
57 | private Map dependedGraphs = new HashMap<>();
58 | /** In case that this {@link DataFlowGraph} represents an inner class, the owner graph represents the class outer class. */
59 | private DataFlowGraph ownerGraph;
60 |
61 | public DataFlowGraph() {
62 | // empty constructor which would otherwise be invisible due to the constructor receiving the builder.
63 | }
64 |
65 | private DataFlowGraph(Builder builder) {
66 | super(builder);
67 | this.classPackage = builder.classPackage == null ? this.classPackage : builder.classPackage;
68 | this.fields.clear();
69 | this.addFields(builder.fields);
70 | this.constructors.clear();
71 | this.constructors.addAll(builder.constructors);
72 | this.methods = builder.methods == null ? this.methods : builder.methods;
73 | this.nodes = builder.nodes == null ? this.nodes : builder.nodes;
74 | this.dependedGraphs = builder.dependedGraphs == null ? this.dependedGraphs : builder.dependedGraphs;
75 | }
76 |
77 | public List getFields() {
78 | return fields;
79 | }
80 |
81 | public void setFields(List fields) {
82 | this.fields = fields;
83 | }
84 |
85 | public List getConstructors() {
86 | return constructors;
87 | }
88 |
89 | public void setConstructors(List constructors) {
90 | this.constructors = constructors;
91 | }
92 |
93 | public Collection getMethods() {
94 | return methods.values();
95 | }
96 |
97 | public Map getMethodMap() {
98 | return this.methods;
99 | }
100 |
101 | public void setMethods(List methods) {
102 | this.methods.clear();
103 | methods.forEach(this::addMethod);
104 | }
105 |
106 | public void addMethod(DataFlowMethod method) {
107 | if (method.getRepresentedNode() == null) {
108 | throw new NullPointerException("The representedNode may not be null, this risks overriding existing methods.");
109 | }
110 | this.methods.put(method.getRepresentedNode(), method);
111 | method.setGraph(this);
112 | }
113 |
114 | public DataFlowMethod getMethod(Node node) {
115 | return methods.get(node);
116 | }
117 |
118 | public final void addField(DataFlowNode node) {
119 | this.fields.add(node);
120 | if (node.getRepresentedNode() == null) {
121 | throw new NullPointerException("The representedNode may not be null, this risks overriding existing fields.");
122 | }
123 | this.nodes.put(node.getRepresentedNode(), node);
124 | node.setOwner(this);
125 | }
126 |
127 | public final void addFields(DataFlowNode... fields) {
128 | Stream.of(fields).forEach(this::addField);
129 | }
130 |
131 | private final void addFields(List fields) {
132 | fields.forEach(this::addField);
133 | }
134 |
135 | public void addNodes(List nodes) {
136 | nodes.forEach(this::addNode);
137 | }
138 |
139 | public void addNode(DataFlowNode node) {
140 | this.nodes.put(node.getRepresentedNode(), node);
141 | }
142 |
143 | public DataFlowNode getNode(Node node) {
144 | return nodes.get(node);
145 | }
146 |
147 | public Map getNodes() {
148 | return this.nodes;
149 | }
150 |
151 | public Map getDependedGraphs() {
152 | return dependedGraphs;
153 | }
154 |
155 | public DataFlowGraph getDependedGraph(String path) {
156 | return this.dependedGraphs.get(path);
157 | }
158 |
159 | public void setDependedGraphs(Map dependedGraphs) {
160 | this.dependedGraphs = dependedGraphs;
161 | }
162 |
163 | public void addDependedGraph(DataFlowGraph graph) {
164 | this.dependedGraphs.put(graph.getClassPackage() + "." + graph.getName(), graph);
165 | }
166 |
167 | public String getClassPackage() {
168 | return classPackage;
169 | }
170 |
171 | public void setClassPackage(String classPackage) {
172 | this.classPackage = classPackage;
173 | }
174 |
175 | @Override
176 | public Optional> getOwner() {
177 | return Optional.ofNullable(this.ownerGraph);
178 | }
179 |
180 | @Override
181 | Collection> getOwnedOwners() {
182 | // streaming and collecting needed for casting.
183 | return this.methods.values().stream().collect(Collectors.toList());
184 | }
185 |
186 | @Override
187 | Collection getDirectOwnedNodes() {
188 | return this.fields;
189 | }
190 |
191 | @Override
192 | public String toString() {
193 | StringBuilder sb = new StringBuilder();
194 |
195 | sb.append(super.toString());
196 | sb.append("fields{");
197 | fields.forEach(f -> sb.append("\n->").append(f.toStringForward(1)));
198 | sb.append("\n");
199 | fields.forEach(f -> sb.append("\n<-").append(f.toStringBackward(1)));
200 | sb.append("\n}\n");
201 |
202 | sb.append("methods{\n");
203 | for (DataFlowMethod m : methods.values()) {
204 | sb.append(m.toString());
205 | }
206 | sb.append("\n}");
207 | return sb.toString();
208 | }
209 |
210 | /**
211 | * Creates builder to build {@link DataFlowGraph}.
212 | *
213 | * @return created builder
214 | */
215 | public static Builder builder() {
216 | return new Builder();
217 | }
218 |
219 | /**
220 | * Builder to build {@link DataFlowGraph}.
221 | */
222 | public static final class Builder extends NodeRepresenter.Builder {
223 | private String classPackage;
224 | private List fields = new ArrayList<>();
225 | private List constructors = new ArrayList<>();
226 | private Map methods = new HashMap<>();
227 | private Map nodes;
228 | private Map dependedGraphs;
229 |
230 | private Builder() {
231 | // Builder should only be constructed via the parent class
232 | }
233 |
234 | public Builder classPackage(String classPackage) {
235 | this.classPackage = classPackage;
236 | return this;
237 | }
238 |
239 | public Builder fields(List fields) {
240 | this.fields.clear();
241 | this.fields.addAll(fields);
242 | return this;
243 | }
244 |
245 | public Builder fields(DataFlowNode... fields) {
246 | this.fields.clear();
247 | this.fields.addAll(Arrays.asList(fields));
248 | return this;
249 | }
250 |
251 | public Builder constructors(List constructors) {
252 | this.constructors.clear();
253 | this.constructors.addAll(constructors);
254 | return this;
255 | }
256 |
257 | public Builder methods(Map methods) {
258 | this.methods = methods;
259 | return this;
260 | }
261 |
262 | public Builder methods(DataFlowMethod... methods) {
263 | Stream.of(methods).forEach(m -> this.methods.put(m.getRepresentedNode(), m));
264 | return this;
265 | }
266 |
267 | public Builder nodes(Map nodes) {
268 | this.nodes = nodes;
269 | return this;
270 | }
271 |
272 | public Builder dependedGraphs(Map dependedGraphs) {
273 | this.dependedGraphs = dependedGraphs;
274 | return this;
275 | }
276 |
277 | public DataFlowGraph build() {
278 | return new DataFlowGraph(this);
279 | }
280 |
281 | }
282 |
283 | }
284 |
--------------------------------------------------------------------------------
/src/main/java/model/DataFlowNode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.List;
23 | import java.util.Objects;
24 | import java.util.Optional;
25 | import java.util.function.Predicate;
26 | import java.util.stream.Collectors;
27 |
28 | import org.apache.commons.lang3.builder.EqualsBuilder;
29 | import org.apache.commons.lang3.builder.ToStringBuilder;
30 | import org.apache.commons.lang3.builder.ToStringStyle;
31 |
32 | import com.github.javaparser.JavaParser;
33 | import com.github.javaparser.ast.CompilationUnit;
34 | import com.github.javaparser.ast.Node;
35 |
36 | import util.GraphUtil;
37 |
38 | /**
39 | * A node inside the {@link DataFlowGraph} containing a {@link JavaParser} {@link Node}. The incoming {@link DataFlowEdge}s are {@link DataFlowNode}s that
40 | * influence the state of this node. The outgoing {@link DataFlowEdge}s point to {@link DataFlowNode}s which state is influenced by this {@link DataFlowNode}.
41 | *
42 | * @author Daan
43 | */
44 | public class DataFlowNode extends OwnedNode {
45 |
46 | /** The {@link DataFlowEdge}s from {@link DataFlowNode}s that influence the state of this node */
47 | private List in = new ArrayList<>();
48 | /** The {@link DataFlowEdge}s to {@link DataFlowNode}s who's state is influenced by this node */
49 | private List out = new ArrayList<>();
50 | /**
51 | * The type of the represented node. This is needed in the case that we need to create a {@link DataFlowNode} without a representedNode, for instance when the
52 | * {@link CompilationUnit} of a dependend graph is not available while constructing a {@link DataFlowGraph}.
53 | */
54 | private String type;
55 | /**
56 | * The {@link DataFlowMethod} that contains this {@link DataFlowNode}, this will be null in case this node was not defined within a method, for instance in
57 | * case of a class field.
58 | */
59 | private OwnedNode> owner;
60 |
61 | /**
62 | * If a method was called on the current {@link DataFlowNode} this {@link NodeCall} will represent that NodeCall. Each {@link DataFlowNode} can only have a
63 | * 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
64 | * by walking through the graph. This value will be null if no method was called.
65 | */
66 | private NodeCall nodeCall;
67 |
68 | public DataFlowNode(Node representedNode) {
69 | super(representedNode);
70 | }
71 |
72 | public DataFlowNode(String name, Node representedNode) {
73 | super(name, representedNode);
74 | }
75 |
76 | private DataFlowNode(Builder builder) {
77 | super(builder);
78 | this.in.clear();
79 | this.in.addAll(builder.in);
80 | this.out.clear();
81 | this.out.addAll(builder.out);
82 | this.setType(builder.type);
83 | this.owner = builder.owner;
84 | this.nodeCall = builder.nodeCall;
85 | }
86 |
87 | public List getIn() {
88 | return in;
89 | }
90 |
91 | public void setIn(List in) {
92 | this.in = in;
93 | }
94 |
95 | public List getOut() {
96 | return out;
97 | }
98 |
99 | public void setOut(List out) {
100 | this.out = out;
101 | }
102 |
103 | public void addEdgeTo(DataFlowNode to) {
104 | DataFlowEdge edge = new DataFlowEdge(this, to);
105 | this.addOutgoing(edge);
106 | to.addIncoming(edge);
107 | }
108 |
109 | public String getType() {
110 | return type;
111 | }
112 |
113 | public void setType(String type) {
114 | this.type = type;
115 | }
116 |
117 | public Optional getNodeCall() {
118 | return Optional.ofNullable(nodeCall);
119 | }
120 |
121 | public void setNodeCall(NodeCall nodeCall) {
122 | this.nodeCall = nodeCall;
123 | }
124 |
125 | public boolean isField() {
126 | return this.getOwner().map(DataFlowGraph.class::isInstance).orElse(false);
127 | }
128 |
129 | public boolean isInputParameter() {
130 | return this.getOwner().filter(ParameterList.class::isInstance).map(ParameterList.class::cast).filter(ParameterList::isInputParametersForMethod).isPresent();
131 | }
132 |
133 | /**
134 | * Walks back over incoming edges until predicate is met or no incoming edges are present.
135 | *
136 | * @see GraphUtil#walkBackUntil(DataFlowNode, Predicate, Predicate)
137 | * @param predicate The {@link Predicate} to meet
138 | * @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.
139 | * @return {@link List} of {@link DataFlowNode}
140 | */
141 | public List walkBackUntil(Predicate predicate, Predicate scope) {
142 | return GraphUtil.walkBackUntil(this, predicate, scope);
143 | }
144 |
145 | /**
146 | * Returns all {@link NodeCall} that are called directly on this {@link DataFlowNode} or on any other {@link DataFlowNode} that has an {@link DataFlowEdge}
147 | * resulting from this node. Only nodes within the defined scope are considered.
148 | *
149 | * @param scope The scope for searching for {@link NodeCall}s.
150 | * @return List of {@link NodeCall}.
151 | */
152 | public List collectNodeCalls(Predicate scope) {
153 | if (!scope.test(this)) {
154 | return new ArrayList<>();
155 | }
156 | List collect =
157 | this.getOut().stream().map(DataFlowEdge::getTo).map(dfn -> dfn.collectNodeCalls(scope)).flatMap(List::stream).collect(Collectors.toList());
158 | if (this.nodeCall != null) {
159 | collect.add(nodeCall);
160 | }
161 | return collect;
162 | }
163 |
164 | /**
165 | * @param node The {@link DataFlowNode} to check.
166 | * @return True if this node is equal to the given node or has a direct incoming edge from the input node, false otherwise.
167 | */
168 | public boolean hasAsDirectInput(DataFlowNode node) {
169 | return this.equals(node) || this.in.stream().map(DataFlowEdge::getFrom).filter(node::equals).findAny().isPresent();
170 | }
171 |
172 | @Override
173 | public int hashCode() {
174 | return Objects.hash(super.hashCode(), in, out);
175 | }
176 |
177 | @Override
178 | public boolean equals(Object obj) {
179 | boolean equals = false;
180 | if (this == obj) {
181 | equals = true;
182 | } else if (obj != null && getClass() == obj.getClass()) {
183 | DataFlowNode other = (DataFlowNode) obj;
184 | equals = new EqualsBuilder().appendSuper(super.equals(obj)).append(in, other.in).append(out, other.out).append(type, other.type).isEquals();
185 | }
186 | return equals;
187 | }
188 |
189 | private void addIncoming(DataFlowEdge edge) {
190 | this.in.add(edge);
191 | }
192 |
193 | private void addOutgoing(DataFlowEdge edge) {
194 | this.out.add(edge);
195 | }
196 |
197 | @Override
198 | public String toString() {
199 | return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).appendSuper(super.toString()).append("in", in).append("out", out).append("type", type)
200 | .build();
201 | }
202 |
203 | public String toStringForward(int tabs) {
204 | if (tabs > 10) {
205 | return "TestDataFlowNode::toStringForward tabs>10";
206 | }
207 | return toStringForward(tabs, 0);
208 | }
209 |
210 | public String toStringForward(int tabs, int firstTabs) {
211 | StringBuilder sb = new StringBuilder();
212 | sb.append(tabs(firstTabs) + this.getName());
213 | boolean first = true;
214 | for (DataFlowEdge e : out) {
215 | if (first) {
216 | first = false;
217 | sb.append("\t-> " + e.getTo().toStringForward(tabs + 1));
218 | } else {
219 | sb.append("\n" + tabs(firstTabs + tabs + 3) + "-> " + e.getTo().toStringForward(tabs + 1, firstTabs));
220 | }
221 | }
222 | return sb.toString();
223 | }
224 |
225 | public String toStringBackward(int tabs) {
226 | if (tabs > 10) {
227 | return "TestDataFlowNode::toStringForward tabs>10";
228 | }
229 | return toStringBackward(tabs, 0);
230 | }
231 |
232 | private String toStringBackward(int tabs, int firstTabs) {
233 | StringBuilder sb = new StringBuilder();
234 | sb.append(this.getName());
235 | boolean first = true;
236 | for (DataFlowEdge e : in) {
237 | if (first) {
238 | first = false;
239 | sb.append("\t<- " + e.getFrom().toStringBackward(tabs + 1));
240 | } else {
241 | sb.append("\n" + tabs(tabs + 1) + "<- " + e.getFrom().toStringBackward(tabs + 1, tabs + 1));
242 | }
243 | }
244 | return sb.toString();
245 | }
246 |
247 | /**
248 | * Creates builder to build {@link DataFlowNode}.
249 | *
250 | * @return created builder
251 | */
252 | public static Builder builder() {
253 | return new Builder();
254 | }
255 |
256 | private String tabs(int tabs) {
257 | StringBuilder sb = new StringBuilder();
258 | for (int i = 0; i < tabs; i++) {
259 | sb.append("\t");
260 | }
261 | return sb.toString();
262 | }
263 |
264 | @Override
265 | public Optional> getOwner() {
266 | return Optional.ofNullable(this.owner);
267 | }
268 |
269 | public void setOwner(OwnedNode> owner) {
270 | this.owner = owner;
271 | }
272 |
273 | /**
274 | * Builder to build {@link DataFlowNode}.
275 | */
276 | public static final class Builder extends NodeRepresenter.Builder {
277 | private OwnedNode> owner;
278 | private List in = new ArrayList<>();
279 | private List out = new ArrayList<>();
280 | private String type;
281 | private NodeCall nodeCall;
282 |
283 | private Builder() {
284 | // Builder should only be constructed via the parent class
285 | }
286 |
287 | public Builder in(List in) {
288 | this.in.clear();
289 | this.in.addAll(in);
290 | return this;
291 | }
292 |
293 | public Builder out(List out) {
294 | this.out.clear();
295 | this.out.addAll(out);
296 | return this;
297 | }
298 |
299 | public Builder out(DataFlowEdge... out) {
300 | this.out.clear();
301 | this.out.addAll(Arrays.asList(out));
302 | return this;
303 | }
304 |
305 | public Builder type(String type) {
306 | this.type = type;
307 | return this;
308 | }
309 |
310 | public Builder owner(OwnedNode> owner) {
311 | this.owner = owner;
312 | return this;
313 | }
314 |
315 | public Builder nodeCall(NodeCall nodeCall) {
316 | this.nodeCall = nodeCall;
317 | return this;
318 | }
319 |
320 | public DataFlowNode build() {
321 | return new DataFlowNode(this);
322 | }
323 |
324 | }
325 |
326 | }
327 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/src/main/java/model/DataFlowMethod.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.Collection;
23 | import java.util.Collections;
24 | import java.util.HashMap;
25 | import java.util.List;
26 | import java.util.Map;
27 | import java.util.Objects;
28 | import java.util.Optional;
29 | import java.util.Set;
30 | import java.util.stream.Collectors;
31 | import java.util.stream.Stream;
32 |
33 | import org.apache.commons.lang3.builder.EqualsBuilder;
34 |
35 | import com.github.javaparser.ast.Node;
36 | import com.github.javaparser.ast.body.CallableDeclaration;
37 |
38 | import util.HashCodeWrapper;
39 |
40 | /**
41 | * DataFlow class representing a method inside a {@link DataFlowGraph}.
42 | *
43 | * @author Daan
44 | */
45 | public class DataFlowMethod extends OwnerNode> {
46 |
47 | // TODO the idea is to not have one list of nodes in DFM containing everything, but to let other owners like NodeCall, ParameterList and later "FlowBlock"
48 | // (representing BlockStatement) have a list of nodes of their own. Then recursively get all owned nodes of a specific OwnedNode via this method.
49 |
50 | /** All nodes defined within this method. This method should be an (indirect) owner for each of these nodes. */
51 | private Map, DataFlowNode> nodes = new HashMap<>();
52 | /** The graph which this method is part of. This is the owner of this method. */
53 | private DataFlowGraph graph;
54 | /**
55 | * The return value of this method, null if this is a void method. Note that a method can have multiple return statements, we model as if a method only has a
56 | * single return type with as Node the Node of the whole method. Then all real return statements have an edge to the single return statement.
57 | */
58 | private DataFlowNode returnNode;
59 | /** The input parameters of the method */
60 | private ParameterList inputParameters;
61 | /** The calls to other methods or constructors done from within this method. These can be either to methods in the same class or different classes. */
62 | private List nodeCalls = new ArrayList<>();
63 |
64 | /** The fields of the class that are read inside this method */
65 | // TODO Should probably be removed since it's a derivative
66 | private List inputFields = new ArrayList<>();
67 | /** The fields of the class that are written inside this method */
68 | // TODO Should probably be removed since it's a derivative
69 | private List changedFields = new ArrayList<>();
70 |
71 | public DataFlowMethod(String name, CallableDeclaration> representedNode) {
72 | super(name, representedNode);
73 | }
74 |
75 | public DataFlowMethod(DataFlowGraph graph, CallableDeclaration> node, String name) {
76 | this(name, node);
77 | this.graph = graph;
78 | graph.addMethod(this);
79 | }
80 |
81 | protected DataFlowMethod(Builder builder) {
82 | super(builder);
83 | if (builder.returnNode != null) {
84 | this.setReturnNode(builder.returnNode);
85 | }
86 | if (builder.inputParameters != null) {
87 | this.setInputParameters(builder.inputParameters);
88 | }
89 | this.addNodes(builder.nodes);
90 | // If it's added via the builder this method will be the owner, otherwise this node should have been added via a nodeCall
91 | builder.nodes.stream().filter(n -> !n.getOwner().isPresent()).forEach(n -> n.setOwner(this));
92 |
93 | this.nodeCalls.clear();
94 | this.nodeCalls.addAll(builder.nodeCalls);
95 | this.addNodes(builder.nodeCalls.stream().map(NodeCall::getReturnNode).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()));
96 | this.addNodes(builder.nodeCalls.stream().map(NodeCall::getIn).filter(Optional::isPresent).map(Optional::get).map(ParameterList::getNodes)
97 | .flatMap(List::stream).collect(Collectors.toList()));
98 | this.inputFields.clear();
99 | this.inputFields.addAll(builder.inputFields);
100 | this.changedFields.clear();
101 | this.changedFields.addAll(builder.changedFields);
102 | this.graph = builder.graph;
103 | }
104 |
105 | public Optional getReturnNode() {
106 | return Optional.ofNullable(returnNode);
107 | }
108 |
109 | public final void setReturnNode(DataFlowNode returnNode) {
110 | this.returnNode = returnNode;
111 | this.addNode(returnNode);
112 | }
113 |
114 | public ParameterList getParameters() {
115 | return inputParameters;
116 | }
117 |
118 | public final void setInputParameters(ParameterList inputParameters) {
119 | this.inputParameters = inputParameters;
120 | this.addNodes(inputParameters.getNodes());
121 | inputParameters.setOwnerAndName(this);
122 | }
123 |
124 | public List getInputFields() {
125 | return inputFields;
126 | }
127 |
128 | public void setInputFields(List inputFields) {
129 | this.inputFields = inputFields;
130 | }
131 |
132 | public List getChangedFields() {
133 | return changedFields;
134 | }
135 |
136 | public void setChangedFields(List changedFields) {
137 | this.changedFields = changedFields;
138 | }
139 |
140 | @Override
141 | public Optional> getOwner() {
142 | return Optional.ofNullable((OwnedNode>) this.graph);
143 | }
144 |
145 | public DataFlowGraph getGraph() {
146 | return graph;
147 | }
148 |
149 | public void setGraph(DataFlowGraph graph) {
150 | this.graph = graph;
151 | }
152 |
153 | public Collection getNodes() {
154 | return nodes.values();
155 | }
156 |
157 | public final void addNodes(List nodes) {
158 | nodes.forEach(this::addNode);
159 | }
160 |
161 | public final void addNode(DataFlowNode created) {
162 | this.nodes.put(new HashCodeWrapper<>(created.getRepresentedNode()), created);
163 | }
164 |
165 | public DataFlowNode getNode(Node node) {
166 | return nodes.get(new HashCodeWrapper<>(node));
167 | }
168 |
169 | public void addParameter(DataFlowNode node) {
170 | this.inputParameters.add(node);
171 | this.addNode(node);
172 | }
173 |
174 | public void addChangedField(DataFlowNode node) {
175 | this.changedFields.add(node);
176 | }
177 |
178 | public void addChangedFields(DataFlowNode... fields) {
179 | Stream.of(fields).forEach(this::addChangedField);
180 | }
181 |
182 | /**
183 | * @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
184 | * same class.
185 | */
186 | public List getNodeCalls() {
187 | return this.nodeCalls;
188 | }
189 |
190 | public void setCalledMethods(List calledMethods) {
191 | this.nodeCalls = calledMethods;
192 | }
193 |
194 | public void addMethodCall(NodeCall calledMethod) {
195 | this.nodeCalls.add(calledMethod);
196 | calledMethod.getIn().map(ParameterList::getNodes).ifPresent(this::addNodes);
197 | }
198 |
199 | @Override
200 | public Set> getOwnedOwners() {
201 | // TODO later add NodeCall and InputParameter
202 | return Collections.emptySet();
203 | }
204 |
205 | @Override
206 | public Collection getDirectOwnedNodes() {
207 | return this.nodes.values();
208 | }
209 |
210 | public boolean isInputBoundary(DataFlowNode n) {
211 | // TODO not tested yet
212 | boolean isInputBoundary = false;
213 | if (this.inputParameters.getNodes().contains(n) //
214 | || this.nodeCalls.stream().map(NodeCall::getReturnNode).filter(Optional::isPresent).map(Optional::get).filter(n::equals).findAny().isPresent() //
215 | // TODO || class fields
216 | ) {
217 | isInputBoundary = true;
218 | }
219 | return isInputBoundary;
220 | }
221 |
222 | /**
223 | * Get's all nodes that are directly connected with an edge to (not from) the input node, which are in scope of this method.
224 | *
225 | * @param node The node to get the direct input nodes for.
226 | * @return The list of {@link DataFlowNode}
227 | */
228 | public List getDirectInputNodesFor(DataFlowNode node) {
229 | return node.getIn().stream().map(DataFlowEdge::getFrom).filter(this::owns).collect(Collectors.toList());
230 | }
231 |
232 | @Override
233 | public String toString() {
234 | StringBuilder sb = new StringBuilder();
235 | sb.append("method " + super.getName() + "{\n");
236 |
237 | if (inputParameters != null) {
238 | sb.append("\tparameters{\n");
239 | for (DataFlowNode p : inputParameters.getParameters()) {
240 | sb.append(p.toStringForward(1, 2) + "\n");
241 | }
242 | sb.append("\t}\n");
243 | }
244 |
245 | sb.append("\tchangedFields{\n");
246 | for (DataFlowNode p : changedFields) {
247 | sb.append(p.toStringForward(1, 2) + "\n");
248 | }
249 | sb.append("\t}\n");
250 |
251 | sb.append("\tnodes{\n");
252 | for (DataFlowNode p : nodes.values()) {
253 | sb.append("\t\t" + p.toString() + "\n");
254 | }
255 | sb.append("\t}\n");
256 |
257 | sb.append("\treturn " + (this.returnNode == null ? "null" : this.returnNode.getName()) + "\n");
258 | sb.append("}");
259 | return sb.toString();
260 | }
261 |
262 | /**
263 | * Creates builder to build {@link DataFlowMethod}.
264 | *
265 | * @return created builder
266 | */
267 | public static Builder builder() {
268 | return new Builder();
269 | }
270 |
271 | @Override
272 | public int hashCode() {
273 | return Objects.hash(super.hashCode(), returnNode, inputParameters, inputFields, changedFields);
274 | }
275 |
276 | @Override
277 | public boolean equals(Object obj) {
278 | boolean equals = false;
279 | if (this == obj) {
280 | equals = true;
281 | } else if (obj != null && getClass() == obj.getClass()) {
282 | DataFlowMethod other = (DataFlowMethod) obj;
283 | equals = new EqualsBuilder().appendSuper(super.equals(obj)).append(returnNode, other.returnNode).append(inputParameters, other.inputParameters)
284 | .append(graph, other.graph).isEquals();
285 | }
286 | return equals;
287 | }
288 |
289 | /**
290 | * Builder to build {@link DataFlowMethod}.
291 | */
292 | public static class Builder extends NodeRepresenter.Builder, DataFlowMethod.Builder> {
293 | protected ParameterList inputParameters;
294 | protected List inputFields = new ArrayList<>();
295 | protected List changedFields = new ArrayList<>();
296 | private DataFlowNode returnNode;
297 | private DataFlowGraph graph;
298 | private List nodes = new ArrayList<>();
299 | private List nodeCalls = new ArrayList<>();
300 |
301 | protected Builder() {
302 | // Builder should only be constructed via the parent class
303 | }
304 |
305 | public Builder returnNode(DataFlowNode returnNode) {
306 | this.returnNode = returnNode;
307 | return this;
308 | }
309 |
310 | public Builder inputParameters(ParameterList inputParameters) {
311 | this.inputParameters = inputParameters;
312 | return this;
313 | }
314 |
315 | public Builder inputParameters(DataFlowNode... inputParameters) {
316 | this.inputParameters = ParameterList.builder().nodes(inputParameters).build();
317 | return this;
318 | }
319 |
320 | public Builder inputFields(List inputFields) {
321 | this.inputFields.clear();
322 | this.inputFields.addAll(inputFields);
323 | return this;
324 | }
325 |
326 | public Builder changedFields(List changedFields) {
327 | this.changedFields.clear();
328 | this.changedFields.addAll(changedFields);
329 | return this;
330 | }
331 |
332 | public Builder changedFields(DataFlowNode... changedFields) {
333 | this.changedFields.clear();
334 | this.changedFields.addAll(Arrays.asList(changedFields));
335 | return this;
336 | }
337 |
338 | public Builder graph(DataFlowGraph graph) {
339 | this.graph = graph;
340 | return this;
341 | }
342 |
343 | public Builder nodes(DataFlowNode... nodes) {
344 | this.nodes.clear();
345 | this.nodes.addAll(Arrays.asList(nodes));
346 | return this;
347 | }
348 |
349 | public Builder nodeCalls(NodeCall... calls) {
350 | this.nodeCalls.clear();
351 | Stream.of(calls).forEach(this.nodeCalls::add);
352 | return this;
353 | }
354 |
355 | public DataFlowMethod build() {
356 | return new DataFlowMethod(this);
357 | }
358 |
359 | }
360 |
361 | }
362 |
--------------------------------------------------------------------------------
/src/test/java/model/DataFlowNodeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package model;
19 |
20 | import java.util.Collection;
21 | import java.util.Collections;
22 | import java.util.Comparator;
23 | import java.util.List;
24 | import java.util.Map;
25 | import java.util.Optional;
26 | import java.util.function.BiFunction;
27 | import java.util.function.Function;
28 | import java.util.stream.Collectors;
29 |
30 | import org.junit.Assert;
31 | import org.junit.Test;
32 |
33 | import com.github.javaparser.ast.CompilationUnit;
34 | import com.github.javaparser.ast.Node;
35 | import com.github.javaparser.ast.body.FieldDeclaration;
36 | import com.github.javaparser.ast.body.MethodDeclaration;
37 | import com.github.javaparser.ast.body.Parameter;
38 | import com.github.javaparser.ast.body.VariableDeclarator;
39 | import com.github.javaparser.ast.expr.SimpleName;
40 | import com.github.javaparser.ast.stmt.ReturnStmt;
41 | import com.google.common.base.Objects;
42 |
43 | import util.HashCodeWrapper;
44 | import util.HashMapWrapper;
45 |
46 | /**
47 | * Unit test for {@link DataFlowNode}.
48 | *
49 | * @author Daan
50 | */
51 | public class DataFlowNodeTest {
52 | private static final Node JAVA_PARSER_NODE = new FieldDeclaration();
53 | private static final List IN = Collections.singletonList(DataFlowEdge.builder().build());
54 | private static final List OUT = Collections.singletonList(DataFlowEdge.builder().build());
55 | private static final String NAME = "a";
56 |
57 | @Test
58 | public void testDataFlowNode_minimum() {
59 | DataFlowNode dataFlowNode = DataFlowNode.builder().build();
60 |
61 | Assert.assertNull("Unexpected javaParserNode", dataFlowNode.getRepresentedNode());
62 | Assert.assertTrue("Unexpected in", dataFlowNode.getIn().isEmpty());
63 | Assert.assertTrue("Unexpected out", dataFlowNode.getOut().isEmpty());
64 | Assert.assertNull("Unexpected name", dataFlowNode.getName());
65 | }
66 |
67 | @Test
68 | public void testDataFlowNode_maximum() {
69 | DataFlowNode dataFlowNode = createAndFillBuilder().build();
70 |
71 | Assert.assertEquals("Unexpected javaParserNode", JAVA_PARSER_NODE, dataFlowNode.getRepresentedNode());
72 | Assert.assertEquals("Unexpected in", IN, dataFlowNode.getIn());
73 | Assert.assertEquals("Unexpected out", OUT, dataFlowNode.getOut());
74 | Assert.assertEquals("Unexpected name", NAME, dataFlowNode.getName());
75 | }
76 |
77 | @Test
78 | public void testEquals_Same() {
79 | DataFlowNode.Builder builder = createAndFillBuilder();
80 | DataFlowNode a = builder.build();
81 | DataFlowNode b = builder.build();
82 | Assert.assertTrue("Expected a and b to be equal", a.equals(b));
83 | }
84 |
85 | @Test
86 | public void testEquals_Different() {
87 | verifyEqualsDifferent(DataFlowNode.Builder::name, "b");
88 | verifyEqualsDifferent(DataFlowNode.Builder::representedNode, new MethodDeclaration());
89 | verifyEqualsDifferent(DataFlowNode.Builder::in, Collections.singletonList(DataFlowEdge.builder().build()));
90 | verifyEqualsDifferent(DataFlowNode.Builder::out, Collections.singletonList(DataFlowEdge.builder().build()));
91 | }
92 |
93 | @Test
94 | public void testHashCode_Same() {
95 | DataFlowNode.Builder builder = createAndFillBuilder();
96 | DataFlowNode a = builder.build();
97 | DataFlowNode b = builder.build();
98 | Assert.assertEquals("Expected hash code to be the same", a.hashCode(), b.hashCode());
99 | }
100 |
101 | @Test
102 | public void testHashCode_Different() {
103 | verifyHashCode_Different(DataFlowNode.Builder::name, "b");
104 | verifyHashCode_Different(DataFlowNode.Builder::representedNode, new MethodDeclaration());
105 | verifyHashCode_Different(DataFlowNode.Builder::in, Collections.singletonList(DataFlowEdge.builder().build()));
106 | verifyHashCode_Different(DataFlowNode.Builder::out, Collections.singletonList(DataFlowEdge.builder().build()));
107 | }
108 |
109 | @Test
110 | public void testCollectNodeCalls_empty() {
111 | Assert.assertTrue(DataFlowNode.builder().build().collectNodeCalls(x -> true).isEmpty());
112 | }
113 |
114 | @Test
115 | public void testCollectNodeCalls() {
116 | DataFlowNode n3 = DataFlowNode.builder().nodeCall(NodeCall.builder().build()).build();
117 | DataFlowNode n2 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n3).build()).build();
118 | DataFlowNode n1 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n2).build()).nodeCall(NodeCall.builder().build()).build();
119 |
120 | Assert.assertEquals(2, n1.collectNodeCalls(x -> true).size());
121 | }
122 |
123 | @Test
124 | public void testCollectNodeCalls_predicate() {
125 | DataFlowNode n3 = DataFlowNode.builder().nodeCall(NodeCall.builder().build()).build();
126 | DataFlowNode n2 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n3).build()).build();
127 | DataFlowNode n1 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n2).build()).nodeCall(NodeCall.builder().build()).build();
128 |
129 | Assert.assertEquals(1, n1.collectNodeCalls(x -> !x.equals(n3)).size());
130 | }
131 |
132 | @Test
133 | public void testCollectNodeCalls_predicateFalseForFirst() {
134 | DataFlowNode n3 = DataFlowNode.builder().nodeCall(NodeCall.builder().build()).build();
135 | DataFlowNode n2 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n3).build()).build();
136 | DataFlowNode n1 = DataFlowNode.builder().out(DataFlowEdge.builder().to(n2).build()).nodeCall(NodeCall.builder().build()).build();
137 |
138 | Assert.assertEquals(0, n1.collectNodeCalls(x -> !x.equals(n1)).size());
139 | }
140 |
141 | /**
142 | * Assert that the names and all incoming and outgoing edges are equal, regardless of the order.
143 | *
144 | * @param expected
145 | * @param fields
146 | * @return Empty optional if assertion passed, optional containing an error message otherwise.
147 | */
148 | public Optional assertNodesEqual(Collection expected, Collection fields) {
149 | Map, DataFlowNode> exp1 =
150 | expected.stream().collect(Collectors.toMap(t -> new HashCodeWrapper<>(t.getRepresentedNode()), Function.identity()));
151 | HashMapWrapper exp = new HashMapWrapper<>(exp1);
152 |
153 | Map, DataFlowNode> res1 =
154 | fields.stream().collect(Collectors.toMap(t -> new HashCodeWrapper<>(t.getRepresentedNode()), Function.identity()));
155 | HashMapWrapper res = new HashMapWrapper<>(res1);
156 |
157 | Optional equal = exp.keySet().equals(res.keySet()) ? Optional.empty()
158 | : Optional.of("Nodes not equal, expected: " + exp.values().stream().map(DataFlowNode::getName).sorted().collect(Collectors.toList()) + " but was: "
159 | + res.values().stream().map(DataFlowNode::getName).sorted().collect(Collectors.toList()) + " with types [" + res.values().stream()
160 | .sorted(Comparator.comparing(DataFlowNode::getName)).map(DataFlowNode::getRepresentedNode).map(Node::getClass).collect(Collectors.toList()));
161 | if (!equal.isPresent()) {
162 | equal = exp.keySet().stream().map(key -> assertNodeEqual(exp.get(key), res.get(key))).filter(Optional::isPresent).map(Optional::get).findFirst();
163 | }
164 | return equal;
165 | }
166 |
167 | /**
168 | * Assert that the incoming and outgoing edges of both nodes are equal
169 | *
170 | * @param exp expected
171 | * @param res result
172 | * @return Empty optional if assertion passed, optional containing an error message otherwise.
173 | */
174 | public Optional assertNodeEqual(DataFlowNode exp, DataFlowNode res) {
175 | List expIn = exp.getIn();
176 | List resIn = res.getIn();
177 | String message =
178 | !(exp.getName().equals(res.getName())) ? "Names are not equal of expected node " + exp.getName() + " and result node " + res.getName() : null;
179 | message = (message == null && expIn.size() != resIn.size())
180 | ? "number of incoming edges not equal expected " + expIn.size() + " but was " + resIn.size() + " for expected node " + exp + " and resultNode " + res
181 | : message;
182 | for (int i = 0; i < expIn.size() && message == null; i++) {
183 | String edgeMessage = assertEdgeEqual(expIn.get(0), resIn.get(0));
184 | if (edgeMessage != null) {
185 | message = "Incoming edges not equal of expected node " + exp + " and result node " + res + ": " + edgeMessage;
186 | }
187 | }
188 |
189 | List expOut = exp.getOut();
190 | List resOut = res.getOut();
191 | message = (message == null && expOut.size() != resOut.size()) ? "number of outgoing edges not equal for expected node " + exp + " and resultNode " + res
192 | : message;
193 | for (int i = 0; i < expOut.size() && message == null; i++) {
194 | String edgeMessage = assertEdgeEqual(expOut.get(0), resOut.get(0));
195 | if (edgeMessage != null) {
196 | message = "Outgoing edges not equal of expected node " + exp + " and result node " + res + ": " + edgeMessage;
197 | }
198 | }
199 |
200 | if (message == null) {
201 | String s = "Owner not equal for node " + exp.getName() + " expected " + exp.getOwner() + " but was " + res.getOwner();
202 | if (exp.getOwner().isPresent() && res.getOwner().isPresent()) {
203 | if (!Objects.equal(exp.getOwner().get().getName(), res.getOwner().get().getName()) || //
204 | !(exp.getOwner().get().getClass().equals(res.getOwner().get().getClass()))) {
205 | message = s;
206 | }
207 | } else if (exp.getOwner().isPresent() != res.getOwner().isPresent()) {
208 | message = s;
209 | }
210 | }
211 |
212 | if (message == null && !exp.getRepresentedNode().equals(res.getRepresentedNode())) {
213 | message = "RepresentedNode not equal for node " + exp.getName() + " expected " + exp.getRepresentedNode() + " (" + exp.getRepresentedNode().getClass()
214 | + ")" + " but was " + res.getRepresentedNode() + " (" + res.getRepresentedNode().getClass() + ")";
215 | }
216 |
217 | return Optional.ofNullable(message);
218 | }
219 |
220 | public DataFlowNode createField(CompilationUnit cu, String name) {
221 | VariableDeclarator represented = cu.findAll(VariableDeclarator.class).stream().filter(v -> v.getNameAsString().equals(name)).findFirst().get();
222 | return createNodeBuilder(name).representedNode(represented).build();
223 | }
224 |
225 | public DataFlowNode createMethodReturn(CompilationUnit cu, String methodName) {
226 | MethodDeclaration method = cu.findAll(MethodDeclaration.class).stream().filter(v -> v.getNameAsString().equals(methodName)).findFirst().get();
227 | return createNodeBuilder(methodName + "_return").representedNode(method).build();
228 | }
229 |
230 | public DataFlowNode createSpecificReturn(CompilationUnit cu, String methodName) {
231 | ReturnStmt ret =
232 | cu.findAll(MethodDeclaration.class).stream().filter(v -> v.getNameAsString().equals(methodName)).findFirst().get().findAll(ReturnStmt.class).get(0);
233 | return createNodeBuilder(methodName + "_return_line" + ret.getBegin().get().line + "_col" + ret.getBegin().get().column).representedNode(ret).build();
234 | }
235 |
236 | public DataFlowNode createParameter(CompilationUnit cu, String name) {
237 | Parameter represented =
238 | cu.findAll(MethodDeclaration.class).stream().map(md -> md.getParameterByName(name)).filter(Optional::isPresent).findFirst().get().get();
239 | return createNodeBuilder(name).representedNode(represented).build();
240 | }
241 |
242 | public DataFlowNode createNode(CompilationUnit cu, String name, Class extends Node> claz) {
243 | return createNode(cu, name, claz, 0);
244 | }
245 |
246 | public DataFlowNode createNode(CompilationUnit cu, String name, Class extends Node> claz, int index) {
247 | Node represented = cu.findAll(claz).get(index);
248 | return createNodeBuilder(name).representedNode(represented).build();
249 | }
250 |
251 | public DataFlowNode.Builder createNodeBuilder(String name) {
252 | return DataFlowNode.builder().name(name).representedNode(new SimpleName(name));
253 | }
254 |
255 | private String assertEdgeEqual(DataFlowEdge exp, DataFlowEdge res) {
256 | String message = null;
257 | if (!exp.getFrom().getName().equals(res.getFrom().getName()) && !exp.getTo().getName().equals(res.getTo().getName())) {
258 | message = exp.toString() + " not equal to " + res.toString();
259 | }
260 | return message;
261 | }
262 |
263 | private DataFlowNode.Builder createAndFillBuilder() {
264 | return DataFlowNode.builder().representedNode(JAVA_PARSER_NODE).in(IN).out(OUT).name(NAME);
265 | }
266 |
267 | private void verifyEqualsDifferent(BiFunction withMapper, T argument) {
268 | DataFlowNode.Builder builder = createAndFillBuilder();
269 | DataFlowNode a = builder.build();
270 | DataFlowNode b = withMapper.apply(builder, argument).build();
271 | Assert.assertFalse("Expected a and b not to be equal", a.equals(b));
272 | }
273 |
274 | private void verifyHashCode_Different(BiFunction withMapper, T argument) {
275 | DataFlowNode.Builder builder = createAndFillBuilder();
276 | DataFlowNode a = builder.build();
277 | DataFlowNode b = withMapper.apply(builder, argument).build();
278 | Assert.assertNotEquals("Expected hash code to be different", a.hashCode(), b.hashCode());
279 | }
280 |
281 | }
282 |
--------------------------------------------------------------------------------
/src/main/java/factory/MethodNodeHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package factory;
19 |
20 | import java.util.List;
21 | import java.util.Map;
22 | import java.util.Optional;
23 | import java.util.stream.Collectors;
24 |
25 | import org.apache.commons.lang3.StringUtils;
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 |
29 | import com.github.javaparser.JavaParser;
30 | import com.github.javaparser.ast.Node;
31 | import com.github.javaparser.ast.NodeList;
32 | import com.github.javaparser.ast.body.FieldDeclaration;
33 | import com.github.javaparser.ast.body.VariableDeclarator;
34 | import com.github.javaparser.ast.comments.LineComment;
35 | import com.github.javaparser.ast.expr.AssignExpr;
36 | import com.github.javaparser.ast.expr.Expression;
37 | import com.github.javaparser.ast.expr.FieldAccessExpr;
38 | import com.github.javaparser.ast.expr.MethodCallExpr;
39 | import com.github.javaparser.ast.expr.NameExpr;
40 | import com.github.javaparser.ast.expr.VariableDeclarationExpr;
41 | import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName;
42 | import com.github.javaparser.ast.stmt.BlockStmt;
43 | import com.github.javaparser.ast.stmt.ExpressionStmt;
44 | import com.github.javaparser.ast.stmt.ReturnStmt;
45 |
46 | import common.DataFlowException;
47 | import model.DataFlowEdge;
48 | import model.DataFlowGraph;
49 | import model.DataFlowMethod;
50 | import model.DataFlowNode;
51 | import model.NodeCall;
52 | import model.OwnedNode;
53 | import model.ParameterList;
54 | import util.ParserUtil;
55 |
56 | /**
57 | * Class for handling {@link JavaParser} {@link Node}s while filling a {@link DataFlowMethod}.
58 | *
59 | * @author Daan
60 | */
61 | public class MethodNodeHandler {
62 | private static final Logger LOG = LoggerFactory.getLogger(MethodNodeHandler.class);
63 |
64 | private ParserUtil parserUtil = new ParserUtil();
65 | private NodeCallFactory nodeCallFactory = new NodeCallFactory();
66 | private DataFlowNodeFactory dfnFactory = new DataFlowNodeFactory();
67 |
68 | /**
69 | * Recursively creates new {@link DataFlowNode} or finds existing ones and creates {@link DataFlowEdge} between those nodes if needed. This is done within the
70 | * scope of a single method. This method assumes all methods to already exist in the {@link DataFlowGraph}, including the {@link DataFlowNode}s for the input
71 | * parameters and return value. If external method calls are done, {@link NodeCall}s representing them will also be created.
72 | *
73 | * @param graph {@link DataFlowGraph}
74 | * @param method {@link DataFlowMethod} to add {@link DataFlowNode} to
75 | * @param overriddenValues The values that have been overridden in previous iterations.
76 | * @param n The {@link Node} to handle. ChildNodes will recursively be handled if needed.
77 | * @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
78 | * owner.
79 | * @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
80 | * be empty.
81 | */
82 | public Optional handleNode(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, Node n, OwnedNode> owner) {
83 | LOG.trace("handling node {}", n);
84 | Optional created = Optional.empty();
85 | if (n instanceof BlockStmt) {
86 | created = handleBlockStmt(graph, method, overriddenValues, (BlockStmt) n, owner);
87 | } else if (n instanceof ExpressionStmt) {
88 | created = handleExpressionStmt(graph, method, overriddenValues, (ExpressionStmt) n, owner);
89 | } else if (n instanceof AssignExpr) {
90 | created = handleAssignExpr(graph, method, overriddenValues, (AssignExpr) n, owner);
91 | } else if (n instanceof ReturnStmt) {
92 | created = handleReturnStmt(graph, method, overriddenValues, (ReturnStmt) n, owner);
93 | } else if (n instanceof NameExpr) {
94 | created = handleNameExpr(graph, method, overriddenValues, (NameExpr) n, owner);
95 | } else if (n instanceof MethodCallExpr) {
96 | created = handleMethodCallExpr(graph, method, overriddenValues, (MethodCallExpr) n, owner);
97 | } else if (n instanceof VariableDeclarationExpr) {
98 | created = handleVariableDeclarationExpr(graph, method, overriddenValues, (VariableDeclarationExpr) n, owner);
99 | } else if (n instanceof VariableDeclarator) {
100 | created = handleVariableDeclarator(graph, method, overriddenValues, (VariableDeclarator) n, owner);
101 | } else if (n instanceof FieldAccessExpr) {
102 | created = handleFieldAccessExpr(graph, method, overriddenValues, (FieldAccessExpr) n, owner);
103 | } else if (n instanceof LineComment) {
104 | // do nothing for comments
105 | } else {
106 | LOG.warn("In method {} could not handle node [{}] of type {}", method.getName(), n, n.getClass());
107 | }
108 | LOG.trace("created: {}", created);
109 | return created;
110 | }
111 |
112 | private Optional handleVariableDeclarator(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues,
113 | VariableDeclarator n, OwnedNode> owner) {
114 | DataFlowNode created = dfnFactory.create(n, owner);
115 | Optional initializer = n.getInitializer();
116 | if (initializer.isPresent()) {
117 | Optional assigner = handleNode(graph, method, overriddenValues, initializer.get(), owner);
118 | if (assigner.isPresent()) {
119 | assigner.get().addEdgeTo(created);
120 | } else {
121 | LOG.warn("In method {} was not able to resolve {} of type {}", method.getName(), initializer.get(), initializer.get().getClass());
122 | }
123 | }
124 | method.addNode(created);
125 | return Optional.ofNullable(created);
126 | }
127 |
128 | private Optional handleVariableDeclarationExpr(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues,
129 | VariableDeclarationExpr n, OwnedNode> owner) {
130 | NodeList variables = n.getVariables();
131 | for (VariableDeclarator vd : variables) {
132 | handleNode(graph, method, overriddenValues, vd, owner);
133 | }
134 | return Optional.empty();
135 | }
136 |
137 | private Optional handleMethodCallExpr(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, MethodCallExpr n,
138 | OwnedNode> owner) {
139 |
140 | // Get the instance on which the method call is executed.
141 | DataFlowNode instance = n.getScope().map(scope -> handleNode(graph, method, overriddenValues, scope, owner).orElse(null)).orElse(null);
142 |
143 | // Create the nodeCall
144 | Optional optionalCalledMethod = nodeCallFactory.create(owner, n, instance);
145 | if (!optionalCalledMethod.isPresent()) {
146 | // logged in called method
147 | return Optional.empty();
148 | }
149 | NodeCall calledMethod = optionalCalledMethod.get();
150 |
151 | // Handle input to call.
152 | NodeList arguments = n.getArguments();
153 | List> optionalInputArguments =
154 | arguments.stream().map(arg -> handleNode(graph, method, overriddenValues, arg, calledMethod)).collect(Collectors.toList());
155 | if (optionalInputArguments.stream().filter(o -> !o.isPresent()).findAny().isPresent()) {
156 | LOG.warn("Could not resolve all input arguments for methodCall {} in method {} with input parameters {}", n.getNameAsString(), method.getName(),
157 | optionalInputArguments);
158 | return Optional.empty();
159 | }
160 | List inputArguments = optionalInputArguments.stream().map(Optional::get).collect(Collectors.toList());
161 |
162 | // Add input to method
163 | ParameterList.Builder params = ParameterList.builder().name(calledMethod.getName() + "CallParameters");
164 | if (inputArguments != null && !inputArguments.isEmpty()) {
165 | params.nodes(inputArguments).build();
166 | }
167 | calledMethod.setIn(params.build());
168 |
169 | if (instance != null) {
170 | instance.setNodeCall(calledMethod);
171 | }
172 | method.addMethodCall(calledMethod);
173 |
174 | calledMethod.getReturnNode().ifPresent(method::addNode);
175 | // Return the return node of the called method so that the return value can be assigned to the caller.
176 | return calledMethod.getReturnNode();
177 | }
178 |
179 | private Optional handleBlockStmt(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, BlockStmt node,
180 | OwnedNode> owner) {
181 | for (Node n : node.getChildNodes()) {
182 | handleNode(graph, method, overriddenValues, n, owner);
183 | }
184 | return Optional.empty();
185 | }
186 |
187 | private Optional handleReturnStmt(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, ReturnStmt n,
188 | OwnedNode> owner) {
189 | DataFlowNode createdReturn = null;
190 | if (n.getExpression().isPresent()) {
191 | Expression expression = n.getExpression().get();
192 | Optional assignToReturn = handleNode(graph, method, overriddenValues, expression, owner);
193 |
194 | if (assignToReturn.isPresent()) {
195 | // TODO remove setting weird custom names later.
196 | String name = method.getName() + "_return_" + n.getBegin().map(t -> "line" + t.line + "_col" + t.column).orElse("?");
197 | createdReturn = dfnFactory.create(n, owner);
198 | createdReturn.setName(name);
199 | assignToReturn.get().addEdgeTo(createdReturn);
200 | method.addNode(createdReturn);
201 | if (method.getReturnNode().isPresent()) {
202 | createdReturn.addEdgeTo(method.getReturnNode().get());
203 | } else {
204 | throw new DataFlowException("Expected the method %s for which the return statement %s is handled to already have a return node", method, n);
205 | }
206 | } else {
207 | LOG.warn("In method {} could not find node for assigning to the return value for node {} of type {}", method.getName(), expression,
208 | expression.getClass());
209 | }
210 | }
211 | return Optional.ofNullable(createdReturn);
212 | }
213 |
214 | private Optional handleNameExpr(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, NameExpr n,
215 | OwnedNode> owner) {
216 | DataFlowNode newDfn = dfnFactory.create(n, owner);
217 | Optional origin = getDataFlowNode(graph, method, overriddenValues, n);
218 | origin.ifPresent(ori -> ori.addEdgeTo(newDfn));
219 | method.addNode(newDfn);
220 | return Optional.of(newDfn);
221 | }
222 |
223 | private Optional handleFieldAccessExpr(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, FieldAccessExpr n,
224 | OwnedNode> owner) {
225 | DataFlowNode newDfn = dfnFactory.create(n, owner);
226 | Optional origin = getDataFlowNode(graph, method, overriddenValues, n);
227 | origin.ifPresent(ori -> ori.addEdgeTo(newDfn));
228 | method.addNode(newDfn);
229 | return Optional.of(newDfn);
230 | }
231 |
232 | private Optional handleExpressionStmt(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, ExpressionStmt n,
233 | OwnedNode> owner) {
234 | for (Node c : n.getChildNodes()) {
235 | handleNode(graph, method, overriddenValues, c, owner);
236 | }
237 | return Optional.empty();
238 | }
239 |
240 | private Optional handleAssignExpr(DataFlowGraph graph, DataFlowMethod method, Map overriddenValues, AssignExpr expr,
241 | OwnedNode> owner) {
242 | Expression assignedJP = expr.getTarget();
243 | Expression assignerJP = expr.getValue();
244 | Optional optionalRealAssignedJP = parserUtil.getJavaParserNode(method, assignedJP);
245 | Optional assignerDF = handleNode(graph, method, overriddenValues, assignerJP, owner);
246 |
247 | if (!optionalRealAssignedJP.isPresent() || !assignerDF.isPresent()) {
248 | // Logging is already done in the method call.
249 | return Optional.empty();
250 | }
251 | if (!(assignedJP instanceof NodeWithSimpleName)) {
252 | LOG.warn("Not able to create a new DFN if the assigned node does not implement NodeWithSimpleName, for node {}", assignedJP);
253 | return Optional.empty();
254 | }
255 |
256 | Node realAssignedJP = optionalRealAssignedJP.get();
257 | DataFlowNode flowNode = dfnFactory.create(expr, method);
258 | String name = nameForInBetweenNode(method, overriddenValues, realAssignedJP, (NodeWithSimpleName>) assignedJP);
259 | flowNode.setName(name);
260 | method.addNode(flowNode);
261 | if (isField(realAssignedJP)) {
262 | // This is the version of the field that will receive the assigner edge.
263 | // If this is the last assignment to the field, an edge to the original field will be created.
264 | overriddenValues.put(realAssignedJP, flowNode);
265 | }
266 |
267 | assignerDF.get().addEdgeTo(flowNode);
268 | return Optional.of(flowNode);
269 | }
270 |
271 | private Optional getDataFlowNode(DataFlowGraph graph, DataFlowMethod method, Map overwriddenValues, Node node) {
272 | Optional optionalResolvedNode = parserUtil.getJavaParserNode(method, node);
273 | DataFlowNode flowNode = null;
274 | if (optionalResolvedNode.isPresent()) {
275 | Node resolvedNode = optionalResolvedNode.get();
276 | flowNode = getLastFlowNode(graph, method, overwriddenValues, resolvedNode);
277 | flowNode = (flowNode != null || !(resolvedNode instanceof VariableDeclarationExpr)) ? flowNode
278 | : ((VariableDeclarationExpr) resolvedNode).getVariables().stream().map(child -> getLastFlowNode(graph, method, overwriddenValues, child))
279 | .filter(n -> n != null).findFirst().orElse(null);
280 | }
281 | if (flowNode == null) {
282 | LOG.warn("In method {} did not resolve the type of node {} of type {}, resolvedNode was {}", method.getName(), node, node.getClass(),
283 | optionalResolvedNode);
284 | }
285 | return Optional.ofNullable(flowNode);
286 | }
287 |
288 | private DataFlowNode getLastFlowNode(DataFlowGraph graph, DataFlowMethod method, Map overwriddenValues, Node resolvedNode) {
289 | DataFlowNode flowNode = overwriddenValues.get(resolvedNode);
290 | flowNode = flowNode != null ? flowNode : method.getNode(resolvedNode);
291 | flowNode = flowNode != null ? flowNode : graph.getNode(resolvedNode);
292 | return flowNode;
293 | }
294 |
295 | private String nameForInBetweenNode(DataFlowMethod method, Map overriddenValues, Node realAssignedJP,
296 | NodeWithSimpleName> nodeWithName) {
297 | String namePostFix = "";
298 | if (overriddenValues.containsKey(realAssignedJP)) {
299 | DataFlowNode overridden = overriddenValues.get(realAssignedJP);
300 | String stringNumber = overridden.getName().substring(overridden.getName().lastIndexOf("."));
301 | namePostFix = StringUtils.isNumeric(stringNumber) ? "." + (new Integer(stringNumber) + 1) : ".2";
302 | }
303 |
304 | // Make the name unique for multiple assignments to the same variable
305 | return method.getName() + "." + nodeWithName.getNameAsString() + namePostFix;
306 | }
307 |
308 | private boolean isField(Node representedNode) {
309 | boolean isField = false;
310 | if (representedNode instanceof VariableDeclarator) {
311 | VariableDeclarator vd = (VariableDeclarator) representedNode;
312 | if (vd.getParentNode().isPresent() && vd.getParentNode().get() instanceof FieldDeclaration) {
313 | isField = true;
314 | }
315 | }
316 | return isField;
317 | }
318 |
319 | }
320 |
--------------------------------------------------------------------------------
/src/test/java/factory/DataFlowGraphFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 by Daan van den Heuvel.
3 | *
4 | * This file is part of JavaDataFlow.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package factory;
19 |
20 | import java.util.Arrays;
21 | import java.util.Collection;
22 | import java.util.List;
23 | import java.util.Map;
24 | import java.util.Optional;
25 | import java.util.function.Function;
26 | import java.util.stream.Collectors;
27 |
28 | import org.hamcrest.FeatureMatcher;
29 | import org.hamcrest.Matcher;
30 | import org.hamcrest.Matchers;
31 | import org.junit.Assert;
32 | import org.junit.Before;
33 | import org.junit.Test;
34 |
35 | import com.github.javaparser.StaticJavaParser;
36 | import com.github.javaparser.ast.CompilationUnit;
37 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
38 | import com.github.javaparser.ast.body.MethodDeclaration;
39 | import com.github.javaparser.ast.body.VariableDeclarator;
40 | import com.github.javaparser.ast.expr.AssignExpr;
41 | import com.github.javaparser.ast.expr.FieldAccessExpr;
42 | import com.github.javaparser.ast.expr.MethodCallExpr;
43 | import com.github.javaparser.ast.expr.NameExpr;
44 | import com.github.javaparser.ast.expr.SimpleName;
45 | import com.google.common.base.Functions;
46 |
47 | import common.SymbolSolverSetup;
48 | import model.DataFlowGraph;
49 | import model.DataFlowMethod;
50 | import model.DataFlowNode;
51 | import model.DataFlowNodeTest;
52 | import model.NodeCall;
53 | import model.ParameterList;
54 |
55 | /**
56 | * Unit test for {@link DataFlowGraphFactory}.
57 | *
58 | * @author Daan
59 | */
60 | public class DataFlowGraphFactoryTest {
61 |
62 | private DataFlowNodeTest dfnTest = new DataFlowNodeTest();
63 |
64 | private DataFlowGraphFactory factory = new DataFlowGraphFactory();
65 |
66 | @Before
67 | public void setup() {
68 | SymbolSolverSetup.setup();
69 | }
70 |
71 | @Test
72 | public void testCreate_setter() {
73 | CompilationUnit cu = StaticJavaParser.parse(//
74 | "public class Claz {\n" + //
75 | " private String s;\n" + //
76 | " public void setS(String a) {\n" + //
77 | " this.s = a;\n" + //
78 | " }\n" + //
79 | "}");
80 |
81 | DataFlowNode s = dfnTest.createField(cu, "s");
82 | DataFlowNode a = dfnTest.createParameter(cu, "a");
83 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class, 0);
84 | DataFlowNode setS_s = dfnTest.createNode(cu, "setS.s", AssignExpr.class);
85 | connectNodesInSquence(a, a_1, setS_s, s);
86 | DataFlowMethod setS = createMethod("setS").inputParameters(a).nodes(a_1, setS_s).changedFields(s).build();
87 | DataFlowGraph expected = createGraph(cu, Arrays.asList(s), setS);
88 |
89 | executeAndVerify(cu, expected);
90 | }
91 |
92 | @Test
93 | public void testCreate_setterMultipleInput() {
94 | CompilationUnit cu = StaticJavaParser.parse(//
95 | "public class Claz {\n" + //
96 | " private String s,t;\n" + //
97 | " public void setS(String a, String b) {\n" + //
98 | " this.s = a;\n" + //
99 | " this.t = b;\n" + //
100 | " }\n" + //
101 | "}"); //
102 | DataFlowNode s = dfnTest.createField(cu, "s");
103 | DataFlowNode t = dfnTest.createField(cu, "t");
104 | DataFlowNode a = dfnTest.createParameter(cu, "a");
105 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class, 0);
106 | DataFlowNode b = dfnTest.createParameter(cu, "b");
107 | DataFlowNode b_1 = dfnTest.createNode(cu, "b", NameExpr.class, 1);
108 | DataFlowNode setS_s = dfnTest.createNode(cu, "setS.s", AssignExpr.class, 0);
109 | DataFlowNode setS_t = dfnTest.createNode(cu, "setS.t", AssignExpr.class, 1);
110 |
111 | connectNodesInSquence(a, a_1, setS_s, s);
112 | connectNodesInSquence(b, b_1, setS_t, t);
113 |
114 | DataFlowMethod setS = createMethod("setS").inputParameters(a, b).nodes(a_1, b_1, setS_s, setS_t).changedFields(s, t).build();
115 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").fields(s, t).methods(setS).build();
116 |
117 | executeAndVerify(cu, expected);
118 | }
119 |
120 | @Test
121 | public void testCreate_setterAssignFieldTwice() {
122 |
123 | CompilationUnit cu = StaticJavaParser.parse(//
124 | "public class Claz {\n" + //
125 | " private String s;\n" + //
126 | " public void setS(String a, String b) {\n" + //
127 | " this.s = a;\n" + //
128 | " this.s = b;\n" + //
129 | " }\n" + //
130 | "}"); //
131 | DataFlowNode s = dfnTest.createField(cu, "s");
132 | DataFlowNode a = dfnTest.createParameter(cu, "a");
133 | DataFlowNode b = dfnTest.createParameter(cu, "b");
134 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class, 0);
135 | DataFlowNode b_1 = dfnTest.createNode(cu, "b", NameExpr.class, 1);
136 | DataFlowNode setS_s1 = dfnTest.createNode(cu, "setS.s", AssignExpr.class, 0);
137 | DataFlowNode setS_s2 = dfnTest.createNode(cu, "setS.s.2", AssignExpr.class, 1);
138 |
139 | connectNodesInSquence(a, a_1, setS_s1);
140 | connectNodesInSquence(b, b_1, setS_s2, s);
141 |
142 | DataFlowMethod setS = createMethod("setS").inputParameters(a, b).nodes(a_1, b_1, setS_s1, setS_s2).changedFields(s).build();
143 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").fields(s).methods(setS).build();
144 |
145 | executeAndVerify(cu, expected);
146 | }
147 |
148 | @Test
149 | public void testCreate_setterAssignFieldToField() {
150 | CompilationUnit cu = StaticJavaParser.parse(//
151 | "public class Claz {\n" + //
152 | " private String s,t;\n" + //
153 | " public void setS(String a) {\n" + //
154 | " this.s = a;\n" + //
155 | " this.t = this.s;\n" + //
156 | " }\n" + //
157 | "}"); //
158 | DataFlowNode s = dfnTest.createField(cu, "s");
159 | DataFlowNode t = dfnTest.createField(cu, "t");
160 | DataFlowNode a = dfnTest.createParameter(cu, "a");
161 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class);
162 | DataFlowNode setS_s = dfnTest.createNode(cu, "setS.s", AssignExpr.class);
163 | DataFlowNode s_1 = dfnTest.createNode(cu, "s", FieldAccessExpr.class, 2);
164 | DataFlowNode setS_t = dfnTest.createNode(cu, "setS.t", AssignExpr.class, 1);
165 |
166 | connectNodesInSquence(a, a_1, setS_s, s);
167 | connectNodesInSquence(setS_s, s_1, setS_t, t);
168 |
169 | DataFlowMethod setS = createMethod("setS").inputParameters(a).nodes(a_1, s_1, setS_s, setS_t).changedFields(s, t).build();
170 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").fields(s, t).methods(setS).build();
171 |
172 | executeAndVerify(cu, expected);
173 | }
174 |
175 | @Test
176 | public void testCreate_return() {
177 | CompilationUnit cu = StaticJavaParser.parse(//
178 | "public class Claz {\n" + //
179 | " public int called(int a) {\n" + //
180 | " return a;\n" + //
181 | " }\n" + //
182 | "}"); //
183 | DataFlowNode a = dfnTest.createParameter(cu, "a");
184 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class);
185 | DataFlowNode ret1 = dfnTest.createSpecificReturn(cu, "called");
186 | DataFlowNode methodReturn = dfnTest.createMethodReturn(cu, "called");
187 |
188 | connectNodesInSquence(a, a_1, ret1, methodReturn);
189 |
190 | DataFlowMethod setS = createMethod("called").inputParameters(a).nodes(a_1, ret1).returnNode(methodReturn).build();
191 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").methods(setS).build();
192 |
193 | executeAndVerify(cu, expected);
194 | }
195 |
196 | @Test
197 | public void testCreate_methodCallingMethod() {
198 | CompilationUnit cu = StaticJavaParser.parse(//
199 | "public class Claz {\n" + //
200 | " public int caller(int a) {\n" + //
201 | " return called(a);\n" + //
202 | " }\n" + //
203 | " public int called(int b) {\n" + //
204 | " return b;\n" + //
205 | " }\n" + //
206 | "}"); //
207 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class);
208 | DataFlowNode nodeCallReturn = dfnTest.createNode(cu, "nodeCall_called_return", MethodCallExpr.class);
209 | NodeCall nodeCall = createNodeCall("called", nodeCallReturn, a_1);
210 |
211 | DataFlowNode a = dfnTest.createParameter(cu, "a");
212 | DataFlowNode specificReturnCaller = dfnTest.createSpecificReturn(cu, "caller");
213 | DataFlowNode genericReturnCaller = dfnTest.createMethodReturn(cu, "caller");
214 | DataFlowMethod caller =
215 | createMethod("caller", a).nodes(specificReturnCaller).nodeCalls(nodeCall).returnNode(genericReturnCaller).representedNode(null).build();
216 |
217 | DataFlowNode b_called = dfnTest.createParameter(cu, "b");
218 | DataFlowNode b_1 = dfnTest.createNode(cu, "b", NameExpr.class, 1);
219 | DataFlowNode specificReturnCalled = dfnTest.createSpecificReturn(cu, "called");
220 | DataFlowNode genericReturnCalled = dfnTest.createMethodReturn(cu, "called");
221 | DataFlowMethod called = createMethod("called", b_called).nodes(b_1, specificReturnCalled).returnNode(genericReturnCalled).build();
222 |
223 | connectNodesInSquence(a, a_1, b_called, b_1, specificReturnCalled, genericReturnCalled, nodeCallReturn, specificReturnCaller, genericReturnCaller);
224 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").methods(caller, called).build();
225 |
226 | executeAndVerify(cu, expected);
227 | }
228 |
229 | @Test
230 | public void testCreate_createVar() {
231 | CompilationUnit cu = StaticJavaParser.parse(//
232 | "public class Claz {\n" + //
233 | " public int met(int a) {\n" + //
234 | " int b = a;\n" + //
235 | " return b;\n" + //
236 | " }\n" + //
237 | "}"); //
238 | DataFlowNode a = dfnTest.createParameter(cu, "a");
239 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class);
240 | DataFlowNode b = dfnTest.createNode(cu, "b", VariableDeclarator.class);
241 | DataFlowNode b_1 = dfnTest.createNode(cu, "b", NameExpr.class, 1);
242 | DataFlowNode specificReturnCaller = dfnTest.createSpecificReturn(cu, "met");
243 | DataFlowNode genericReturnCaller = dfnTest.createMethodReturn(cu, "met");
244 |
245 | connectNodesInSquence(a, a_1, b, b_1, specificReturnCaller, genericReturnCaller);
246 | DataFlowMethod caller =
247 | createMethod("met").inputParameters(a).nodes(a_1, b, b_1, specificReturnCaller).returnNode(genericReturnCaller).representedNode(null).build();
248 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").methods(caller).build();
249 |
250 | executeAndVerify(cu, expected);
251 | }
252 |
253 | @Test
254 | public void testCreate_inputMethods() {
255 | CompilationUnit cu = StaticJavaParser.parse(//
256 | "public class Claz {\n" + //
257 | " StringBuilder sb = new StringBuilder(); \n" + //
258 | " public StringBuilder met(String a) {\n" + //
259 | " return sb.append(a);\n" + //
260 | " }\n" + //
261 | "}"); //
262 | DataFlowNode a = dfnTest.createParameter(cu, "a");
263 | DataFlowNode a_1 = dfnTest.createNode(cu, "a", NameExpr.class, 1);
264 | this.connectNodesInSquence(a, a_1);
265 |
266 | DataFlowNode sb_output = dfnTest.createNode(cu, "nodeCall_append_return", MethodCallExpr.class);
267 | DataFlowNode specificReturnCaller = dfnTest.createSpecificReturn(cu, "met");
268 | DataFlowNode genericReturnCaller = dfnTest.createMethodReturn(cu, "met");
269 | this.connectNodesInSquence(sb_output, specificReturnCaller, genericReturnCaller);
270 |
271 | DataFlowNode field = dfnTest.createField(cu, "sb");
272 | DataFlowNode fieldUsage = dfnTest.createNode(cu, "sb", NameExpr.class, 0);
273 | this.connectNodesInSquence(field, fieldUsage);
274 |
275 | DataFlowMethod caller = createMethod("met").inputParameters(a).nodeCalls(createNodeCall("append", sb_output, a_1)).nodes(specificReturnCaller, fieldUsage)
276 | .returnNode(genericReturnCaller).build();
277 |
278 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").fields(field).methods(caller).build();
279 |
280 | executeAndVerify(cu, expected);
281 | }
282 |
283 | @Test
284 | public void testCreate_methodInMethodCall() {
285 | CompilationUnit cu = StaticJavaParser.parse(//
286 | "public class Claz {\n" + //
287 | " StringBuilder sb = new StringBuilder(); \n" + //
288 | " StringBuilder sb1 = new StringBuilder(); \n" + //
289 | " public StringBuilder caller(String a) {\n" + //
290 | " return sb1.append(sb.indexOf(a));\n" + //
291 | " }\n" + //
292 | "}"); //
293 | DataFlowNode sb_input = dfnTest.createNode(cu, "a", NameExpr.class, 2);
294 | DataFlowNode sb_output = dfnTest.createNode(cu, "nodeCall_indexOf_return", MethodCallExpr.class, 1);
295 | NodeCall sb_call = createNodeCall("indexOf", sb_output, sb_input);
296 |
297 | DataFlowNode sb1_output = dfnTest.createNode(cu, "nodeCall_append_return", MethodCallExpr.class, 0);
298 | NodeCall sb1_call = createNodeCall("append", sb1_output, sb_output);
299 |
300 | DataFlowNode sb = dfnTest.createField(cu, "sb");
301 | DataFlowNode sb1 = dfnTest.createField(cu, "sb1");
302 | DataFlowNode sbUsage = dfnTest.createNode(cu, "sb", NameExpr.class, 1);
303 | DataFlowNode sb1Usage = dfnTest.createNode(cu, "sb1", NameExpr.class, 0);
304 | sbUsage.setOwner(sb1_call);
305 | this.connectNodesInSquence(sb, sbUsage);
306 | this.connectNodesInSquence(sb1, sb1Usage);
307 |
308 | DataFlowNode a = dfnTest.createParameter(cu, "a");
309 | DataFlowNode specificReturnCaller = dfnTest.createSpecificReturn(cu, "caller");
310 | DataFlowNode genericReturnCaller = dfnTest.createMethodReturn(cu, "caller");
311 |
312 | DataFlowMethod caller = createMethod("caller").inputParameters(a).nodes(specificReturnCaller, sbUsage, sb1Usage).nodeCalls(sb_call, sb1_call)
313 | .returnNode(genericReturnCaller).build();
314 |
315 | this.connectNodesInSquence(a, sb_input);
316 | this.connectNodesInSquence(sb1_output, specificReturnCaller, genericReturnCaller);
317 |
318 | DataFlowGraph expected = DataFlowGraph.builder().name("Claz").fields(sb, sb1).methods(caller).build();
319 |
320 | executeAndVerify(cu, expected);
321 | }
322 |
323 | private DataFlowGraph createGraph(CompilationUnit cu, List s, DataFlowMethod... setS) {
324 | ClassOrInterfaceDeclaration representedNode = cu.findFirst(ClassOrInterfaceDeclaration.class).get();
325 | return DataFlowGraph.builder().representedNode(representedNode).name(representedNode.getNameAsString()).fields(s).methods(setS).build();
326 | }
327 |
328 | private NodeCall createNodeCall(String name, DataFlowNode nodeCallReturn, DataFlowNode... params) {
329 | NodeCall call =
330 | NodeCall.builder().name(name).in(ParameterList.builder().nodes(params).name(name + "Parameters").build()).returnNode(nodeCallReturn).build();
331 | return call;
332 | }
333 |
334 | private DataFlowMethod.Builder createMethod(String name, DataFlowNode... params) {
335 | MethodDeclaration m = new MethodDeclaration();
336 | m.setName(new SimpleName(name));
337 | DataFlowMethod.Builder method = DataFlowMethod.builder().name(name).representedNode(m);
338 | if (params != null && params.length > 0) {
339 | method.inputParameters(ParameterList.builder().name(name + "Parameters").nodes(params).build());
340 | }
341 | return method;
342 | }
343 |
344 | private void connectNodesInSquence(DataFlowNode... nodes) {
345 | for (int i = 0; i < nodes.length - 1; i++) {
346 | nodes[i].addEdgeTo(nodes[i + 1]);
347 | }
348 | }
349 |
350 | private DataFlowGraph executeAndVerify(CompilationUnit cu, DataFlowGraph expected) {
351 | DataFlowGraph graph = factory.create(cu);
352 | assertGraph(expected, graph);
353 | return graph;
354 | }
355 |
356 | private void assertGraph(DataFlowGraph expected, DataFlowGraph graph) {
357 | Assert.assertEquals("Unexpected name for graph", expected.getName(), graph.getName());
358 | dfnTest.assertNodesEqual(expected.getFields(), graph.getFields()).ifPresent(m -> fail(expected, graph, "Fields not equal: " + m));
359 | assertMethodsEqual(expected.getMethods(), graph.getMethods()).ifPresent(m -> fail(expected, graph, "Methods not equal: " + m));
360 | assertMethodsEqual(expected.getConstructors(), graph.getConstructors()).ifPresent(m -> fail(expected, graph, "Constructors not equal: " + m));
361 | }
362 |
363 | private void fail(DataFlowGraph expected, DataFlowGraph graph, String message) {
364 | System.out.println("============================== Expected ==============================");
365 | System.out.println(expected);
366 | System.out.println("=============================== Actual ===============================");
367 | System.out.println(graph.toString());
368 | Assert.fail(message);
369 | }
370 |
371 | private Optional assertMethodsEqual(Collection exp, Collection res) {
372 | Map> expToResMap = exp.stream().collect(Collectors.toMap(Functions.identity(), e -> getEqualMethod(res, e)));
373 | Optional notFound =
374 | expToResMap.entrySet().stream().filter(e -> !e.getValue().isPresent()).map(Map.Entry::getKey).map(dfm -> "no method found for " + dfm).findFirst();
375 | if (!notFound.isPresent()) {
376 | notFound = expToResMap.entrySet().stream().map(e -> assertMethodEqual(e.getKey(), e.getValue().get())).filter(Optional::isPresent).map(Optional::get)
377 | .findFirst();
378 | }
379 | return notFound;
380 | }
381 |
382 | private Optional