├── .gitignore ├── .idea ├── .gitignore ├── .name ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml ├── myanalyzer.iml └── vcs.xml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── docs ├── CMakeLists.txt ├── CONTRIBUTING.md ├── DataflowAnalysis.md └── ProgramRepresentation.md ├── include ├── World.h ├── analysis │ ├── Analysis.h │ ├── AnalysisResult.h │ ├── dataflow │ │ ├── AnalysisDriver.h │ │ ├── ConstantPropagation.h │ │ ├── DataflowAnalysis.h │ │ ├── LiveVariable.h │ │ ├── ReachingDefinition.h │ │ ├── fact │ │ │ ├── DataflowResult.h │ │ │ ├── MapFact.h │ │ │ ├── NodeResult.h │ │ │ └── SetFact.h │ │ └── solver │ │ │ └── Solver.h │ └── graph │ │ └── CFG.h ├── config │ └── AnalysisConfig.h ├── ir │ ├── IR.h │ ├── Stmt.h │ └── Var.h ├── language │ ├── CPPMethod.h │ └── Type.h └── util │ ├── Copyable.h │ └── Logger.h ├── lib ├── CMakeLists.txt ├── World.cpp ├── analysis │ ├── Analysis.cpp │ ├── dataflow │ │ ├── ConstantPropagation.cpp │ │ ├── LiveVariable.cpp │ │ └── ReachingDefinition.cpp │ └── graph │ │ └── DefaultCFG.cpp ├── config │ └── DefaultAnalysisConfig.cpp ├── ir │ ├── ClangStmtWrapper.cpp │ ├── ClangVarWrapper.cpp │ ├── DefaultIR.cpp │ ├── DefaultIRBuilder.cpp │ ├── DefaultStmtBuilder.cpp │ ├── DefaultVarBuilder.cpp │ └── NopStmt.cpp ├── language │ ├── CPPMethod.cpp │ ├── DefaultTypeBuilder.cpp │ └── Type.cpp └── util │ └── Logger.cpp ├── resources ├── dataflow │ ├── ConstPropagation │ │ └── ConstPropagationTest.cpp │ ├── LiveVar │ │ └── LiveVarTest.cpp │ └── ReachDef │ │ └── ReachDefTest.cpp ├── example01 │ ├── include │ │ ├── factor.h │ │ └── fib.h │ └── src │ │ ├── factor │ │ └── factor.cpp │ │ ├── fib │ │ └── fib.cpp │ │ └── main.cpp └── example02 │ ├── dump.sh │ ├── foo.h │ └── main.cpp ├── tests ├── CMakeLists.txt ├── TestCPPMethod.cpp ├── TestConstantPropagation.cpp ├── TestDataflowFacts.cpp ├── TestIR.cpp ├── TestLiveVariable.cpp ├── TestReachingDefinition.cpp ├── TestWorld.cpp ├── doctest.h └── main.cpp └── tools ├── CLI11.h ├── CMakeLists.txt ├── constant-propagation-analyzer.cpp ├── live-variable-analyzer.cpp └── reaching-definition-analyzer.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug 2 | build 3 | .DS_Store 4 | .cache 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | cpp-static-analyzer -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/myanalyzer.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(cpp-static-analyzer 4 | VERSION 1.1.0 5 | DESCRIPTION "This is a simple cpp static analysis framework." 6 | ) 7 | 8 | set(CMAKE_CXX_STANDARD 17) # the standard used in this project and llvm 9 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") # add for those build llvm without rtti 10 | 11 | find_package(LLVM REQUIRED CONFIG) 12 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 13 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 14 | 15 | find_package(Clang REQUIRED CONFIG) 16 | message(STATUS "Found Clang ${LLVM_VERSION}") 17 | message(STATUS "Using ClangConfig.cmake in ${Clang_DIR}") 18 | 19 | include_directories(${LLVM_INCLUDE_DIRS}) 20 | link_directories(${LLVM_LIBRARY_DIRS}) 21 | 22 | separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) 23 | add_definitions(${LLVM_DEFINITIONS_LIST}) 24 | 25 | llvm_map_components_to_libnames(LLVM_LIBS support frontendopenmp) 26 | 27 | # define CLANG_LIBS: some clang libs to be used in link time 28 | # !!! DO NOT change the order of the libs !!! 29 | # libs will be linked in this order, and changing the order could lead to link errors 30 | set(CLANG_LIBS 31 | clangTooling 32 | clangFrontendTool 33 | clangFrontend 34 | clangDriver 35 | clangSerialization 36 | clangCodeGen 37 | clangParse 38 | clangSema 39 | clangStaticAnalyzerFrontend 40 | clangStaticAnalyzerCheckers 41 | clangStaticAnalyzerCore 42 | clangAnalysis 43 | clangARCMigrate 44 | clangRewriteFrontend 45 | clangEdit 46 | clangAST 47 | clangASTMatchers 48 | clangLex 49 | clangBasic 50 | ) 51 | 52 | include_directories(include) 53 | 54 | add_subdirectory(lib) 55 | add_subdirectory(tools) 56 | 57 | enable_testing() 58 | add_subdirectory(tests) 59 | 60 | add_subdirectory(docs) 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Simple CPP Static Analysis Framework 2 | 3 | > Copyright (C) 2023 Jiacai Cui <201220014@smail.nju.edu.cn> 4 | 5 | ## Introduction 6 | 7 | This is a simple cpp static analysis framework developed during 8 | a software engineering lab course of Nanjing University. 9 | 10 | The project is personal and distributed 11 | under [GNU general public license](LICENSE). 12 | 13 | The design of this project is well inspired by 14 | [tai-e](https://github.com/pascal-lab/Tai-e.git), 15 | a static analysis framework for java, please view 16 | its [technique report](https://arxiv.org/abs/2208.00337) 17 | for more details. 18 | 19 | ## Environment Setup 20 | 21 | This cpp static analyzer uses clang as its front end. 22 | So you need to install llvm and clang on your local 23 | system inorder to build and run this project. 24 | It is developed under [llvm](https://llvm.org/) 25 | 16.0.2, tested under llvm 16.0.2 and 17.0.0, 26 | but more recent versions should also be ok. 27 | 28 | It is recommended to install llvm using precompiled binaries 29 | instead of building from source manually. 30 | 31 | Here is the way to set up the proper environment of this 32 | project. 33 | 34 | ### On macOS 35 | 36 | Use [homebrew](https://brew.sh/) as the package manager, run 37 | 38 | ```shell 39 | brew install cmake ninja llvm doxygen 40 | ``` 41 | 42 | Then, check your installed version of cmake, ninja, llvm, and clang by 43 | 44 | ```shell 45 | cmake --version 46 | ninja --version 47 | llvm-config --version 48 | clang --version 49 | doxygen --version 50 | ``` 51 | 52 | ### On Ubuntu 22.04 53 | 54 | Using [llvm apt source](https://apt.llvm.org/), run 55 | 56 | ```shell 57 | sudo apt update 58 | sudo apt install lsb-release wget software-properties-common gnupg zlib1g zlib1g-dev git cmake ninja-build build-essential doxygen graphviz 59 | wget https://apt.llvm.org/llvm.sh 60 | chmod +x llvm.sh 61 | ./llvm.sh 17 all 62 | ``` 63 | 64 | Then, check your installed version of cmake, ninja, llvm, and clang by 65 | 66 | ```shell 67 | cmake --version 68 | ninja --version 69 | clang-17 --version 70 | llvm-config-17 --version 71 | doxygen --version 72 | ``` 73 | 74 | ## Get the Project 75 | 76 | ```shell 77 | git clone https://github.com/JacyCui/cpp-static-analyzer.git 78 | ``` 79 | 80 | ### Build 81 | 82 | In the project root directory, run 83 | 84 | ```shell 85 | mkdir build 86 | cd build 87 | cmake -G=Ninja .. 88 | ninja 89 | ``` 90 | 91 | ### Run Tests 92 | 93 | After compiling, **in the project root directory**, run 94 | 95 | ```shell 96 | ./build/tests/tests 97 | ``` 98 | 99 | And then, you should see something like below, which means the **627** 100 | testing assertions are passed. 101 | 102 | ```shell 103 | # a lot of log information here ... 104 | =============================================================================== 105 | [doctest] test cases: 35 | 35 passed | 0 failed | 0 skipped 106 | [doctest] assertions: 627 | 627 passed | 0 failed | 107 | [doctest] Status: SUCCESS! 108 | ``` 109 | 110 | ### Run the Example Dataflow Analysis 111 | 112 | After compiling, **in the project root directory**, run 113 | 114 | ```shell 115 | ./build/tools/reaching-definition-analyzer --source-dir=resources/dataflow/ReachDef 116 | ``` 117 | 118 | This will run the reaching definition analysis 119 | for all source files in the `resources/dataflow/ReachDef` directory. 120 | 121 | ```shell 122 | ./build/tools/reaching-definition-analyzer --help 123 | A Simple CPP Reaching Definition Static Analyzer 124 | Copyright (c) 2023-2023 125 | Usage: ./build/tools/reaching-definition-analyzer/reaching-definition-analyzer [OPTIONS] 126 | 127 | Options: 128 | -h,--help Print this help message and exit 129 | -S,--source-dir TEXT REQUIRED 130 | directory of all source files 131 | -I,--include-dir TEXT directory of all header files 132 | --std,--standard TEXT c++ language standard (support all standards that clang supports) 133 | ``` 134 | 135 | Similarly, **in the project root directory**, run 136 | 137 | ```shell 138 | ./build/tools/live-variable-analyzer --source-dir=resources/dataflow/LiveVar 139 | ``` 140 | 141 | This will run the live variable analysis 142 | for all source files in the `resources/dataflow/LiveVar` directory. 143 | 144 | ```shell 145 | ./build/tools/live-variable-analyzer --help 146 | A Simple CPP Live Variable Static Analyzer 147 | Copyright (c) 2023-2023 148 | Usage: ./build/tools/live-variable-analyzer [OPTIONS] 149 | 150 | Options: 151 | -h,--help Print this help message and exit 152 | -S,--source-dir TEXT REQUIRED 153 | directory of all source files 154 | -I,--include-dir TEXT directory of all header files 155 | --std,--standard TEXT c++ language standard (support all standards that clang supports) 156 | ``` 157 | 158 | ## How to use it as a library in your project 159 | 160 | ### Linking Steps 161 | 162 | **Step01**: Take this repository as a submodule of your project repository. 163 | 164 | ```shell 165 | git submodule add https://github.com/JacyCui/cpp-static-analyzer.git path/to/put/this/project 166 | ``` 167 | 168 | **Step02**: Link to `libanalyzer` in you `CMakeLists.txt`. 169 | 170 | ```cmake 171 | # suppose your target is called your_target 172 | 173 | add_subdirectory(path/to/put/this/project) 174 | 175 | target_include_directories(your_target 176 | PUBLIC path/to/put/this/project/include 177 | ) 178 | 179 | target_link_libraries(your_target 180 | libanalyzer 181 | ) 182 | ``` 183 | 184 | **Step03**: Use APIs provided [here](https://jacycui.github.io/cpp-static-analyzer/) in your source code. 185 | An example usage is provided in the [test of reaching definition](tests/TestReachingDefinition.cpp). 186 | 187 | 188 | ### Get the API Documentation 189 | 190 | You can build the html doxygen api documentation locally 191 | **in the build directory** by running 192 | 193 | ```shell 194 | # in the build directory 195 | ninja libanalyzer-api-doc 196 | ``` 197 | 198 | And you'll find your html documentation located at `build/docs/api-doc/html` directory. 199 | 200 | You can read it by opening `build/docs/api-doc/html/index.html` in your local web browser. 201 | 202 | Note that the documentation is not included in the default build target. 203 | 204 | You must build it explicitly like above if you need to read it. 205 | 206 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Doxygen 2 | REQUIRED dot 3 | OPTIONAL_COMPONENTS mscgen dia) 4 | 5 | set(DOXYGEN_OUTPUT_DIRECTORY api-doc) 6 | 7 | doxygen_add_docs(libanalyzer-api-doc 8 | ${PROJECT_SOURCE_DIR}/include 9 | ${PROJECT_SOURCE_DIR}/README.md 10 | ${PROJECT_SOURCE_DIR}/docs 11 | COMMENT "Generate html documentation of libanalyzer." 12 | ) 13 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | ## Commit Message Convention 4 | 5 | Grammar for commit messages: 6 | 7 | ```text 8 | : 9 | 10 | [optional body] 11 | ``` 12 | 13 | The commit type `` can include the following: 14 | 15 | - `feat` – a new feature is introduced with the changes 16 | - `fix` – a bug fix has occurred 17 | - `chore` – changes that do not relate to a fix or feature and don't modify src or test files (for example updating dependencies) 18 | - `refactor` – refactored code that neither fixes a bug nor adds a feature 19 | - `docs` – updates to documentation such as the README or other markdown files 20 | - `style` – changes that do not affect the meaning of the code, likely related to code formatting such as white-space, missing semi-colons, and so on. 21 | - `test` – including new or correcting previous tests 22 | - `perf` – performance improvements 23 | - `ci` – continuous integration related 24 | - `build` – changes that affect the build system or external dependencies 25 | - `revert` – reverts a previous commit 26 | 27 | The commit type should be all lowercase and limited to the above 11 types. `` should be a brief description of the commit and should not exceed one line. If you have more to describe, put them in 28 | the optional body. 29 | 30 | ## Comment Convention 31 | 32 | Doxygen recognizable comments should be provided for each class and methods declared in the header files. 33 | 34 | Method that overrides a base class method can just inherit comments in the base class. 35 | 36 | You can take `include/World.h` as an example. 37 | -------------------------------------------------------------------------------- /docs/DataflowAnalysis.md: -------------------------------------------------------------------------------- 1 | # Dataflow Analysis 2 | 3 | > You should first read the tutorial about program representation before proceeding to this guide. 4 | 5 | 6 | 7 | ## High Level Overview of an Analysis 8 | 9 | An analysis is an algorithm that takes a program and some configuration as input and returns some results. Typically, results are bound to statements. 10 | 11 | ```mermaid 12 | --- 13 | title: Top Interfaces for an Analsis 14 | --- 15 | classDiagram 16 | class Analysis { 17 | <> 18 | +getAnalysisConfig() AnalysisConfig 19 | } 20 | 21 | class AnalysisConfig { 22 | <> 23 | +getDescription()* String 24 | } 25 | 26 | note for MethodAnalysis "R is a generic result type of some analysis" 27 | 28 | class MethodAnalysis~R~ { 29 | <> 30 | +analyze(IR)* R 31 | } 32 | 33 | Analysis <|-- MethodAnalysis 34 | Analysis o-- AnalysisConfig 35 | 36 | note for StmtResult "R is a generic result type for a single statement" 37 | 38 | class StmtResult~R~ { 39 | <> 40 | +isRelevant(Stmt)* bool 41 | +getResult(Stmt)* R 42 | } 43 | 44 | MethodAnalysis --> StmtResult 45 | ``` 46 | 47 | You can easily create your own classes to override these interfaces in order to develop your own analysis. 48 | 49 | 50 | 51 | ## Dataflow Analysis Framework 52 | 53 | This project provide a complete and easy-to-use framework to develop dataflow analysis. 54 | 55 | ```mermaid 56 | --- 57 | title: Dataflow Analysis Framework 58 | --- 59 | classDiagram 60 | class MethodAnalysis~R~ { 61 | <> 62 | +analyze(IR)* R 63 | } 64 | 65 | class AnalysisDriver { 66 | <> 67 | +analyze(IR) DataflowResult 68 | #makeAnalysis()* DataflowAnalysis 69 | } 70 | 71 | MethodAnalysis <|-- AnalysisDriver 72 | AnalysisDriver --> Solver 73 | AnalysisDriver --> DataflowAnalysis 74 | AnalysisDriver --> DataflowResult 75 | 76 | class Solver { 77 | <> 78 | +solve(DataflowAnalysis)* DataflowResult 79 | } 80 | 81 | class AbstractSolver { 82 | <> 83 | +solve(DataflowAnalysis) DataflowResult 84 | #initializeForward(DataflowAnalysis, DataflowResult) 85 | #initializeBackward(DataflowAnalysis, DataflowResult) 86 | #doSolveForward(DataflowAnalysis, DataflowResult)* 87 | #doSolveBackward(DataflowAnalysis, DataflowResult)* 88 | } 89 | 90 | Solver <|-- AbstractSolver 91 | 92 | class DataflowAnalysis { 93 | <> 94 | +isForward()* bool 95 | +newBoundaryFact()* Fact 96 | +newInitialFact()* Fact 97 | +meetInto(Fact fact, Fact target)* 98 | +transferNode(Stmt, Fact in, Fact out)* bool 99 | +getCFG() CFG 100 | } 101 | 102 | NodeResult~Fact~ <|-- DataflowResult~Fact~ 103 | 104 | class NodeResult~Fact~ { 105 | +getInFact(Stmt)* Fact 106 | +getOutFact(Stmt)* Fact 107 | } 108 | 109 | class DataflowResult~Fact~ { 110 | +getInFact(Stmt) Fact 111 | +getOutFact(Stmt) Fact 112 | +setInFact(Stmt) Fact 113 | +setOutFact(Stmt) Fact 114 | } 115 | ``` 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/ProgramRepresentation.md: -------------------------------------------------------------------------------- 1 | # Program Representation 2 | 3 | 4 | 5 | > It is highly recommended that you generate API documentation first accroding to project `README` before working through this tutorial. The *API documentation* mentioned below all refers to the one generated according to project `README`. 6 | 7 | 8 | 9 | ## World 10 | 11 | ```cpp 12 | #include "World.h" 13 | 14 | namespace al=analyzer; 15 | namespace lang=al::language; 16 | 17 | al::World::initialize("resources/example01/src", "resources/example01/include"); 18 | const World& world = al::World::get(); 19 | ``` 20 | 21 | All resources about the program representation is managed by the only singleton `World` object, which can be accessed through `get` static method of `World` class. And this `world` object is immutable. 22 | 23 | A CPP program is divided into several `CPPMethod` objects when the `World` is initialized. You can get them through a bunch of methods provided by the `world` object. Several ways to get cpp methods through `world` are listed below. 24 | 25 | ```cpp 26 | const std::unordered_map>& getAllMethods() const; 27 | std::shared_ptr getMethodBySignature(const std::string& signature) const; 28 | std::shared_ptr getMainMethod() const; 29 | ``` 30 | 31 | Refer to API documentation for more information about these methods. 32 | 33 | 34 | 35 | ## CPPMethod 36 | 37 | A `CPPMethod` object contains all information that you'll need to perform an intra-procedural analysis or build a whole-program analysis. There's also a bunch of convenient methods provided in this class. Frequently used methods are listed below. 38 | 39 | ```cpp 40 | const std::string& getMethodSignatureAsString() const; 41 | std::string getMethodSourceCode() const; 42 | std::string getContainingFilePath() const; 43 | std::size_t getParamCount() const; 44 | std::shared_ptr getParamType(std::size_t i) const; 45 | const std::string& getParamName(std::size_t i) const; 46 | std::shared_ptr getReturnType() const; 47 | std::shared_ptr getIR(); 48 | bool isGlobalMethod() const; 49 | bool isClassStaticMethod() const; 50 | bool isClassMemberMethod() const; 51 | bool isVirtual() const; 52 | ``` 53 | 54 | Refer to API documentation for more detailed information about these methods. 55 | 56 | The most important method above is `getIR`, whose first call triggers the build of intermediate representation of this method using the `IRBuilder` provided by `world`. 57 | 58 | 59 | 60 | ## IR and CFG 61 | 62 | IR and CFG are basic infrastructures for most static analysis algorithms. The Construction of them are based on the interfaces below. 63 | 64 | ```mermaid 65 | --- 66 | title: How World Supports IR and CFG building 67 | --- 68 | classDiagram 69 | class World { 70 | +getTypeBuilder() TypeBuilder 71 | +getIRBuilder() IRBuilder 72 | +getVarBuilder() VarBuilder 73 | +getStmtBuilder() StmtBuilder 74 | +......() 75 | } 76 | 77 | class TypeBuilder { 78 | <> 79 | +buildType(ClangQualType)* Type 80 | } 81 | 82 | class IRBuilder { 83 | <> 84 | +buildIR(CPPMethod)* IR 85 | } 86 | 87 | class StmtBuilder { 88 | <> 89 | +buildStmt(CPPMethod, ClangStmt, Map~ClangVarDecl, Var~)* Stmt 90 | } 91 | 92 | class VarBuilder { 93 | <> 94 | +buildVar(CPPMethod, ClangVarDecl)* Var 95 | } 96 | 97 | World o-- TypeBuilder 98 | World o-- IRBuilder 99 | World o-- StmtBuilder 100 | World o-- VarBuilder 101 | 102 | class Type { 103 | <> 104 | +getName()*: String 105 | } 106 | 107 | class IR { 108 | <> 109 | +getMethod()* CPPMethod 110 | +getCFG()* CFG 111 | +getParams()* List~Var~ 112 | +getVars()* List~Var~ 113 | +getStmts()* List~Stmt~ 114 | } 115 | 116 | class Stmt { 117 | <> 118 | +getStartLine()* int 119 | +getEndLine()* int 120 | +getStartColumn()* int 121 | +getEndColumn()* int 122 | +getMethod()* CPPMethod 123 | +getDefs()* Set~Var~ 124 | +getUses()* Set~Var~ 125 | +str()* String 126 | } 127 | 128 | class Var { 129 | <> 130 | +getMethod()* CPPMethod 131 | +getName()* String 132 | +getType()* Type 133 | } 134 | 135 | class CFG { 136 | <> 137 | +getEntry()* Stmt 138 | +getExit()* Stmt 139 | +hasEdge(Stmt source, Stmt target)* bool 140 | +getPredsOf(Stmt)* Set~Stmt~ 141 | +getSuccsOf(Stmt)* Set~Stmt~ 142 | +getInEdgesOf(Stmt)* Set~CFGEdge~ 143 | +getOutEdgesOf(Stmt)* Set~CFGEdge~ 144 | +getIR()* IR 145 | +getEdgeNum()* int 146 | } 147 | 148 | IRBuilder <.. StmtBuilder 149 | IRBuilder <.. VarBuilder 150 | IRBuilder <.. TypeBuilder 151 | 152 | TypeBuilder --> Type 153 | IRBuilder --> IR 154 | StmtBuilder --> Stmt 155 | VarBuilder --> Var 156 | IR <.. CFG 157 | ``` 158 | 159 | All the above interfaces have a default implementation. 160 | 161 | Since they are all interfaces and they all rely on interfaces, you can easily substitute my default implementation by your own implementation of one part without having to change any other part. 162 | 163 | 164 | 165 | ## How to get more details 166 | 167 | The code of this project is well commented, documented and tested, so that the best way to get familiar with this project is **reading the tests** and meanwhile **seaching through header files and api documentations**. 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /include/World.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_WORLD_H 2 | #define STATIC_ANALYZER_WORLD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "language/CPPMethod.h" 13 | #include "ir/IR.h" 14 | #include "util/Logger.h" 15 | 16 | namespace analyzer { 17 | 18 | namespace lang = language; 19 | 20 | /** 21 | * @class WorldBuilder 22 | * @brief Interface for the world builder 23 | */ 24 | class WorldBuilder { 25 | public: 26 | 27 | /** 28 | * @brief build the world 29 | */ 30 | virtual void build() = 0; 31 | 32 | virtual ~WorldBuilder() = default; 33 | 34 | }; 35 | 36 | /** 37 | * @class World 38 | * @brief Manages the whole-program information of the program being analyzed. 39 | */ 40 | class World final: public WorldBuilder { 41 | public: 42 | 43 | /** 44 | * @brief Initialize the word the whole program, including ast, cfg and call graph 45 | * @param sourceDir the directory path of all source files 46 | * @param includeDir the directory path of all 47 | * @param std language standard, e.g. c++98, c++11, c99 48 | * @param optArgs optional compilation arguments, such as -D__MACRO__ etc. 49 | */ 50 | static void initialize(const std::string& sourceDir, const std::string& includeDir="", 51 | const std::string& std=std::string("c++98"), 52 | const std::vector& optArgs={}); 53 | 54 | /** 55 | * @brief must be called after calling {@code initialize} 56 | * @return const reference to the world instance created by {@code initialize} 57 | */ 58 | [[nodiscard]] static const World& get(); 59 | 60 | /** 61 | * @return the current logger of the entire world 62 | */ 63 | [[nodiscard]] static util::Logger& getLogger(); 64 | 65 | /** 66 | * @param newLogger a new logger to be used from now on 67 | */ 68 | static void setLogger(util::Logger newLogger); 69 | 70 | public: 71 | 72 | void build() override; 73 | 74 | /** 75 | * @return a map from source file path to source file content 76 | */ 77 | [[nodiscard]] const std::unordered_map& getSourceCode() const; 78 | 79 | /** 80 | * @return the ASTUnit list of the whole program 81 | */ 82 | [[nodiscard]] const std::vector>& getAstList() const; 83 | 84 | /** 85 | * @brief pretty dump asts of all source codes in the world into a stream 86 | * @param[out] out the llvm raw out stream (e.g. outs(), errs() or other user defined streams) 87 | */ 88 | void dumpAST(llvm::raw_ostream& out) const; 89 | 90 | /** 91 | * @brief pretty dump asts of a given source file 92 | * @param fileName relative path of the source file from current working directory 93 | * @param[out] out the llvm raw out stream (e.g. outs(), errs() or other user defined streams) 94 | */ 95 | void dumpAST(const std::string& fileName, llvm::raw_ostream& out) const; 96 | 97 | /** 98 | * @return return a vector of all methods in the program 99 | */ 100 | [[nodiscard]] const std::unordered_map>& getAllMethods() const; 101 | 102 | /** 103 | * @brief get the cpp method by it's method 104 | * @param signature signature string (e.g. int add(int, int)) 105 | * @return a cpp method, nullptr if it doesn't exist 106 | */ 107 | [[nodiscard]] std::shared_ptr getMethodBySignature(const std::string& signature) const; 108 | 109 | /** 110 | * @return return the main method (nullptr if there's no main method) 111 | */ 112 | [[nodiscard]] std::shared_ptr getMainMethod() const; 113 | 114 | /** 115 | * @return the global ir builder of this world 116 | */ 117 | [[nodiscard]] const std::unique_ptr& getIRBuilder() const; 118 | 119 | /** 120 | * @return the global type builder of this world 121 | */ 122 | [[nodiscard]] const std::unique_ptr& getTypeBuilder() const; 123 | 124 | /** 125 | * @return the global variable builder of this world 126 | */ 127 | [[nodiscard]] const std::unique_ptr& getVarBuilder() const; 128 | 129 | /** 130 | * @return the global statement builder of this world 131 | */ 132 | [[nodiscard]] const std::unique_ptr& getStmtBuilder() const; 133 | 134 | private: 135 | 136 | static World* theWorld; ///< the only world instance in the program 137 | 138 | static util::Logger logger; ///< the logger for the whole world 139 | 140 | private: 141 | 142 | std::unordered_map sourceCode; ///< sourcefile name -> sourcefile content 143 | 144 | std::vector args; ///< compiler arguments 145 | 146 | std::vector> astList; ///< asts of a program 147 | 148 | std::unordered_map> allMethods; ///< all cpp methods in the program 149 | 150 | std::shared_ptr mainMethod; ///< main method 151 | 152 | std::unique_ptr irBuilder; ///< global ir builder 153 | 154 | std::unique_ptr typeBuilder; ///< global type builder 155 | 156 | std::unique_ptr varBuilder; ///< global variable builder 157 | 158 | std::unique_ptr stmtBuilder; ///< global statement builder 159 | 160 | /** 161 | * @brief Construct the world 162 | * @param sourceCode cpp or c source file and corresponding code string 163 | * @param args compiler arguments 164 | */ 165 | World(std::unordered_map&& sourceCode, 166 | std::vector&& args); 167 | 168 | /** 169 | * @brief build a map from method signature to CPPMethod 170 | */ 171 | void buildMethodMap(); 172 | 173 | public: 174 | 175 | World(const World&) = delete; 176 | 177 | World& operator=(const World&) = delete; 178 | 179 | }; 180 | 181 | /** 182 | * @brief get c/cpp source codes recursively from a source file directory 183 | * @param sourceDir the directory containing all the source files 184 | * @return a map from filename(relative, end with .c / .cpp / .cxx / .cc) to its contents 185 | */ 186 | [[nodiscard]] std::unordered_map loadSourceCodes(const std::string& sourceDir); 187 | 188 | namespace language { 189 | 190 | /** 191 | * @brief generate string signature of a function decl ast 192 | * @param functionDecl function declaration ast 193 | * @return a string representation of signature 194 | */ 195 | [[nodiscard]] std::string generateFunctionSignature(const clang::FunctionDecl* functionDecl); 196 | 197 | } 198 | 199 | } // analyzer 200 | 201 | #endif //STATIC_ANALYZER_WORLD_H 202 | -------------------------------------------------------------------------------- /include/analysis/Analysis.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_ANALYSIS_H 2 | #define STATIC_ANALYZER_ANALYSIS_H 3 | 4 | #include 5 | 6 | #include "config/AnalysisConfig.h" 7 | #include "ir/IR.h" 8 | 9 | namespace analyzer::analysis { 10 | 11 | /** 12 | * @class Analysis 13 | * @brief abstract base class for all analysis 14 | */ 15 | class Analysis { 16 | public: 17 | 18 | [[nodiscard]] const std::unique_ptr& getAnalysisConfig() const; 19 | 20 | virtual ~Analysis() = default; 21 | 22 | protected: 23 | 24 | /** 25 | * @brief construct an analysis from its configuration 26 | * @param analysisConfig a unique configuration object 27 | */ 28 | explicit Analysis(std::unique_ptr& analysisConfig) 29 | :analysisConfig(std::move(analysisConfig)) 30 | { 31 | 32 | } 33 | 34 | std::unique_ptr analysisConfig; ///< the configuration of this analysis 35 | 36 | }; 37 | 38 | /** 39 | * @class MethodAnalysis 40 | * @brief abstract base class for all method analyses, i.e. intra-procedural analyses. 41 | * @tparam R the result type 42 | */ 43 | template 44 | class MethodAnalysis: public Analysis { 45 | public: 46 | 47 | /** 48 | * @brief analyze an intermediate representation to get the result 49 | * @param myIR the ir to be analyzed 50 | * @return the method analysis result 51 | */ 52 | [[nodiscard]] virtual std::shared_ptr analyze(std::shared_ptr myIR) = 0; 53 | 54 | protected: 55 | 56 | explicit MethodAnalysis(std::unique_ptr& analysisConfig) 57 | :Analysis(analysisConfig) 58 | { 59 | 60 | } 61 | 62 | }; 63 | 64 | } // analysis 65 | 66 | #endif //STATIC_ANALYZER_ANALYSIS_H 67 | -------------------------------------------------------------------------------- /include/analysis/AnalysisResult.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_ANALYSISRESULT_H 2 | #define STATIC_ANALYZER_ANALYSISRESULT_H 3 | 4 | #include "ir/Stmt.h" 5 | 6 | namespace analyzer::analysis { 7 | 8 | /** 9 | * @class StmtResult 10 | * @brief An interface for querying analysis results of Stmt. 11 | * @tparam R type of analysis results 12 | */ 13 | template 14 | class StmtResult { 15 | public: 16 | 17 | /** 18 | * @brief judge the relevance of a given statement 19 | * @param stmt a statement 20 | * @return true if the stmt is relevant to this result 21 | */ 22 | [[nodiscard]] virtual bool isRelevant(std::shared_ptr stmt) const = 0; 23 | 24 | /** 25 | * @brief get the result of a given statement 26 | * @param stmt statement to get result 27 | * @return the analysis result of stmt, nullptr if it doesn't exist 28 | */ 29 | [[nodiscard]] virtual std::shared_ptr getResult(std::shared_ptr stmt) const = 0; 30 | 31 | virtual ~StmtResult() = default; 32 | 33 | }; 34 | 35 | } 36 | 37 | #endif //STATIC_ANALYZER_ANALYSISRESULT_H 38 | -------------------------------------------------------------------------------- /include/analysis/dataflow/AnalysisDriver.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_ANALYSISDRIVER_H 2 | #define STATIC_ANALYZER_ANALYSISDRIVER_H 3 | 4 | #include "analysis/Analysis.h" 5 | 6 | #include "analysis/dataflow/solver/Solver.h" 7 | 8 | namespace analyzer::analysis::dataflow { 9 | 10 | /** 11 | * @brief an abstract analysis driver for all dataflow analysis 12 | * @class AnalysisDriver 13 | * @tparam Fact the dataflow fact type 14 | */ 15 | template 16 | class AnalysisDriver: public MethodAnalysis> { 17 | public: 18 | 19 | /** 20 | * @brief analyze the given ir to get the dataflow result 21 | * @param myIR the ir to be analyzed 22 | * @return the dataflow result 23 | */ 24 | [[nodiscard]] std::shared_ptr> 25 | analyze(std::shared_ptr myIR) override 26 | { 27 | World::getLogger().Progress("Start dataflow analysis: " + this->analysisConfig->getDescription()); 28 | std::shared_ptr cfg = myIR->getCFG(); 29 | 30 | World::getLogger().Info("Getting dataflow analysis algorithm ..."); 31 | std::unique_ptr> dataflowAnalysis = makeAnalysis(cfg); 32 | 33 | World::getLogger().Info("Getting dataflow analysis solver (worklist solver by default) ..."); 34 | std::unique_ptr> mySolver = solver::makeSolver(); 35 | 36 | World::getLogger().Info("Solving the dataflow analysis ..."); 37 | std::shared_ptr> result = mySolver->solve(dataflowAnalysis); 38 | 39 | World::getLogger().Success("Finish dataflow analysis: " + this->analysisConfig->getDescription()); 40 | return result; 41 | } 42 | 43 | protected: 44 | 45 | /** 46 | * @brief Creates an analysis object for given cfg. 47 | * @param cfg a control flow graph 48 | * @return a dataflow analysis object 49 | */ 50 | virtual std::unique_ptr> 51 | makeAnalysis(const std::shared_ptr& cfg) const = 0; 52 | 53 | /** 54 | * @brief Construct an analysis driver from analysis config 55 | * @param analysisConfig the configuration of this analysis 56 | */ 57 | explicit AnalysisDriver(std::unique_ptr& analysisConfig) 58 | :MethodAnalysis>(analysisConfig) 59 | { 60 | 61 | } 62 | 63 | }; 64 | 65 | } // dataflow 66 | 67 | #endif //STATIC_ANALYZER_ANALYSISDRIVER_H 68 | -------------------------------------------------------------------------------- /include/analysis/dataflow/ConstantPropagation.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_CONSTANTPROPAGATION_H 2 | #define STATIC_ANALYZER_CONSTANTPROPAGATION_H 3 | 4 | #include 5 | #include 6 | 7 | #include "analysis/dataflow/AnalysisDriver.h" 8 | #include "analysis/dataflow/fact/MapFact.h" 9 | #include "llvm/IR/Constants.h" 10 | 11 | namespace analyzer::analysis::dataflow { 12 | 13 | /** 14 | * @class CPValue 15 | * @brief constant propagation value 16 | */ 17 | class CPValue { 18 | public: 19 | 20 | /** 21 | * @return the Undefined value of constant propagation 22 | */ 23 | [[nodiscard]] static std::shared_ptr getUndef(); 24 | 25 | /** 26 | * @return the Not A Constant value of constant propagation 27 | */ 28 | [[nodiscard]] static std::shared_ptr getNAC(); 29 | 30 | /** 31 | * @brief make a constant value for constant propagation 32 | * @param constantValue the constant value 33 | * @return the constant value for constant propagation 34 | */ 35 | [[nodiscard]] static std::shared_ptr makeConstant(const llvm::APSInt &constantValue); 36 | 37 | public: 38 | 39 | /** 40 | * @class CPValueType 41 | * @brief constant propagation value kind 42 | */ 43 | enum class Kind { 44 | UNDEF, ///< undefined value for const propagation 45 | NAC, ///< Not A Constant value for const propagation 46 | CONSTANT ///< constant value for const propagation 47 | }; 48 | 49 | /** 50 | * @brief constructor for constant propagation value 51 | * @param kind the constant propagation value type 52 | * @param constantValue the constant value 53 | */ 54 | explicit CPValue(Kind kind, llvm::APSInt constantValue = llvm::APSInt()); 55 | 56 | /** 57 | * @return true if the constant propagation value is undefined, false otherwise 58 | */ 59 | [[nodiscard]] bool isUndef() const; 60 | 61 | /** 62 | * @return true if the constant propagation value is not a constant, false otherwise 63 | */ 64 | [[nodiscard]] bool isNAC() const; 65 | 66 | /** 67 | * @return true if the constant propagation value is a constant, false otherwise 68 | */ 69 | [[nodiscard]] bool isConstant() const; 70 | 71 | /** 72 | * @return the constant value 73 | */ 74 | [[nodiscard]] const llvm::APSInt& getConstantValue() const; 75 | 76 | /** 77 | * @return the string representation of this constant propagation value 78 | */ 79 | [[nodiscard]] std::string str() const; 80 | 81 | /** 82 | * @brief operator== for constant propagation value 83 | * @param other the other constant propagation value 84 | * @return true if the constant propagation value is equal to the other constant propagation value, false otherwise 85 | */ 86 | bool operator==(const CPValue& other) const { 87 | return kind == other.kind && constantValue == other.constantValue; 88 | } 89 | 90 | private: 91 | 92 | static std::shared_ptr Undef, NAC; ///< two special constant propagation value 93 | 94 | private: 95 | 96 | Kind kind; ///< constant propagation value kind 97 | 98 | llvm::APSInt constantValue; ///< constant value 99 | 100 | }; 101 | 102 | /** 103 | * @class CPFact 104 | * @brief constant propagation fact 105 | */ 106 | class CPFact: public fact::MapFact { 107 | public: 108 | 109 | /** 110 | * @brief get the CPValue of a given var 111 | * @param key the var to be searched 112 | * @return the CPValue to which the specified key is mapped, 113 | * or Undef if this map contains no mapping for the given var 114 | */ 115 | [[nodiscard]] std::shared_ptr get(const std::shared_ptr& key) const override; 116 | 117 | /** 118 | * @brief Updates the key-value mapping in this fact. 119 | * @param key the var to update 120 | * @param value the CPValue to be bound to the var 121 | * @return true if the update changes this fact, otherwise 122 | */ 123 | bool update(const std::shared_ptr& key, const std::shared_ptr& value) override; 124 | 125 | }; 126 | 127 | 128 | /** 129 | * @class CPResult 130 | * @brief constant propagation result 131 | */ 132 | class CPResult : public fact::DataflowResult { 133 | public: 134 | 135 | /** 136 | * @brief constructor for constant propagation result 137 | */ 138 | explicit CPResult(); 139 | 140 | /** 141 | * @brief update the constant propagation value of a given clang expr 142 | * @param expr the clang expr to be updated 143 | * @param value the constant propagation value to be bound to the clang expr 144 | */ 145 | void updateExprValue(const clang::Expr* expr, const std::shared_ptr& value); 146 | 147 | /** 148 | * @brief get the constant propagation value of a given clang expr 149 | * @param expr the clang expr to be searched 150 | * @return the constant propagation value to which the specified clang expr is mapped, 151 | * or nullptr if this map contains no mapping for the given clang expr 152 | */ 153 | [[nodiscard]] std::shared_ptr getExprValue(const clang::Expr* expr) const; 154 | 155 | private: 156 | 157 | std::unordered_map> 158 | exprValues; ///< map from clang expression to constant propagation dataflow value 159 | 160 | }; 161 | 162 | /** 163 | * @class ConstantPropagation 164 | * @brief constant propagation analysis 165 | */ 166 | class ConstantPropagation: public AnalysisDriver { 167 | public: 168 | 169 | /** 170 | * @brief constructor for constant propagation analysis 171 | * @param analysisConfig the analysis configuration 172 | */ 173 | explicit ConstantPropagation(std::unique_ptr& analysisConfig); 174 | 175 | protected: 176 | 177 | [[nodiscard]] std::unique_ptr> 178 | makeAnalysis(const std::shared_ptr& cfg) const override; 179 | 180 | }; 181 | 182 | } // dataflow 183 | 184 | 185 | #endif //STATIC_ANALYZER_CONSTANTPROPAGATION_H 186 | -------------------------------------------------------------------------------- /include/analysis/dataflow/DataflowAnalysis.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_DataflowAnalysis_H 2 | #define STATIC_ANALYZER_DataflowAnalysis_H 3 | 4 | #include "World.h" 5 | #include "analysis/graph/CFG.h" 6 | #include "analysis/Analysis.h" 7 | #include "analysis/dataflow/fact/DataflowResult.h" 8 | 9 | namespace analyzer::analysis::dataflow { 10 | 11 | /** 12 | * @brief Template interface for defining data-flow analysis. 13 | * @tparam Fact type of data-flow facts 14 | */ 15 | template 16 | class DataflowAnalysis { 17 | public: 18 | 19 | /** 20 | * @return true if this analysis is forward, otherwise false. 21 | */ 22 | [[nodiscard]] virtual bool isForward() const = 0; 23 | 24 | /** 25 | * @return new fact in boundary conditions, i.e., the fact for 26 | * entry (exit) node in forward (backward) analysis. 27 | */ 28 | [[nodiscard]] virtual std::shared_ptr newBoundaryFact() const = 0; 29 | 30 | /** 31 | * @return new initial fact for non-boundary nodes. 32 | */ 33 | [[nodiscard]] virtual std::shared_ptr newInitialFact() const = 0; 34 | 35 | /** 36 | * @brief Meets a fact into another (target) fact. 37 | * This function will be used to handle control-flow confluences. 38 | * @param fact fact to be meet 39 | * @param target target fact 40 | */ 41 | virtual void meetInto(std::shared_ptr fact, std::shared_ptr target) const = 0; 42 | 43 | /** 44 | * @brief Node Transfer function for the analysis. 45 | * The function transfers data-flow from in (out) fact to out (in) fact 46 | * for forward (backward) analysis. 47 | * @param stmt stmt to be transferred 48 | * @param in in facts 49 | * @param out out facts 50 | * @return true if the transfer changed the out (in) fact, otherwise false. 51 | */ 52 | virtual bool transferNode(std::shared_ptr stmt, std::shared_ptr in, std::shared_ptr out) const = 0; 53 | 54 | /** 55 | * @brief By default, a data-flow analysis does not have edge transfer, i.e., 56 | * does not need to perform transfer for any edges. 57 | * @param edge the edge to check 58 | * @return true if this analysis needs to perform transfer for given edge, otherwise false. 59 | */ 60 | [[nodiscard, maybe_unused]] virtual bool needTransferEdge(std::shared_ptr edge) const = 0; 61 | 62 | /** 63 | * @brief Edge Transfer function for this analysis. 64 | * Note that this function should NOT modify nodeFact. 65 | * @param edge the edge that the transfer function is applied on 66 | * @param nodeFact the fact of the source node of the edge. Note that 67 | * which node is the source node of an edge depends on 68 | * the direction of the analysis. 69 | * @return the resulting edge fact 70 | */ 71 | [[nodiscard, maybe_unused]] virtual std::shared_ptr transferEdge 72 | (std::shared_ptr edge, std::shared_ptr nodeFact) const = 0; 73 | 74 | /** 75 | * @return the control-flow graph that this analysis works on. 76 | */ 77 | [[nodiscard]] virtual std::shared_ptr getCFG() const = 0; 78 | 79 | /** 80 | * @return the dataflow result to be modified by the solver 81 | * ( Note: called by the solver only once ) 82 | */ 83 | [[nodiscard]] virtual std::shared_ptr> getResult() const = 0; 84 | 85 | virtual ~DataflowAnalysis() = default; 86 | 87 | }; 88 | 89 | /** 90 | * @class AbstractDataflowAnalysis 91 | * @brief An abstract dataflow analysis that implements some general logic 92 | * @tparam Fact dataflow fact type 93 | */ 94 | template 95 | class AbstractDataflowAnalysis: public DataflowAnalysis { 96 | public: 97 | 98 | [[nodiscard]] bool needTransferEdge( 99 | [[maybe_unused]] std::shared_ptr edge) const override 100 | { 101 | return false; 102 | } 103 | 104 | [[nodiscard]] std::shared_ptr transferEdge 105 | ([[maybe_unused]] std::shared_ptr edge, 106 | [[maybe_unused]] std::shared_ptr nodeFact) const override 107 | { 108 | World::getLogger().Error("Transfer Edge is unsupported in dataflow analysis by default."); 109 | throw std::runtime_error("Transfer Edge is unsupported in dataflow analysis by default."); 110 | } 111 | 112 | [[nodiscard]] std::shared_ptr getCFG() const override 113 | { 114 | return cfg; 115 | } 116 | 117 | protected: 118 | 119 | /** 120 | * @brief construct a dataflow analysis from a given cfg 121 | * @param cfg the cfg to be analyzed 122 | */ 123 | explicit AbstractDataflowAnalysis(const std::shared_ptr& cfg) 124 | :cfg(cfg) 125 | { 126 | 127 | } 128 | 129 | std::shared_ptr cfg; ///< the cfg to be analyzed 130 | 131 | }; 132 | 133 | 134 | } // dataflow 135 | 136 | #endif //STATIC_ANALYZER_DataflowAnalysis_H 137 | -------------------------------------------------------------------------------- /include/analysis/dataflow/LiveVariable.h: -------------------------------------------------------------------------------- 1 | #ifndef CPP_STATIC_ANALYZER_LIVEVARIABLE_H 2 | #define CPP_STATIC_ANALYZER_LIVEVARIABLE_H 3 | 4 | #include 5 | 6 | #include "analysis/dataflow/AnalysisDriver.h" 7 | #include "analysis/dataflow/fact/SetFact.h" 8 | 9 | namespace analyzer::analysis::dataflow { 10 | 11 | /** 12 | * @class LiveVariable 13 | * @brief live variable analysis 14 | */ 15 | class LiveVariable: public AnalysisDriver> { 16 | public: 17 | 18 | /** 19 | * @brief constructor for live variable analysis 20 | * @param analysisConfig the analysis configuration 21 | */ 22 | explicit LiveVariable(std::unique_ptr& analysisConfig); 23 | 24 | protected: 25 | 26 | [[nodiscard]] std::unique_ptr>> 27 | makeAnalysis(const std::shared_ptr& cfg) const override; 28 | 29 | }; 30 | 31 | 32 | } // dataflow 33 | 34 | #endif //CPP_STATIC_ANALYZER_LIVEVARIABLE_H 35 | -------------------------------------------------------------------------------- /include/analysis/dataflow/ReachingDefinition.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_REACHINGDEFINITION_H 2 | #define STATIC_ANALYZER_REACHINGDEFINITION_H 3 | 4 | #include 5 | 6 | #include "analysis/dataflow/AnalysisDriver.h" 7 | #include "analysis/dataflow/fact/SetFact.h" 8 | 9 | namespace analyzer::analysis::dataflow { 10 | 11 | /** 12 | * @class ReachingDefinition 13 | * @brief reaching definition analysis 14 | */ 15 | class ReachingDefinition: public AnalysisDriver> { 16 | public: 17 | 18 | /** 19 | * @brief constructor for reaching definition analysis 20 | * @param analysisConfig the analysis configuration 21 | */ 22 | explicit ReachingDefinition(std::unique_ptr& analysisConfig); 23 | 24 | protected: 25 | 26 | [[nodiscard]] std::unique_ptr>> 27 | makeAnalysis(const std::shared_ptr& cfg) const override; 28 | 29 | }; 30 | 31 | 32 | } // dataflow 33 | 34 | 35 | #endif //STATIC_ANALYZER_REACHINGDEFINITION_H 36 | -------------------------------------------------------------------------------- /include/analysis/dataflow/fact/DataflowResult.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_DATAFLOWRESULT_H 2 | #define STATIC_ANALYZER_DATAFLOWRESULT_H 3 | 4 | #include 5 | 6 | #include "analysis/dataflow/fact/NodeResult.h" 7 | 8 | namespace analyzer::analysis::dataflow::fact { 9 | 10 | template 11 | class DataflowResult: public NodeResult { 12 | public: 13 | 14 | [[nodiscard]] std::shared_ptr getInFact(std::shared_ptr node) const override 15 | { 16 | if (inFacts.find(node) != inFacts.end()) { 17 | return inFacts.at(node); 18 | } 19 | return nullptr; 20 | } 21 | 22 | [[nodiscard]] std::shared_ptr getOutFact(std::shared_ptr node) const override 23 | { 24 | if (outFacts.find(node) != outFacts.end()) { 25 | return outFacts.at(node); 26 | } 27 | return nullptr; 28 | } 29 | 30 | /** 31 | * @brief Associates a dataflow fact with a node as its flowing-in fact. 32 | * @param node a statement 33 | * @param fact a dataflow fact 34 | */ 35 | void setInFact(std::shared_ptr node, std::shared_ptr fact) { 36 | inFacts.insert_or_assign(node, fact); 37 | } 38 | 39 | /** 40 | * @brief Associates a dataflow fact with a node as its flowing-out fact. 41 | * @param node a statement 42 | * @param fact a dataflow fact 43 | */ 44 | void setOutFact(std::shared_ptr node, std::shared_ptr fact) { 45 | outFacts.insert_or_assign(node, fact); 46 | } 47 | 48 | /** 49 | * @brief construct a predefined dataflow result 50 | * @param inFacts all in-flowing facts 51 | * @param outFacts all out-flowing facts 52 | */ 53 | DataflowResult(std::unordered_map, std::shared_ptr> inFacts, 54 | std::unordered_map, std::shared_ptr> outFacts) 55 | :inFacts(std::move(inFacts)), outFacts(std::move(outFacts)) 56 | { 57 | 58 | } 59 | 60 | /** 61 | * @brief construct an empty dataflow result 62 | */ 63 | DataflowResult() 64 | :DataflowResult({}, {}) 65 | { 66 | 67 | } 68 | 69 | private: 70 | 71 | std::unordered_map, std::shared_ptr> 72 | inFacts; ///< in-flowing facts of each statement 73 | 74 | std::unordered_map, std::shared_ptr> 75 | outFacts; ///< out-flowing facts of each statement 76 | 77 | }; 78 | 79 | } // fact 80 | 81 | #endif //STATIC_ANALYZER_DATAFLOWRESULT_H 82 | -------------------------------------------------------------------------------- /include/analysis/dataflow/fact/MapFact.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_MAPFACT_H 2 | #define STATIC_ANALYZER_MAPFACT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util/Copyable.h" 9 | 10 | namespace analyzer::analysis::dataflow::fact { 11 | 12 | /** 13 | * @class MapFact 14 | * @brief Represents map-like data-flow facts. 15 | * 16 | * Elements are checked by identity rather than equality !!! 17 | * 18 | * @tparam K key type 19 | * @tparam V value type 20 | */ 21 | template 22 | class MapFact: public util::Copyable> { 23 | public: 24 | 25 | /** 26 | * @brief get the value of a given key 27 | * @param key the key to be searched 28 | * @return the value to which the specified key is mapped, 29 | * or nullptr if this map contains no mapping for the key. 30 | */ 31 | [[nodiscard]] virtual std::shared_ptr get(const std::shared_ptr& key) const 32 | { 33 | if (map.find(key) == map.end()) { 34 | return nullptr; 35 | } 36 | return map.at(key); 37 | } 38 | 39 | /** 40 | * @brief Updates the key-value mapping in this fact. 41 | * @param key the key to update 42 | * @param value the value to be bound to the key 43 | * @return true if the update changes this fact, otherwise 44 | */ 45 | virtual bool update(const std::shared_ptr& key, const std::shared_ptr& value) 46 | { 47 | if (map.find(key) == map.end()) { 48 | map.emplace(key, value); 49 | return true; 50 | } 51 | if (map.at(key) == value) { 52 | return false; 53 | } 54 | map.at(key) = value; 55 | return true; 56 | } 57 | 58 | /** 59 | * @brief Removes the key-value mapping for given key. 60 | * @param key the key to remove its value 61 | * @return the previous value associated with key, or null if there was no mapping for key. 62 | */ 63 | virtual std::shared_ptr remove(const std::shared_ptr& key) 64 | { 65 | if (map.find(key) == map.end()) { 66 | return nullptr; 67 | } 68 | std::shared_ptr result = map.at(key); 69 | map.erase(key); 70 | return result; 71 | } 72 | 73 | /** 74 | * @brief Copies the content from given fact to this fact. 75 | * @param fact the fact to be copied 76 | * @return true if this fact changed as a result of the call, otherwise false. 77 | */ 78 | virtual bool copyFrom(const std::shared_ptr> fact) 79 | { 80 | bool changed = false; 81 | for (const auto& [key, value] : fact->getMap()) { 82 | changed = update(key, value) || changed; 83 | } 84 | return changed; 85 | } 86 | 87 | /** 88 | * @brief creates and returns a copy of this fact 89 | * @return a copy of this fact 90 | */ 91 | [[nodiscard]] std::shared_ptr> copy() const override 92 | { 93 | return std::make_shared>(map); 94 | } 95 | 96 | /** 97 | * @brief Clears all content in this fact. 98 | */ 99 | virtual void clear() 100 | { 101 | map.clear(); 102 | } 103 | 104 | /** 105 | * @return a set of the keys contained in this fact. 106 | */ 107 | [[nodiscard]] virtual std::unordered_set> keySet() const 108 | { 109 | std::unordered_set> result; 110 | for (auto& [k, _] : map) { 111 | result.emplace(k); 112 | } 113 | return result; 114 | } 115 | 116 | /** 117 | * @return a set of the values contained in this fact. 118 | */ 119 | [[nodiscard]] virtual std::unordered_set> valueSet() const 120 | { 121 | std::unordered_set> result; 122 | for (auto& [_, v] : map) { 123 | result.emplace(v); 124 | } 125 | return result; 126 | } 127 | 128 | /** 129 | * @return true if this fact is empty, otherwise false 130 | */ 131 | [[nodiscard]] virtual bool isEmpty() const 132 | { 133 | return map.empty(); 134 | } 135 | 136 | /** 137 | * @return the size of this map fact 138 | */ 139 | [[nodiscard]] virtual std::size_t size() const 140 | { 141 | return map.size(); 142 | } 143 | 144 | /** 145 | * @brief compares equality of map fact 146 | * @param other another map fact 147 | * @return true if this map fact is equal to other map fact, otherwise false 148 | */ 149 | [[nodiscard]] virtual bool equalsTo(const std::shared_ptr>& other) const 150 | { 151 | return map == other->getMap(); 152 | } 153 | 154 | /** 155 | * @brief call processor function for each key-value pairs in this map fact 156 | * @param processor a processor function to process each key-value pair 157 | */ 158 | virtual void forEach(std::function, std::shared_ptr)> processor) 159 | { 160 | for (auto [k, v] : map) { 161 | processor(k, v); 162 | } 163 | } 164 | 165 | /** 166 | * @brief Constructs a new MapFact with the same mappings as specified Map. 167 | * @param map the map whose mappings are to be placed in this map. 168 | */ 169 | explicit MapFact(std::unordered_map, std::shared_ptr> map) 170 | :map(std::move(map)) 171 | { 172 | 173 | } 174 | 175 | /** 176 | * @brief Construct an empty set 177 | */ 178 | MapFact() 179 | : MapFact(std::unordered_map, std::shared_ptr>{}) 180 | { 181 | 182 | } 183 | 184 | private: 185 | 186 | /** 187 | * @return a constant reference to the inner map 188 | */ 189 | const std::unordered_map, std::shared_ptr>& getMap() const 190 | { 191 | return map; 192 | } 193 | 194 | std::unordered_map, std::shared_ptr> 195 | map; ///< The map holding the mappings of this MapFact. 196 | 197 | }; 198 | 199 | } // fact 200 | 201 | #endif //STATIC_ANALYZER_MAPFACT_H 202 | -------------------------------------------------------------------------------- /include/analysis/dataflow/fact/NodeResult.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_NODERESULT_H 2 | #define STATIC_ANALYZER_NODERESULT_H 3 | 4 | #include "analysis/AnalysisResult.h" 5 | 6 | namespace analyzer::analysis::dataflow::fact { 7 | 8 | /** 9 | * @brief An interface for querying data-flow results. 10 | * @class NodeResult 11 | * @tparam Fact the type of dataflow fact 12 | */ 13 | template 14 | class NodeResult: public StmtResult { 15 | public: 16 | 17 | /** 18 | * @brief get the flowing-in fact of a given node 19 | * @param node a statement to get the in fact 20 | * @return a dataflow fact, nullptr if not exist 21 | */ 22 | [[nodiscard]] virtual std::shared_ptr getInFact(std::shared_ptr node) const = 0; 23 | 24 | /** 25 | * @brief get the flowing-out fact of a given node 26 | * @param node a statement to get the out fact 27 | * @return a dataflow fact, nullptr if not exist 28 | */ 29 | [[nodiscard]] virtual std::shared_ptr getOutFact(std::shared_ptr node) const = 0; 30 | 31 | /** 32 | * @brief typically, all statements are relevant in NodeResult 33 | * @param stmt an unused argument 34 | * @return true 35 | */ 36 | [[nodiscard]] bool isRelevant(std::shared_ptr stmt) const override 37 | { 38 | return true; 39 | } 40 | 41 | /** 42 | * @brief typically, the result of a statement is its out-flowing fact 43 | * @param stmt the statement to get result 44 | * @return the result of given stmt, which is also its out-flowing fact 45 | */ 46 | [[nodiscard]] std::shared_ptr getResult(std::shared_ptr stmt) const override 47 | { 48 | return getOutFact(stmt); 49 | } 50 | 51 | }; 52 | 53 | } // fact 54 | 55 | #endif //STATIC_ANALYZER_NODERESULT_H 56 | -------------------------------------------------------------------------------- /include/analysis/dataflow/fact/SetFact.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_SETFACT_H 2 | #define STATIC_ANALYZER_SETFACT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util/Copyable.h" 9 | 10 | namespace analyzer::analysis::dataflow::fact { 11 | 12 | /** 13 | * @class SetFact 14 | * @brief Represents set-like data-flow facts. 15 | * 16 | * Elements are checked by identity rather than equality !!! 17 | * 18 | * @tparam E elements type 19 | */ 20 | template 21 | class SetFact: public util::Copyable> { 22 | public: 23 | 24 | /** 25 | * @brief check the existence of an element 26 | * @param e the element to be checked 27 | * @return true if this set contains the specified element, otherwise false. 28 | */ 29 | [[nodiscard]] virtual bool contains(const std::shared_ptr& e) const 30 | { 31 | return set.find(e) != set.end(); 32 | } 33 | 34 | /** 35 | * @brief Adds an element to this fact. 36 | * @param e the element to be added 37 | * @return true if this fact changed as a result of the call, otherwise false. 38 | */ 39 | virtual bool add(const std::shared_ptr& e) 40 | { 41 | return set.emplace(e).second; 42 | } 43 | 44 | /** 45 | * @brief Removes an element from this fact 46 | * @param e the element to be removed 47 | * @return true if any elements were removed as a result of the call, otherwise false. 48 | */ 49 | virtual bool remove(const std::shared_ptr& e) 50 | { 51 | return set.erase(e) == 1; 52 | } 53 | 54 | /** 55 | * @brief Removes all the elements of this fact that satisfy the given predicate. 56 | * @param filter a predicate for elements to be removed 57 | * @return true if any elements were removed as a result of the call, otherwise false. 58 | */ 59 | virtual bool removeIf(std::function&)> filter) 60 | { 61 | std::size_t oldSize = set.size(); 62 | std::vector> toRemove; 63 | for (const std::shared_ptr& e : set) { 64 | if (filter(e)) { 65 | toRemove.emplace_back(e); 66 | } 67 | } 68 | for (const std::shared_ptr& e : toRemove) { 69 | set.erase(e); 70 | } 71 | return set.size() != oldSize; 72 | } 73 | 74 | /** 75 | * @brief Removes all elements of other fact. 76 | * @param other another set fact 77 | * @return true if this fact changed as a result of the call, otherwise false. 78 | */ 79 | virtual bool removeAll(const std::shared_ptr>& other) 80 | { 81 | return removeIf([&](const std::shared_ptr& e) -> bool { 82 | return other->contains(e); 83 | }); 84 | } 85 | 86 | /** 87 | * @brief Unions other fact into this fact. 88 | * @param other another set fact 89 | * @return true if this fact changed as a result of the call, otherwise false. 90 | */ 91 | virtual bool unionN(const std::shared_ptr>& other) 92 | { 93 | std::size_t oldSize = set.size(); 94 | set.insert(other->getSet().begin(), other->getSet().end()); 95 | return set.size() != oldSize; 96 | } 97 | 98 | /** 99 | * @brief create a new fact as the union of this and other 100 | * @param other another set fact 101 | * @return a new fact which is the union of this and other facts. 102 | */ 103 | [[nodiscard]] virtual std::shared_ptr> 104 | unionWith(const std::shared_ptr>& other) const 105 | { 106 | std::unordered_set> result; 107 | std::set_union(set.begin(), set.end(), 108 | other->getSet().begin(), other->getSet().end(), 109 | std::inserter(result, result.begin())); 110 | return std::make_shared>(std::move(result)); 111 | } 112 | 113 | /** 114 | * @brief Intersects this fact with other fact. 115 | * @param other another set fact 116 | * @return true if this fact changed as a result of the call, otherwise false. 117 | */ 118 | virtual bool intersect(const std::shared_ptr>& other) 119 | { 120 | return removeIf([&] (const std::shared_ptr& e) -> bool { 121 | return !other->contains(e); 122 | }); 123 | } 124 | 125 | /** 126 | * @brief create a new fact as the intersection of this and other 127 | * @param other another set fact 128 | * @return a new fact which is the intersection of this and other facts. 129 | */ 130 | [[nodiscard]] virtual std::shared_ptr> 131 | intersectWith(const std::shared_ptr>& other) const 132 | { 133 | std::unordered_set> result; 134 | std::set_intersection(set.begin(), set.end(), 135 | other->getSet().begin(), other->getSet().end(), 136 | std::inserter(result, result.begin())); 137 | return std::make_shared>(std::move(result)); 138 | } 139 | 140 | /** 141 | * @brief sets the content of this set to the same as other set. 142 | * @param other another set fact 143 | */ 144 | virtual void setSetFact(const std::shared_ptr>& other) 145 | { 146 | set = other->getSet(); 147 | } 148 | 149 | /** 150 | * @brief creates and returns 151 | * @return a copy of this fact 152 | */ 153 | [[nodiscard]] std::shared_ptr> copy() const override 154 | { 155 | return std::make_shared>(set); 156 | } 157 | 158 | /** 159 | * @brief clears all content in this fact. 160 | */ 161 | virtual void clear() 162 | { 163 | set.clear(); 164 | } 165 | 166 | /** 167 | * @return true if this fact is empty, otherwise false 168 | */ 169 | [[nodiscard]] virtual bool isEmpty() const 170 | { 171 | return set.empty(); 172 | } 173 | 174 | /** 175 | * @return the size of this set fact 176 | */ 177 | [[nodiscard]] virtual std::size_t size() const 178 | { 179 | return set.size(); 180 | } 181 | 182 | /** 183 | * @brief compares equality of set fact 184 | * @param other another set fact 185 | * @return true if this set fact is equal to other set fact, otherwise false 186 | */ 187 | [[nodiscard]] virtual bool equalsTo(const std::shared_ptr>& other) const 188 | { 189 | return set == other->getSet(); 190 | } 191 | 192 | /** 193 | * @brief call processor function for each elements in this set fact 194 | * @param processor a processor function to process each element 195 | */ 196 | virtual void forEach(std::function)> processor) 197 | { 198 | for (std::shared_ptr e : set) { 199 | processor(e); 200 | } 201 | } 202 | 203 | /** 204 | * @brief Construct a set from a given set 205 | * @param set 206 | */ 207 | explicit SetFact(std::unordered_set> set) 208 | :set(std::move(set)) 209 | { 210 | 211 | } 212 | 213 | /** 214 | * @brief Construct an empty set 215 | */ 216 | SetFact() 217 | : SetFact(std::unordered_set>{}) 218 | { 219 | 220 | } 221 | 222 | private: 223 | 224 | /** 225 | * @return a const reference to the inner set 226 | */ 227 | const std::unordered_set>& getSet() const 228 | { 229 | return set; 230 | } 231 | 232 | std::unordered_set> set; ///< the inner set of this dataflow fact 233 | 234 | }; 235 | 236 | } // fact 237 | 238 | 239 | #endif //STATIC_ANALYZER_SETFACT_H 240 | -------------------------------------------------------------------------------- /include/analysis/dataflow/solver/Solver.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_SOLVER_H 2 | #define STATIC_ANALYZER_SOLVER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "analysis/dataflow/fact/DataflowResult.h" 8 | #include "analysis/dataflow/DataflowAnalysis.h" 9 | 10 | namespace analyzer::analysis::dataflow::solver { 11 | 12 | /** 13 | * @class Solver 14 | * @brief Interface of data-flow analysis solver. 15 | * @tparam Fact 16 | */ 17 | template 18 | class Solver { 19 | public: 20 | 21 | /** 22 | * @brief solves the given analysis problem 23 | * @param dataflowAnalysis a dataflow analysis problem 24 | * @return the analysis result 25 | */ 26 | [[nodiscard]] virtual std::shared_ptr> 27 | solve(const std::unique_ptr>& dataflowAnalysis) const = 0; 28 | 29 | virtual ~Solver() = default; 30 | 31 | }; 32 | 33 | /** 34 | * @class AbstractSolver 35 | * @brief provides common functionalities for Solver 36 | * @tparam Fact type of data-flow facts 37 | */ 38 | template 39 | class AbstractSolver: public Solver { 40 | public: 41 | 42 | [[nodiscard]] std::shared_ptr> 43 | solve(const std::unique_ptr>& dataflowAnalysis) const override 44 | { 45 | World::getLogger().Info("Initializing dataflow facts ..."); 46 | std::shared_ptr> result = initialize(dataflowAnalysis); 47 | World::getLogger().Info("Doing the solving ..."); 48 | doSolve(dataflowAnalysis, result); 49 | return result; 50 | } 51 | 52 | protected: 53 | 54 | /** 55 | * @brief initialize the data facts for a forward analysis 56 | * @param dataflowAnalysis the corresponding dataflow analysis 57 | * @param result the result to be initialized 58 | */ 59 | virtual void initializeForward( 60 | const std::unique_ptr>& dataflowAnalysis, 61 | std::shared_ptr> result) const 62 | { 63 | std::shared_ptr cfg = dataflowAnalysis->getCFG(); 64 | std::shared_ptr entry = cfg->getEntry(); 65 | result->setInFact(entry, dataflowAnalysis->newBoundaryFact()); 66 | result->setOutFact(entry, dataflowAnalysis->newBoundaryFact()); 67 | for (const std::shared_ptr& stmt : cfg->getIR()->getStmts()) { 68 | result->setInFact(stmt, dataflowAnalysis->newInitialFact()); 69 | result->setOutFact(stmt, dataflowAnalysis->newInitialFact()); 70 | } 71 | std::shared_ptr exit = cfg->getExit(); 72 | result->setInFact(exit, dataflowAnalysis->newInitialFact()); 73 | result->setOutFact(exit, dataflowAnalysis->newInitialFact()); 74 | } 75 | 76 | /** 77 | * @brief initialize the data facts for a backward analysis 78 | * @param dataflowAnalysis the corresponding dataflow analysis 79 | * @param result the result to be initialized 80 | */ 81 | virtual void initializeBackward( 82 | const std::unique_ptr>& dataflowAnalysis, 83 | std::shared_ptr> result) const 84 | { 85 | std::shared_ptr cfg = dataflowAnalysis->getCFG(); 86 | std::shared_ptr exit = cfg->getExit(); 87 | result->setInFact(exit, dataflowAnalysis->newBoundaryFact()); 88 | result->setOutFact(exit, dataflowAnalysis->newBoundaryFact()); 89 | for (const std::shared_ptr& stmt : cfg->getIR()->getStmts()) { 90 | result->setInFact(stmt, dataflowAnalysis->newInitialFact()); 91 | result->setOutFact(stmt, dataflowAnalysis->newInitialFact()); 92 | } 93 | std::shared_ptr entry = cfg->getEntry(); 94 | result->setInFact(entry, dataflowAnalysis->newInitialFact()); 95 | result->setOutFact(entry, dataflowAnalysis->newInitialFact()); 96 | } 97 | 98 | /** 99 | * @brief solve a forward dataflow analysis 100 | * @param dataflowAnalysis the dataflow analysis to be solved 101 | * @param result where to store the results 102 | */ 103 | virtual void doSolveForward( 104 | const std::unique_ptr>& dataflowAnalysis, 105 | std::shared_ptr> result) const = 0; 106 | 107 | /** 108 | * @brief solve a backward dataflow analysis 109 | * @param dataflowAnalysis the dataflow analysis to be solved 110 | * @param result where to store the results 111 | */ 112 | virtual void doSolveBackward( 113 | const std::unique_ptr>& dataflowAnalysis, 114 | std::shared_ptr> result) const = 0; 115 | 116 | private: 117 | 118 | [[nodiscard]] std::shared_ptr> 119 | initialize(const std::unique_ptr>& dataflowAnalysis) const 120 | { 121 | std::shared_ptr cfg = dataflowAnalysis->getCFG(); 122 | std::shared_ptr> result = dataflowAnalysis->getResult(); 123 | if (dataflowAnalysis->isForward()) { 124 | this->initializeForward(dataflowAnalysis, result); 125 | } else { 126 | this->initializeBackward(dataflowAnalysis, result); 127 | } 128 | return result; 129 | } 130 | 131 | void doSolve(const std::unique_ptr>& dataflowAnalysis, 132 | std::shared_ptr> result) const 133 | { 134 | if (dataflowAnalysis->isForward()) { 135 | this->doSolveForward(dataflowAnalysis, result); 136 | } else { 137 | this->doSolveBackward(dataflowAnalysis, result); 138 | } 139 | } 140 | 141 | }; 142 | 143 | /** 144 | * @class WorkListSolver 145 | * @brief a Work-list solver 146 | * @tparam Fact type of dataflow fact 147 | */ 148 | template 149 | class WorkListSolver: public AbstractSolver { 150 | protected: 151 | 152 | void doSolveForward( 153 | const std::unique_ptr>& dataflowAnalysis, 154 | std::shared_ptr> result) const override 155 | { 156 | std::queue> workList; 157 | std::shared_ptr cfg = dataflowAnalysis->getCFG(); 158 | workList.push(cfg->getEntry()); 159 | for (std::shared_ptr& s : cfg->getIR()->getStmts()) { 160 | workList.push(s); 161 | } 162 | workList.push(cfg->getExit()); 163 | while (!workList.empty()) { 164 | std::shared_ptr stmt = workList.front(); 165 | workList.pop(); 166 | if (stmt == cfg->getEntry()) { 167 | continue; 168 | } 169 | for (const std::shared_ptr& pred : cfg->getPredsOf(stmt)) { 170 | dataflowAnalysis->meetInto(result->getOutFact(pred), 171 | result->getInFact(stmt)); 172 | } 173 | if (dataflowAnalysis->transferNode(stmt, 174 | result->getInFact(stmt), result->getOutFact(stmt))) { 175 | for (const std::shared_ptr& succ : cfg->getSuccsOf(stmt)) { 176 | workList.push(succ); 177 | } 178 | } 179 | } 180 | } 181 | 182 | void doSolveBackward( 183 | const std::unique_ptr>& dataflowAnalysis, 184 | std::shared_ptr> result) const override 185 | { 186 | std::queue> workList; 187 | std::shared_ptr cfg = dataflowAnalysis->getCFG(); 188 | workList.push(cfg->getExit()); 189 | for (std::shared_ptr& s: cfg->getIR()->getStmts()) { 190 | workList.push(s); 191 | } 192 | workList.push(cfg->getEntry()); 193 | while (!workList.empty()) { 194 | std::shared_ptr stmt = workList.front(); 195 | workList.pop(); 196 | if (stmt == cfg->getExit()) { 197 | continue; 198 | } 199 | for (const std::shared_ptr& succ : cfg->getSuccsOf(stmt)) { 200 | dataflowAnalysis->meetInto(result->getInFact(succ), 201 | result->getOutFact(stmt)); 202 | } 203 | if (dataflowAnalysis->transferNode(stmt, 204 | result->getInFact(stmt), result->getOutFact(stmt))) { 205 | for (const std::shared_ptr& pred : cfg->getPredsOf(stmt)) { 206 | workList.push(pred); 207 | } 208 | } 209 | } 210 | } 211 | 212 | }; 213 | 214 | /** 215 | * @tparam Fact the dataflow fact 216 | * @brief factory method for obtaining a solver of a given dataflow fact 217 | * @return a solver implemented by worklist 218 | */ 219 | template 220 | std::unique_ptr> makeSolver() { 221 | return std::make_unique>(); 222 | } 223 | 224 | } // solver 225 | 226 | #endif //STATIC_ANALYZER_SOLVER_H 227 | -------------------------------------------------------------------------------- /include/analysis/graph/CFG.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_CFG_H 2 | #define STATIC_ANALYZER_CFG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace analyzer::ir { 9 | class IR; 10 | class Stmt; 11 | } 12 | 13 | namespace analyzer::analysis::graph { 14 | 15 | /** 16 | * @brief the interface for a CFG Edge 17 | * @class CFGEdge 18 | */ 19 | class CFGEdge { 20 | public: 21 | /** 22 | * @brief edge kind enumeration 23 | */ 24 | enum class Kind { 25 | ENTRY_EDGE, ///< edges from the entry node 26 | EXIT_EDGE, ///< edges to the exit node 27 | FALL_THROUGH_EDGE, ///< edges from sequential execution 28 | JUMP_EDGE, ///< edges from if-else / switch-case / while / for 29 | UNKNOWN_EDGE ///< unknown kind of cfg edge 30 | }; 31 | 32 | /** 33 | * @return the kind of the CFG edge 34 | */ 35 | [[nodiscard]] virtual Kind getKind() const = 0; 36 | 37 | /** 38 | * 39 | * @return the source statement of this CFG edge 40 | */ 41 | [[nodiscard]] virtual std::shared_ptr getSource() const = 0; 42 | 43 | /** 44 | * @return the target statements of this CFG edge 45 | */ 46 | [[nodiscard]] virtual std::shared_ptr getTarget() const = 0; 47 | 48 | virtual ~CFGEdge() = default; 49 | }; 50 | 51 | /** 52 | * @brief the interface for a CFG using statements as its nodes 53 | * @class CFG 54 | */ 55 | class CFG { 56 | public: 57 | 58 | /** 59 | * @return the entry nop statement of this CFG 60 | */ 61 | [[nodiscard]] virtual std::shared_ptr getEntry() const = 0; 62 | 63 | /** 64 | * @return the exit nop statement of this CFG 65 | */ 66 | [[nodiscard]] virtual std::shared_ptr getExit() const = 0; 67 | 68 | /** 69 | * @brief check the existence of a CFG edge 70 | * @param source the source statement 71 | * @param target the target statement 72 | * @return true if there's CFG edge from source to target 73 | */ 74 | [[nodiscard]] virtual bool 75 | hasEdge(std::shared_ptr source, std::shared_ptr target) const = 0; 76 | 77 | /** 78 | * @brief get the predecessor statements of a given statement 79 | * @param stmt a statement (check by identity) 80 | * @return a vector of predecessors 81 | */ 82 | [[nodiscard]] virtual std::unordered_set> 83 | getPredsOf(std::shared_ptr stmt) const = 0; 84 | 85 | /** 86 | * @brief get the successor statements of a given statement 87 | * @param stmt a statement (check by identity) 88 | * @return a vector of successors 89 | */ 90 | [[nodiscard]] virtual std::unordered_set> 91 | getSuccsOf(std::shared_ptr stmt) const = 0; 92 | 93 | /** 94 | * @brief get the in edges of a given statement 95 | * @param stmt a statement (check by identity) 96 | * @return a vector of in edges 97 | */ 98 | [[nodiscard]] virtual std::unordered_set> 99 | getInEdgesOf(std::shared_ptr stmt) const = 0; 100 | 101 | /** 102 | * @brief get the out edges of a given statement 103 | * @param stmt a statement (check by identity) 104 | * @return a vector of out edges 105 | */ 106 | [[nodiscard]] virtual std::unordered_set> 107 | getOutEdgesOf(std::shared_ptr stmt) const = 0; 108 | 109 | /** 110 | * @return the intermediate representation corresponding to this CFG 111 | */ 112 | [[nodiscard]] virtual std::shared_ptr getIR() const = 0; 113 | 114 | /** 115 | * @return the number of edges of this cfg 116 | */ 117 | [[nodiscard]] virtual std::size_t getEdgeNum() const = 0; 118 | 119 | virtual ~CFG() = default; 120 | }; 121 | 122 | /** 123 | * @class DefaultCFGEdge 124 | * @brief the default implementation of a cfg edge 125 | */ 126 | class DefaultCFGEdge: public CFGEdge { 127 | public: 128 | 129 | [[nodiscard]] Kind getKind() const override; 130 | 131 | [[nodiscard]] std::shared_ptr getSource() const override; 132 | 133 | [[nodiscard]] std::shared_ptr getTarget() const override; 134 | 135 | // the method below should not be called from user 136 | 137 | /** 138 | * Construct a default CFG edge 139 | * @param source the source statement 140 | * @param target the target statement 141 | * @param kind the edge kind 142 | */ 143 | DefaultCFGEdge(std::shared_ptr source, std::shared_ptr target, 144 | Kind kind = Kind::UNKNOWN_EDGE); 145 | 146 | private: 147 | 148 | std::shared_ptr source; ///< the source statement 149 | 150 | std::shared_ptr target; ///< the target statement 151 | 152 | Kind kind; ///< the edge kind 153 | }; 154 | 155 | /** 156 | * @class DefaultCFG 157 | * @brief a default implementation of cfg 158 | */ 159 | class DefaultCFG final: public CFG { 160 | public: 161 | 162 | [[nodiscard]] std::shared_ptr getEntry() const override; 163 | 164 | [[nodiscard]] std::shared_ptr getExit() const override; 165 | 166 | [[nodiscard]] bool 167 | hasEdge(std::shared_ptr source, std::shared_ptr target) const override; 168 | 169 | [[nodiscard]] std::unordered_set> 170 | getPredsOf(std::shared_ptr stmt) const override; 171 | 172 | [[nodiscard]] std::unordered_set> 173 | getSuccsOf(std::shared_ptr stmt) const override; 174 | 175 | [[nodiscard]] std::unordered_set> 176 | getInEdgesOf(std::shared_ptr stmt) const override; 177 | 178 | [[nodiscard]] std::unordered_set> 179 | getOutEdgesOf(std::shared_ptr stmt) const override; 180 | 181 | [[nodiscard]] std::shared_ptr getIR() const override; 182 | 183 | [[nodiscard]] std::size_t getEdgeNum() const override; 184 | 185 | // the method below should not be called from user 186 | 187 | /** 188 | * @brief set the ir that generates this cfg 189 | * @param myIR intermediate representation 190 | */ 191 | void setIR(const std::shared_ptr& myIR); 192 | 193 | /** 194 | * @brief add an edge to this cfg 195 | * @param edge a default cfg edge 196 | */ 197 | void addEdge(const std::shared_ptr& edge); 198 | 199 | /** 200 | * @brief set the entry node of this cfg 201 | * @param entry the entry node (typically a nop statement) of this cfg 202 | */ 203 | void setEntry(const std::shared_ptr& entry); 204 | 205 | /** 206 | * @brief set the exit node of this cfg 207 | * @param exit the exit node (typically a nop statement) of this cfg 208 | */ 209 | void setExit(const std::shared_ptr& exit); 210 | 211 | /** 212 | * @brief construct an empty default cfg 213 | */ 214 | DefaultCFG(); 215 | 216 | private: 217 | 218 | std::unordered_multimap, std::shared_ptr> 219 | inEdges; ///< in edges of each statement 220 | 221 | std::unordered_multimap, std::shared_ptr> 222 | outEdges; ///< out edges of each statement 223 | 224 | std::weak_ptr myIR; ///< ir of this cfg 225 | 226 | std::shared_ptr entry; ///< entry node of this cfg 227 | 228 | std::shared_ptr exit; ///< exit node of this cfg 229 | 230 | std::size_t edgeNum; ///< number of edges in this cfg 231 | 232 | }; 233 | 234 | } // graph 235 | 236 | #endif //STATIC_ANALYZER_CFG_H 237 | -------------------------------------------------------------------------------- /include/config/AnalysisConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_ANALYSISCONFIG_H 2 | #define STATIC_ANALYZER_ANALYSISCONFIG_H 3 | 4 | #include 5 | 6 | namespace analyzer::config { 7 | 8 | /** 9 | * @class AnalysisConfig 10 | * @brief configuration for an analysis 11 | */ 12 | class AnalysisConfig { 13 | public: 14 | 15 | /** 16 | * @return the description of this analysis, just for human reading 17 | */ 18 | [[nodiscard]] virtual const std::string& getDescription() const = 0; 19 | 20 | virtual ~AnalysisConfig() = default; 21 | 22 | }; 23 | 24 | class DefaultAnalysisConfig final: public AnalysisConfig { 25 | public: 26 | 27 | [[nodiscard]] const std::string& getDescription() const override; 28 | 29 | /** 30 | * @brief construct a default analysis config that only has a description 31 | * @param description the description of the corresponding analysis 32 | */ 33 | explicit DefaultAnalysisConfig(std::string description = ""); 34 | 35 | private: 36 | 37 | std::string description; ///< the description of this analysis 38 | 39 | }; 40 | 41 | } 42 | 43 | #endif //STATIC_ANALYZER_ANALYSISCONFIG_H 44 | -------------------------------------------------------------------------------- /include/ir/IR.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_IR_H 2 | #define STATIC_ANALYZER_IR_H 3 | 4 | #include 5 | #include 6 | 7 | #include "ir/Stmt.h" 8 | #include "analysis/graph/CFG.h" 9 | 10 | namespace analyzer::language { 11 | class CPPMethod; 12 | } 13 | 14 | namespace analyzer::ir { 15 | 16 | namespace lang = language; 17 | namespace graph = analysis::graph; 18 | 19 | /** 20 | * @class IR 21 | * @brief interface for intermediate representation 22 | */ 23 | class IR { 24 | public: 25 | 26 | /** 27 | * @return the cpp method this ir is representing 28 | */ 29 | [[nodiscard]] virtual const lang::CPPMethod& getMethod() const = 0; 30 | 31 | /** 32 | * @return the cfg corresponding to this ir 33 | */ 34 | [[nodiscard]] virtual std::shared_ptr getCFG() const = 0; 35 | 36 | /** 37 | * @return the variables corresponding to parameters 38 | */ 39 | [[nodiscard]] virtual std::vector> getParams() const = 0; 40 | 41 | /** 42 | * @return the variables in this ir 43 | */ 44 | [[nodiscard]] virtual std::vector> getVars() const = 0; 45 | 46 | /** 47 | * @return the statements in this ir 48 | */ 49 | [[nodiscard]] virtual std::vector> getStmts() const = 0; 50 | 51 | virtual ~IR() = default; 52 | 53 | }; 54 | 55 | /** 56 | * @class DefaultIR 57 | * @brief the default implementation of ir 58 | */ 59 | class DefaultIR final: public IR { 60 | public: 61 | 62 | [[nodiscard]] const lang::CPPMethod& getMethod() const override; 63 | 64 | [[nodiscard]] std::shared_ptr getCFG() const override; 65 | 66 | [[nodiscard]] std::vector> getParams() const override; 67 | 68 | [[nodiscard]] std::vector> getVars() const override; 69 | 70 | [[nodiscard]] std::vector> getStmts() const override; 71 | 72 | // functions below should not be called from user 73 | 74 | /** 75 | * @brief Construct a default ir of method 76 | * @param method the method this ir is representing 77 | * @param params the parameter variables in this ir 78 | * @param vars the variables concerned in this ir 79 | * @param stmts the statements of this ir 80 | * @param cfg the cfg derived from this ir 81 | */ 82 | DefaultIR(const lang::CPPMethod& method, 83 | std::vector> params, 84 | std::vector> vars, 85 | std::vector> stmts, 86 | const std::shared_ptr& cfg); 87 | 88 | private: 89 | 90 | const lang::CPPMethod& method; ///< the method this ir is representing 91 | 92 | std::vector> params; ///< the parameter variables in this ir 93 | 94 | std::vector> vars; ///< the variables concerned in this ir 95 | 96 | std::vector> stmts; ///< the statements of this ir 97 | 98 | std::shared_ptr cfg; ///< the cfg derived from this ir 99 | 100 | }; 101 | 102 | /** 103 | * @class IRBuilder 104 | * @brief Interface for builder of IR 105 | */ 106 | class IRBuilder { 107 | public: 108 | 109 | /** 110 | * @brief Builds IR for concrete methods. 111 | * @param method the method used to build ir 112 | * @return the intermediate representation of method 113 | */ 114 | [[nodiscard]] virtual std::shared_ptr buildIR(const lang::CPPMethod& method) const = 0; 115 | 116 | virtual ~IRBuilder() = default; 117 | 118 | }; 119 | 120 | /** 121 | * @class DefaultIRBuilder 122 | * @brief build a default IR for a cpp method with the help of clang 123 | */ 124 | class DefaultIRBuilder: public IRBuilder { 125 | public: 126 | 127 | // the method below should not be called from user 128 | 129 | [[nodiscard]] std::shared_ptr buildIR(const lang::CPPMethod& method) const override; 130 | 131 | }; 132 | 133 | /** 134 | * @class DefaultIRBuilderHelper 135 | * @brief a helper class for default ir builder 136 | */ 137 | class DefaultIRBuilderHelper { 138 | public: 139 | 140 | // the method below should not be called from user 141 | 142 | /** 143 | * @brief construct a helper to build the default ir of method 144 | * @param method a cpp method 145 | */ 146 | explicit DefaultIRBuilderHelper(const lang::CPPMethod& method); 147 | 148 | /** 149 | * @return a default ir built from method 150 | */ 151 | std::shared_ptr build(); 152 | 153 | private: 154 | 155 | const lang::CPPMethod& method; ///< the method to build ir 156 | 157 | std::vector> params; ///< the parameter variables 158 | 159 | std::unordered_map> varPool; ///< all variables concerned 160 | 161 | std::unordered_map> stmts; ///< all non-empty statements in this ir 162 | 163 | std::vector> stmtVec; ///< all statements in this ir 164 | 165 | /** 166 | * @brief build parameter variables 167 | */ 168 | void buildParams(); 169 | 170 | /** 171 | * @brief build statements 172 | */ 173 | void buildStmts(); 174 | 175 | /** 176 | * @brief build cfg edges 177 | * @param cfg the default cfg to manipulate 178 | */ 179 | void buildEdges(std::shared_ptr& cfg); 180 | 181 | }; 182 | 183 | } // ir 184 | 185 | #endif //STATIC_ANALYZER_IR_H 186 | -------------------------------------------------------------------------------- /include/ir/Stmt.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_STMT_H 2 | #define STATIC_ANALYZER_STMT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ir/Var.h" 9 | 10 | namespace analyzer::ir { 11 | 12 | namespace lang = language; 13 | 14 | /** 15 | * @class Stmt 16 | * @brief Representation of statements 17 | */ 18 | class Stmt { 19 | public: 20 | 21 | /** 22 | * @return the start line number of the statement (start from 1) 23 | */ 24 | [[nodiscard]] virtual int getStartLine() const = 0; 25 | 26 | /** 27 | * @return the end line number of the statement (included) 28 | */ 29 | [[nodiscard]] virtual int getEndLine() const = 0; 30 | 31 | /** 32 | * @return the start column number of the statement (start from 1) 33 | */ 34 | [[nodiscard]] virtual int getStartColumn() const = 0; 35 | 36 | /** 37 | * @return the end column number of the statement (included) 38 | */ 39 | [[nodiscard]] virtual int getEndColumn() const = 0; 40 | 41 | /** 42 | * @return the method that contains this Stmt 43 | */ 44 | [[nodiscard]] virtual const lang::CPPMethod& getMethod() const = 0; 45 | 46 | /** 47 | * @return the variables defined in this Stmt 48 | */ 49 | [[nodiscard]] virtual std::unordered_set> getDefs() const = 0; 50 | 51 | /** 52 | * @return the variables used in this Stmt 53 | */ 54 | [[nodiscard]] virtual std::unordered_set> getUses() const = 0; 55 | 56 | /** 57 | * @return the string representation of this statement 58 | */ 59 | [[nodiscard]] virtual std::string str() const = 0; 60 | 61 | /** 62 | * @return the clang AST node of this statement, nullptr if this statement is nop 63 | */ 64 | [[nodiscard]] virtual const clang::Stmt* getClangStmt() const = 0; 65 | 66 | virtual ~Stmt() = default; 67 | 68 | }; 69 | 70 | /** 71 | * @class NopStmt 72 | * @brief A default implementation for an empty statement 73 | */ 74 | class NopStmt: public Stmt { 75 | public: 76 | 77 | [[nodiscard]] int getStartLine() const override; 78 | 79 | [[nodiscard]] int getEndLine() const override; 80 | 81 | [[nodiscard]] int getStartColumn() const override; 82 | 83 | [[nodiscard]] int getEndColumn() const override; 84 | 85 | [[nodiscard]] const lang::CPPMethod& getMethod() const override; 86 | 87 | [[nodiscard]] std::unordered_set> getDefs() const override; 88 | 89 | [[nodiscard]] std::unordered_set> getUses() const override; 90 | 91 | [[nodiscard]] std::string str() const override; 92 | 93 | [[nodiscard]] const clang::Stmt* getClangStmt() const override; 94 | 95 | /** 96 | * @brief construct an empty statement in a given method 97 | * @param method a cpp method 98 | */ 99 | explicit NopStmt(const lang::CPPMethod& method); 100 | 101 | private: 102 | 103 | const lang::CPPMethod& method; ///< the method containing this empty statement 104 | 105 | }; 106 | 107 | /** 108 | * @class StmtBuilder 109 | * @brief the interface for statement builder 110 | */ 111 | class StmtBuilder { 112 | public: 113 | 114 | /** 115 | * @brief build a statement in method based on a clang statement 116 | * @param method a cpp method that defines the statement 117 | * @param clangStmt the corresponding clang statement 118 | * @return a statement 119 | */ 120 | [[nodiscard]] virtual std::shared_ptr 121 | buildStmt(const lang::CPPMethod &method, const clang::Stmt* clangStmt, 122 | std::unordered_map>& varPool) = 0; 123 | 124 | /** 125 | * @brief build an empty statement in a given method 126 | * @param method a cpp method that defines the statement 127 | * @return an empty statement 128 | */ 129 | [[nodiscard]] virtual std::shared_ptr 130 | buildEmptyStmt(const lang::CPPMethod &method) = 0; 131 | 132 | virtual ~StmtBuilder() = default; 133 | 134 | }; 135 | 136 | /** 137 | * @class ClangStmtWrapper 138 | * @brief a implementation of Stmt by wrapping the clang Stmt* 139 | */ 140 | class ClangStmtWrapper: public Stmt { 141 | public: 142 | 143 | [[nodiscard]] int getStartLine() const override; 144 | 145 | [[nodiscard]] int getEndLine() const override; 146 | 147 | [[nodiscard]] int getStartColumn() const override; 148 | 149 | [[nodiscard]] int getEndColumn() const override; 150 | 151 | [[nodiscard]] const lang::CPPMethod& getMethod() const override; 152 | 153 | [[nodiscard]] std::unordered_set> getDefs() const override; 154 | 155 | [[nodiscard]] std::unordered_set> getUses() const override; 156 | 157 | [[nodiscard]] std::string str() const override; 158 | 159 | [[nodiscard]] const clang::Stmt* getClangStmt() const override; 160 | 161 | /** 162 | * @brief Construct a statement of method by wrapping a clang statement 163 | * @param method the method containing this method 164 | * @param clangStmt a clang AST node of type Stmt 165 | */ 166 | ClangStmtWrapper(const lang::CPPMethod& method, const clang::Stmt* clangStmt, 167 | std::unordered_map>& varPool); 168 | 169 | private: 170 | 171 | const clang::Stmt* clangStmt; ///< the corresponding clang ast node 172 | 173 | const lang::CPPMethod& method; ///< thd cpp method containing this statement 174 | 175 | std::unordered_set> uses; ///< the variables used in this statement 176 | 177 | std::unordered_set> defs; ///< the variables defined in this statement 178 | 179 | int startLine; ///< the start line of this statement 180 | 181 | int startColumn; ///< the start column of this statement 182 | 183 | int endLine; ///< the end line of this statement 184 | 185 | int endColumn; ///< the end column of this statement 186 | 187 | std::string source; ///< the source code of this statement 188 | 189 | }; 190 | 191 | /** 192 | * @class DefaultStmtBuilder 193 | * @brief the default implementation of statement builder 194 | */ 195 | class DefaultStmtBuilder: public StmtBuilder { 196 | 197 | [[nodiscard]] std::shared_ptr 198 | buildStmt(const lang::CPPMethod& method, const clang::Stmt* clangStmt, 199 | std::unordered_map>& varPool) override; 200 | 201 | [[nodiscard]] std::shared_ptr 202 | buildEmptyStmt(const lang::CPPMethod &method) override; 203 | 204 | }; 205 | 206 | } 207 | 208 | #endif //STATIC_ANALYZER_STMT_H 209 | -------------------------------------------------------------------------------- /include/ir/Var.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_VAR_H 2 | #define STATIC_ANALYZER_VAR_H 3 | 4 | #include 5 | 6 | #include "language/Type.h" 7 | 8 | namespace analyzer::language { 9 | class CPPMethod; 10 | } 11 | 12 | namespace lang = analyzer::language; 13 | 14 | namespace analyzer::ir { 15 | 16 | /** 17 | * @class Var 18 | * @brief Representation of method/constructor parameters and local variables. 19 | */ 20 | class Var { 21 | public: 22 | 23 | /** 24 | * @return the method that defines this variable 25 | */ 26 | [[nodiscard]] virtual const lang::CPPMethod& getMethod() const = 0; 27 | 28 | /** 29 | * @return the name of this variable 30 | */ 31 | [[nodiscard]] virtual const std::string& getName() const = 0; 32 | 33 | /** 34 | * @return the type of this variable 35 | */ 36 | [[nodiscard]] virtual std::shared_ptr getType() const = 0; 37 | 38 | /** 39 | * @return the clang VarDecl ast node of this variable 40 | */ 41 | [[nodiscard]] virtual const clang::VarDecl* getClangVarDecl() const = 0; 42 | 43 | virtual ~Var() = default; 44 | 45 | }; 46 | 47 | /** 48 | * @class VarBuilder 49 | * @brief the interface for variable builder 50 | */ 51 | class VarBuilder { 52 | public: 53 | 54 | /** 55 | * @brief build a var in method defined by varDecl 56 | * @param method the method containing the var 57 | * @param varDecl clang variable declaration 58 | * @return the variable used in this project 59 | */ 60 | [[nodiscard]] virtual std::shared_ptr 61 | buildVar(const lang::CPPMethod& method, const clang::VarDecl* varDecl) = 0; 62 | 63 | virtual ~VarBuilder() = default; 64 | 65 | }; 66 | 67 | /** 68 | * @class ClangVarWrapper 69 | * @brief a implementation of Var by wrapping the clang VarDecl* 70 | */ 71 | class ClangVarWrapper final: public Var { 72 | public: 73 | 74 | [[nodiscard]] const lang::CPPMethod& getMethod() const override; 75 | 76 | [[nodiscard]] const std::string& getName() const override; 77 | 78 | [[nodiscard]] std::shared_ptr getType() const override; 79 | 80 | [[nodiscard]] const clang::VarDecl* getClangVarDecl() const override; 81 | 82 | /** 83 | * Construct a clang wrapper 84 | * @param method the method that defines this variable 85 | * @param varDecl the clang VarDecl of this variable 86 | */ 87 | ClangVarWrapper(const lang::CPPMethod& method, const clang::VarDecl* varDecl); 88 | 89 | private: 90 | 91 | const lang::CPPMethod& method; ///< the method that defines this variable 92 | 93 | const clang::VarDecl* varDecl; ///< the clang VarDecl of this variable 94 | 95 | std::string name; ///< the name of this variable 96 | 97 | std::shared_ptr type; ///< the type of this variable 98 | 99 | }; 100 | 101 | /** 102 | * @class DefaultVarBuilder 103 | * @brief the default implementation of var builder 104 | */ 105 | class DefaultVarBuilder: public VarBuilder { 106 | public: 107 | 108 | // the method below should not be called from user 109 | 110 | std::shared_ptr buildVar(const lang::CPPMethod& method, const clang::VarDecl* varDecl) override; 111 | 112 | }; 113 | 114 | } 115 | 116 | #endif //STATIC_ANALYZER_VAR_H 117 | -------------------------------------------------------------------------------- /include/language/CPPMethod.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_CPPMETHOD_H 2 | #define STATIC_ANALYZER_CPPMETHOD_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "language/Type.h" 11 | #include "ir/IR.h" 12 | 13 | namespace analyzer::language { 14 | 15 | /** 16 | * @class CPPMethod 17 | * @brief Represents methods in the program. Each instance contains various 18 | * information of a method, including method name, signature, method body (IR), etc. 19 | */ 20 | class CPPMethod final { 21 | public: 22 | 23 | /** 24 | * @return the string representation of the method signature (without parameter name) 25 | */ 26 | [[nodiscard]] const std::string& getMethodSignatureAsString() const; 27 | 28 | /** 29 | * @return the source code of the method (declaration + definition) 30 | */ 31 | [[nodiscard]] std::string getMethodSourceCode() const; 32 | 33 | /** 34 | * @return the relative path of the file containing this method 35 | */ 36 | [[nodiscard]] std::string getContainingFilePath() const; 37 | 38 | /** 39 | * @return the number of parameters of this method 40 | */ 41 | [[nodiscard]] std::size_t getParamCount() const; 42 | 43 | /** 44 | * @return the list of parameter types of this method 45 | */ 46 | [[nodiscard]] const std::vector>& getParamTypes() const; 47 | 48 | /** 49 | * @brief get the type of the ith parameter 50 | * @param i the index integer 51 | * @return type of the parameter 52 | */ 53 | [[nodiscard]] std::shared_ptr getParamType(std::size_t i) const; 54 | 55 | /** 56 | * @brief get the name of the ith parameter 57 | * @param i the index integer 58 | * @return name of the parameter 59 | */ 60 | [[nodiscard]] const std::string& getParamName(std::size_t i) const; 61 | 62 | /** 63 | * @return the return type of the method 64 | */ 65 | [[nodiscard]] std::shared_ptr getReturnType() const; 66 | 67 | /** 68 | * @return the intermediate representation of this method body 69 | */ 70 | [[nodiscard]] std::shared_ptr getIR(); 71 | 72 | /** 73 | * @return whether the method is a global method 74 | */ 75 | [[nodiscard]] bool isGlobalMethod() const; 76 | 77 | /** 78 | * @return whether the method is a class static method 79 | */ 80 | [[nodiscard]] bool isClassStaticMethod() const; 81 | 82 | /** 83 | * @return whether the method is a class member method 84 | */ 85 | [[nodiscard]] bool isClassMemberMethod() const; 86 | 87 | /** 88 | * @return whether the method is virtual 89 | */ 90 | [[nodiscard]] bool isVirtual() const; 91 | 92 | // Functions below should not be called from clients 93 | 94 | /** 95 | * Construct a cpp method definition 96 | * @param astUnit the ast unit of the cpp file 97 | * @param funcDecl function declaration ast node 98 | * @param signatureStr method signature 99 | */ 100 | CPPMethod(const std::unique_ptr& astUnit, 101 | const clang::FunctionDecl* funcDecl, std::string signatureStr); 102 | 103 | /** 104 | * @return the ast unit of the cpp file containing this method 105 | */ 106 | [[nodiscard]] const std::unique_ptr& getASTUnit() const; 107 | 108 | /** 109 | * @return function declaration ast node of this method 110 | */ 111 | [[nodiscard]] const clang::FunctionDecl* getFunctionDecl() const; 112 | 113 | /** 114 | * @return the clang cfg of this method 115 | */ 116 | [[nodiscard]] const std::unique_ptr& getClangCFG() const; 117 | 118 | CPPMethod(const CPPMethod&) = delete; 119 | 120 | CPPMethod& operator=(const CPPMethod&) = delete; 121 | 122 | private: 123 | 124 | std::size_t paramCount; ///< the number of parameters 125 | 126 | std::vector> paramTypes; ///< all parameter types 127 | 128 | std::vector paramNames; ///< all parameter names 129 | 130 | std::shared_ptr returnType; ///< the return type 131 | 132 | std::shared_ptr myIR; ///< intermediate representation of the function body 133 | 134 | const std::unique_ptr& astUnit; ///< a clang ast unit 135 | 136 | const clang::FunctionDecl* funcDecl; ///< a function declaration, not implicit and has body 137 | 138 | const std::string signatureStr; ///< a signature string 139 | 140 | std::unique_ptr clangCFG; ///< the clang cfg of this method 141 | }; 142 | 143 | } // language 144 | 145 | #endif //STATIC_ANALYZER_CPPMETHOD_H 146 | -------------------------------------------------------------------------------- /include/language/Type.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_TYPE_H 2 | #define STATIC_ANALYZER_TYPE_H 3 | 4 | #include 5 | 6 | namespace analyzer::language { 7 | 8 | /** 9 | * @class Type 10 | * @brief Represents the type 11 | */ 12 | class Type { 13 | public: 14 | 15 | /** 16 | * @return the string representation of this type 17 | */ 18 | [[nodiscard]] virtual const std::string& getName() const = 0; 19 | 20 | virtual ~Type() = default; 21 | 22 | }; 23 | 24 | /** 25 | * @class ClangTypeWrapper 26 | * @brief a simple implementation of type by wrapping clang qualified type 27 | */ 28 | class ClangTypeWrapper: public Type { 29 | public: 30 | 31 | [[nodiscard]] const std::string& getName() const override; 32 | 33 | // the method below should not be called from user 34 | 35 | /** 36 | * @return the qualified clang type 37 | */ 38 | [[nodiscard]] const clang::QualType& getQualType() const; 39 | 40 | /** 41 | * @brief construct a wrapper for clang::QualType 42 | * @param qualType a clang qualType object 43 | */ 44 | explicit ClangTypeWrapper(const clang::QualType& qualType); 45 | 46 | private: 47 | 48 | std::string typeName; ///< type name string 49 | 50 | clang::QualType qualType; ///< a qualified type 51 | 52 | }; 53 | 54 | /** 55 | * @class TypeBuilder 56 | * @brief build type from clang qual type 57 | */ 58 | class TypeBuilder { 59 | public: 60 | 61 | /** 62 | * @brief build type from clang qual type 63 | * @param qualType a clang qual type 64 | * @return a type used in this analyzer 65 | */ 66 | [[nodiscard]] virtual std::shared_ptr buildType(const clang::QualType& qualType) = 0; 67 | 68 | virtual ~TypeBuilder() = default; 69 | 70 | }; 71 | 72 | /** 73 | * @class DefaultTypeBuilder 74 | * @brief the default implementation of type builder 75 | */ 76 | class DefaultTypeBuilder: public TypeBuilder { 77 | public: 78 | 79 | // the method below should not be called from user 80 | 81 | [[nodiscard]] std::shared_ptr buildType(const clang::QualType& qualType) override; 82 | 83 | }; 84 | 85 | } // language 86 | 87 | #endif //STATIC_ANALYZER_TYPE_H 88 | -------------------------------------------------------------------------------- /include/util/Copyable.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_COPYABLE_H 2 | #define STATIC_ANALYZER_COPYABLE_H 3 | 4 | #include 5 | 6 | namespace analyzer::util { 7 | 8 | /** 9 | * @class Copyable 10 | * @brief A copyable interface 11 | * @tparam T type of copy object 12 | */ 13 | template 14 | class Copyable { 15 | 16 | /** 17 | * @brief Creates and returns a copy of this object. 18 | * @return a copy of this object. 19 | */ 20 | [[nodiscard]] virtual std::shared_ptr copy() const = 0; 21 | 22 | }; 23 | 24 | } // util 25 | 26 | #endif //STATIC_ANALYZER_COPYABLE_H 27 | -------------------------------------------------------------------------------- /include/util/Logger.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_LOGGER_H 2 | #define STATIC_ANALYZER_LOGGER_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace analyzer::util { 10 | 11 | /** 12 | * @class Logger 13 | * @brief Used to output different kinds of log information 14 | */ 15 | class Logger final { 16 | public: 17 | 18 | /** 19 | * @brief Construct a logger using given stream 20 | * @param os output stream to carry log information 21 | * @param enabled whether this logger is enabled 22 | */ 23 | explicit Logger(llvm::raw_ostream* os, bool enabled = true); 24 | 25 | /** 26 | * @brief set the output stream of the logger 27 | * @param outStream pointer to an output stream 28 | */ 29 | void setOutStream(llvm::raw_ostream* outStream); 30 | 31 | /** 32 | * @brief get the current output stream of the logger 33 | * @return pointer to an output stream 34 | */ 35 | [[nodiscard]] llvm::raw_ostream* getOutStream() const; 36 | 37 | /** 38 | * @return whether this logger is enabled 39 | */ 40 | [[nodiscard]] bool isEnabled() const; 41 | 42 | /** 43 | * @brief enable this logger 44 | */ 45 | void enable(); 46 | 47 | /** 48 | * @brief disable this logger 49 | */ 50 | void disable(); 51 | 52 | /** 53 | * @brief log as progress (BLUE) 54 | * @param str log information 55 | */ 56 | void Progress(const std::string& str); 57 | 58 | /** 59 | * @brief log as warning (YELLOW) 60 | * @param str log information 61 | */ 62 | void Warning(const std::string& str); 63 | 64 | /** 65 | * @brief log as info (WHITE) 66 | * @param str log information 67 | */ 68 | void Info(const std::string& str); 69 | 70 | /** 71 | * @brief log as error (RED) 72 | * @param str log information 73 | */ 74 | void Error(const std::string& str); 75 | 76 | /** 77 | * @brief log as debug (CYAN) 78 | * @param str log information 79 | */ 80 | void Debug(const std::string& str); 81 | 82 | /** 83 | * @brief log as success (GREEN) 84 | * @param str log information 85 | */ 86 | void Success(const std::string& str); 87 | 88 | private: 89 | 90 | enum class Color { 91 | RESET, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE 92 | }; ///< ANSI Color Type 93 | 94 | static std::unordered_map colors; ///< ANSI Color Control String 95 | 96 | private: 97 | 98 | llvm::raw_ostream* os; ///< an output stream 99 | 100 | bool enabled; ///< whether the logger is enabled 101 | 102 | }; 103 | 104 | } // util 105 | 106 | #endif //STATIC_ANALYZER_LOGGER_H 107 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(libanalyzer 2 | World.cpp 3 | util/Logger.cpp 4 | ir/DefaultIR.cpp 5 | ir/DefaultIRBuilder.cpp 6 | ir/ClangVarWrapper.cpp 7 | ir/ClangStmtWrapper.cpp 8 | ir/DefaultStmtBuilder.cpp 9 | ir/DefaultVarBuilder.cpp 10 | ir/NopStmt.cpp 11 | language/CPPMethod.cpp 12 | language/Type.cpp 13 | language/DefaultTypeBuilder.cpp 14 | config/DefaultAnalysisConfig.cpp 15 | analysis/Analysis.cpp 16 | analysis/graph/DefaultCFG.cpp 17 | analysis/dataflow/ReachingDefinition.cpp 18 | analysis/dataflow/LiveVariable.cpp 19 | analysis/dataflow/ConstantPropagation.cpp 20 | ) 21 | 22 | target_link_libraries(libanalyzer 23 | ${LLVM_LIBS} 24 | ${CLANG_LIBS} 25 | ) 26 | -------------------------------------------------------------------------------- /lib/World.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "World.h" 10 | 11 | namespace fs = std::filesystem; 12 | namespace tl = clang::tooling; 13 | namespace mt = clang::ast_matchers; 14 | 15 | namespace analyzer { 16 | 17 | World* World::theWorld = nullptr; 18 | 19 | util::Logger World::logger(&llvm::outs()); 20 | 21 | const World& World::get() 22 | { 23 | if (theWorld == nullptr) { 24 | logger.Error("The world is not initialized!"); 25 | throw std::runtime_error("The world is not initialized!"); 26 | } 27 | return *theWorld; 28 | } 29 | 30 | void World::initialize(const std::string& sourceDir, const std::string& includeDir, 31 | const std::string& std, const std::vector& optArgs) 32 | { 33 | if (theWorld != nullptr) { 34 | delete theWorld; 35 | theWorld = nullptr; 36 | } 37 | if (includeDir.empty() && std.empty()) { 38 | theWorld = new World(loadSourceCodes(sourceDir), std::vector()); 39 | } else if (includeDir.empty()) { 40 | theWorld = new World(loadSourceCodes(sourceDir), 41 | std::vector{"-std=" + std}); 42 | } else { 43 | theWorld = new World(loadSourceCodes(sourceDir), 44 | std::vector{"-I" + includeDir, "-std=" + std}); 45 | } 46 | theWorld->args.insert(theWorld->args.end(), optArgs.begin(), optArgs.end()); 47 | theWorld->build(); 48 | } 49 | 50 | util::Logger& World::getLogger() 51 | { 52 | return logger; 53 | } 54 | 55 | void World::setLogger(util::Logger newLogger) 56 | { 57 | logger = newLogger; 58 | } 59 | 60 | World::World(std::unordered_map&& sourceCode, std::vector&& args) 61 | :sourceCode(std::move(sourceCode)), args(std::move(args)) 62 | { 63 | 64 | } 65 | 66 | void World::build() { 67 | logger.Progress("Start building the world..."); 68 | 69 | logger.Progress("Setting the builders ..."); 70 | 71 | logger.Info("Using the default ir builder ..."); 72 | irBuilder = std::make_unique(); 73 | 74 | logger.Info("Using the default type builder ..."); 75 | typeBuilder = std::make_unique(); 76 | 77 | logger.Info("Using the default variable builder ..."); 78 | varBuilder = std::make_unique(); 79 | 80 | logger.Info("Using the default statement builder ..."); 81 | stmtBuilder = std::make_unique(); 82 | 83 | logger.Success("Builders setting finished ..."); 84 | 85 | for (const auto& [filename, content] : this->sourceCode) { 86 | uint64_t t1 = llvm::errs().tell(); 87 | std::unique_ptr p = tl::buildASTFromCodeWithArgs(content, this->args, filename); 88 | uint64_t t2 = llvm::errs().tell(); 89 | if (t1 != t2) { 90 | logger.Error("Detect syntax or semantic error in source file: " + filename); 91 | // throw std::runtime_error("Detect syntax or semantic error in source file: " + filename); 92 | } 93 | astList.emplace_back(std::move(p)); 94 | } 95 | 96 | mainMethod = nullptr; 97 | buildMethodMap(); 98 | 99 | logger.Success("World building finished!"); 100 | } 101 | 102 | void World::buildMethodMap() 103 | { 104 | logger.Progress("Building function list..."); 105 | for (const std::unique_ptr& ast: astList) { 106 | 107 | class FunctionRegister: public mt::MatchFinder::MatchCallback { 108 | private: 109 | 110 | const std::unique_ptr& ast; 111 | 112 | std::vector functions; 113 | 114 | public: 115 | 116 | explicit FunctionRegister(const std::unique_ptr& ast) 117 | : ast(ast) 118 | { 119 | 120 | } 121 | 122 | void run(const mt::MatchFinder::MatchResult& Result) override { 123 | if (const auto* fd = Result.Nodes.getNodeAs("function")) { 124 | if (!ast->getSourceManager().isInSystemHeader(fd->getLocation()) && 125 | fd->isThisDeclarationADefinition() && fd->hasBody() && !fd->isImplicit()) { 126 | functions.emplace_back(fd); 127 | } 128 | } 129 | } 130 | 131 | std::vector &getFunctions() { 132 | return functions; 133 | } 134 | 135 | } functionRegister(ast); 136 | 137 | mt::DeclarationMatcher funcDelMatcher = mt::functionDecl().bind("function"); 138 | mt::MatchFinder finder; 139 | finder.addMatcher(funcDelMatcher, &functionRegister); 140 | finder.matchAST(ast->getASTContext()); 141 | 142 | for (const clang::FunctionDecl* fd : functionRegister.getFunctions()) { 143 | std::string sig = lang::generateFunctionSignature(fd); 144 | logger.Info("Building function " + sig + " ..."); 145 | if (allMethods.find(sig) == allMethods.end()) { 146 | if (fd->getNameAsString() == "main") { 147 | if (!mainMethod) { 148 | mainMethod = std::make_shared(ast, fd, sig); 149 | allMethods.emplace(sig, mainMethod); 150 | } else { 151 | logger.Error("Duplicate definition of main function!"); 152 | throw std::runtime_error("Duplicate definition of main function!"); 153 | } 154 | } else { 155 | allMethods.emplace(sig, std::make_shared(ast, fd, sig)); 156 | } 157 | } else { 158 | logger.Warning("Found another definition for " + sig + ", this definition is ignored!"); 159 | } 160 | } 161 | } 162 | logger.Success("Function list building finished!"); 163 | } 164 | 165 | const std::unordered_map& World::getSourceCode() const 166 | { 167 | return sourceCode; 168 | } 169 | 170 | const std::vector>& World::getAstList() const 171 | { 172 | return astList; 173 | } 174 | 175 | void World::dumpAST(llvm::raw_ostream& out) const 176 | { 177 | for (const std::unique_ptr& ast: astList) { 178 | out << "----------------------------------------\n"; 179 | out << ast->getMainFileName() << ": \n"; 180 | out << "----------------------------------------\n"; 181 | ast->getASTContext().getTranslationUnitDecl()->dump(out); 182 | } 183 | } 184 | 185 | void World::dumpAST(const std::string& fileName, llvm::raw_ostream& out) const 186 | { 187 | auto it = std::find_if(astList.begin(), astList.end(), [&](const auto& ast) -> bool { 188 | return ast->getMainFileName() == fileName; 189 | }); 190 | if (it != astList.end()) { 191 | (*it)->getASTContext().getTranslationUnitDecl()->dump(out); 192 | } else { 193 | logger.Warning(fileName + " doesn't exist! AST dump operation is skipped."); 194 | } 195 | } 196 | 197 | const std::unordered_map>& World::getAllMethods() const 198 | { 199 | return allMethods; 200 | } 201 | 202 | std::shared_ptr World::getMethodBySignature(const std::string& signature) const 203 | { 204 | if (allMethods.find(signature) == allMethods.end()) { 205 | return nullptr; 206 | } 207 | return allMethods.at(signature); 208 | } 209 | 210 | std::shared_ptr World::getMainMethod() const 211 | { 212 | if (mainMethod) { 213 | return mainMethod; 214 | } 215 | return nullptr; 216 | } 217 | 218 | const std::unique_ptr& World::getIRBuilder() const 219 | { 220 | return irBuilder; 221 | } 222 | 223 | const std::unique_ptr& World::getTypeBuilder() const 224 | { 225 | return typeBuilder; 226 | } 227 | 228 | const std::unique_ptr& World::getVarBuilder() const 229 | { 230 | return varBuilder; 231 | } 232 | 233 | const std::unique_ptr& World::getStmtBuilder() const 234 | { 235 | return stmtBuilder; 236 | } 237 | 238 | std::unordered_map loadSourceCodes(const std::string& sourceDir) 239 | { 240 | World::getLogger().Progress("Loading source code from " + sourceDir + "..."); 241 | std::unordered_map result; 242 | for (const auto& entry : fs::recursive_directory_iterator(sourceDir)) { 243 | if (entry.is_regular_file()) { 244 | const fs::path& p = entry.path(); 245 | const std::string& ext = p.extension().string(); 246 | if (ext == ".cpp" || ext == ".cc" || ext == ".c" || ext == ".cxx") { 247 | std::ifstream fileStream(p); 248 | if (!fileStream.is_open()) { 249 | World::getLogger().Error("Fail to open file: " + p.string()); 250 | throw std::runtime_error("Fail to open file: " + p.string()); 251 | } 252 | World::getLogger().Info("Processing " + fs::relative(p).string() + " ..."); 253 | result.insert_or_assign( 254 | fs::relative(p).string(), 255 | std::string(std::istreambuf_iterator(fileStream), 256 | std::istreambuf_iterator())); 257 | fileStream.close(); 258 | } 259 | } 260 | } 261 | World::getLogger().Success("Source loading finished!"); 262 | return result; 263 | } 264 | 265 | namespace language { 266 | 267 | std::string generateFunctionSignature(const clang::FunctionDecl *functionDecl) { 268 | std::string result; 269 | result.append(functionDecl->getReturnType().getAsString()); 270 | result.append(" "); 271 | result.append(functionDecl->getQualifiedNameAsString()); 272 | result.append("("); 273 | bool firstParam = true; 274 | for (const clang::ParmVarDecl *param: functionDecl->parameters()) { 275 | if (!firstParam) { 276 | result.append(", "); 277 | } 278 | result.append(param->getType().getAsString()); 279 | firstParam = false; 280 | } 281 | result.append(")"); 282 | return result; 283 | } 284 | 285 | } 286 | 287 | } // analyzer 288 | -------------------------------------------------------------------------------- /lib/analysis/Analysis.cpp: -------------------------------------------------------------------------------- 1 | #include "analysis/Analysis.h" 2 | 3 | namespace analyzer::analysis { 4 | 5 | const std::unique_ptr& Analysis::getAnalysisConfig() const 6 | { 7 | return analysisConfig; 8 | } 9 | 10 | } 11 | 12 | -------------------------------------------------------------------------------- /lib/analysis/dataflow/LiveVariable.cpp: -------------------------------------------------------------------------------- 1 | #include "analysis/dataflow/LiveVariable.h" 2 | 3 | namespace analyzer::analysis::dataflow { 4 | 5 | LiveVariable::LiveVariable(std::unique_ptr& analysisConfig) 6 | :AnalysisDriver>(analysisConfig) 7 | { 8 | 9 | } 10 | 11 | std::unique_ptr>> 12 | LiveVariable::makeAnalysis(const std::shared_ptr& cfg) const 13 | { 14 | class Analysis: public AbstractDataflowAnalysis> { 15 | public: 16 | 17 | [[nodiscard]] bool isForward() const override 18 | { 19 | return false; 20 | } 21 | 22 | [[nodiscard]] std::shared_ptr> newBoundaryFact() const override 23 | { 24 | return newInitialFact(); 25 | } 26 | 27 | [[nodiscard]] std::shared_ptr> newInitialFact() const override 28 | { 29 | return std::make_shared>(); 30 | } 31 | 32 | void meetInto(std::shared_ptr> fact, 33 | std::shared_ptr> target) const override 34 | { 35 | target->unionN(fact); 36 | } 37 | 38 | [[nodiscard]] bool transferNode( 39 | std::shared_ptr stmt, 40 | std::shared_ptr> in, 41 | std::shared_ptr> out) const override 42 | { 43 | std::shared_ptr> oldIn = in->copy(); 44 | in->setSetFact(out); 45 | for (const std::shared_ptr& def : stmt->getDefs()) { 46 | in->remove(def); 47 | } 48 | for (const std::shared_ptr& use : stmt->getUses()) { 49 | in->add(use); 50 | } 51 | return !in->equalsTo(oldIn); 52 | } 53 | 54 | [[nodiscard]] std::shared_ptr>> 55 | getResult() const override 56 | { 57 | return result; 58 | } 59 | 60 | explicit Analysis(const std::shared_ptr& myCFG) 61 | : AbstractDataflowAnalysis>(myCFG) 62 | { 63 | result = std::make_shared>>(); 64 | } 65 | 66 | private: 67 | 68 | std::shared_ptr>> result; 69 | 70 | }; 71 | 72 | return std::make_unique(cfg); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lib/analysis/dataflow/ReachingDefinition.cpp: -------------------------------------------------------------------------------- 1 | #include "analysis/dataflow/ReachingDefinition.h" 2 | 3 | namespace analyzer::analysis::dataflow { 4 | 5 | ReachingDefinition::ReachingDefinition(std::unique_ptr& analysisConfig) 6 | :AnalysisDriver>(analysisConfig) 7 | { 8 | 9 | } 10 | 11 | std::unique_ptr>> 12 | ReachingDefinition::makeAnalysis(const std::shared_ptr& cfg) const 13 | { 14 | 15 | class Analysis: public AbstractDataflowAnalysis> { 16 | public: 17 | 18 | [[nodiscard]] bool isForward() const override 19 | { 20 | return true; 21 | } 22 | 23 | [[nodiscard]] std::shared_ptr> newBoundaryFact() const override 24 | { 25 | return newInitialFact(); 26 | } 27 | 28 | [[nodiscard]] std::shared_ptr> newInitialFact() const override 29 | { 30 | return std::make_shared>(); 31 | } 32 | 33 | void meetInto(std::shared_ptr> fact, 34 | std::shared_ptr> target) const override 35 | { 36 | target->unionN(fact); 37 | } 38 | 39 | [[nodiscard]] bool transferNode( 40 | std::shared_ptr stmt, 41 | std::shared_ptr> in, 42 | std::shared_ptr> out) const override 43 | { 44 | std::shared_ptr> oldOut = out->copy(); 45 | out->setSetFact(in); 46 | for (const std::shared_ptr& def : stmt->getDefs()) { 47 | out->removeAll(defs.at(def)); 48 | } 49 | if (!stmt->getDefs().empty()) { 50 | out->add(stmt); 51 | } 52 | return !out->equalsTo(oldOut); 53 | } 54 | 55 | [[nodiscard]] std::shared_ptr>> 56 | getResult() const override 57 | { 58 | return result; 59 | } 60 | 61 | explicit Analysis(const std::shared_ptr& myCFG) 62 | : AbstractDataflowAnalysis>(myCFG) 63 | { 64 | computeDefs(cfg->getIR()); 65 | result = std::make_shared>>(); 66 | } 67 | 68 | private: 69 | 70 | std::shared_ptr>> result; 71 | 72 | void computeDefs(const std::shared_ptr& myIR) 73 | { 74 | for (const std::shared_ptr& var : myIR->getVars()) { 75 | defs.emplace(var, std::make_shared>()); 76 | } 77 | for (const std::shared_ptr& stmt : myIR->getStmts()) { 78 | for (const std::shared_ptr& var : stmt->getDefs()) { 79 | defs.at(var)->add(stmt); 80 | } 81 | } 82 | } 83 | 84 | std::unordered_map, std::shared_ptr>> defs; 85 | 86 | }; 87 | 88 | return std::make_unique(cfg); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /lib/analysis/graph/DefaultCFG.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "analysis/graph/CFG.h" 4 | 5 | namespace analyzer::analysis::graph { 6 | 7 | DefaultCFGEdge::DefaultCFGEdge(std::shared_ptr source, std::shared_ptr target, Kind kind) 8 | :source(std::move(source)), target(std::move(target)), kind(kind) 9 | { 10 | 11 | } 12 | 13 | CFGEdge::Kind DefaultCFGEdge::getKind() const 14 | { 15 | return kind; 16 | } 17 | 18 | std::shared_ptr DefaultCFGEdge::getSource() const 19 | { 20 | return source; 21 | } 22 | 23 | std::shared_ptr DefaultCFGEdge::getTarget() const 24 | { 25 | return target; 26 | } 27 | 28 | DefaultCFG::DefaultCFG() 29 | :edgeNum(0) 30 | { 31 | 32 | } 33 | 34 | std::shared_ptr DefaultCFG::getEntry() const 35 | { 36 | return entry; 37 | } 38 | 39 | std::shared_ptr DefaultCFG::getExit() const 40 | { 41 | return exit; 42 | } 43 | 44 | bool DefaultCFG::hasEdge(std::shared_ptr source, std::shared_ptr target) const 45 | { 46 | std::unordered_set>&& succs = getSuccsOf(source); 47 | return succs.find(target) != succs.end(); 48 | } 49 | 50 | std::unordered_set> DefaultCFG::getPredsOf(std::shared_ptr stmt) const 51 | { 52 | std::unordered_set> result; 53 | for (const std::shared_ptr& inEdge : getInEdgesOf(stmt)) { 54 | result.emplace(inEdge->getSource()); 55 | } 56 | return result; 57 | } 58 | 59 | std::unordered_set> DefaultCFG::getSuccsOf(std::shared_ptr stmt) const 60 | { 61 | std::unordered_set> result; 62 | for (const std::shared_ptr& outEdge : getOutEdgesOf(stmt)) { 63 | result.emplace(outEdge->getTarget()); 64 | } 65 | return result; 66 | } 67 | 68 | std::unordered_set> DefaultCFG::getInEdgesOf(std::shared_ptr stmt) const 69 | { 70 | std::unordered_set> result; 71 | result.reserve(inEdges.count(stmt)); 72 | auto range = inEdges.equal_range(stmt); 73 | for (auto&& it = range.first; it != range.second; it++) { 74 | result.emplace(it->second); 75 | } 76 | return result; 77 | } 78 | 79 | std::unordered_set> DefaultCFG::getOutEdgesOf(std::shared_ptr stmt) const 80 | { 81 | std::unordered_set> result; 82 | result.reserve(outEdges.count(stmt)); 83 | auto range = outEdges.equal_range(stmt); 84 | for (auto&& it = range.first; it != range.second; it++) { 85 | result.emplace(it->second); 86 | } 87 | return result; 88 | } 89 | 90 | void DefaultCFG::addEdge(const std::shared_ptr& edge) 91 | { 92 | inEdges.emplace(edge->getTarget(), edge); 93 | outEdges.emplace(edge->getSource(), edge); 94 | edgeNum++; 95 | } 96 | 97 | std::size_t DefaultCFG::getEdgeNum() const 98 | { 99 | return edgeNum; 100 | } 101 | 102 | void DefaultCFG::setEntry(const std::shared_ptr& entry) 103 | { 104 | this->entry = entry; 105 | } 106 | 107 | void DefaultCFG::setExit(const std::shared_ptr& exit) 108 | { 109 | this->exit = exit; 110 | } 111 | 112 | std::shared_ptr DefaultCFG::getIR() const 113 | { 114 | return myIR.lock(); 115 | } 116 | 117 | void DefaultCFG::setIR(const std::shared_ptr& myIR) 118 | { 119 | this->myIR = myIR; 120 | } 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /lib/config/DefaultAnalysisConfig.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "config/AnalysisConfig.h" 4 | 5 | namespace analyzer::config { 6 | 7 | DefaultAnalysisConfig::DefaultAnalysisConfig(std::string description) 8 | :description(std::move(description)) 9 | { 10 | 11 | } 12 | 13 | const std::string& DefaultAnalysisConfig::getDescription() const 14 | { 15 | return description; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /lib/ir/ClangStmtWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "language/CPPMethod.h" 6 | #include "ir/Stmt.h" 7 | #include "World.h" 8 | 9 | namespace analyzer::ir { 10 | 11 | ClangStmtWrapper::ClangStmtWrapper(const lang::CPPMethod& method, 12 | const clang::Stmt* clangStmt, std::unordered_map>& varPool) 13 | : method(method), clangStmt(clangStmt) 14 | { 15 | 16 | class StmtProcessor: public clang::RecursiveASTVisitor { 17 | private: 18 | 19 | std::unordered_map>& vars; 20 | 21 | std::unordered_set>& uses; 22 | 23 | std::unordered_set>& defs; 24 | 25 | const lang::CPPMethod& method; 26 | 27 | public: 28 | 29 | StmtProcessor(std::unordered_map>& vars, 30 | std::unordered_set> &uses, 31 | std::unordered_set> &defs, 32 | const lang::CPPMethod& method) 33 | :vars(vars), uses(uses), defs(defs), method(method) 34 | { 35 | 36 | } 37 | 38 | bool VisitVarDecl(clang::VarDecl* D) 39 | { 40 | if (vars.find(D) == vars.end()) { 41 | vars.emplace(D, World::get().getVarBuilder()->buildVar(method, D)); 42 | } 43 | if (D->hasInit()) { 44 | defs.emplace(vars.at(D)); 45 | } 46 | return true; 47 | } 48 | 49 | bool VisitDeclRefExpr(clang::DeclRefExpr* S) 50 | { 51 | auto* varDecl = clang::dyn_cast(S->getDecl()); 52 | if (!varDecl) { 53 | return true; 54 | } 55 | if (vars.find(varDecl) == vars.end()) { 56 | vars.emplace(varDecl, World::get().getVarBuilder()->buildVar(method, varDecl)); 57 | } 58 | std::shared_ptr var = vars.at(varDecl); 59 | clang::DynTypedNodeList parents = method.getASTUnit()->getASTContext().getParents(*S); 60 | if (parents.empty()) { 61 | if (defs.find(var) == defs.end()) { 62 | defs.emplace(var); 63 | } 64 | return true; 65 | } 66 | const auto* parentStmt = parents[0].get(); 67 | while (parentStmt) { 68 | const auto* parenExpr = clang::dyn_cast(parentStmt); 69 | if (parenExpr) { 70 | parents = method.getASTUnit()->getASTContext().getParents(*parenExpr); 71 | if (parents.empty()) { 72 | break; 73 | } 74 | parentStmt = parents[0].get(); 75 | continue; 76 | } 77 | const auto* implicitCast = clang::dyn_cast(parentStmt); 78 | if (implicitCast) { 79 | if (implicitCast->getCastKind() == clang::CK_LValueToRValue) { 80 | if (uses.find(var) == uses.end()) { 81 | uses.emplace(var); 82 | } 83 | return true; 84 | } 85 | } 86 | break; 87 | } 88 | if (defs.find(var) == defs.end()) { 89 | defs.emplace(var); 90 | } 91 | return true; 92 | } 93 | 94 | bool VisitUnaryOperator(clang::UnaryOperator* S) 95 | { 96 | if (auto* declRef = clang::dyn_cast(S->getSubExpr())) { 97 | if (auto* varDecl = clang::dyn_cast(declRef->getDecl())) { 98 | if (vars.find(varDecl) == vars.end()) { 99 | vars.emplace(varDecl, World::get().getVarBuilder()->buildVar(method, varDecl)); 100 | } 101 | uses.emplace(vars.at(varDecl)); 102 | } 103 | } 104 | return true; 105 | } 106 | 107 | bool VisitCompoundAssignOperator(clang::CompoundAssignOperator *S) 108 | { 109 | if (auto* declRef = clang::dyn_cast(S->getLHS())) { 110 | if (auto* varDecl = clang::dyn_cast(declRef->getDecl())) { 111 | if (vars.find(varDecl) == vars.end()) { 112 | vars.emplace(varDecl, World::get().getVarBuilder()->buildVar(method, varDecl)); 113 | } 114 | uses.emplace(vars.at(varDecl)); 115 | } 116 | } 117 | return true; 118 | } 119 | 120 | } stmtProcessor(varPool, uses, defs, method); 121 | 122 | stmtProcessor.TraverseStmt(const_cast(clangStmt)); 123 | 124 | const clang::SourceManager& sourceManager = method.getASTUnit()->getSourceManager(); 125 | 126 | startLine = static_cast(sourceManager.getPresumedLineNumber( 127 | sourceManager.getExpansionLoc(clangStmt->getBeginLoc()))); 128 | endLine = static_cast(sourceManager.getPresumedLineNumber( 129 | sourceManager.getExpansionLoc(clangStmt->getEndLoc()))); 130 | startColumn = static_cast(sourceManager.getPresumedColumnNumber( 131 | sourceManager.getExpansionLoc(clangStmt->getBeginLoc()))); 132 | endColumn = static_cast(sourceManager.getPresumedColumnNumber( 133 | sourceManager.getExpansionLoc(clangStmt->getEndLoc()))); 134 | 135 | clang::SourceRange&& range = clangStmt->getSourceRange(); 136 | clang::CharSourceRange&& expansionRange = sourceManager.getExpansionRange(range); 137 | const clang::LangOptions& langOptions = method.getASTUnit()->getASTContext().getLangOpts(); 138 | source = clang::Lexer::getSourceText(expansionRange,sourceManager, langOptions).str(); 139 | } 140 | 141 | int ClangStmtWrapper::getStartLine() const 142 | { 143 | return startLine; 144 | } 145 | 146 | int ClangStmtWrapper::getEndLine() const 147 | { 148 | return endLine; 149 | } 150 | 151 | int ClangStmtWrapper::getStartColumn() const 152 | { 153 | return startColumn; 154 | } 155 | 156 | int ClangStmtWrapper::getEndColumn() const 157 | { 158 | return endColumn; 159 | } 160 | 161 | const lang::CPPMethod& ClangStmtWrapper::getMethod() const 162 | { 163 | return method; 164 | } 165 | 166 | std::unordered_set> ClangStmtWrapper::getDefs() const 167 | { 168 | return defs; 169 | } 170 | 171 | std::unordered_set> ClangStmtWrapper::getUses() const 172 | { 173 | return uses; 174 | } 175 | 176 | std::string ClangStmtWrapper::str() const 177 | { 178 | return source; 179 | } 180 | 181 | const clang::Stmt* ClangStmtWrapper::getClangStmt() const 182 | { 183 | return clangStmt; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /lib/ir/ClangVarWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ir/Var.h" 4 | #include "World.h" 5 | 6 | namespace analyzer::ir { 7 | 8 | ClangVarWrapper::ClangVarWrapper(const lang::CPPMethod& method, const clang::VarDecl* varDecl) 9 | :method(method), varDecl(varDecl) 10 | { 11 | name = varDecl->getNameAsString(); 12 | type = World::get().getTypeBuilder()->buildType(varDecl->getType()); 13 | } 14 | 15 | const lang::CPPMethod& ClangVarWrapper::getMethod() const 16 | { 17 | return method; 18 | } 19 | 20 | const std::string& ClangVarWrapper::getName() const 21 | { 22 | return name; 23 | } 24 | 25 | std::shared_ptr ClangVarWrapper::getType() const 26 | { 27 | return type; 28 | } 29 | 30 | const clang::VarDecl* ClangVarWrapper::getClangVarDecl() const 31 | { 32 | return varDecl; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /lib/ir/DefaultIR.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ir/IR.h" 5 | #include "language/CPPMethod.h" 6 | 7 | namespace analyzer::ir { 8 | 9 | DefaultIR::DefaultIR(const lang::CPPMethod& method, 10 | std::vector> params, 11 | std::vector> vars, 12 | std::vector> stmts, 13 | const std::shared_ptr& cfg) 14 | :method(method), params(std::move(params)), vars(std::move(vars)), 15 | stmts(std::move(stmts)), cfg(cfg) 16 | { 17 | std::sort(this->stmts.begin(), this->stmts.end(), 18 | [](const std::shared_ptr& s1, const std::shared_ptr& s2) -> bool { 19 | return s1->getStartLine() < s2->getStartLine(); 20 | }); 21 | } 22 | 23 | const lang::CPPMethod& DefaultIR::getMethod() const 24 | { 25 | return method; 26 | } 27 | 28 | std::shared_ptr DefaultIR::getCFG() const 29 | { 30 | return cfg; 31 | } 32 | 33 | std::vector> DefaultIR::getParams() const 34 | { 35 | return params; 36 | } 37 | 38 | std::vector> DefaultIR::getVars() const 39 | { 40 | return vars; 41 | } 42 | 43 | std::vector> DefaultIR::getStmts() const 44 | { 45 | return stmts; 46 | } 47 | 48 | } // ir -------------------------------------------------------------------------------- /lib/ir/DefaultIRBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ir/IR.h" 4 | #include "ir/Stmt.h" 5 | #include "language/CPPMethod.h" 6 | #include "World.h" 7 | 8 | namespace analyzer::ir { 9 | 10 | namespace lang = language; 11 | namespace graph = analysis::graph; 12 | 13 | std::shared_ptr DefaultIRBuilder::buildIR(const lang::CPPMethod& method) const 14 | { 15 | return DefaultIRBuilderHelper(method).build(); 16 | } 17 | 18 | DefaultIRBuilderHelper::DefaultIRBuilderHelper(const lang::CPPMethod& method) 19 | :method(method) 20 | { 21 | 22 | } 23 | 24 | std::shared_ptr DefaultIRBuilderHelper::build() 25 | { 26 | World::getLogger().Progress("Using default ir builder to build ir for " 27 | + method.getMethodSignatureAsString() + " ..."); 28 | World::getLogger().Info("Building parameter variables ..."); 29 | buildParams(); 30 | World::getLogger().Info("Building statements in this method ..."); 31 | buildStmts(); 32 | World::getLogger().Info("Building CFG for this method ..."); 33 | std::shared_ptr cfg = std::make_shared(); 34 | buildEdges(cfg); 35 | World::getLogger().Info("Encapsulating the above parts to form ir ..."); 36 | for (const auto& [_, s]: stmts) { 37 | stmtVec.emplace_back(s); 38 | } 39 | std::vector> vars; 40 | vars.reserve(varPool.size()); 41 | for (auto& [_, var] : varPool) { 42 | vars.emplace_back(var); 43 | } 44 | std::shared_ptr myIR = std::make_shared( 45 | method, std::move(params), std::move(vars), std::move(stmtVec), cfg); 46 | cfg->setIR(myIR); 47 | World::getLogger().Success("IR of " + method.getMethodSignatureAsString() + 48 | " has been built by default ir builder ..."); 49 | return myIR; 50 | } 51 | 52 | void DefaultIRBuilderHelper::buildParams() 53 | { 54 | std::size_t n = method.getParamCount(); 55 | for (std::size_t i = 0; i < n; i++) { 56 | std::shared_ptr pi = World::get().getVarBuilder()->buildVar(method, 57 | method.getFunctionDecl()->getParamDecl(i)); 58 | params.emplace_back(pi); 59 | varPool.emplace(pi->getClangVarDecl(), pi); 60 | } 61 | } 62 | 63 | void DefaultIRBuilderHelper::buildStmts() 64 | { 65 | for (const clang::CFGBlock* block: method.getClangCFG()->const_nodes()) { 66 | for (const clang::CFGElement& element: *block) { 67 | if (std::optional cfgStmt = element.getAs()) { 68 | std::shared_ptr s = World::get(). 69 | getStmtBuilder()->buildStmt(method, cfgStmt->getStmt(), varPool); 70 | stmts.emplace(cfgStmt->getStmt(), s); 71 | } 72 | } 73 | } 74 | } 75 | 76 | void DefaultIRBuilderHelper::buildEdges(std::shared_ptr& cfg) 77 | { 78 | 79 | std::shared_ptr entry = World::get().getStmtBuilder()->buildEmptyStmt(method); 80 | cfg->setEntry(entry); 81 | std::shared_ptr exit = World::get().getStmtBuilder()->buildEmptyStmt(method); 82 | cfg->setExit(exit); 83 | 84 | std::unordered_map> emptyBlocks; 85 | 86 | for (const clang::CFGBlock* block: method.getClangCFG()->const_nodes()) { 87 | if (block == &method.getClangCFG()->getExit()) { 88 | continue; 89 | } 90 | 91 | std::shared_ptr source = nullptr; 92 | std::shared_ptr target = nullptr; 93 | 94 | // build fall through edges for this block 95 | for (const clang::CFGElement& element: *block) { 96 | if (std::optional cfgStmt = element.getAs()) { 97 | target = stmts.at(cfgStmt->getStmt()); 98 | if (source) { 99 | cfg->addEdge(std::make_shared( 100 | source, target, graph::CFGEdge::Kind::FALL_THROUGH_EDGE)); 101 | } 102 | source = target; 103 | } 104 | } 105 | 106 | graph::CFGEdge::Kind kind = graph::CFGEdge::Kind::UNKNOWN_EDGE; 107 | 108 | if (block == &method.getClangCFG()->getEntry()) { 109 | kind = graph::CFGEdge::Kind::ENTRY_EDGE; 110 | source = entry; 111 | } else { 112 | kind = graph::CFGEdge::Kind::JUMP_EDGE; 113 | if (!target) { 114 | if (emptyBlocks.find(block) == emptyBlocks.end()) { 115 | emptyBlocks.emplace(block, World::get().getStmtBuilder()->buildEmptyStmt(method)); 116 | stmtVec.emplace_back(emptyBlocks.at(block)); 117 | } 118 | target = emptyBlocks.at(block); 119 | } 120 | source = target; 121 | } 122 | 123 | for (const clang::CFGBlock* succ : block->succs()) { 124 | if (!succ) { 125 | continue; 126 | } 127 | if (succ == &method.getClangCFG()->getExit()) { 128 | cfg->addEdge(std::make_shared 129 | (source, exit, graph::CFGEdge::Kind::EXIT_EDGE)); 130 | } else { 131 | bool isEmpty = true; 132 | for (const clang::CFGElement& element: *succ) { 133 | if (std::optional cfgStmt = element.getAs()) { 134 | target = stmts.at(cfgStmt->getStmt()); 135 | cfg->addEdge(std::make_shared 136 | (source, target, kind)); 137 | isEmpty = false; 138 | break; 139 | } 140 | } 141 | if (isEmpty) { 142 | if (emptyBlocks.find(succ) == emptyBlocks.end()) { 143 | emptyBlocks.emplace(succ, World::get().getStmtBuilder()->buildEmptyStmt(method)); 144 | stmtVec.emplace_back(emptyBlocks.at(succ)); 145 | } 146 | target = emptyBlocks.at(succ); 147 | cfg->addEdge(std::make_shared 148 | (source, target, kind)); 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /lib/ir/DefaultStmtBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "ir/Stmt.h" 2 | 3 | namespace analyzer::ir { 4 | 5 | std::shared_ptr DefaultStmtBuilder::buildStmt(const lang::CPPMethod &method, 6 | const clang::Stmt *clangStmt, std::unordered_map>& varPool) 7 | { 8 | return std::make_shared(method, clangStmt, varPool); 9 | } 10 | 11 | std::shared_ptr DefaultStmtBuilder::buildEmptyStmt(const lang::CPPMethod &method) 12 | { 13 | return std::make_shared(method); 14 | } 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /lib/ir/DefaultVarBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "ir/Var.h" 2 | 3 | namespace analyzer::ir { 4 | 5 | std::shared_ptr DefaultVarBuilder::buildVar(const lang::CPPMethod& method, const clang::VarDecl* varDecl) 6 | { 7 | return std::make_shared(method, varDecl); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /lib/ir/NopStmt.cpp: -------------------------------------------------------------------------------- 1 | #include "ir/Stmt.h" 2 | 3 | namespace analyzer::ir { 4 | 5 | NopStmt::NopStmt(const lang::CPPMethod& method) 6 | :method(method) 7 | { 8 | 9 | } 10 | 11 | const lang::CPPMethod& NopStmt::getMethod() const 12 | { 13 | return method; 14 | } 15 | 16 | std::unordered_set> NopStmt::getDefs() const 17 | { 18 | return {}; 19 | } 20 | 21 | std::unordered_set> NopStmt::getUses() const 22 | { 23 | return {}; 24 | } 25 | 26 | int NopStmt::getStartLine() const 27 | { 28 | return -1; 29 | } 30 | 31 | int NopStmt::getEndLine() const 32 | { 33 | return -1; 34 | } 35 | 36 | int NopStmt::getStartColumn() const 37 | { 38 | return -1; 39 | } 40 | 41 | int NopStmt::getEndColumn() const 42 | { 43 | return -1; 44 | } 45 | 46 | std::string NopStmt::str() const 47 | { 48 | return "nop"; 49 | } 50 | 51 | const clang::Stmt* NopStmt::getClangStmt() const 52 | { 53 | return nullptr; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/language/CPPMethod.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "World.h" 6 | #include "language/CPPMethod.h" 7 | 8 | namespace analyzer::language { 9 | 10 | CPPMethod::CPPMethod(const std::unique_ptr &astUnit, 11 | const clang::FunctionDecl *funcDecl, std::string signatureStr) 12 | :astUnit(astUnit), funcDecl(funcDecl), signatureStr(std::move(signatureStr)), myIR(nullptr) 13 | { 14 | paramCount = funcDecl->getNumParams(); 15 | for (unsigned int i = 0; i < paramCount; i++) { 16 | paramTypes.emplace_back( 17 | World::get().getTypeBuilder()->buildType(funcDecl->getParamDecl(i)->getType()) 18 | ); 19 | paramNames.emplace_back(funcDecl->getParamDecl(i)->getNameAsString()); 20 | } 21 | returnType = World::get().getTypeBuilder()->buildType(funcDecl->getReturnType()); 22 | clangCFG = clang::CFG::buildCFG(funcDecl, funcDecl->getBody(), 23 | &funcDecl->getASTContext(), 24 | clang::CFG::BuildOptions()); 25 | } 26 | 27 | const std::unique_ptr& CPPMethod::getASTUnit() const 28 | { 29 | return astUnit; 30 | } 31 | 32 | 33 | const clang::FunctionDecl* CPPMethod::getFunctionDecl() const 34 | { 35 | return funcDecl; 36 | } 37 | 38 | const std::unique_ptr& CPPMethod::getClangCFG() const 39 | { 40 | return clangCFG; 41 | } 42 | 43 | const std::string& CPPMethod::getMethodSignatureAsString() const 44 | { 45 | return signatureStr; 46 | } 47 | 48 | std::string CPPMethod::getMethodSourceCode() const 49 | { 50 | clang::SourceRange&& range = funcDecl->getSourceRange(); 51 | const clang::SourceManager& sourceManager = astUnit->getSourceManager(); 52 | clang::CharSourceRange&& expansionRange = sourceManager.getExpansionRange(range); 53 | const clang::LangOptions& langOptions = astUnit->getASTContext().getLangOpts(); 54 | llvm::StringRef sourceCode = clang::Lexer::getSourceText( 55 | expansionRange,sourceManager, langOptions); 56 | return sourceCode.str(); 57 | } 58 | 59 | std::string CPPMethod::getContainingFilePath() const 60 | { 61 | return astUnit->getSourceManager().getFilename(funcDecl->getLocation()).str(); 62 | } 63 | 64 | std::size_t CPPMethod::getParamCount() const 65 | { 66 | return paramCount; 67 | } 68 | 69 | const std::vector>& CPPMethod::getParamTypes() const 70 | { 71 | return paramTypes; 72 | } 73 | 74 | std::shared_ptr CPPMethod::getParamType(std::size_t i) const 75 | { 76 | return paramTypes.at(i); 77 | } 78 | 79 | const std::string& CPPMethod::getParamName(std::size_t i) const 80 | { 81 | return paramNames.at(i); 82 | } 83 | 84 | std::shared_ptr CPPMethod::getReturnType() const 85 | { 86 | return returnType; 87 | } 88 | 89 | std::shared_ptr CPPMethod::getIR() 90 | { 91 | if (!myIR) { 92 | myIR = World::get().getIRBuilder()->buildIR(*this); 93 | } 94 | return myIR; 95 | } 96 | 97 | bool CPPMethod::isGlobalMethod() const 98 | { 99 | return funcDecl->isGlobal(); 100 | } 101 | 102 | bool CPPMethod::isClassStaticMethod() const 103 | { 104 | return funcDecl->isStatic(); 105 | } 106 | 107 | bool CPPMethod::isClassMemberMethod() const 108 | { 109 | return funcDecl->isCXXClassMember(); 110 | } 111 | 112 | bool CPPMethod::isVirtual() const 113 | { 114 | if(const auto* cxxMethodDecl = clang::dyn_cast(funcDecl)) { 115 | return cxxMethodDecl->isVirtual(); 116 | } 117 | return false; 118 | } 119 | 120 | } // language 121 | -------------------------------------------------------------------------------- /lib/language/DefaultTypeBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "language/Type.h" 2 | 3 | namespace analyzer::language { 4 | 5 | std::shared_ptr DefaultTypeBuilder::buildType(const clang::QualType& qualType) 6 | { 7 | return std::make_shared(qualType); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /lib/language/Type.cpp: -------------------------------------------------------------------------------- 1 | #include "language/Type.h" 2 | 3 | namespace analyzer::language { 4 | 5 | ClangTypeWrapper::ClangTypeWrapper(const clang::QualType &qualType) 6 | : qualType(qualType) 7 | { 8 | typeName = qualType.getAsString(); 9 | } 10 | 11 | const std::string& ClangTypeWrapper::getName() const 12 | { 13 | return typeName; 14 | } 15 | 16 | const clang::QualType& ClangTypeWrapper::getQualType() const 17 | { 18 | return qualType; 19 | } 20 | 21 | } // language -------------------------------------------------------------------------------- /lib/util/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "util/Logger.h" 2 | 3 | namespace analyzer::util { 4 | 5 | std::unordered_map Logger::colors = { 6 | {Color::RESET, "\033[0m"}, 7 | {Color::BLACK, "\033[30m"}, 8 | {Color::RED, "\033[31m"}, 9 | {Color::GREEN, "\033[32m"}, 10 | {Color::YELLOW, "\033[33m"}, 11 | {Color::BLUE, "\033[34m"}, 12 | {Color::MAGENTA, "\033[35m"}, 13 | {Color::CYAN, "\033[36m"}, 14 | {Color::WHITE, "\033[37m"} 15 | }; 16 | 17 | Logger::Logger(llvm::raw_ostream* os, bool enabled) 18 | :os(os), enabled(enabled) 19 | { 20 | 21 | } 22 | 23 | void Logger::setOutStream(llvm::raw_ostream* outStream) 24 | { 25 | os = outStream; 26 | } 27 | 28 | llvm::raw_ostream* Logger::getOutStream() const 29 | { 30 | return os; 31 | } 32 | 33 | bool Logger::isEnabled() const 34 | { 35 | return enabled; 36 | } 37 | 38 | void Logger::enable() 39 | { 40 | enabled = true; 41 | } 42 | 43 | void Logger::disable() 44 | { 45 | enabled = false; 46 | } 47 | 48 | void Logger::Progress(const std::string& str) 49 | { 50 | if (!enabled) { 51 | return; 52 | } 53 | *os << colors.at(Color::CYAN) << "[ Progress ] " << str 54 | << colors.at(Color::RESET) << "\n"; 55 | } 56 | 57 | void Logger::Warning(const std::string& str) 58 | { 59 | if (!enabled) { 60 | return; 61 | } 62 | *os << colors.at(Color::YELLOW) << "[ Warning ] " << str 63 | << colors.at(Color::RESET) << "\n"; 64 | } 65 | 66 | void Logger::Info(const std::string& str) 67 | { 68 | if (!enabled) { 69 | return; 70 | } 71 | *os << "[ Info ] " << str << "\n"; 72 | } 73 | 74 | void Logger::Error(const std::string& str) 75 | { 76 | if (!enabled) { 77 | return; 78 | } 79 | *os << colors.at(Color::RED) << "[ Error ] " << str 80 | << colors.at(Color::RESET) << "\n"; 81 | } 82 | 83 | void Logger::Debug(const std::string& str) 84 | { 85 | if (!enabled) { 86 | return; 87 | } 88 | *os << colors.at(Color::BLUE) << "[ Debug ] " << str 89 | << colors.at(Color::RESET) << "\n"; 90 | } 91 | 92 | void Logger::Success(const std::string& str) 93 | { 94 | if (!enabled) { 95 | return; 96 | } 97 | *os << colors.at(Color::GREEN) << "[ Success ] " << str 98 | << colors.at(Color::RESET) << "\n"; 99 | } 100 | 101 | } // util 102 | -------------------------------------------------------------------------------- /resources/dataflow/ConstPropagation/ConstPropagationTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @class ConstPropagation 3 | * @brief Test case for constant propagation analysis. 4 | */ 5 | class ConstPropagation { 6 | 7 | int dummy() { 8 | int x; 9 | int y; 10 | y = x = 1; 11 | return y; 12 | } 13 | 14 | void typeCast() { 15 | char a = 'a'; 16 | int x = (int)(char)10000000; 17 | long long y = (long long)1000000000000000000; 18 | unsigned long long z = (unsigned long long)(unsigned int)-1; 19 | } 20 | 21 | int ifElse(int n) { 22 | int x, y, z, a; 23 | int u, v; 24 | if (n > 0) { 25 | x = 1; 26 | y = 3; 27 | if (n == 1) { 28 | u = 2; 29 | v = 2; 30 | } 31 | } else { 32 | x = 2; 33 | y = 3; 34 | if (n == 0) { 35 | v = 3; 36 | } 37 | } 38 | z = x + y; 39 | return z; 40 | } 41 | 42 | int binaryOp(int n) { 43 | int x = 15, y = 2; 44 | int neg = -x; 45 | int add = x + y; 46 | int sub = x - y; 47 | int mul = x * y; 48 | int div = x / y; 49 | int mod = x % y; 50 | int And = x & y; 51 | int Or = x | y; 52 | int Xor = x ^ y; 53 | int LShift = x << y; 54 | int RShift = x >> y; 55 | int zero = x - (div * y) - mod; 56 | n /= zero; 57 | return n; 58 | } 59 | 60 | int loop() { 61 | int a = 10, b = 2, c; 62 | while (a > b) { 63 | c = b; 64 | --a; 65 | } 66 | return c; 67 | } 68 | 69 | void incDec() { 70 | int x = 0; 71 | int y = 0; 72 | x++; 73 | y--; 74 | int a = x++; 75 | int b = ++x; 76 | int c = y--; 77 | int d = --y; 78 | } 79 | 80 | int array() { 81 | int a[2][2]; 82 | int i = 2, j = 3; 83 | return a[i][j]; 84 | } 85 | 86 | int foo(int n) { 87 | return n; 88 | } 89 | 90 | int call() { 91 | int zero = 0; 92 | return foo(1 / zero); 93 | } 94 | 95 | }; 96 | -------------------------------------------------------------------------------- /resources/dataflow/LiveVar/LiveVarTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @class LiveVar 3 | * @brief Test case for live variable analysis. 4 | */ 5 | class LiveVar { 6 | 7 | int ifElse(int m, int n, int k) { 8 | int x = m; 9 | if (n > 0) { 10 | return x + n; 11 | } else { 12 | return k + n; 13 | } 14 | } 15 | 16 | int loop(int x) { 17 | int y = 0; 18 | for (int i = 0; i < x; ++i) { 19 | y += 20; 20 | } 21 | return y; 22 | } 23 | 24 | int loopBranch(int m, int n, int k) { 25 | int a; 26 | for (int i = m - 1; i < k; i++) { 27 | if (i >= n) { 28 | a = n; 29 | } 30 | a = a + i; 31 | } 32 | return a; 33 | } 34 | 35 | void branchLoop(int c, bool d) { 36 | int x, y, z; 37 | x = 1; 38 | y = 2; 39 | if (c > 0) { 40 | do { 41 | x = y + 1; 42 | y = 2 * z; 43 | if (d) { 44 | x = y + z; 45 | } 46 | z = 1; 47 | } while (c < 20); 48 | } 49 | z = x; 50 | } 51 | 52 | void unaryOperator() { 53 | int a = 1; 54 | a++; 55 | a = 2; 56 | int* c; 57 | c = &a; 58 | } 59 | 60 | int multipleParen() { 61 | int x = 0; 62 | int y = (((x))); 63 | return y; 64 | } 65 | 66 | }; 67 | -------------------------------------------------------------------------------- /resources/dataflow/ReachDef/ReachDefTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @class ReachDef 3 | * @brief Test case for reaching definition analysis. 4 | */ 5 | class ReachDef { 6 | 7 | int foo(int a, int b, int c) { 8 | int x; 9 | if (a > 0) { 10 | x = a; 11 | } else { 12 | x = b; 13 | } 14 | int y = x; 15 | x = c; 16 | return x; 17 | } 18 | 19 | int loop(int a, int b) { 20 | int c; 21 | while (a > b) { 22 | c = b; 23 | --a; 24 | } 25 | return c; 26 | } 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /resources/example01/include/factor.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_FACTOR_H 2 | #define STATIC_ANALYZER_FACTOR_H 3 | 4 | namespace example01 { 5 | 6 | class AbstractFactor { 7 | public: 8 | virtual int factor(int i) = 0; 9 | 10 | virtual int getNum() = 0; 11 | }; 12 | 13 | class Factor : public AbstractFactor { 14 | private: 15 | int n; 16 | public: 17 | Factor(int n); 18 | 19 | virtual int factor(int i); 20 | 21 | virtual int getNum(); 22 | }; 23 | 24 | } 25 | 26 | #endif //STATIC_ANALYZER_FACTOR_H 27 | -------------------------------------------------------------------------------- /resources/example01/include/fib.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_ANALYZER_FIB_H 2 | #define STATIC_ANALYZER_FIB_H 3 | 4 | namespace example01 { 5 | 6 | class AbstractFib { 7 | public: 8 | virtual int fib(int i) = 0; 9 | 10 | virtual int getNum() = 0; 11 | }; 12 | 13 | class Fib : public AbstractFib { 14 | private: 15 | int n; 16 | public: 17 | Fib(int n); 18 | 19 | virtual int fib(int i); 20 | 21 | virtual int getNum(); 22 | }; 23 | 24 | } 25 | 26 | #endif //STATIC_ANALYZER_FIB_H 27 | -------------------------------------------------------------------------------- /resources/example01/src/factor/factor.cpp: -------------------------------------------------------------------------------- 1 | #include "factor.h" 2 | 3 | namespace example01 { 4 | 5 | int Factor::factor(int i) { 6 | int res = 1; 7 | while (i > 1) { 8 | res *= i; 9 | i--; 10 | } 11 | return res; 12 | } 13 | 14 | Factor::Factor(int n) { 15 | this->n = n; 16 | } 17 | 18 | int Factor::getNum() { 19 | return n; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /resources/example01/src/fib/fib.cpp: -------------------------------------------------------------------------------- 1 | #include "fib.h" 2 | 3 | namespace example01 { 4 | 5 | int Fib::fib(int i) { 6 | int a = 0, b = 1; 7 | while (i > 0) { 8 | int tmp = b; 9 | b = a + b; 10 | a = tmp; 11 | i--; 12 | } 13 | return a; 14 | } 15 | 16 | Fib::Fib(int n) { 17 | this->n = n; 18 | } 19 | 20 | int Fib::getNum() { 21 | return n; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /resources/example01/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "factor.h" 2 | #include "fib.h" 3 | 4 | using namespace example01; 5 | 6 | int main(int argc, const char* argv[]) { 7 | AbstractFactor* f1 = new Factor(10); 8 | int a = 8; 9 | int c = f1->factor(a); 10 | int d = f1->getNum(); 11 | AbstractFib* f2 = new Fib(10); 12 | int e = 5; 13 | int f = f2->fib(e); 14 | int g = f2->getNum(); 15 | return c + d + f + g; 16 | } 17 | -------------------------------------------------------------------------------- /resources/example02/dump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | clang -cc1 -analyze -analyzer-checker=debug.DumpCFG main.cpp 3 | -------------------------------------------------------------------------------- /resources/example02/foo.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Foo { 4 | 5 | void foo() { 6 | int a = 2; 7 | printf("%d\n", a); 8 | } 9 | 10 | }; 11 | -------------------------------------------------------------------------------- /resources/example02/main.cpp: -------------------------------------------------------------------------------- 1 | int test1(int a, int b) { 2 | int c, d; 3 | c = a + b; 4 | d = a - b; 5 | if (c > d) { 6 | return c; 7 | } else { 8 | return d; 9 | } 10 | } 11 | 12 | int main(int argc, char* argv[]) { 13 | int a = 3; 14 | int b; 15 | int c; 16 | a = 1; 17 | if (a > 1) { 18 | b = 2; 19 | } else { 20 | b = 3; 21 | } 22 | c = b + a; 23 | int e = 1, d; 24 | return c; 25 | } 26 | 27 | int fib(int i) { 28 | int a = 0, b = 1; 29 | while (i > 0) { 30 | int tmp = b; 31 | b = a + b; 32 | a = tmp; 33 | i--; 34 | } 35 | return a; 36 | } 37 | 38 | int factor(int n) { 39 | int result = 1; 40 | for (int i = 1; i <= n; i++) { 41 | result = result * i; 42 | } 43 | return result; 44 | } 45 | 46 | void test2(int a, int b, int c, int d, int e) { 47 | a = 1; 48 | b = 2; 49 | while (1) { 50 | c = a + b; 51 | d = c - a; 52 | while (1) { 53 | d = b + d; 54 | if (1) break; 55 | d = a + b; 56 | e = e + 1; 57 | } 58 | b = a + b; 59 | e = c - a; 60 | } 61 | a = b * d; 62 | b = a - d; 63 | } 64 | 65 | #include "foo.h" 66 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(tests 2 | main.cpp 3 | TestWorld.cpp 4 | TestCPPMethod.cpp 5 | TestIR.cpp 6 | TestDataflowFacts.cpp 7 | TestReachingDefinition.cpp 8 | TestLiveVariable.cpp 9 | TestConstantPropagation.cpp 10 | ) 11 | 12 | target_link_libraries(tests 13 | libanalyzer 14 | ) 15 | 16 | target_compile_definitions(tests 17 | PUBLIC 18 | DOCTEST_CONFIG_USE_STD_HEADERS 19 | ) 20 | 21 | add_test(NAME doctest COMMAND tests) 22 | 23 | set_tests_properties(doctest 24 | PROPERTIES 25 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 26 | ) 27 | 28 | -------------------------------------------------------------------------------- /tests/TestCPPMethod.cpp: -------------------------------------------------------------------------------- 1 | #include "doctest.h" 2 | 3 | #include "World.h" 4 | #include "language/CPPMethod.h" 5 | 6 | namespace al = analyzer; 7 | namespace lang = al::language; 8 | 9 | class CPPMethodTestFixture { 10 | protected: 11 | std::shared_ptr f1, f2, f3, f4, f5, f6, f7; 12 | public: 13 | CPPMethodTestFixture() 14 | { 15 | al::World::initialize("resources/example01/src", 16 | "resources/example01/include"); 17 | const auto& allMethods = al::World::get().getAllMethods(); 18 | f1 = allMethods.at("int main(int, const char **)"); 19 | f2 = allMethods.at("int example01::Fib::fib(int)"); 20 | f3 = allMethods.at("void example01::Fib::Fib(int)"); 21 | f4 = allMethods.at("int example01::Fib::getNum()"); 22 | const al::World& world = al::World::get(); 23 | f5 = world.getMethodBySignature("int example01::Factor::factor(int)"); 24 | f6 = world.getMethodBySignature("void example01::Factor::Factor(int)"); 25 | f7 = world.getMethodBySignature("int example01::Factor::getNum()"); 26 | } 27 | }; 28 | 29 | TEST_SUITE_BEGIN("testCPPMethod"); 30 | 31 | TEST_CASE_FIXTURE(CPPMethodTestFixture, "testGetContainingFilePath" 32 | * doctest::description("testing method containing file getting")) { 33 | 34 | al::World::getLogger().Progress("Testing method containing file getting ..."); 35 | 36 | CHECK_EQ(f1->getContainingFilePath(), "resources/example01/src/main.cpp"); 37 | CHECK_EQ(f2->getContainingFilePath(), "resources/example01/src/fib/fib.cpp"); 38 | CHECK_EQ(f3->getContainingFilePath(), "resources/example01/src/fib/fib.cpp"); 39 | CHECK_EQ(f4->getContainingFilePath(), "resources/example01/src/fib/fib.cpp"); 40 | CHECK_EQ(f5->getContainingFilePath(), "resources/example01/src/factor/factor.cpp"); 41 | CHECK_EQ(f6->getContainingFilePath(), "resources/example01/src/factor/factor.cpp"); 42 | CHECK_EQ(f7->getContainingFilePath(), "resources/example01/src/factor/factor.cpp"); 43 | 44 | al::World::getLogger().Success("Finish testing method containing file getting ..."); 45 | 46 | } 47 | 48 | TEST_CASE_FIXTURE(CPPMethodTestFixture, "testGetMethodSourceCode" 49 | * doctest::description("testing method source getting")) { 50 | 51 | al::World::getLogger().Progress("Testing method source getting ..."); 52 | 53 | CHECK_EQ(f1->getMethodSourceCode(), 54 | R"(int main(int argc, const char* argv[]) { 55 | AbstractFactor* f1 = new Factor(10); 56 | int a = 8; 57 | int c = f1->factor(a); 58 | int d = f1->getNum(); 59 | AbstractFib* f2 = new Fib(10); 60 | int e = 5; 61 | int f = f2->fib(e); 62 | int g = f2->getNum(); 63 | return c + d + f + g; 64 | })"); 65 | 66 | CHECK_EQ(f2->getMethodSourceCode(), 67 | R"(int Fib::fib(int i) { 68 | int a = 0, b = 1; 69 | while (i > 0) { 70 | int tmp = b; 71 | b = a + b; 72 | a = tmp; 73 | i--; 74 | } 75 | return a; 76 | })"); 77 | 78 | CHECK_EQ(f3->getMethodSourceCode(), 79 | R"(Fib::Fib(int n) { 80 | this->n = n; 81 | })"); 82 | 83 | CHECK_EQ(f4->getMethodSourceCode(), 84 | R"(int Fib::getNum() { 85 | return n; 86 | })"); 87 | 88 | CHECK_EQ(f5->getMethodSourceCode(), 89 | R"(int Factor::factor(int i) { 90 | int res = 1; 91 | while (i > 1) { 92 | res *= i; 93 | i--; 94 | } 95 | return res; 96 | })"); 97 | 98 | CHECK_EQ(f6->getMethodSourceCode(), 99 | R"(Factor::Factor(int n) { 100 | this->n = n; 101 | })"); 102 | 103 | CHECK_EQ(f7->getMethodSourceCode(), 104 | R"(int Factor::getNum() { 105 | return n; 106 | })"); 107 | 108 | al::World::getLogger().Success("Finish testing method source getting ..."); 109 | 110 | } 111 | 112 | TEST_CASE_FIXTURE(CPPMethodTestFixture, "testGetDeclarationInformation" 113 | * doctest::description("testing method declaration information get")) { 114 | 115 | al::World::getLogger().Progress("Testing method declaration information get ..."); 116 | 117 | CHECK_EQ(f1->getParamCount(), 2); 118 | CHECK_EQ(f1->getParamName(0), "argc"); 119 | CHECK_EQ(f1->getParamType(0)->getName(), "int"); 120 | CHECK_EQ(f1->getParamName(1), "argv"); 121 | CHECK_EQ(f1->getParamType(1)->getName(), "const char **"); 122 | std::vector types; 123 | for (const auto& type : f1->getParamTypes()) { 124 | types.emplace_back(type->getName()); 125 | } 126 | std::sort(types.begin(), types.end()); 127 | CHECK_EQ(types, std::vector{"const char **" ,"int"}); 128 | CHECK_EQ(f1->getReturnType()->getName(), "int"); 129 | 130 | CHECK_EQ(f2->getParamCount(), 1); 131 | CHECK_EQ(f2->getParamName(0), "i"); 132 | CHECK_EQ(f2->getParamType(0)->getName(), "int"); 133 | CHECK_EQ(f2->getReturnType()->getName(), "int"); 134 | 135 | CHECK_EQ(f3->getParamCount(), 1); 136 | CHECK_EQ(f3->getParamName(0), "n"); 137 | CHECK_EQ(f3->getParamType(0)->getName(), "int"); 138 | CHECK_EQ(f3->getReturnType()->getName(), "void"); 139 | 140 | CHECK_EQ(f4->getParamCount(), 0); 141 | CHECK_EQ(f4->getReturnType()->getName(), "int"); 142 | 143 | CHECK_EQ(f5->getParamCount(), 1); 144 | CHECK_EQ(f5->getParamName(0), "i"); 145 | CHECK_EQ(f5->getParamType(0)->getName(), "int"); 146 | CHECK_EQ(f5->getReturnType()->getName(), "int"); 147 | 148 | CHECK_EQ(f6->getParamCount(), 1); 149 | CHECK_EQ(f6->getParamName(0), "n"); 150 | CHECK_EQ(f6->getParamType(0)->getName(), "int"); 151 | CHECK_EQ(f6->getReturnType()->getName(), "void"); 152 | 153 | CHECK_EQ(f7->getParamCount(), 0); 154 | CHECK_EQ(f7->getReturnType()->getName(), "int"); 155 | 156 | const auto* f8 = clang::dyn_cast(f7->getFunctionDecl()); 157 | al::World::getLogger().Debug(f8->getThisObjectType().getAsString()); 158 | al::World::getLogger().Debug(f7->getMethodSignatureAsString()); 159 | al::World::getLogger().Debug(std::to_string(f8->getNumParams())); 160 | 161 | al::World::getLogger().Success("Finish testing method declaration information get ..."); 162 | 163 | } 164 | 165 | TEST_CASE_FIXTURE(CPPMethodTestFixture, "testMethodKindCheck" 166 | * doctest::description("testing method kind checking")) { 167 | 168 | al::World::getLogger().Progress("Testing method kind checking ..."); 169 | 170 | CHECK(f1->isGlobalMethod()); 171 | CHECK_FALSE(f1->isClassStaticMethod()); 172 | CHECK_FALSE(f1->isClassMemberMethod()); 173 | CHECK_FALSE(f1->isVirtual()); 174 | 175 | CHECK_FALSE(f2->isGlobalMethod()); 176 | CHECK_FALSE(f2->isClassStaticMethod()); 177 | CHECK(f2->isClassMemberMethod()); 178 | CHECK(f2->isVirtual()); 179 | 180 | CHECK_FALSE(f3->isGlobalMethod()); 181 | CHECK_FALSE(f3->isClassStaticMethod()); 182 | CHECK(f3->isClassMemberMethod()); 183 | CHECK_FALSE(f3->isVirtual()); 184 | 185 | CHECK_FALSE(f4->isGlobalMethod()); 186 | CHECK_FALSE(f4->isClassStaticMethod()); 187 | CHECK(f4->isClassMemberMethod()); 188 | CHECK(f4->isVirtual()); 189 | 190 | CHECK_FALSE(f5->isGlobalMethod()); 191 | CHECK_FALSE(f5->isClassStaticMethod()); 192 | CHECK(f5->isClassMemberMethod()); 193 | CHECK(f5->isVirtual()); 194 | 195 | CHECK_FALSE(f6->isGlobalMethod()); 196 | CHECK_FALSE(f6->isClassStaticMethod()); 197 | CHECK(f6->isClassMemberMethod()); 198 | CHECK_FALSE(f6->isVirtual()); 199 | 200 | CHECK_FALSE(f7->isGlobalMethod()); 201 | CHECK_FALSE(f7->isClassStaticMethod()); 202 | CHECK(f7->isClassMemberMethod()); 203 | CHECK(f7->isVirtual()); 204 | 205 | al::World::getLogger().Success("Finish testing method kind checking ..."); 206 | 207 | } 208 | 209 | TEST_SUITE_END(); 210 | -------------------------------------------------------------------------------- /tests/TestDataflowFacts.cpp: -------------------------------------------------------------------------------- 1 | #include "doctest.h" 2 | 3 | #include "World.h" 4 | #include "analysis/dataflow/fact/SetFact.h" 5 | #include "analysis/dataflow/fact/MapFact.h" 6 | 7 | namespace al = analyzer; 8 | namespace fact = al::analysis::dataflow::fact; 9 | 10 | TEST_SUITE_BEGIN("testDataflowFacts"); 11 | 12 | TEST_CASE("testSetFact" 13 | * doctest::description("testing set like facts for dataflow analysis")) { 14 | 15 | al::World::getLogger().Progress("Testing set like facts for dataflow analysis ..."); 16 | 17 | std::shared_ptr s1 = std::make_shared("s1"); 18 | std::shared_ptr s2 = std::make_shared("s2"); 19 | std::shared_ptr s3 = std::make_shared("s3"); 20 | std::shared_ptr s4 = std::make_shared("s4"); 21 | std::shared_ptr s5 = std::make_shared("s5"); 22 | std::shared_ptr s6 = std::make_shared("s6"); 23 | 24 | std::shared_ptr> test1 = std::make_shared>(); 25 | 26 | CHECK(test1->isEmpty()); 27 | CHECK_EQ(test1->size(), 0); 28 | 29 | CHECK(test1->add(s1)); 30 | CHECK(test1->add(s2)); 31 | CHECK(test1->add(s3)); 32 | CHECK(test1->add(s4)); 33 | CHECK_FALSE(test1->add(s2)); 34 | CHECK_FALSE(test1->add(s3)); 35 | CHECK_FALSE(test1->add(s4)); 36 | 37 | CHECK_FALSE(test1->isEmpty()); 38 | CHECK_EQ(test1->size(), 4); 39 | 40 | CHECK(test1->contains(s1)); 41 | CHECK(test1->contains(s2)); 42 | CHECK(test1->contains(s3)); 43 | CHECK(test1->contains(s4)); 44 | CHECK_FALSE(test1->contains(s5)); 45 | CHECK_FALSE(test1->contains(s6)); 46 | 47 | std::vector output1; 48 | test1->forEach([&](const std::shared_ptr& p) -> void { 49 | output1.emplace_back(*p); 50 | }); 51 | std::sort(output1.begin(), output1.end()); 52 | CHECK_EQ(output1, std::vector{"s1", "s2", "s3", "s4"}); 53 | 54 | std::shared_ptr> test2 = test1->copy(); 55 | CHECK(test2->equalsTo(test2)); 56 | test2->clear(); 57 | CHECK(test2->isEmpty()); 58 | CHECK(test2->add(s3)); 59 | CHECK(test2->add(s4)); 60 | CHECK(test2->add(s5)); 61 | CHECK(test2->add(s6)); 62 | CHECK_EQ(test2->size(), 4); 63 | 64 | CHECK(test2->unionN(test1)); 65 | CHECK_EQ(test2->size(), 6); 66 | 67 | CHECK(test2->remove(s1)); 68 | CHECK(test2->remove(s2)); 69 | CHECK_FALSE(test2->remove(s1)); 70 | CHECK_FALSE(test2->remove(s2)); 71 | 72 | CHECK(test2->intersect(test1)); 73 | CHECK_EQ(test2->size(), 2); 74 | 75 | CHECK(test2->removeIf([&](const std::shared_ptr& e) -> bool { 76 | return e == s3 || e == s4; 77 | })); 78 | CHECK(test2->isEmpty()); 79 | 80 | test2->setSetFact(test1); 81 | CHECK_EQ(test2->size(), 4); 82 | CHECK(test1->equalsTo(test2)); 83 | 84 | test2->removeAll(test1); 85 | CHECK(test2->isEmpty()); 86 | 87 | al::World::getLogger().Success("Finish testing set like facts for dataflow analysis ..."); 88 | 89 | } 90 | 91 | TEST_CASE("testMapFact" 92 | * doctest::description("testing map like facts for dataflow analysis")) { 93 | 94 | al::World::getLogger().Progress("Testing map like facts for dataflow analysis ..."); 95 | 96 | std::shared_ptr k1 = std::make_shared("k1"); 97 | std::shared_ptr k2 = std::make_shared("k2"); 98 | std::shared_ptr k3 = std::make_shared("k3"); 99 | std::shared_ptr k4 = std::make_shared("k4"); 100 | std::shared_ptr k5 = std::make_shared("k5"); 101 | std::shared_ptr k6 = std::make_shared("k6"); 102 | 103 | std::shared_ptr v1 = std::make_shared("v1"); 104 | std::shared_ptr v2 = std::make_shared("v2"); 105 | std::shared_ptr v3 = std::make_shared("v3"); 106 | std::shared_ptr v4 = std::make_shared("v4"); 107 | std::shared_ptr v5 = std::make_shared("v5"); 108 | std::shared_ptr v6 = std::make_shared("v6"); 109 | 110 | std::shared_ptr> test1 = std::make_shared>(); 111 | 112 | CHECK(test1->isEmpty()); 113 | 114 | CHECK(test1->update(k1, v1)); 115 | CHECK_EQ(test1->size(), 1); 116 | CHECK_FALSE(test1->update(k1, v1)); 117 | CHECK_EQ(test1->size(), 1); 118 | CHECK(test1->update(k1, v2)); 119 | CHECK(test1->update(k1, v1)); 120 | CHECK(test1->update(k2, v2)); 121 | CHECK(test1->update(k3, v3)); 122 | CHECK(test1->update(k4, v4)); 123 | CHECK(test1->update(k5, v5)); 124 | 125 | CHECK_EQ(test1->size(), 5); 126 | 127 | CHECK_EQ(test1->get(k1), v1); 128 | CHECK_EQ(test1->get(k2), v2); 129 | CHECK_EQ(test1->get(k3), v3); 130 | CHECK_EQ(test1->get(k4), v4); 131 | CHECK_EQ(test1->get(k5), v5); 132 | 133 | CHECK_FALSE(test1->get(k6)); 134 | 135 | CHECK_EQ(test1->keySet(), std::unordered_set>{k1, k2, k3, k4, k5}); 136 | CHECK_EQ(test1->valueSet(), std::unordered_set>{v1, v2, v3, v4, v5}); 137 | 138 | std::unordered_map output1; 139 | test1->forEach([&](const std::shared_ptr& k, 140 | const std::shared_ptr& v) -> void { 141 | output1.emplace(*k, *v); 142 | }); 143 | CHECK_EQ(output1, std::unordered_map{ 144 | {"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}, {"k5", "v5"} 145 | }); 146 | 147 | std::shared_ptr> test2 = test1->copy(); 148 | CHECK_EQ(test2->size(), 5); 149 | CHECK(test1->equalsTo(test2)); 150 | CHECK(test2->equalsTo(test1)); 151 | 152 | test2->clear(); 153 | CHECK(test2->isEmpty()); 154 | 155 | CHECK(test2->copyFrom(test1)); 156 | CHECK_EQ(test2->keySet(), std::unordered_set>{k1, k2, k3, k4, k5}); 157 | CHECK_EQ(test2->valueSet(), std::unordered_set>{v1, v2, v3, v4, v5}); 158 | CHECK(test2->equalsTo(test1)); 159 | 160 | CHECK_FALSE(test1->copyFrom(test2)); 161 | CHECK(test1->equalsTo(test2)); 162 | 163 | CHECK_EQ(test2->remove(k1), v1); 164 | CHECK_EQ(test2->remove(k2), v2); 165 | CHECK_EQ(test2->remove(k3), v3); 166 | CHECK_EQ(test2->size(), 2); 167 | CHECK_FALSE(test2->equalsTo(test1)); 168 | 169 | al::World::getLogger().Success("Finish testing map like facts for dataflow analysis ..."); 170 | 171 | } 172 | 173 | TEST_SUITE_END(); 174 | 175 | -------------------------------------------------------------------------------- /tests/TestReachingDefinition.cpp: -------------------------------------------------------------------------------- 1 | #include "doctest.h" 2 | 3 | #include "World.h" 4 | #include "analysis/dataflow/ReachingDefinition.h" 5 | 6 | namespace al = analyzer; 7 | namespace air = al::ir; 8 | namespace cf = al::config; 9 | namespace graph = al::analysis::graph; 10 | namespace df = al::analysis::dataflow; 11 | namespace dfact = al::analysis::dataflow::fact; 12 | 13 | class ReachDefTestFixture { 14 | protected: 15 | std::shared_ptr ir1, ir2; 16 | std::unique_ptr rd; 17 | public: 18 | ReachDefTestFixture() { 19 | al::World::initialize("resources/dataflow/ReachDef"); 20 | const al::World& world = al::World::get(); 21 | ir1 = world.getMethodBySignature("int ReachDef::foo(int, int, int)")->getIR(); 22 | ir2 = world.getMethodBySignature("int ReachDef::loop(int, int)")->getIR(); 23 | 24 | std::unique_ptr analysisConfig 25 | = std::make_unique("reaching definition analysis"); 26 | rd = std::make_unique(analysisConfig); 27 | 28 | CHECK_FALSE(analysisConfig); 29 | } 30 | }; 31 | 32 | 33 | TEST_SUITE_BEGIN("testReachingDefinition"); 34 | 35 | TEST_CASE_FIXTURE(ReachDefTestFixture, "testDataflowPreparation" 36 | * doctest::description("testing dataflow preparation work ...")) { 37 | 38 | al::World::getLogger().Progress("Testing dataflow preparation work ..."); 39 | 40 | std::unordered_map> stmtMap1; 41 | for (const std::shared_ptr& s : ir1->getStmts()) { 42 | al::World::getLogger().Debug(s->str()); 43 | stmtMap1.emplace(s->str(), s); 44 | } 45 | std::shared_ptr s11 = stmtMap1.at("int x;"); 46 | std::shared_ptr s12 = stmtMap1.at("a > 0"); 47 | std::shared_ptr s13 = stmtMap1.at("x = a"); 48 | std::shared_ptr s14 = stmtMap1.at("x = b"); 49 | std::shared_ptr s15 = stmtMap1.at("int y = x;"); 50 | std::shared_ptr s16 = stmtMap1.at("x = c"); 51 | std::shared_ptr s17 = stmtMap1.at("x"); 52 | std::shared_ptr s18 = stmtMap1.at("return x"); 53 | 54 | std::shared_ptr cfg1 = ir1->getCFG(); 55 | CHECK_EQ(cfg1->getEdgeNum(), 10); 56 | CHECK(cfg1->hasEdge(cfg1->getEntry(), s11)); 57 | CHECK(cfg1->hasEdge(s11, s12)); 58 | CHECK(cfg1->hasEdge(s12, s13)); 59 | CHECK(cfg1->hasEdge(s12, s14)); 60 | CHECK(cfg1->hasEdge(s13, s15)); 61 | CHECK(cfg1->hasEdge(s14, s15)); 62 | CHECK(cfg1->hasEdge(s15, s16)); 63 | CHECK(cfg1->hasEdge(s16, s17)); 64 | CHECK(cfg1->hasEdge(s17, s18)); 65 | CHECK(cfg1->hasEdge(s18, cfg1->getExit())); 66 | 67 | std::unordered_map> stmtMap2; 68 | for (const std::shared_ptr& s : ir2->getStmts()) { 69 | al::World::getLogger().Debug(s->str()); 70 | stmtMap2.emplace(s->str(), s); 71 | } 72 | std::shared_ptr s21 = stmtMap2.at("int c;"); 73 | std::shared_ptr s22 = stmtMap2.at("a > b"); 74 | std::shared_ptr s23 = stmtMap2.at("c = b"); 75 | std::shared_ptr s24 = stmtMap2.at("--a"); 76 | std::shared_ptr s25 = stmtMap2.at("nop"); 77 | std::shared_ptr s26 = stmtMap2.at("c"); 78 | std::shared_ptr s27 = stmtMap2.at("return c"); 79 | 80 | std::shared_ptr cfg2 = ir2->getCFG(); 81 | CHECK_EQ(cfg2->getEdgeNum(), 9); 82 | CHECK(cfg2->hasEdge(cfg2->getEntry(), s21)); 83 | CHECK(cfg2->hasEdge(s21, s22)); 84 | CHECK(cfg2->hasEdge(s22, s23)); 85 | CHECK(cfg2->hasEdge(s23, s24)); 86 | CHECK(cfg2->hasEdge(s24, s25)); 87 | CHECK(cfg2->hasEdge(s25, s22)); 88 | CHECK(cfg2->hasEdge(s22, s26)); 89 | CHECK(cfg2->hasEdge(s26, s27)); 90 | CHECK(cfg2->hasEdge(s27, cfg2->getExit())); 91 | 92 | CHECK_EQ(rd->getAnalysisConfig()->getDescription(), "reaching definition analysis"); 93 | 94 | al::World::getLogger().Success("Finish testing dataflow preparation work ..."); 95 | 96 | } 97 | 98 | TEST_CASE_FIXTURE(ReachDefTestFixture, "testReachDefCaseFoo" 99 | * doctest::description("testing reaching definition example foo with if-else")) { 100 | 101 | al::World::getLogger().Progress("Testing reaching definition example foo with if-else ..."); 102 | 103 | std::shared_ptr cfg = ir1->getCFG(); 104 | std::unordered_map> stmtMap; 105 | for (const std::shared_ptr& s : ir1->getStmts()) { 106 | al::World::getLogger().Debug(s->str()); 107 | stmtMap.emplace(s->str(), s); 108 | } 109 | 110 | std::shared_ptr s1 = stmtMap.at("int x;"); 111 | std::shared_ptr s2 = stmtMap.at("a > 0"); 112 | std::shared_ptr s3 = stmtMap.at("x = a"); 113 | std::shared_ptr s4 = stmtMap.at("x = b"); 114 | std::shared_ptr s5 = stmtMap.at("int y = x;"); 115 | std::shared_ptr s6 = stmtMap.at("x = c"); 116 | std::shared_ptr s7 = stmtMap.at("x"); 117 | std::shared_ptr s8 = stmtMap.at("return x"); 118 | 119 | std::shared_ptr>> result = rd->analyze(ir1); 120 | 121 | CHECK(result->getInFact(cfg->getEntry())->isEmpty()); 122 | CHECK(result->getOutFact(cfg->getEntry())->isEmpty()); 123 | CHECK(result->getInFact(s1)->isEmpty()); 124 | CHECK(result->getOutFact(s1)->isEmpty()); 125 | CHECK(result->getInFact(s2)->isEmpty()); 126 | CHECK(result->getOutFact(s2)->isEmpty()); 127 | CHECK(result->getInFact(s3)->isEmpty()); 128 | CHECK(result->getOutFact(s3)->equalsTo( 129 | std::make_shared>(std::unordered_set{s3}))); 130 | CHECK(result->getInFact(s4)->isEmpty()); 131 | CHECK(result->getOutFact(s4)->equalsTo( 132 | std::make_shared>(std::unordered_set{s4}))); 133 | CHECK(result->getInFact(s5)->equalsTo( 134 | std::make_shared>(std::unordered_set{s3, s4}))); 135 | CHECK(result->getOutFact(s5)->equalsTo( 136 | std::make_shared>(std::unordered_set{s3, s4, s5}))); 137 | CHECK(result->getInFact(s6)->equalsTo( 138 | std::make_shared>(std::unordered_set{s3, s4, s5}))); 139 | CHECK(result->getOutFact(s6)->equalsTo( 140 | std::make_shared>(std::unordered_set{s5, s6}))); 141 | CHECK(result->getInFact(s7)->equalsTo( 142 | std::make_shared>(std::unordered_set{s5, s6}))); 143 | CHECK(result->getOutFact(s7)->equalsTo( 144 | std::make_shared>(std::unordered_set{s5, s6}))); 145 | CHECK(result->getInFact(s8)->equalsTo( 146 | std::make_shared>(std::unordered_set{s5, s6}))); 147 | CHECK(result->getOutFact(s8)->equalsTo( 148 | std::make_shared>(std::unordered_set{s5, s6}))); 149 | CHECK(result->getInFact(cfg->getExit())->equalsTo( 150 | std::make_shared>(std::unordered_set{s5, s6}))); 151 | CHECK(result->getOutFact(cfg->getExit())->equalsTo( 152 | std::make_shared>(std::unordered_set{s5, s6}))); 153 | 154 | for (const std::shared_ptr& s : 155 | {cfg->getEntry(), s1, s2, s3, s4, s5, s6, s7, s8, cfg->getExit()}) { 156 | CHECK(result->getResult(s)->equalsTo(result->getOutFact(s))); 157 | } 158 | 159 | al::World::getLogger().Success("Finish testing reaching definition example foo with if-else ..."); 160 | 161 | } 162 | 163 | TEST_CASE_FIXTURE(ReachDefTestFixture, "testReachDefCaseLoop" 164 | * doctest::description("testing reaching definition example loop with while")) { 165 | 166 | al::World::getLogger().Progress("Testing reaching definition example loop with while ..."); 167 | 168 | std::shared_ptr cfg = ir2->getCFG(); 169 | std::unordered_map> stmtMap; 170 | for (const std::shared_ptr& s : ir2->getStmts()) { 171 | al::World::getLogger().Debug(s->str()); 172 | stmtMap.emplace(s->str(), s); 173 | } 174 | 175 | std::shared_ptr s1 = stmtMap.at("int c;"); 176 | std::shared_ptr s2 = stmtMap.at("a > b"); 177 | std::shared_ptr s3 = stmtMap.at("c = b"); 178 | std::shared_ptr s4 = stmtMap.at("--a"); 179 | std::shared_ptr s5 = stmtMap.at("nop"); 180 | std::shared_ptr s6 = stmtMap.at("c"); 181 | std::shared_ptr s7 = stmtMap.at("return c"); 182 | 183 | std::shared_ptr>> result = rd->analyze(ir2); 184 | 185 | CHECK(result->getInFact(cfg->getEntry())->isEmpty()); 186 | CHECK(result->getOutFact(cfg->getEntry())->isEmpty()); 187 | CHECK(result->getInFact(s1)->isEmpty()); 188 | CHECK(result->getOutFact(s1)->isEmpty()); 189 | CHECK(result->getInFact(s2)->equalsTo( 190 | std::make_shared>(std::unordered_set{s3, s4}))); 191 | CHECK(result->getOutFact(s2)->equalsTo( 192 | std::make_shared>(std::unordered_set{s3, s4}))); 193 | CHECK(result->getInFact(s3)->equalsTo( 194 | std::make_shared>(std::unordered_set{s3, s4}))); 195 | CHECK(result->getOutFact(s3)->equalsTo( 196 | std::make_shared>(std::unordered_set{s3, s4}))); 197 | CHECK(result->getInFact(s4)->equalsTo( 198 | std::make_shared>(std::unordered_set{s3, s4}))); 199 | CHECK(result->getOutFact(s4)->equalsTo( 200 | std::make_shared>(std::unordered_set{s3, s4}))); 201 | CHECK(result->getInFact(s5)->equalsTo( 202 | std::make_shared>(std::unordered_set{s3, s4}))); 203 | CHECK(result->getOutFact(s5)->equalsTo( 204 | std::make_shared>(std::unordered_set{s3, s4}))); 205 | CHECK(result->getInFact(s6)->equalsTo( 206 | std::make_shared>(std::unordered_set{s3, s4}))); 207 | CHECK(result->getOutFact(s6)->equalsTo( 208 | std::make_shared>(std::unordered_set{s3, s4}))); 209 | CHECK(result->getInFact(s7)->equalsTo( 210 | std::make_shared>(std::unordered_set{s3, s4}))); 211 | CHECK(result->getOutFact(s7)->equalsTo( 212 | std::make_shared>(std::unordered_set{s3, s4}))); 213 | CHECK(result->getInFact(cfg->getExit())->equalsTo( 214 | std::make_shared>(std::unordered_set{s3, s4}))); 215 | CHECK(result->getOutFact(cfg->getExit())->equalsTo( 216 | std::make_shared>(std::unordered_set{s3, s4}))); 217 | 218 | for (const std::shared_ptr& s : 219 | {cfg->getEntry(), s1, s2, s3, s4, s5, s6, s7, cfg->getExit()}) { 220 | CHECK(result->getResult(s)->equalsTo(result->getOutFact(s))); 221 | } 222 | 223 | al::World::getLogger().Success("Finish testing reaching definition example loop with while ..."); 224 | 225 | } 226 | 227 | TEST_SUITE_END(); 228 | 229 | -------------------------------------------------------------------------------- /tests/TestWorld.cpp: -------------------------------------------------------------------------------- 1 | #include "doctest.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "World.h" 8 | #include "language/CPPMethod.h" 9 | 10 | namespace al=analyzer; 11 | 12 | class WorldTestFixture { 13 | private: 14 | static const al::World& initializeAndGet() 15 | { 16 | al::World::initialize("resources/example01/src", 17 | "resources/example01/include"); 18 | return al::World::get(); 19 | } 20 | protected: 21 | const al::World &world; 22 | public: 23 | WorldTestFixture() 24 | :world(initializeAndGet()) 25 | { 26 | 27 | } 28 | }; 29 | 30 | TEST_SUITE_BEGIN("testWorld"); 31 | 32 | TEST_CASE_FIXTURE(WorldTestFixture, "testGetSourceCode" 33 | * doctest::description("test world building")) { 34 | 35 | al::World::getLogger().Progress("Testing world building ..."); 36 | 37 | const std::unordered_map &fileLists = world.getSourceCode(); 38 | 39 | std::string p1("resources/example01/src/main.cpp"); 40 | CHECK(fileLists.find(p1) != fileLists.end()); 41 | std::ifstream f1(p1); 42 | CHECK(f1.is_open()); 43 | CHECK_EQ(fileLists.at(p1), std::string(std::istreambuf_iterator(f1), 44 | std::istreambuf_iterator())); 45 | f1.close(); 46 | 47 | std::string p2("resources/example01/src/factor/factor.cpp"); 48 | CHECK(fileLists.find(p2) != fileLists.end()); 49 | std::ifstream f2(p2); 50 | CHECK(f2.is_open()); 51 | CHECK_EQ(fileLists.at(p2), std::string(std::istreambuf_iterator(f2), 52 | std::istreambuf_iterator())); 53 | 54 | std::string p3("resources/example01/src/fib/fib.cpp"); 55 | CHECK(fileLists.find(p3) != fileLists.end()); 56 | std::ifstream f3(p3); 57 | CHECK(f3.is_open()); 58 | CHECK_EQ(fileLists.at(p3), std::string(std::istreambuf_iterator(f3), 59 | std::istreambuf_iterator())); 60 | 61 | al::World::getLogger().Success("Finish testing world building ..."); 62 | 63 | } 64 | 65 | TEST_CASE_FIXTURE(WorldTestFixture, "testInitialization" 66 | * doctest::description("testing world initialization")) { 67 | 68 | al::World::getLogger().Progress("Testing world initialization ..."); 69 | 70 | std::vector fileList; 71 | for (const std::unique_ptr &ast: world.getAstList()) { 72 | fileList.emplace_back(ast->getMainFileName().str()); 73 | } 74 | std::sort(fileList.begin(), fileList.end()); 75 | CHECK_EQ(fileList, std::vector{ 76 | "resources/example01/src/factor/factor.cpp", 77 | "resources/example01/src/fib/fib.cpp", 78 | "resources/example01/src/main.cpp" 79 | }); 80 | 81 | al::World::getLogger().Success("Testing world initialization ..."); 82 | 83 | } 84 | 85 | TEST_CASE_FIXTURE(WorldTestFixture, "testDumpAST" 86 | * doctest::description("testing pretty ast dumper")) { 87 | 88 | al::World::getLogger().Progress("Testing pretty ast dumper ..."); 89 | 90 | std::string str; 91 | llvm::raw_string_ostream output(str); 92 | world.dumpAST(output); 93 | world.dumpAST(std::string("resources/example01/src/factor/factor.cpp"), output); 94 | uint64_t t1 = analyzer::World::getLogger().getOutStream()->tell(); 95 | world.dumpAST(std::string("resources/example01/src/test.cpp"), output); 96 | uint64_t t2 = analyzer::World::getLogger().getOutStream()->tell(); 97 | CHECK_NE(t1, t2); 98 | //al::World::getLogger().Debug(str); 99 | 100 | al::World::getLogger().Success("Finish testing pretty ast dumper ..."); 101 | 102 | } 103 | 104 | TEST_CASE_FIXTURE(WorldTestFixture, "testGetAllMethods" 105 | * doctest::description("testing function list building")) { 106 | 107 | al::World::getLogger().Progress("Testing function list building ..."); 108 | 109 | std::vector signatureList; 110 | for (const auto& [sig, method]: world.getAllMethods()) { 111 | CHECK_EQ(sig, method->getMethodSignatureAsString()); 112 | signatureList.emplace_back(method->getMethodSignatureAsString()); 113 | } 114 | std::sort(signatureList.begin(), signatureList.end()); 115 | CHECK_EQ(signatureList, std::vector{ 116 | "int example01::Factor::factor(int)", 117 | "int example01::Factor::getNum()", 118 | "int example01::Fib::fib(int)", 119 | "int example01::Fib::getNum()", 120 | "int main(int, const char **)", 121 | "void example01::Factor::Factor(int)", 122 | "void example01::Fib::Fib(int)" 123 | }); 124 | 125 | al::World::getLogger().Success("Finish testing function list building ..."); 126 | 127 | } 128 | 129 | TEST_SUITE_END(); 130 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | 3 | #include "doctest.h" 4 | 5 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(reaching-definition-analyzer reaching-definition-analyzer.cpp) 2 | 3 | target_link_libraries(reaching-definition-analyzer 4 | libanalyzer 5 | ) 6 | 7 | add_executable(live-variable-analyzer live-variable-analyzer.cpp) 8 | 9 | target_link_libraries(live-variable-analyzer 10 | libanalyzer 11 | ) 12 | 13 | add_executable(constant-propagation-analyzer constant-propagation-analyzer.cpp) 14 | 15 | target_link_libraries(constant-propagation-analyzer 16 | libanalyzer 17 | ) 18 | 19 | -------------------------------------------------------------------------------- /tools/constant-propagation-analyzer.cpp: -------------------------------------------------------------------------------- 1 | #include "CLI11.h" 2 | 3 | #include "World.h" 4 | #include "analysis/dataflow/ConstantPropagation.h" 5 | 6 | namespace al = analyzer; 7 | namespace air = al::ir; 8 | namespace cf = al::config; 9 | namespace graph = al::analysis::graph; 10 | namespace df = al::analysis::dataflow; 11 | namespace dfact = al::analysis::dataflow::fact; 12 | 13 | int main(int argc, const char **argv) 14 | { 15 | 16 | CLI::App app("A Simple CPP Constant Propagation Static Analyzer\nCopyright (c) 2023-2023"); 17 | 18 | std::string sourceDir, includeDir, std; 19 | 20 | app.add_option("-S,--source-dir,", sourceDir, 21 | "directory of all source files")->required(); 22 | 23 | app.add_option("-I,--include-dir", includeDir, 24 | "directory of all header files"); 25 | 26 | app.add_option("--std,--standard", std, 27 | "c++ language standard (support all standards that clang supports)"); 28 | 29 | CLI11_PARSE(app, argc, argv); 30 | 31 | if (includeDir.empty() && std.empty()) { 32 | al::World::initialize(sourceDir); 33 | } else if (std.empty()) { 34 | al::World::initialize(sourceDir, includeDir); 35 | } else { 36 | al::World::initialize(sourceDir, includeDir, std); 37 | } 38 | 39 | std::unique_ptr analysisConfig 40 | = std::make_unique("constant propagation analysis"); 41 | 42 | std::unique_ptr cp = std::make_unique(analysisConfig); 43 | 44 | const al::World& world = al::World::get(); 45 | for (const auto& [signature, method] : world.getAllMethods()) { 46 | al::World::getLogger().Progress("Start constant propagation analysis for: " + signature); 47 | 48 | std::string fileName = method->getContainingFilePath(); 49 | 50 | std::shared_ptr myIR = method->getIR(); 51 | 52 | std::shared_ptr> result = cp->analyze(myIR); 53 | 54 | al::World::getLogger().Info("-------------- Analysis Result of " + signature + " -----------------"); 55 | 56 | for (const std::shared_ptr& stmt : myIR->getStmts()) { 57 | al::World::getLogger().Info("* " + fileName 58 | + " " + std::to_string(stmt->getStartLine()) + ": " + stmt->str()); 59 | al::World::getLogger().Info(" In: "); 60 | result->getInFact(stmt)->forEach( 61 | [&](const std::shared_ptr& k, const std::shared_ptr& v) 62 | { 63 | al::World::getLogger().Info(" " + k->getName() + ": " + v->str()); 64 | }); 65 | al::World::getLogger().Info(" Out: "); 66 | result->getOutFact(stmt)->forEach( 67 | [&](const std::shared_ptr& k, const std::shared_ptr& v) 68 | { 69 | al::World::getLogger().Info(" " + k->getName() + ": " + v->str()); 70 | }); 71 | } 72 | 73 | al::World::getLogger().Info("-------------------------------------------------"); 74 | 75 | al::World::getLogger().Success("Finish constant propagation analysis for: " + signature); 76 | 77 | } 78 | 79 | return 0; 80 | 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /tools/live-variable-analyzer.cpp: -------------------------------------------------------------------------------- 1 | #include "CLI11.h" 2 | 3 | #include "World.h" 4 | #include "analysis/dataflow/LiveVariable.h" 5 | 6 | namespace al = analyzer; 7 | namespace air = al::ir; 8 | namespace cf = al::config; 9 | namespace graph = al::analysis::graph; 10 | namespace df = al::analysis::dataflow; 11 | namespace dfact = al::analysis::dataflow::fact; 12 | 13 | int main(int argc, const char **argv) 14 | { 15 | 16 | CLI::App app("A Simple CPP Live Variable Static Analyzer\nCopyright (c) 2023-2023"); 17 | 18 | std::string sourceDir, includeDir, std; 19 | 20 | app.add_option("-S,--source-dir,", sourceDir, 21 | "directory of all source files")->required(); 22 | 23 | app.add_option("-I,--include-dir", includeDir, 24 | "directory of all header files"); 25 | 26 | app.add_option("--std,--standard", std, 27 | "c++ language standard (support all standards that clang supports)"); 28 | 29 | CLI11_PARSE(app, argc, argv); 30 | 31 | if (includeDir.empty() && std.empty()) { 32 | al::World::initialize(sourceDir); 33 | } else if (std.empty()) { 34 | al::World::initialize(sourceDir, includeDir); 35 | } else { 36 | al::World::initialize(sourceDir, includeDir, std); 37 | } 38 | 39 | std::unique_ptr analysisConfig 40 | = std::make_unique("live variable analysis"); 41 | 42 | std::unique_ptr lv = std::make_unique(analysisConfig); 43 | 44 | const al::World& world = al::World::get(); 45 | for (const auto& [signature, method] : world.getAllMethods()) { 46 | al::World::getLogger().Progress("Start live variable analysis for: " + signature); 47 | 48 | std::string fileName = method->getContainingFilePath(); 49 | 50 | std::shared_ptr myIR = method->getIR(); 51 | 52 | std::shared_ptr>> result = 53 | lv->analyze(myIR); 54 | 55 | al::World::getLogger().Info("-------------- Analysis Result of " + signature + " -----------------"); 56 | 57 | for (const std::shared_ptr& stmt : myIR->getStmts()) { 58 | al::World::getLogger().Info("* " + fileName 59 | + " " + std::to_string(stmt->getStartLine()) + ": " + stmt->str()); 60 | al::World::getLogger().Info(" In: "); 61 | result->getInFact(stmt)->forEach([&](const std::shared_ptr& v) 62 | { 63 | al::World::getLogger().Info(" " + v->getName()); 64 | }); 65 | al::World::getLogger().Info(" Out: "); 66 | result->getOutFact(stmt)->forEach([&](const std::shared_ptr& v) 67 | { 68 | al::World::getLogger().Info(" " + v->getName()); 69 | }); 70 | } 71 | 72 | al::World::getLogger().Info("-------------------------------------------------"); 73 | 74 | al::World::getLogger().Success("Finish live variable analysis for: " + signature); 75 | 76 | } 77 | 78 | return 0; 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /tools/reaching-definition-analyzer.cpp: -------------------------------------------------------------------------------- 1 | #include "CLI11.h" 2 | 3 | #include "World.h" 4 | #include "analysis/dataflow/ReachingDefinition.h" 5 | 6 | namespace al = analyzer; 7 | namespace air = al::ir; 8 | namespace cf = al::config; 9 | namespace graph = al::analysis::graph; 10 | namespace df = al::analysis::dataflow; 11 | namespace dfact = al::analysis::dataflow::fact; 12 | 13 | int main(int argc, const char **argv) 14 | { 15 | 16 | CLI::App app("A Simple CPP Reaching Definition Static Analyzer\nCopyright (c) 2023-2023"); 17 | 18 | std::string sourceDir, includeDir, std; 19 | 20 | app.add_option("-S,--source-dir,", sourceDir, 21 | "directory of all source files")->required(); 22 | 23 | app.add_option("-I,--include-dir", includeDir, 24 | "directory of all header files"); 25 | 26 | app.add_option("--std,--standard", std, 27 | "c++ language standard (support all standards that clang supports)"); 28 | 29 | CLI11_PARSE(app, argc, argv); 30 | 31 | if (includeDir.empty() && std.empty()) { 32 | al::World::initialize(sourceDir); 33 | } else if (std.empty()) { 34 | al::World::initialize(sourceDir, includeDir); 35 | } else { 36 | al::World::initialize(sourceDir, includeDir, std); 37 | } 38 | 39 | std::unique_ptr analysisConfig 40 | = std::make_unique("reaching definition analysis"); 41 | 42 | std::unique_ptr rd = std::make_unique(analysisConfig); 43 | 44 | const al::World& world = al::World::get(); 45 | for (const auto& [signature, method] : world.getAllMethods()) { 46 | al::World::getLogger().Progress("Start reaching definition analysis for: " + signature); 47 | 48 | std::string fileName = method->getContainingFilePath(); 49 | 50 | std::shared_ptr myIR = method->getIR(); 51 | 52 | std::shared_ptr>> result = 53 | rd->analyze(myIR); 54 | 55 | al::World::getLogger().Info("-------------- Analysis Result of " + signature + " -----------------"); 56 | 57 | for (const std::shared_ptr& stmt : myIR->getStmts()) { 58 | al::World::getLogger().Info("* " + fileName 59 | + " " + std::to_string(stmt->getStartLine()) + ": " + stmt->str()); 60 | al::World::getLogger().Info(" In: "); 61 | result->getInFact(stmt)->forEach([&](const std::shared_ptr& s) 62 | { 63 | al::World::getLogger().Info(" " + fileName 64 | + " " + std::to_string(s->getStartLine()) + ": " + s->str()); 65 | }); 66 | al::World::getLogger().Info(" Out: "); 67 | result->getOutFact(stmt)->forEach([&](const std::shared_ptr& s) 68 | { 69 | al::World::getLogger().Info(" " + fileName 70 | + " " + std::to_string(s->getStartLine()) + ": " + s->str()); 71 | }); 72 | } 73 | 74 | al::World::getLogger().Info("-------------------------------------------------"); 75 | 76 | al::World::getLogger().Success("Finish reaching definition analysis for: " + signature); 77 | 78 | } 79 | 80 | return 0; 81 | 82 | } 83 | 84 | --------------------------------------------------------------------------------