├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ ├── common │ └── DataFlowException.java │ ├── facade │ ├── JavaDataFlow.java │ └── StaticJavaDataFlow.java │ ├── factory │ ├── DataFlowGraphFactory.java │ ├── DataFlowNodeFactory.java │ ├── MethodNodeHandler.java │ └── NodeCallFactory.java │ ├── model │ ├── DataFlowEdge.java │ ├── DataFlowGraph.java │ ├── DataFlowMethod.java │ ├── DataFlowNode.java │ ├── NodeCall.java │ ├── NodeRepresenter.java │ ├── OwnedNode.java │ ├── OwnerNode.java │ └── ParameterList.java │ └── util │ ├── GraphUtil.java │ ├── HashCodeWrapper.java │ ├── HashMapWrapper.java │ └── ParserUtil.java └── test └── java ├── common ├── DataFlowMethodBuilder.java ├── GraphBuilder.java ├── NodeBuilder.java └── SymbolSolverSetup.java ├── factory ├── DataFlowGraphFactoryTest.java ├── MethodNodeHandlerTest.java └── NodeCallFactoryTest.java ├── model ├── DataFlowEdgeTest.java ├── DataFlowGraphTest.java ├── DataFlowMethodTest.java ├── DataFlowNodeTest.java ├── NodeCallTest.java └── ParameterListTest.java └── util ├── GraphUtilTest.java └── MethodMatcher.java /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Join the chat at https://gitter.im/JavaDataFlow/community](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/JavaDataFlow/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | [![maven-central](https://img.shields.io/maven-central/v/com.github.daanvdh.javadataflow/JavaDataFlow.svg)](https://search.maven.org/search?q=g:com.github.daanvdh.javadataflow) 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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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 assertMethodEqual(DataFlowMethod expMethod, DataFlowMethod equalMethod) { 383 | Optional parametersEqual = dfnTest.assertNodesEqual(expMethod.getParameters().getNodes(), equalMethod.getParameters().getNodes()) 384 | .map(s -> "for " + expMethod.getName() + " parameters not equal: " + s); 385 | Optional nodesEqual = parametersEqual.isPresent() ? parametersEqual 386 | : dfnTest.assertNodesEqual(expMethod.getNodes(), equalMethod.getNodes()).map(s -> "for " + expMethod.getName() + ": " + s); 387 | Optional changedFieldsEqual = nodesEqual.isPresent() ? nodesEqual 388 | : dfnTest.assertNodesEqual(expMethod.getChangedFields(), equalMethod.getChangedFields()) 389 | .map(s -> "for " + expMethod.getName() + " changedFields not equal: " + s); 390 | return changedFieldsEqual; 391 | } 392 | 393 | private Optional getEqualMethod(Collection methods, DataFlowMethod lookup) { 394 | return methods.stream().filter(m -> createMatcher(lookup).matches(m)).findFirst(); 395 | } 396 | 397 | private Matcher createMatcher(DataFlowMethod method) { 398 | EqualFeatureMatcher methodNameMatcher = new EqualFeatureMatcher<>(DataFlowMethod::getName, method.getName(), "methodName"); 399 | 400 | EqualFeatureMatcher> parameterMatcher = 401 | new EqualFeatureMatcher<>((m) -> m.getParameters().getNodes().stream().map(DataFlowNode::getName).collect(Collectors.toList()), 402 | method.getParameters().getNodes().stream().map(DataFlowNode::getName).collect(Collectors.toList()), "methodParameters"); 403 | 404 | return Matchers.allOf(methodNameMatcher, parameterMatcher); 405 | } 406 | 407 | public class EqualFeatureMatcher extends FeatureMatcher { 408 | private final Function mapper; 409 | 410 | /** 411 | * Constructs an instance of {@link EqualFeatureMatcher}. 412 | * 413 | * @param mapper a {@link Function} to maps object to its feature 414 | * @param expected the expected value 415 | * @param description the description of the feature 416 | */ 417 | public EqualFeatureMatcher(Function mapper, U expected, String description) { 418 | super(Matchers.equalTo(expected), description, description); 419 | this.mapper = mapper; 420 | } 421 | 422 | @Override 423 | protected U featureValueOf(T actual) { 424 | return mapper.apply(actual); 425 | } 426 | } 427 | 428 | } 429 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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 claz) { 243 | return createNode(cu, name, claz, 0); 244 | } 245 | 246 | public DataFlowNode createNode(CompilationUnit cu, String name, Class 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/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/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/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/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 | --------------------------------------------------------------------------------