├── .github ├── artemis.png └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── TODO ├── artemi ├── artemi.ex.yaml ├── artemi.py ├── artemi.yaml ├── axtest.py ├── axtest.yaml ├── java_fuzzer │ ├── LICENSE │ ├── README.md │ ├── common.sh │ ├── config.yml │ ├── mrt.sh │ ├── rb │ │ ├── Basics.rb │ │ ├── Config.rb │ │ ├── ControlFlow.rb │ │ ├── Exceptions.rb │ │ ├── Fuzzer.rb │ │ ├── FuzzerUtils.java │ │ ├── LibMethods.rb │ │ ├── Loops.rb │ │ ├── Methods.rb │ │ ├── Statements.rb │ │ └── Vectorization.rb │ └── rt.sh ├── jfuzz │ ├── README.md │ ├── bin │ │ ├── darwin │ │ │ └── x86_64 │ │ │ │ └── jfuzz │ │ └── linux │ │ │ └── x86_64 │ │ │ └── jfuzz │ └── jfuzz.cc ├── jvm.py ├── requirements.txt ├── runner.py ├── uniq_compi_err.py ├── uniq_mut_err.py └── utils.py ├── build.gradle ├── eclipse-formatter.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── io │ └── artemis │ ├── Artemis.java │ ├── AxChecker.java │ ├── AxLog.java │ ├── AxNames.java │ ├── AxRandom.java │ ├── mut │ ├── LoopInserter.java │ ├── MethInvocator.java │ ├── MethMutator.java │ ├── Mutator.java │ ├── StmtMutator.java │ └── StmtWrapper.java │ ├── pol │ ├── ArtemisPolicy.java │ ├── MutationPolicy.java │ └── PolicyFactory.java │ ├── skl │ ├── ExHandleSkl.java │ ├── LiLoopSkl.java │ ├── MiCtrlSeqSkl.java │ ├── MiLoopSkl.java │ ├── RedirectSkl.java │ └── SwLoopSkl.java │ ├── syn │ ├── CbManager.java │ ├── CodeBrick.java │ ├── CodeSyn.java │ ├── LoopSkl.java │ ├── NewInstance.java │ ├── PPoint.java │ └── SklPh.java │ └── util │ ├── CannotReachHereException.java │ ├── DontSupportException.java │ ├── NotImplementedException.java │ ├── Options.java │ ├── Pair.java │ └── Spoons.java └── resources └── skeletons /.github/artemis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/test-jitcomp/Artemis/56417b588d4cd94ce3a8350826a75205dcffea94/.github/artemis.png -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and archive check 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | check: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up JDK 11 18 | uses: actions/setup-java@v3 19 | with: 20 | java-version: '11' 21 | distribution: 'temurin' 22 | - name: Build and archive 23 | run: | 24 | ./gradlew build --no-daemon 25 | ./gradlew archiveArtemis 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | 22 | # Keystore files 23 | *.jks 24 | *.keystore 25 | 26 | # Google Services (e.g. APIs or Firebase) 27 | google-services.json 28 | 29 | # Android Profiling 30 | *.hprof 31 | 32 | .DS_Store 33 | 34 | # Byte-compiled / optimized / DLL files 35 | __pycache__/ 36 | *.py[cod] 37 | *$py.class 38 | 39 | # C extensions 40 | *.so 41 | 42 | # Distribution / packaging 43 | .Python 44 | build/ 45 | develop-eggs/ 46 | dist/ 47 | downloads/ 48 | eggs/ 49 | .eggs/ 50 | lib/ 51 | lib64/ 52 | parts/ 53 | sdist/ 54 | var/ 55 | wheels/ 56 | share/python-wheels/ 57 | *.egg-info/ 58 | .installed.cfg 59 | *.egg 60 | MANIFEST 61 | 62 | # PyInstaller 63 | # Usually these files are written by a python script from a template 64 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 65 | *.manifest 66 | *.spec 67 | 68 | # Installer logs 69 | pip-log.txt 70 | pip-delete-this-directory.txt 71 | 72 | # Unit test / coverage reports 73 | htmlcov/ 74 | .tox/ 75 | .nox/ 76 | .coverage 77 | .coverage.* 78 | .cache 79 | nosetests.xml 80 | coverage.xml 81 | *.cover 82 | *.py,cover 83 | .hypothesis/ 84 | .pytest_cache/ 85 | cover/ 86 | 87 | # Translations 88 | *.mo 89 | *.pot 90 | 91 | # Django stuff: 92 | *.log 93 | local_settings.py 94 | db.sqlite3 95 | db.sqlite3-journal 96 | 97 | # Flask stuff: 98 | instance/ 99 | .webassets-cache 100 | 101 | # Scrapy stuff: 102 | .scrapy 103 | 104 | # Sphinx documentation 105 | docs/_build/ 106 | 107 | # PyBuilder 108 | .pybuilder/ 109 | target/ 110 | 111 | # Jupyter Notebook 112 | .ipynb_checkpoints 113 | 114 | # IPython 115 | profile_default/ 116 | ipython_config.py 117 | 118 | # pyenv 119 | # For a library or package, you might want to ignore these files since the code is 120 | # intended to run in multiple environments; otherwise, check them in: 121 | # .python-version 122 | 123 | # pipenv 124 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 125 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 126 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 127 | # install all needed dependencies. 128 | #Pipfile.lock 129 | 130 | # poetry 131 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 132 | # This is especially recommended for binary packages to ensure reproducibility, and is more 133 | # commonly ignored for libraries. 134 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 135 | #poetry.lock 136 | 137 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 138 | __pypackages__/ 139 | 140 | # Celery stuff 141 | celerybeat-schedule 142 | celerybeat.pid 143 | 144 | # SageMath parsed files 145 | *.sage.py 146 | 147 | # Environments 148 | .env 149 | .venv 150 | env/ 151 | venv/ 152 | ENV/ 153 | env.bak/ 154 | venv.bak/ 155 | 156 | # Spyder project settings 157 | .spyderproject 158 | .spyproject 159 | 160 | # Rope project settings 161 | .ropeproject 162 | 163 | # mkdocs documentation 164 | /site 165 | 166 | # mypy 167 | .mypy_cache/ 168 | .dmypy.json 169 | dmypy.json 170 | 171 | # Pyre type checker 172 | .pyre/ 173 | 174 | # pytype static type analyzer 175 | .pytype/ 176 | 177 | # Cython debug symbols 178 | cython_debug/ 179 | 180 | # PyCharm 181 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 182 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 183 | # and can be added to the global gitignore or merged into this file. For a more nuclear 184 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 185 | .idea/ 186 | 187 | # Artemis 188 | MYTESTDRIVER.java 189 | spooned -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 1. Support random program generation without skeletons (loop and statement), 2 | code bricks, and initializers. 3 | 4 | For a general framework, we need to abstract CodeSyn as a general 5 | synthesizer interface providing synExpr(), synStmt(), SynCodeSeg(), 6 | symBlock(), synLoop(), etc. The skeleton based CodeSyn then becomes 7 | SklCodeSyn and we should provide RndCodeSyn the synthesizer that generates 8 | programs from scratch. 9 | 10 | For RndCodeSyn, we have to synthesize code that can trigger diverse opt 11 | passes in a JIT compiler. For this, refer to CSmith for a general generation, 12 | YARPGen v.1 for scalar optimizations, and YARPGen v.2 for loop optimizations 13 | especially various loop idioms leveraging loop sequence, nests, and arrays. 14 | 15 | -------------------------------------------------------------------------------- /artemi/artemi.ex.yaml: -------------------------------------------------------------------------------- 1 | out_dir: /tmp/artemis/out # Abs path to the output directory of this script, e.g., /tmp/artemi 2 | num_proc: 4 # Number of process to use 3 | prog_timeout: 60 # Timeout (seconds) for each run of the generated .java file 4 | rand_seed: 1 # Random seed to artemi 5 | num_mutation: 8 # Number of mutations for each .java file generated by generator 6 | save_timeouts: False # Whether to save tests that are timed out 7 | 8 | java: 9 | home: $JAVA_HOME # Abs path to Java home, e.g., $JAVA_HOME or /usr/lib/jvm/java-8-openjdk-amd64 10 | classpath: [ ] # List of string, default classpath (should be abs path) to command javac and java 11 | 12 | jvm: 13 | type: hotspot # One of: 'target-art', 'host-art', 'hotspot', 'openj9', 'graal' 14 | options: [ ] # List of string, the default JVM running options 15 | # 16 | # These options are for 'hotspot', 'openj9', and 'graal' 17 | # Leave them untouched if you're testing other JVMs 18 | # 19 | java_home: /home/artemis/hs-jdk17 # Abs path to the home of the JVM you'd like to test 20 | classpath: [ ] # List of string, default classpath (should be abs path) to JVM 21 | # 22 | # These options are for 'target-art'- and 'host-art' 23 | # Leave them untouched if you're testing other JVMs 24 | # 25 | min_api: 28 # Minimum android API level compatability, should be >=1 26 | # 27 | # These options are for 'host-art' 28 | # Leave them untouched if you're testing other JVMs 29 | # 30 | host_home: # Abs path to AOSP's $ANDROID_BUILD_TOP/out directory 31 | # 32 | # These options are for 'target-art' 33 | # Leave them untouched if you're testing other JVMs 34 | # 35 | android_home: # Abs path to the Android SDK directory, e.g., $ANDROID_HOME or /home/whom/Android/Sdk 36 | serial_no: # Serial no of the emulator/device to use, e.g., emulator-5554 37 | build_tools: # Version of Android SDK build tools to use, e.g., 29.0.0 38 | app_process: false # Use app_process instead of dalvikvm on device 39 | 40 | generator: 41 | name: Java*Fuzzer # One of 'Java*Fuzzer', 'JFuzz', 'ExistingTests' 42 | out_dir: /tmp/artemis/jaf # Abs path to the output directory of Java*Fuzzer, e.g., /tmp/artemi/jaf 43 | # 44 | # These options are for 'Java*Fuzzer' 45 | # Leave them untouched if you're using other generators 46 | # 47 | conf: None # Either "none"/"None" or abs path to the config yml file of Java*Fuzzer, see java_fuzzer/config.yml 48 | # 49 | # These options are for 'JFuzz' 50 | # Leave them untouched if you're using other generators 51 | # 52 | max_stmt_list_size: 10 # Max number of statement in a statement list 53 | max_nested_branch: 5 # Max number of nested if/switch statements 54 | max_nested_loop: 3 # Max number of nested do/for/while statements 55 | max_nested_try_catch: 2 # Max number of nested try-catch statements 56 | # 57 | # Theses options are for 'ExistingTests' 58 | # Leave them untouched if you're using other generators 59 | # 60 | exist_dir: # Abs path to the existing directory saving existing tests 61 | 62 | artemis: 63 | jar: /tmp/artemis/artemis.jar # Abs path to Artemis's jar file, e.g., /tmp/artemi/artemis.jar 64 | code_bricks: /tmp/artemis/cbs # Abs path to the code brick directory 65 | policy: artemis # Mutation policy of Artemis, one of: ['artemis'] 66 | min_loop_trip: 10000 # Minimum loop trip, see "-m" option of Artemis 67 | max_loop_trip: 20000 # Maximum loop trip, see "-M" option of Artemis 68 | extra_opts: { } # Extra options, see "-X" option of Artemis 69 | -------------------------------------------------------------------------------- /artemi/artemi.yaml: -------------------------------------------------------------------------------- 1 | out_dir: # Abs path to the output directory of this script, e.g., /tmp/artemi 2 | num_proc: 4 # Number of process to use 3 | prog_timeout: 60 # Timeout (seconds) for each run of the generated .java file 4 | rand_seed: 1 # Random seed to artemi 5 | num_mutation: 8 # Number of mutations for each .java file generated by generator 6 | save_timeouts: False # Whether to save tests that are timed out 7 | 8 | java: 9 | home: # Abs path to Java home, e.g., $JAVA_HOME or /usr/lib/jvm/java-8-openjdk-amd64 10 | classpath: [ ] # List of string, default classpath (should be abs path) to command javac and java 11 | 12 | jvm: 13 | type: hotspot # One of: 'target-art', 'host-art', 'hotspot', 'openj9', 'graal' 14 | options: [ ] # List of string, the default JVM running options 15 | # 16 | # These options are for 'hotspot', 'openj9', and 'graal' 17 | # Leave them untouched if you're testing other JVMs 18 | # 19 | java_home: # Abs path to the home of the JVM you'd like to test 20 | classpath: [ ] # List of string, default classpath (should be abs path) to JVM 21 | # 22 | # These options are for 'target-art'- and 'host-art' 23 | # Leave them untouched if you're testing other JVMs 24 | # 25 | min_api: 28 # Minimum android API level compatability, should be >=1 # 26 | # 27 | # These options are for 'host-art' 28 | # Leave them untouched if you're testing other JVMs 29 | # 30 | host_home: # Abs path to AOSP's $ANDROID_BUILD_TOP/out directory 31 | # 32 | # These options are for 'target-art' 33 | # Leave them untouched if you're testing other JVMs 34 | # 35 | android_home: # Abs path to the Android SDK directory, e.g., $ANDROID_HOME or /home/whom/Android/Sdk 36 | serial_no: # Serial no of the emulator/device to use, e.g., emulator-5554 37 | build_tools: # Version of Android SDK build tools to use, e.g., 29.0.0 38 | app_process: false # Use app_process instead of dalvikvm on device 39 | 40 | generator: 41 | name: Java*Fuzzer # One of 'Java*Fuzzer', 'JFuzz', 'ExistingTests' 42 | out_dir: # Abs path to the output directory of Java*Fuzzer, e.g., /tmp/artemi/jaf 43 | # 44 | # These options are for 'Java*Fuzzer' 45 | # Leave them untouched if you're using other generators 46 | # 47 | conf: None # Either "none"/"None" or abs path to the config yml file of Java*Fuzzer, see java_fuzzer/config.yml 48 | # 49 | # These options are for 'JFuzz' 50 | # Leave them untouched if you're using other generators 51 | # 52 | max_stmt_list_size: 10 # Max number of statement in a statement list 53 | max_nested_branch: 5 # Max number of nested if/switch statements 54 | max_nested_loop: 3 # Max number of nested do/for/while statements 55 | max_nested_try_catch: 2 # Max number of nested try-catch statements 56 | # 57 | # Theses options are for 'ExistingTests' 58 | # Leave them untouched if you're using other generators 59 | # 60 | exist_dir: # Abs path to the existing directory saving existing tests 61 | 62 | artemis: 63 | jar: # Abs path to Artemis's jar file, e.g., /tmp/artemi/artemis.jar 64 | code_bricks: # Abs path to the code brick directory 65 | policy: artemis # Mutation policy of Artemis, one of: ['artemis'] 66 | min_loop_trip: 32 # Minimum loop trip, see "-m" option of Artemis 67 | max_loop_trip: 256 # Maximum loop trip, see "-M" option of Artemis 68 | extra_opts: { } # Extra options, see "-X" option of Artemis 69 | -------------------------------------------------------------------------------- /artemi/axtest.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import itertools 24 | import random 25 | import sys 26 | from pathlib import Path 27 | from subprocess import TimeoutExpired 28 | from yaml import safe_load as yaml_load 29 | 30 | from artemi import \ 31 | Command, \ 32 | script_check, \ 33 | parse_conf, \ 34 | check_conf_type, \ 35 | check_conf_dir, \ 36 | parse_java_conf, \ 37 | parse_generator_conf, \ 38 | parse_artemis_conf, \ 39 | create_java_from_conf, \ 40 | create_artemis_from_conf, \ 41 | create_generator_from_conf 42 | 43 | 44 | def save_failure(ref_dir, mu_dir, failure_dir): 45 | res = Command.mkdir(failure_dir, can_exist=True) 46 | script_check(res.retcode == 0, f'Cannot mkdir for {failure_dir.absolute()}: {res.output}') 47 | 48 | for f in ref_dir.iterdir(): 49 | if f.suffix != '.java': continue 50 | res = Command.copy(f, failure_dir) 51 | script_check(res.retcode == 0, f'Cannot copy {f} to {failure_dir}: {res.output}') 52 | 53 | res = Command.copy(mu_dir, failure_dir, is_dir=True) 54 | script_check(res.retcode == 0, f'Cannot copy {mu_dir} to {failure_dir}: {res.output}') 55 | 56 | 57 | def read_conf(conf_path: Path): 58 | conf_obj = parse_conf('', yaml_load(conf_path.open(encoding='utf-8'))) 59 | 60 | check_conf_type('.prog_timeout', conf_obj['prog_timeout'], int) 61 | check_conf_type('.rand_seed', conf_obj['rand_seed'], int) 62 | check_conf_type('.num_mutation', conf_obj['num_mutation'], int) 63 | check_conf_type('.stop_limit', conf_obj['stop_limit'], int) 64 | 65 | check_conf_type('.out_dir', conf_obj['out_dir'], str) 66 | conf_obj['out_dir'] = check_conf_dir('.out_dir', conf_obj['out_dir']) 67 | 68 | check_conf_type('.java', conf_obj['java'], dict) 69 | conf_obj['java'] = parse_java_conf('.java', conf_obj['java']) 70 | 71 | check_conf_type('.generator', conf_obj['generator'], dict) 72 | conf_obj['generator'] = parse_generator_conf('.generator', conf_obj['generator']) 73 | 74 | check_conf_type('.artemis', conf_obj['artemis'], dict) 75 | conf_obj['artemis'] = parse_artemis_conf('.artemis', conf_obj['artemis']) 76 | 77 | return conf_obj 78 | 79 | 80 | UNIQ_COMP_SCRIPT = Path('uniq_compi_err.py') 81 | UNIQ_MUT_SCRIPT = Path('uniq_mut_err.py') 82 | ARTEMIS_WORKDIR = 'mutants' 83 | ARTEMIS_OUT_FNAME = 'mutation.txt' 84 | ARTEMIS_ERR_OUT_FNAME = 'mutation_err.txt' 85 | ARTEMIS_MUTANT_COMP_ERR_OUT_FMAME = 'compilation_err.txt' 86 | 87 | 88 | def main(conf_path: Path): 89 | conf = read_conf(conf_path) 90 | 91 | random.seed(conf['rand_seed']) 92 | java = create_java_from_conf(conf['java']) 93 | java_gen = create_generator_from_conf(conf['generator']) 94 | ax = create_artemis_from_conf(conf['artemis'], java) 95 | 96 | out_dir = conf['out_dir'] 97 | out_dir.mkdir(exist_ok=True) 98 | 99 | stop_limit = conf['stop_limit'] 100 | prog_timeout = conf['prog_timeout'] 101 | num_mutation = conf['num_mutation'] 102 | 103 | diff_file = out_dir / 'diffs.txt' 104 | failures_dir = out_dir / 'failures' 105 | failures_dir.mkdir(exist_ok=True) 106 | with diff_file.open('w') as _: pass # clear existing data 107 | 108 | diff_cnt = 0 109 | failure_cnt = 0 110 | 111 | for index, ref_file in zip(itertools.count(start=1, step=1), java_gen): 112 | if index == stop_limit: break 113 | 114 | ref_dir = ref_file.parent 115 | print(f"\n[{index}] Test artemis using {ref_file.absolute()}") 116 | 117 | res = java.compile(ref_file) 118 | script_check(res.clazz is not None, f"Failed to compile ref Java: {ref_file}: {res.err_msg}") 119 | try: 120 | print(f"Running reference in JVM...") 121 | ref_res = java.run(res, timeout=prog_timeout) 122 | except TimeoutExpired: 123 | print("Timeout: run reference in JVM") 124 | print("Continue to next") 125 | continue 126 | 127 | ax_dir: Path = ref_dir / ARTEMIS_WORKDIR 128 | res = Command.mkdir(ax_dir, can_exist=True) 129 | script_check(res.retcode == 0, f'Cannot mkdir for {ax_dir.absolute()}: {res.output}') 130 | 131 | for i in range(num_mutation): 132 | seed = random.randint(0, 0xFFFFFFFF) 133 | print(f"-----------------------") 134 | print(f"{i} New test with seed: {seed}") 135 | 136 | mu_dir = ax_dir / str(i) 137 | res = Command.mkdir(mu_dir, can_exist=True) 138 | script_check(res.retcode == 0, f'Cannot mkdir for {mu_dir.absolute()}: {res.output}') 139 | 140 | res = ax.mutate(ref_file, out_dir=mu_dir, seed=seed) 141 | if res.mutant is None: 142 | ax_out_name = ARTEMIS_ERR_OUT_FNAME 143 | print(f'Failed to generate mutant, save error message to {ax_out_name}') 144 | else: 145 | ax_out_name = ARTEMIS_OUT_FNAME 146 | print(f'Generated mutant, save output to {ax_out_name}') 147 | with (mu_dir / ax_out_name).open('w') as f: 148 | f.write(res.output) 149 | if ax_out_name == ARTEMIS_ERR_OUT_FNAME: 150 | failure_dir = failures_dir / str(failure_cnt) 151 | print(f"Copy this failure to {failure_dir}") 152 | save_failure(ref_dir, mu_dir, failure_dir) 153 | failure_cnt += 1 154 | print(f'Continue to next') 155 | continue 156 | 157 | mu_file = res.mutant 158 | 159 | # Copy every other java thing to mu_dir 160 | for f in ref_dir.iterdir(): 161 | if f.suffix != '.java': continue 162 | if f.name == mu_file.name: continue 163 | res = Command.copy(f, mu_dir) 164 | script_check(res.retcode == 0, f'Cannot copy {f} to {mu_dir}: {res.output}') 165 | 166 | res = java.compile(mu_file) 167 | if res.clazz is None: 168 | print(f"Failed to compile mutant, save error message to {ARTEMIS_MUTANT_COMP_ERR_OUT_FMAME}") 169 | with (mu_dir / ARTEMIS_MUTANT_COMP_ERR_OUT_FMAME).open('w') as f: 170 | f.write(res.err_msg) 171 | failure_dir = failures_dir / str(failure_cnt) 172 | print(f"Copy this failure to {failure_dir}") 173 | save_failure(ref_dir, mu_dir, failure_dir) 174 | failure_cnt += 1 175 | print(f'Continue to next') 176 | continue 177 | 178 | try: 179 | print(f"Running mutant in JVM...") 180 | mu_res = java.run(res, timeout=prog_timeout*2) 181 | except TimeoutExpired: 182 | print("Timeout: run mutant in JVM") 183 | print(f'Continue to next') 184 | continue 185 | 186 | if ref_res.retcode != mu_res.retcode or ref_res.output != mu_res.output: 187 | print(f"AHA: Additional findings, found difference: {diff_cnt}") 188 | diff_cnt += 1 189 | with diff_file.open('a') as f: 190 | f.write("-------------\n") 191 | f.write(f"reference: {ref_file.absolute()}\n") 192 | f.write(f"mutant: {mu_file.absolute()}\n") 193 | else: 194 | print("AHA: Pass") 195 | 196 | res = Command.copy(UNIQ_COMP_SCRIPT, out_dir) 197 | script_check(res.retcode == 0, f'Cannot copy {UNIQ_COMP_SCRIPT} to {out_dir}: {res.output}') 198 | res = Command.copy(UNIQ_MUT_SCRIPT, out_dir) 199 | script_check(res.retcode == 0, f'Cannot copy {UNIQ_MUT_SCRIPT} to {out_dir}: {res.output}') 200 | 201 | 202 | if __name__ == '__main__': 203 | if len(sys.argv) != 2: 204 | print(f'usage: {sys.argv[0]} ') 205 | exit(1) 206 | main(Path(sys.argv[1])) 207 | -------------------------------------------------------------------------------- /artemi/axtest.yaml: -------------------------------------------------------------------------------- 1 | out_dir: # Abs path to the output directory of this script, e.g., /tmp/artemi 2 | prog_timeout: 60 # Timeout (seconds) for each run of the generated .java file 3 | rand_seed: 1 # Random seed to artemi 4 | num_mutation: 8 # Number of mutations for each .java file 5 | stop_limit: 10 # Stop testing if generated this number of .java files 6 | 7 | java: 8 | home: # Abs path to Java home, e.g., $JAVA_HOME or /usr/lib/jvm/java-8-openjdk-amd64 9 | classpath: [ ] # List of string, default classpath (should be abs path) to command javac and java 10 | 11 | generator: 12 | name: Java*Fuzzer # One of 'Java*Fuzzer', 'JFuzz', 'ExistingTests' 13 | # 14 | # Shared configs 15 | # 16 | out_dir: # Abs path to the output directory of Java*Fuzzer, e.g., /tmp/artemi/jaf 17 | # 18 | # Java*Fuzzer-only configs 19 | # 20 | conf: None # Either "none"/"None" or abs path to the config yml file of Java*Fuzzer, see java_fuzzer/config.yml 21 | # 22 | # JFuzz-only configs 23 | # 24 | max_stmt_list_size: 10 # Max number of statement in a statement list 25 | max_nested_branch: 5 # Max number of nested if/switch statements 26 | max_nested_loop: 3 # Max number of nested do/for/while statements 27 | max_nested_try_catch: 2 # Max number of nested try-catch statements 28 | # 29 | # ExistingTests-only configs 30 | # 31 | exist_dir: # Abs path to the existing directory saving existing tests 32 | 33 | artemis: 34 | jar: # Abs path to Artemis's jar file, e.g., /tmp/artemi/artemis.jar 35 | code_bricks: # Abs path to the code brick directory, "-B" option of Artemis 36 | policy: artemis # Mutation policy of Artemis, one of: ['artemis'], "-p" option of Artemis 37 | min_loop_trip: 32 # Minimum loop trip, "-m" option of Artemis 38 | max_loop_trip: 256 # Maximum loop trip, "-M" option of Artemis 39 | extra_opts: { } # Extra options, "-X" option of Artemis 40 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/common.sh: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------------------- 2 | # 3 | # Copyright (C) 2016 Intel Corporation 4 | # Modifications copyright (C) 2017-2018 Azul Systems 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | #-------------------------------------------------------------------------- 19 | 20 | #---------------------------------------------------------- 21 | # Java* Fuzzer test generator 22 | # 23 | # Modifications 2017-2018: Nina Rinskaya (Azul Systems), Ivan Popov (Azul Systems) 24 | #---------------------------------------------------------- 25 | 26 | [ -z "${JAVA_HOME}" ] && echo "Error: JAVA_HOME not set" && exit 1 27 | 28 | export REFERENCE_JAVA_HOME="${REFERENCE_JAVA_HOME:-"${JAVA_HOME}"}" 29 | 30 | export JAVAC="${JAVAC:-${REFERENCE_JAVA_HOME}/bin/javac}" 31 | export JAVAC_OPTS=${JAVAC_OPTS:-""} 32 | 33 | export JAVA_REFERENCE="${JAVA_REFERENCE:-${REFERENCE_JAVA_HOME}/bin/java}" 34 | export JAVA_REFERENCE_OPTS=${JAVA_REFERENCE_OPTS:-"-Xmx1G"} 35 | 36 | export TESTED_JAVA_HOME="${TESTED_JAVA_HOME:-"${JAVA_HOME}"}" 37 | export JAVA_UNDER_TEST="${JAVA_UNDER_TEST:-${TESTED_JAVA_HOME}/bin/java}" 38 | export JAVA_UNDER_TEST_OPTS=${JAVA_UNDER_TEST_OPTS:-"-Xmx1G"} 39 | 40 | export SAVE_PASSED="false" 41 | 42 | RUN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 43 | 44 | TEST_NAME=Test 45 | export TIME_OUT="${TIME_OUT:-300}" 46 | export REF_TIME_OUT="${REF_TIME_OUT:-$(( TIME_OUT ))}" 47 | 48 | export IGNORE_DEBUG_OUTPUT_PATTERNS='.*CompilerOracle.*' 49 | #-------------------------------------------------------------------------------- 50 | # Print error message and exit 51 | function Err { 52 | echo "Error: $1" 53 | exit 1 54 | } 55 | #-------------------------------------------------------------------------------- 56 | # Set timeout 57 | function SetTimeout { 58 | TIME_OUT=$1 59 | } 60 | 61 | function Check_Java() { 62 | JAVA_EXEC=$1 63 | if ! ${JAVA_EXEC} -version ; then 64 | Err "Failed to run java at ${JAVA_EXEC}" 65 | fi 66 | } 67 | 68 | #-------------------------------------------------------------------------------- 69 | # Run class Test on specified JVM (reference or tested) 70 | function RunJava() { 71 | _TIME_OUT=$1 72 | shift 73 | VM=$1 74 | shift 75 | VM_ARGS=${*%${!#}} # all arguments except the last one 76 | _TEST_NAME=${@:$#} # last argument 77 | TEST_LOCATION=$(pwd) 78 | timeout -s 9 $_TIME_OUT ${VM} ${VM_ARGS} -cp ${TEST_LOCATION} ${_TEST_NAME} 79 | RunJava_res=$? 80 | if [ $RunJava_res -eq 124 ]; then 81 | echo "TIME_OUT!" 82 | fi 83 | return $RunJava_res 84 | 85 | } 86 | [ -n "${JAVA_REFERENCE}" ] && Check_Java ${JAVA_REFERENCE} 87 | Check_Java ${JAVA_UNDER_TEST} 88 | 89 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/config.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Intel Corporation 2 | # Modifications copyright (C) 2017-2018 Azul Systems 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Java* Fuzzer for Android* Configuration File 17 | # with modifications for Java* Fuzzer test generator 18 | 19 | # general: 20 | mode: "default" # the mode of the generator: either 'defaut' or 'MM_extreme' (memory management) 21 | max_size: 256 # max length of arrays and loops; should not be less than 10 22 | min_size_fraction: 0.3 # minimal fraction of max_size to be guaranteed (other fraction is random(max_size-min_size_fraction*max_size) ) 23 | max_nested_size: 5000 # max total number of iterations of nested loops 24 | max_nested_size_not_mainTest: 500 # max total number of iterations of nested loops in methods called from mainTest 25 | max_stmts: 75 # generated statements max count 26 | #max_nlen: 3 # max length of a random name 27 | max_arr_dim: 2 # array max dimension 28 | width: 120 # width of resulting text 29 | max_shift: 110 # max indentation of a text line 30 | p_big_array: 5 # probability of big array creation 31 | min_big_array: 22276 # 12kB for byte type, 24kB for short and char, 48kB for int and float, 96kB for double 32 | max_big_array: 1000 # a random size to add to a big array 33 | outer_control: false # Ability to setup a random seed for Java code 34 | outer_control_prob: 3 # 1 - 100% invocations, 2 - 50% invocations, 3 - 33% invocations, etc. 35 | 36 | # methods: 37 | max_meths: 25 # max count of methods (excluding main and mainTest) in a class 38 | max_args: 3 # max count of a method arguments 39 | max_classes: 3 40 | max_threads: 0 41 | p_constructor: 30 # probability of a non-trivial constructor 42 | max_callers_chain: 3 # maximum chain of methods calling each other (including constructors) 43 | p_non_static_method: 25 44 | mainTest_calls_num: 10 # number of mainTest method calls, should be adjusted with CompileThreshold 45 | time_sleep_complete_tier1: 0 # time in milliseconds to sleep after mainTest_calls_num invocations of mainTest to make sure Tier1 compilation is done; 0 if no sleep needed 46 | mainTest_calls_num_tier2: 0 # number of mainTest method calls after sleep, should be adjusted with CompileThreshold (Tier2); 0 if no need to call mainTest again 47 | 48 | 49 | # expressions: 50 | # MAX_NUM = 100 51 | # MAX_NUM = 0x80000000 # max int + 1 52 | max_num: 0x10000 # 16-bit int + 1 - max int literal 53 | max_exp_depth: 3 # max depth of expression recursion 54 | p_null_literal: 30 # probability of null literal 55 | 56 | # statements: 57 | max_if_stmts: 15 # max count of statements in if part of if statement 58 | max_el_stmts: 15 # max count of statements in else part of if statement 59 | max_try_stmts: 15 # max count of statements in try 60 | max_loop_stmts: 15 # max count of statements in a loop 61 | max_loop_depth: 3 # max depth of nested loops 62 | start_frac: 16 # fraction of the max value of induction var for initial value 63 | min_small_meth_calls: 100 # minimal number of small method calls 64 | max_small_meth_calls: 1000 # maximal number of small method calls 65 | 66 | # expression probabilities: 67 | #p_stat_field: 5 # probability of static field as a var 68 | p_invoc_expr: 50 # probability of method invocation in expression 69 | p_inl_invoc_expr: 2 # probability of inlinable method invocation in expression 70 | 71 | # variables 72 | p_volatile: 15 # variability for a variable to be volatile 73 | 74 | var_types: {non_static: 3, static: 3, static_other: 0, local: 10, local_other: 0, block: 1} 75 | 76 | types: {Array: 1, Object: 0, boolean: 1, String: 0, byte: 1, char: 0, short: 1, 77 | int: 18, long: 6, float: 3, double: 2} 78 | 79 | exp_kind: {literal: 2, scalar: 8, array: 2, field: 0, oper: 32, 80 | assign: 0, cond: 0, inlinvoc: 0, invoc: 5, libinvoc: 3} 81 | 82 | op_cats: {relational: 2, boolean: 1, integral: 2, arith: 30, 83 | uarith: 3, indecrem_pre: 6, indecrem_post: 6, boolean_assn: 1, 84 | integral_assn: 1, arith_assn: 2, object_assn: 0, array_assn: 1} 85 | 86 | ind_kinds: {"-1": 12, "0": 18, "+1": 12, any: 1} 87 | 88 | operators: { 89 | relational: {'==': 1, '!=': 3, '<': 1, '<=': 1, '>': 1, '>=': 1}, 90 | boolean: {'==': 1, '!=': 1, '&': 1, '|': 1, '^': 1, '&&': 3, '||': 3, '!': 2}, 91 | integral: {'&': 1, '|': 1, '^': 1, '<<': 2, '>>': 2, '>>>': 1, '~': 1}, 92 | arith: {'+': 18, '-': 18, '*': 12, '/': 1, '%': 1}, 93 | uarith: {'-': 1}, 94 | indecrem_pre: {'++': 1, '--': 1}, 95 | indecrem_post: {'++': 1, '--': 1}, 96 | boolean_assn: {'=': 1}, 97 | integral_assn: {'=': 8, '&=': 1, '|=': 1, '^=': 1, '<<=': 3, '>>=': 3, '>>>=': 1}, 98 | arith_assn: {'=': 27, '+=': 18, '-=': 18, '*=': 12, '/=': 1, '%=': 1}, 99 | object_assn: {'=': 1}, 100 | array_assn: {'=': 1} 101 | } 102 | 103 | # statement probabilities: 104 | p_empty_seq: 1 # probability of empty sequence of statements 105 | p_else: 60 # probability of Else in If statement 106 | p_triang: 20 # probability of induction var in For initial value expr 107 | p_meth_reuse: 50 # probability of re-using known meth in an invocation 108 | p_return: 10 # probability of return statement 109 | p_var_reuse: 98 # probability of reusing existing var or arr 110 | p_big_switch: 1 # probability of big switch 111 | p_packed_switch: 60 # probability of packed switch 112 | 113 | for_step: {-3: 1, -2: 1, -1: 4, 1: 32, 2: 1, 3: 1} 114 | p_ind_var_type: {'int': 20, 'long': 1, 'float': 1, 'double': 1} # induction var types 115 | 116 | statements: { 117 | ForLoopStmt: [48, 24, 1.5], 118 | WhileDoStmt: [16, 8, 1.5], 119 | EnhancedForStmt: [ 4, 2, 1.5], 120 | ContinueStmt: [ 0, 1, 1], 121 | BreakStmt: [ 0, 1, 1], 122 | IfStmt: [ 3, 4, 1.5], 123 | SwitchStmt: [ 2, 3, 1], 124 | AssignmentStmt: [80, 40, 1], 125 | IntDivStmt: [ 0, 1, 1], 126 | ReturnStmt: [ 0, 1, 1], 127 | TryStmt: [ 1, 1, 2], 128 | ExcStmt: [ 1, 1, 2], 129 | VectStmt: [ 0, 8, 1], 130 | InvocationStmt: [50, 40, 2], 131 | CondInvocStmt: [ 0, 0, 2], 132 | SmallMethStmt: [ 10, 5, 2], 133 | NewThreadStmt: [ 40, 1, 2] 134 | } 135 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/mrt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #-------------------------------------------------------------------------- 4 | # 5 | # Copyright (C) 2016 Intel Corporation 6 | # Modifications copyright (C) 2017-2018 Azul Systems 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | #-------------------------------------------------------------------------- 21 | 22 | #---------------------------------------------------------- 23 | # Java* Fuzzer test generator 24 | # 25 | # Modifications 2017-2018: Nina Rinskaya (Azul Systems), Ivan Popov (Azul Systems) 26 | #---------------------------------------------------------- 27 | 28 | #-------------------------------------------------------------------------------- 29 | # Run the Fuzzer tool through rt.sh script in multiple processes. 30 | # Parameters: 31 | # -NP - number of processes to launch (default: 5) 32 | # -NT - number of tests to generate and run 33 | # -R - path to a dir for storing results 34 | # -P - string for forming test name prefix like this: $-. 35 | # For example, "-P a" leads to prefixes a1-, a2-, ... (default: n|o|"") 36 | # -A - the rest of arguments are passed to rt.sh 37 | #-------------------------------------------------------------------------------- 38 | 39 | 40 | #RUN_SCRIPT="bash rt.sh" 41 | #RB_UPDATE="-u rb" 42 | SUMMARY_NAME=summary.txt 43 | TRACE_FILE=trace 44 | 45 | pref="r" 46 | num_of_proc=5 47 | tests_count=-1 48 | res_dir_arg="mrt_unknown" 49 | rt_args="" 50 | 51 | while [ "$1" != "" ]; do 52 | case $1 in 53 | -P) pref=$2 54 | shift;; 55 | -R) res_dir_arg=$2 56 | shift;; 57 | -NP) num_of_proc=$2 58 | shift;; 59 | -NT) tests_count=$2 60 | shift;; 61 | -A) shift 62 | rt_args=$@ 63 | break;; 64 | *) 65 | echo Unexpected argument: $1 66 | exit;; 67 | esac 68 | shift 69 | done 70 | 71 | #-------------------------------------------------------------------------------- Prepare for the run 72 | CURR_DIR=$(pwd) # current dir 73 | RUN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Fuzzer scripts dir 74 | source ${RUN_DIR}/common.sh 75 | #export FUZZER_ROOT_DIR 76 | #export RUN_DIR 77 | RUN_SCRIPT="bash ${RUN_DIR}/rt.sh" 78 | 79 | # Transform results dir path to absolute path 80 | if [[ "${res_dir_arg}" = /* ]]; then 81 | res_dir=${res_dir_arg} 82 | else 83 | res_dir=`readlink -f ${CURR_DIR}/${res_dir_arg}` 84 | fi 85 | 86 | [[ -d $res_dir ]] || ( mkdir -p $res_dir && chmod -R 775 $res_dir ) 87 | res_file="$pref-$SUMMARY_NAME" 88 | 89 | echo $tests_count tests will be run in each of $num_of_proc processes 90 | echo Prefix: $pref Res dir: $res_dir Arguments: $rt_args 91 | #read -p "Continue?(y/n): " ans 92 | #if [ "$ans" != "y" ]; then 93 | # echo "The run is cancelled" 94 | # exit 95 | #fi 96 | 97 | #-------------------------------------------------------------------------------- Run the tool 98 | start_time=`date` 99 | host=`uname -n` 100 | echo "Host: $host 101 | Tests: $num_of_proc x $tests_count 102 | Args: $rt_args 103 | 104 | Started at: $start_time 105 | 106 | " >> $res_dir/$res_file 107 | pids="" 108 | iters=0 109 | while [ $iters != $num_of_proc ]; do 110 | let iters=iters+1 111 | $RUN_SCRIPT $tests_count -r $res_dir -f $res_file -p ${pref}${iters}- -kd $rt_args & 112 | pids="$pids $!" 113 | done 114 | trap 'kill $pids' SIGINT SIGTERM 115 | wait 116 | 117 | pids="" 118 | 119 | touch "$res_dir/$TRACE_FILE" 120 | chmod 777 "$res_dir/$TRACE_FILE" 121 | echo "$pref runs complete" >> $res_dir/$TRACE_FILE 122 | 123 | end_time=`date` 124 | echo All the test runs are complete: $num_of_proc x $tests_count, Args: $rt_args 125 | echo Results are in $res_dir 126 | echo -e "\n\nStarted at: $start_time" 127 | echo -e "\nFinished at: $end_time\n" | tee -a $res_dir/$res_file 128 | 129 | #-------------------------------------------------------------------------------- Send notification 130 | host=`uname -n` 131 | echo "All the test runs are complete. 132 | Results: $res_dir, $res_file 133 | 134 | `cat $res_dir/$res_file`" 135 | 136 | rm -f `find "$res_dir" -name core` 137 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/rb/ControlFlow.rb: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Intel Corporation 2 | # Modifications copyright (C) 2017-2018 Azul Systems 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | #========================================================== 17 | # Java* Fuzzer for Android* 18 | # Statements that imply control transfer 19 | # 20 | # Authors: Mohammad R. Haghighat, Dmitry Khukhro, Andrey Yakovlev 21 | #========================================================== 22 | 23 | #---------------------------------------------------------- 24 | # Java* Fuzzer test generator 25 | # 26 | # Modifications 2017-2018: Nina Rinskaya (Azul Systems) 27 | #---------------------------------------------------------- 28 | # 29 | 30 | # IF statement 31 | class IfStmt < Statement 32 | 33 | def initialize(cont, par) 34 | super(cont, par, true) 35 | if (stmtsRemainder() < 2) 36 | @emptyFlag = true 37 | return 38 | end 39 | @nestedStmts["if"] = genStmtSeq($conf.max_if_stmts) 40 | wrand([0,0,0,0,1,1,2]).times { |i| # additional if's 41 | @nestedStmts["elif"+i.to_s] = genStmtSeq($conf.max_if_stmts) 42 | } 43 | @conds = Hash.new() 44 | @nestedStmts.keys.each do |part| 45 | @conds[part] = Expression.new(self, "boolean") 46 | end 47 | @nestedStmts["else"] = genStmtSeq($conf.max_el_stmts) if prob($conf.p_else) 48 | retNum = 0 49 | @nestedStmts.keys.each do |key| # add return 50 | if !(ret = ReturnStmt.new(cont, self, false)).emptyFlag 51 | retNum += 1 52 | @nestedStmts[key] << ret 53 | end 54 | end 55 | @nestedStmts["if"].pop if @nestedStmts["else"] and retNum >= @nestedStmts.keys.size 56 | end 57 | 58 | def gen 59 | res = "" 60 | (["if"] + @nestedStmts.keys.grep(/elif/).sort + @nestedStmts.keys.grep(/else/)).each do |key| 61 | kw = (key[/elif/] ? "else if" : key) 62 | res += ln((key == "if" ? "" : "} ") + kw + (key == "else" ? "" : " (" + @conds[key].gen() + ")") + " {") 63 | shift(1) 64 | res += @nestedStmts[key].collect{|st| st.gen()}.join() 65 | shift(-1) 66 | end 67 | res + ln("}") 68 | end 69 | end 70 | 71 | #=============================================================================== 72 | # SWITCH statement 73 | # - Packed or sparse (random) 74 | # - order of cases 75 | # - <=64 or >64 + switch value = induction var 76 | # - break or no break 77 | # - default or no default 78 | class SwitchStmt < Statement 79 | 80 | def initialize(cont, par) 81 | super(cont, par, true, false) 82 | if (stmtsRemainder() < 3) 83 | @emptyFlag = true 84 | return 85 | end 86 | @needDefault = prob(50) 87 | iVarsList = ForLoopStmt.inductionVarsList(@parent) 88 | caseBig = ((!iVarsList.empty?) and prob($conf.p_big_switch)) 89 | packed = prob($conf.p_packed_switch) 90 | step = packed ? 1 : 5 91 | minValue = mrand(128) 92 | @caseCount = caseBig ? 70 : wrand([1,1,2,2,2,3,4,5,6,7,8,9,10]) 93 | totalAlts = @caseCount + (@needDefault ? 1 : 0) 94 | @caseValues = [] 95 | if packed 96 | @caseCount.times {|i| @caseValues << (minValue + i)} 97 | else 98 | vals = (1..@caseCount * step).to_a 99 | @caseCount.times {|i| 100 | val = wrand(vals) 101 | @caseValues << (minValue + val) 102 | vals -= [val] 103 | } 104 | end 105 | if caseBig or (!iVarsList.empty? and prob(70)) 106 | value = ExpScal.new(self, wrand(iVarsList), 1) 107 | if !caseBig 108 | value = Expression.new(self, value.resType, 1, 'oper', {'op'=>Operator.get('%'), 'vals'=>[value, 109 | ExpIntLit.new(self, @caseCount.to_s, 2)]}) 110 | end 111 | else 112 | value = Expression.indExp(self, 1, @caseCount.to_s) 113 | end 114 | if !packed 115 | value = Expression.new(self, value.resType, 2, 'oper', {'op'=>Operator.get('*'), 'vals'=>[value, 116 | ExpIntLit.new(self, step.to_s, 3)]}) 117 | end 118 | if minValue > 0 119 | value = Expression.new(self, value.resType, 2, 'oper', {'op'=>Operator.get('+', 'infix'), 'vals'=>[value, 120 | ExpIntLit.new(self, minValue.to_s, 3)]}) 121 | end 122 | @valueExpr = value 123 | numBreaks = 0 124 | @caseCount.times {|i| 125 | @nestedStmts[i.to_s] = (prob($conf.p_switch_empty_case) ? [] : genStmtSeq($conf.max_if_stmts)) 126 | if (prob(75) and @nestedStmts[i.to_s].size > 0) 127 | @nestedStmts[i.to_s] << BreakStmt.new(cont, self, false) 128 | numBreaks += 1 129 | end 130 | } 131 | @nestedStmts["default"] = genStmtSeq($conf.max_el_stmts) if @needDefault 132 | end 133 | 134 | def gen 135 | val = typeGT?(@valueExpr.resType, "int") ? "(int)(" + @valueExpr.gen() + ")" : @valueExpr.gen() 136 | res = ln("switch (" + val + ") {") 137 | @caseValues.each_index {|i| 138 | res += ln("case " + @caseValues[i].to_s + ":") 139 | shift(1) 140 | res += @nestedStmts[i.to_s].collect{|st| st.gen()}.join() 141 | shift(-1) 142 | 143 | } 144 | if @needDefault 145 | res += ln("default:") 146 | shift(1) 147 | res += @nestedStmts["default"].collect{|st| st.gen()}.join() 148 | shift(-1) 149 | end 150 | res + ln("}") 151 | end 152 | end 153 | 154 | #=============================================================================== 155 | # CONTINUE statement inside an if statement 156 | class ContinueStmt < Statement 157 | 158 | def initialize(cont, par) 159 | super(cont, par, false, false) 160 | if (loopNesting() == 0) 161 | @emptyFlag = true 162 | return 163 | end 164 | @condExpr = Expression.new(self, "boolean") 165 | end 166 | 167 | def gen 168 | ln("if (" + @condExpr.gen() + ") continue;") 169 | end 170 | end 171 | 172 | #=============================================================================== 173 | # BREAK statement inside an if statement 174 | class BreakStmt < Statement 175 | 176 | def initialize(cont, par, underIf=true) 177 | super(cont, par, false, false) 178 | if (loopNesting() == 0 and !withinSwitch?()) 179 | @emptyFlag = true 180 | return 181 | end 182 | @condExpr = nil 183 | @condExpr = Expression.new(self, "boolean") if underIf 184 | end 185 | 186 | def withinSwitch? 187 | return true if @parent and @parent.instance_of?(SwitchStmt) 188 | @parent and @parent.withinSwitch?() 189 | end 190 | 191 | def gen 192 | cond = "" 193 | cond = "if (" + @condExpr.gen() + ") " unless @condExpr.nil? 194 | ln(cond + "break;") 195 | end 196 | end 197 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/rb/Exceptions.rb: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Intel Corporation 2 | # Modifications copyright (C) 2017-2018 Azul Systems 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | #========================================================== 17 | # Java* Fuzzer for Android* 18 | # Try-catch blocks and throwing exceptions 19 | # 20 | # Authors: Mohammad R. Haghighat, Dmitry Khukhro, Andrey Yakovlev 21 | #========================================================== 22 | 23 | #---------------------------------------------------------- 24 | # Java* Fuzzer test generator 25 | # 26 | # Modifications 2017-2018: Nina Rinskaya (Azul Systems) 27 | #---------------------------------------------------------- 28 | 29 | #---------------------------------------------------------------------------- 30 | # ArrayIndexOutOfBoundsException - read/write from/to array 31 | # NegativeArraySizeException - Negative array size when creating 32 | # NullPointerException - synchronized(object=null), get/write field, invoke method of object=null, 33 | # read/write/length from/to/of array=null 34 | # ClassDefNotFoundException?- Class failed to resolve during filled-new-array 35 | # ArithmeticException - / by zero 36 | #---------------------------------------------------------------------------- 37 | $USER_DEF_EXC = "UserDefinedException" 38 | $EXC_LIST = ["ArithmeticException", "ArrayIndexOutOfBoundsException", "NegativeArraySizeException", 39 | "NullPointerException", $USER_DEF_EXC] 40 | $TEST_CLASS_NAME = "TestClass" 41 | 42 | class TryStmt < Statement 43 | attr_reader :exception 44 | 45 | def initialize(cont, par) 46 | super(cont, par, true, false) 47 | if (stmtsRemainder() < 4 or 48 | loopNesting() > 3) # limiting depth of nested loops with try-catch 49 | @emptyFlag = true 50 | return 51 | end 52 | @exception = wrand($EXC_LIST) 53 | @nestedStmts["block"] = genStmtSeq($conf.max_try_stmts, false) 54 | @nestedStmts["catch"] = [AfterTryStmt.new(cont, par, "catch", @exception)] 55 | addExc = prob(15) ? wrand($EXC_LIST - [@exception]) : "" 56 | @nestedStmts["add_catch"] = [AfterTryStmt.new(cont, par, "catch", addExc)] unless addExc.empty? 57 | @nestedStmts["finally"] = [AfterTryStmt.new(cont, par, "finally")] if prob(30) 58 | if [@exception, addExc].member?($USER_DEF_EXC) and !(defined? $excAdded) 59 | $auxClasses += "class " + $USER_DEF_EXC + " extends RuntimeException {\n" 60 | $auxClasses += " public int field;\n" 61 | $auxClasses += "}\n" 62 | $excAdded = true 63 | end 64 | end 65 | 66 | def gen 67 | res = ln("try {") 68 | shift(1) 69 | res += @nestedStmts["block"].collect{|st| st.gen()}.join() 70 | shift(-1) 71 | res += ln("}") 72 | add_flag = @nestedStmts.has_key?("add_catch") 73 | if add_flag and prob(50) 74 | res += @nestedStmts["add_catch"][0].gen() 75 | add_flag = false 76 | end 77 | res += @nestedStmts["catch"][0].gen() 78 | if add_flag 79 | res += @nestedStmts["add_catch"][0].gen() 80 | end 81 | if @nestedStmts.has_key?("finally") 82 | res += @nestedStmts["finally"][0].gen() 83 | end 84 | res 85 | end 86 | 87 | #------------------------------------------------------ 88 | # returns true if given exception is caught in statement hierarchy starting with stmt 89 | def TryStmt.isCaught?(exception, stmt) 90 | while (stmt) 91 | return true if stmt.instance_of?(TryStmt) and (stmt.exception == exception or 92 | (stmt.nestedStmts.has_key?("add_catch") and stmt.nestedStmts["add_catch"][0].excName == exception)) 93 | stmt = stmt.parent 94 | end 95 | false 96 | end 97 | end 98 | 99 | #=============================================================================== 100 | # exception processing unit: catch or finally block 101 | # Arguments: kind - either "catch" or "finally"; exc - name of exception for catch 102 | class AfterTryStmt < Statement 103 | attr_reader :excName 104 | 105 | def initialize(cont, par, kind, exc=nil) 106 | super(cont, par, true, false) 107 | @kind = kind 108 | @excName = exc 109 | @nestedStmts["block"] = genStmtSeq($conf.max_el_stmts) 110 | end 111 | 112 | def gen 113 | res = ln("catch (" + @excName + " " + @context.genUniqueName("exc") + ") {") if @kind == "catch" 114 | res = ln("finally {") if @kind == "finally" 115 | shift(1) 116 | res += @nestedStmts["block"].collect{|st| st.gen()}.join() 117 | shift(-1) 118 | res + ln("}") 119 | end 120 | end 121 | 122 | #=============================================================================== 123 | # a statement that causes throwing one of select exceptions 124 | class ExcStmt < Statement 125 | 126 | def initialize(cont, par) 127 | super(cont, par, true) 128 | if ((exceptions = excList()).empty?) 129 | @emptyFlag = true 130 | return 131 | end 132 | exc = wrand(exceptions) 133 | case exc 134 | when "ArithmeticException" 135 | var = Var.new(cont, "int", Nam::NULL) 136 | divdr = ExpScal.new(self, var, 1) 137 | dest = Expression.new(self, 'int', 1, $conf.exp_kind.getRand(['scalar', 'array']), nil, Exp::DEST) 138 | if prob(30) 139 | val = Expression.new(self, 'int', 2) 140 | else 141 | val = Expression.new(self, 'int', 2, $conf.exp_kind.getRand(['scalar', 'array'])) 142 | end 143 | oper = $conf.operators['arith'].getRand(['/','%']) 144 | div = Expression.new(self, (typeGT?(val.resType, 'int') ? val.resType : 'int'), 145 | 1, 'oper', {'op'=>Operator.get(oper, nil, 'arith'), 'vals'=>[val, divdr]}, Exp::CAST) 146 | assn = Expression.new(self, 'int', 0, 'assign', {'op'=>Operator.get('=', nil, 'integral_assn'), 'vals'=>[dest, div]}) 147 | @nestedStmts["stmts"] = [AssignmentStmt.new(cont, self, assn)] 148 | when "ArrayIndexOutOfBoundsException" 149 | if prob(50) # read 150 | dest = Expression.new(self, 'int', 1, $conf.exp_kind.getRand(['scalar', 'array']), nil, Exp::DEST) 151 | val = Expression.new(self, 'int', 1, 'array', nil, Exp::AIND) 152 | else # write 153 | dest = Expression.new(self, 'int', 1, 'array', nil, Exp::DEST|Exp::AIND) 154 | val = Expression.new(self, 'int', 1) 155 | end 156 | assn = Expression.new(self, 'int', 0, 'assign', {'op'=>Operator.get('=', nil, 'integral_assn'), 'vals'=>[dest, val]}) 157 | @nestedStmts["stmts"] = [AssignmentStmt.new(cont, self, assn)] 158 | when "NegativeArraySizeException" 159 | var = Expression.new(self, 'int', 1, 'scalar', nil, Exp::DEST) 160 | assn = Expression.new(self, 'int', 0, 'assign', {'op'=>Operator.get('=', nil, 'integral_assn'), 'vals'=>[var, "-10"]}) 161 | @nestedStmts["stmts"] = [AssignmentStmt.new(cont, self, assn)] 162 | dest = Expression.new(self, 'int', 1, 'arrname', nil, Exp::DEST) 163 | @nestedStmts["stmts"] << (dest.gen() + " = " + dest.operands[0].gen_new(var.gen()) + ";") 164 | when "NullPointerException" 165 | @nestedStmts["stmts"] = NPException() 166 | when $USER_DEF_EXC 167 | @nestedStmts["stmts"] = ["if ((" + Expression.new(self, "int").gen() + 168 | ") < " + $conf.max_num.to_s + ") throw new " + $USER_DEF_EXC + "();"] 169 | else 170 | error("ExcStmt.initialize: exc = " + exc, true) 171 | end 172 | end 173 | 174 | #------------------------------------------------------ 175 | # throw NullPointerException 176 | def NPException 177 | caseCode = rand(2) # object, array 178 | stmt = [] 179 | if caseCode == 0 # object 180 | if ! (defined? $classAdded) 181 | $auxClasses += "class "+$TEST_CLASS_NAME+" {\n" 182 | $auxClasses += " public int field;\n" 183 | $auxClasses += " public void meth() {field = 1;}\n" 184 | $auxClasses += "}\n" 185 | $classAdded = true 186 | end 187 | objName = @context.genUniqueName("var", $TEST_CLASS_NAME) 188 | stmt << ($TEST_CLASS_NAME + " " + objName + " = null;") 189 | elsif # array 190 | #objName = Expression.new(self, 'int', 1, 'arrname', nil, Exp::DEST).gen() 191 | #stmt << (objName + " = null;") 192 | objName = Arr.new(@context, 1, "int", Nam::NULL).name 193 | end 194 | case caseCode*10 + rand(4) 195 | when 0 # synchronized(object) 196 | stmt << "synchronized(" + objName + ") {" 197 | stmt << AssignmentStmt.new(@context, self) 198 | stmt << "}" 199 | when 1 # get field 200 | var = Expression.new(self, 'int', 1, 'scalar', nil, Exp::DEST) 201 | stmt << var.gen() + " = " + objName + ".field;" 202 | when 2 # write field 203 | stmt << objName + ".field = 3;" 204 | when 3 # invoke method 205 | stmt << objName + ".meth();" 206 | when 10, 11 # read array 207 | var = Expression.new(self, 'int', 1, 'scalar', nil, Exp::DEST) 208 | stmt << var.gen() + " = " + objName + "[1];" 209 | when 12 # write array 210 | stmt << objName + "[2] = 3;" 211 | when 13 # length 212 | var = Expression.new(self, 'int', 1, 'scalar', nil, Exp::DEST) 213 | stmt << var.gen() + " = " + objName + ".length;" 214 | end 215 | return stmt 216 | end 217 | 218 | #------------------------------------------------------ 219 | def excList 220 | res = [] 221 | stmt = @parent 222 | while (stmt) 223 | if stmt.instance_of?(TryStmt) 224 | res << stmt.exception 225 | res << stmt.nestedStmts["add_catch"].excName if stmt.nestedStmts.has_key?("add_catch") 226 | end 227 | stmt = stmt.parent 228 | end 229 | res 230 | end 231 | 232 | #------------------------------------------------------ 233 | def gen 234 | res = "" 235 | @nestedStmts["stmts"].each do |stmt| 236 | res += stmt.instance_of?(String) ? ln(stmt) : stmt.gen() 237 | end 238 | res 239 | end 240 | end 241 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/rb/Fuzzer.rb: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Intel Corporation 2 | # Modifications copyright (C) 2017-2018 Azul Systems 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | #---------------------------------------------------------- 17 | # Java* Fuzzer for Android* 18 | # Basic classes 19 | # 20 | # Authors: Mohammad R. Haghighat, Dmitry Khukhro, Andrey Yakovlev 21 | #---------------------------------------------------------- 22 | # 23 | # To do: 24 | # - threads interacting with each other 25 | # - intrinsics 26 | # ---------------------------------------------------------- 27 | # Bugs: 28 | # - Rare case: Java loads classes only when they are needed. Sometimes static initialization of a class depends on another's class 29 | # instantiation and the second class is not yet loaded. I couldn't catch all of the the root causes yet. 30 | # - variables that are not used in statements ? 31 | # - EnhancedForStmt: induction var (which is not) is not used - find out why 32 | # =============================================================================== 33 | 34 | #---------------------------------------------------------- 35 | # Java* Fuzzer test generator 36 | # 37 | # Modifications 2017-2018: Nina Rinskaya (Azul Systems) 38 | #---------------------------------------------------------- 39 | # 40 | 41 | $debug = true 42 | require 'Config.rb' 43 | require 'Basics.rb' 44 | require 'Statements.rb' 45 | require 'Loops.rb' 46 | require 'ControlFlow.rb' 47 | require 'Exceptions.rb' 48 | require 'Methods.rb' 49 | require 'Vectorization.rb' 50 | require 'LibMethods.rb' 51 | 52 | $names = Hash.new(0) # need global list of names to avoid hiding names in nested contexts 53 | $imports = Hash.new(0) 54 | $conf = Conf.new(true) 55 | $stop_creating_outer_fields = false # We must not create class fields after the beginning of source code generation 56 | $run_methods = 0 57 | defOperators() 58 | defLibMethods() 59 | #------------------------------------------------------------ 60 | # generate and output Java classes of the test 61 | $auxClasses = "" # auxiliary classes required by other classes code 62 | while true 63 | $globalContext = Context.new(nil,Con::GLOBAL,nil,nil,true) 64 | mainClass = JavaClass.new(true) # main class 65 | break if strongEnough?(mainClass) 66 | end 67 | puts wrapLines("// Generated by Java* Fuzzer test generator (" + TOOL_VERSION + "). " + Time.now.ctime + "\n") 68 | #puts wrapLines("// srand = "+$random_seed.to_s+"\n\n") 69 | puts wrapLines("package "+$conf.package+";\n\n") if $conf.package.length > 0 70 | puts wrapLines($imports.keys.collect {|s| "import " + s + ";\n"}.join() + "\n") if $imports.size > 0 71 | $stop_creating_outer_fields = true # We must not create class fields after the beginning of source code generation 72 | $globalContext.classList().each do |cls| 73 | res = cls.gen() 74 | puts wrapLines(res) 75 | puts wrapLines("///////////////////////////////////////////////////////////////////////") 76 | end 77 | puts wrapLines($auxClasses + "\n") if $auxClasses.size > 0 78 | printMethodCallers() 79 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/rb/LibMethods.rb: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Intel Corporation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #========================================================== 16 | # Java* Fuzzer for Android* 17 | # Library method invocations 18 | # 19 | # Authors: Mohammad R. Haghighat, Dmitry Khukhro, Andrey Yakovlev 20 | #========================================================== 21 | 22 | class LibMeth 23 | attr_reader :name, :type, :argTypes, :import, :objType 24 | 25 | def initialize(name, type, argTypes=nil, import=nil, objType=nil) 26 | @name = name 27 | @type = type 28 | @argTypes = argTypes 29 | @import = import 30 | @objType = objType 31 | end 32 | end # class LibMeth 33 | 34 | #------------------------------------------------------ 35 | def defLibMethods 36 | $libMethList = [ 37 | LibMeth.new("Math.abs", "int", ["int"]), 38 | LibMeth.new("Math.abs", "long", ["long"]), 39 | LibMeth.new("Math.abs", "float", ["float"]), 40 | LibMeth.new("Math.abs", "double", ["double"]), 41 | LibMeth.new("Math.max", "int", ["int", "int"]), 42 | LibMeth.new("Math.max", "long", ["long", "long"]), 43 | LibMeth.new("Math.min", "int", ["int", "int"]), 44 | LibMeth.new("Math.min", "long", ["long", "long"]), 45 | LibMeth.new("Math.sqrt", "double", ["double"]), 46 | # LibMeth.new("Double.doubleToRawLongBits", "long", ["double"]), # problem with raw bits for NaN 47 | LibMeth.new("Double.longBitsToDouble", "double", ["long"]), 48 | # LibMeth.new("Float.floatToRawIntBits", "int", ["float"]), 49 | LibMeth.new("Float.intBitsToFloat", "float", ["int"]), 50 | LibMeth.new("Integer.reverseBytes", "int", ["int"]), 51 | LibMeth.new("Long.reverseBytes", "long", ["long"]), 52 | LibMeth.new("Short.reverseBytes", "short", ["short"]) 53 | ] 54 | end 55 | 56 | #=============================================================================== 57 | # lib method invocation expression 58 | class ExpLibInvoc < Expression 59 | attr_reader :method 60 | 61 | # invocation of a method; type=nil means any type but void 62 | def initialize(stmt, type, depth) 63 | #return if loopDepth(stmt) > 1 64 | @method = wrand($libMethList.find_all {|meth| meth.type == type}) 65 | return unless @method 66 | vals = [] 67 | @method.argTypes.each {|t| 68 | vals << Expression.new(stmt, t, depth+1, nil, nil, Exp::CAST) 69 | } 70 | super(stmt, @method.type, depth, 'libinvoc', {'op'=>nil, 'vals'=>vals}) 71 | end 72 | 73 | #------------------------------------------------------ 74 | def gen 75 | @method.import.each {|imp| $imports[imp] += 1} if @method.import 76 | res = @method.name + "(" 77 | @operands.each {|exp| res += exp.gen() + ", "} 78 | res = res[0..-3] if @operands.size > 0 79 | res + ")" 80 | end 81 | end 82 | 83 | #=============================================================================== 84 | # lib method invocation statement 85 | class LibInvocStmt < Statement 86 | 87 | def initialize(cont, par, expr=nil) 88 | super(cont, par) 89 | if (expr) 90 | expr.parentStmt = self 91 | @invocExpr = expr 92 | else 93 | @invocExpr = ExpLibInvoc.new(self, (prob(99) ? "void" : nil), 0) 94 | end 95 | @emptyFlag = true unless @invocExpr.kind == 'libinvoc' 96 | end 97 | 98 | def gen 99 | ln(@invocExpr.gen() + ";") 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /artemi/java_fuzzer/rb/Vectorization.rb: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Intel Corporation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #========================================================== 16 | # Java* Fuzzer for Android* 17 | # Code for provoking vectorization optimizations 18 | # 19 | # Authors: Mohammad R. Haghighat, Dmitry Khukhro, Andrey Yakovlev 20 | #========================================================== 21 | 22 | #=============================================================================== 23 | # Vectorizable statement - assignment that vectorization optimization can be applied to 24 | # Templates (where i is induction variable): 25 | # n += i 26 | # n += i + 27 | # n += i * 28 | # n += i | 29 | # n += i - 30 | # n += i ^ 31 | # n += i * c1 + c2 - c3 32 | # n += + i * i (also strength reduction is applicable) 33 | # Reduced (non-)accumulated output? 34 | # arr.elem in left or right part? 35 | class VectStmt < AssignmentStmt 36 | 37 | def initialize(cont, par) 38 | ivars = ForLoopStmt.inductionVarsList(par) 39 | if ivars.empty?() # no induction vars 40 | @emptyFlag = true 41 | return 42 | end 43 | super(cont, par, 0) # create an instance w/o any expression 44 | indVar = ivars[0] # induction var of nearest loop 45 | assnOp = Operator.get("+=") 46 | left = Expression.new(self, $conf.types.getRand(TSET_INTEGRAL + ['float']), 1, 'scalar', nil, Exp::DEST) 47 | case rand(4) 48 | when 0 # n += i 49 | right = ExpScal.new(self, indVar, 1) 50 | when 1 # n += i 51 | op = Operator.get(wrand(['+', '-', '*', '|', '^']), 'infix',nil,left.resType) 52 | op = Operator.get(wrand(['+', '-', '*', '|', '^']), 'infix') if !op 53 | scal = Expression.new(self, $conf.types.getRand(TSET_INTEGRAL + ['float']), 2, 'scalar') 54 | right = ExpBinOper.new(self, indVar, op, scal, 1) 55 | when 2 # n += i * c1 + c2 - c3 56 | scal1 = Expression.new(self, $conf.types.getRand(TSET_INTEGRAL + ['float']), 4, 'scalar') 57 | scal2 = Expression.new(self, $conf.types.getRand(TSET_INTEGRAL + ['float']), 3, 'scalar') 58 | scal3 = Expression.new(self, $conf.types.getRand(TSET_INTEGRAL + ['float']), 2, 'scalar') 59 | right = ExpBinOper.new(self, indVar, '*', scal1, 3) 60 | right = ExpBinOper.new(self, right, '+', scal2, 2) 61 | right = ExpBinOper.new(self, right, '-', scal3, 1) 62 | when 3 # n += [ +] i * i 63 | right = ExpBinOper.new(self, indVar, '*', indVar, 2) 64 | if prob(50) 65 | lit = Expression.new(self, $conf.types.getRand(TSET_INTEGRAL + ['float']), 2, 'literal') 66 | right = ExpBinOper.new(self, lit, '+', right, 2) 67 | end 68 | else 69 | error("VectStmt.initialize: unexpected value", true) 70 | end 71 | assignment = Expression.new(self, left.resType, 0, 'assign', {'op'=>assnOp, 'vals'=>[left, right]}) 72 | super(cont, par, assignment) 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /artemi/jfuzz/README.md: -------------------------------------------------------------------------------- 1 | JFuzz 2 | ===== 3 | 4 | JFuzz is a tool for generating random programs with the objective 5 | of fuzz testing the ART infrastructure. Each randomly generated program 6 | can be run under various modes of execution, such as using the interpreter, 7 | using the optimizing compiler, using an external reference implementation, 8 | or using various target architectures. Any difference between the outputs 9 | (**divergence**) may indicate a bug in one of the execution modes. 10 | 11 | JFuzz can be combined with DexFuzz to get multi-layered fuzz testing. 12 | 13 | How to run JFuzz 14 | ================ 15 | 16 | jfuzz [-s seed] [-d expr-depth] [-l stmt-length] 17 | [-i if-nest] [-n loop-nest] [-v] [-h] 18 | 19 | where 20 | 21 | -s : defines a deterministic random seed 22 | (randomized using time by default) 23 | -d : defines a fuzzing depth for expressions 24 | (higher values yield deeper expressions) 25 | -l : defines a fuzzing length for statement lists 26 | (higher values yield longer statement sequences) 27 | -i : defines a fuzzing nest for if/switch statements 28 | (higher values yield deeper nested conditionals) 29 | -n : defines a fuzzing nest for for/while/do-while loops 30 | (higher values yield deeper nested loops) 31 | -t : defines a fuzzing nest for try-catch-finally blocks 32 | (higher values yield deeper nested try-catch-finally blocks) 33 | -v : prints version number and exits 34 | -h : prints help and exits 35 | 36 | The current version of JFuzz sends all output to stdout, and uses 37 | a fixed testing class named Test. So a typical test run looks as follows. 38 | 39 | jfuzz > Test.java 40 | mkdir classes 41 | javac -d classes Test.java 42 | dx --dex --output=classes.dex classes 43 | art -cp classes.dex Test 44 | 45 | How to start JFuzz testing 46 | ========================== 47 | 48 | run_jfuzz_test.py 49 | [--num_tests=NUM_TESTS] 50 | [--device=DEVICE] 51 | [--mode1=MODE] [--mode2=MODE] 52 | [--report_script=SCRIPT] 53 | [--jfuzz_arg=ARG] 54 | [--true_divergence] 55 | [--dexer=DEXER] 56 | [--debug_info] 57 | 58 | where 59 | 60 | --num_tests : number of tests to run (10000 by default) 61 | --device : target device serial number (passed to adb -s) 62 | --mode1 : m1 63 | --mode2 : m2, with m1 != m2, and values one of 64 | ri = reference implementation on host (default for m1) 65 | hint = Art interpreter on host 66 | hopt = Art optimizing on host (default for m2) 67 | tint = Art interpreter on target 68 | topt = Art optimizing on target 69 | --report_script : path to script called for each divergence 70 | --jfuzz_arg : argument for jfuzz 71 | --true_divergence : don't bisect timeout divergences 72 | --dexer=DEXER : use either dx or d8 to obtain dex files 73 | --debug_info : include debugging info 74 | 75 | How to start JFuzz nightly testing 76 | ================================== 77 | 78 | run_jfuzz_test_nightly.py 79 | [--num_proc NUM_PROC] 80 | 81 | where 82 | 83 | --num_proc : number of run_jfuzz_test.py instances to run (8 by default) 84 | 85 | Remaining arguments are passed to run\_jfuzz_test.py. 86 | 87 | How to start J/DexFuzz testing (multi-layered) 88 | ============================================== 89 | 90 | run_dex_fuzz_test.py 91 | [--num_tests=NUM_TESTS] 92 | [--num_inputs=NUM_INPUTS] 93 | [--device=DEVICE] 94 | [--dexer=DEXER] 95 | [--debug_info] 96 | 97 | where 98 | 99 | --num_tests : number of tests to run (10000 by default) 100 | --num_inputs : number of JFuzz programs to generate 101 | --device : target device serial number (passed to adb -s) 102 | --dexer=DEXER : use either dx or d8 to obtain dex files 103 | --debug_info : include debugging info 104 | 105 | Background 106 | ========== 107 | 108 | Although test suites are extremely useful to validate the correctness of a 109 | system and to ensure that no regressions occur, any test suite is necessarily 110 | finite in size and scope. Tests typically focus on validating particular 111 | features by means of code sequences most programmers would expect. Regression 112 | tests often use slightly less idiomatic code sequences, since they reflect 113 | problems that were not anticipated originally, but occurred “in the field”. 114 | Still, any test suite leaves the developer wondering whether undetected bugs 115 | and flaws still linger in the system. 116 | 117 | Over the years, fuzz testing has gained popularity as a testing technique for 118 | discovering such lingering bugs, including bugs that can bring down a system 119 | in an unexpected way. Fuzzing refers to feeding a large amount of random data 120 | as input to a system in an attempt to find bugs or make it crash. Generation- 121 | based fuzz testing constructs random, but properly formatted input data. 122 | Mutation-based fuzz testing applies small random changes to existing inputs 123 | in order to detect shortcomings in a system. Profile-guided or coverage-guided 124 | fuzzing adds a direction to the way these random changes are applied. Multi- 125 | layered approaches generate random inputs that are subsequently mutated at 126 | various stages of execution. 127 | 128 | The randomness of fuzz testing implies that the size and scope of testing is no 129 | longer bounded. Every new run can potentially discover bugs and crashes that were 130 | hereto undetected. 131 | -------------------------------------------------------------------------------- /artemi/jfuzz/bin/darwin/x86_64/jfuzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/test-jitcomp/Artemis/56417b588d4cd94ce3a8350826a75205dcffea94/artemi/jfuzz/bin/darwin/x86_64/jfuzz -------------------------------------------------------------------------------- /artemi/jfuzz/bin/linux/x86_64/jfuzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/test-jitcomp/Artemis/56417b588d4cd94ce3a8350826a75205dcffea94/artemi/jfuzz/bin/linux/x86_64/jfuzz -------------------------------------------------------------------------------- /artemi/requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==6.0 2 | -------------------------------------------------------------------------------- /artemi/runner.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import itertools 24 | import signal 25 | import time 26 | from abc import abstractmethod 27 | from multiprocessing import Pool, Manager, Process, TimeoutError 28 | from queue import Empty as QueueIsEmpty 29 | from typing import TypeVar, Generic, Optional 30 | 31 | from utils import script_check, signal_name 32 | 33 | 34 | """ 35 | MultiProcRunner works like MapReduce that it employs: 36 | - 1 generator (embedded in the caller process) to generate items sequentially; 37 | - N executors, each in a single process, to execute generated items and return results; 38 | - 1 handler, in a single process, to handler all executed results. 39 | 40 | They collaborated with each other like the following diagram: 41 | ------------------------------------------------------------------------------------ 42 | 43 | G E N E R A T O R [1 generator, required, () => T] 44 | / / | \ \\ 45 | / / | \ \\ 46 | executor executor executor ... executor executor [N executors, required, T => R] 47 | \ \ | / / 48 | \ \ | / / 49 | H A N D L E R [1 handler, optional, R => void] 50 | 51 | ------------------------------------------------------------------------------------ 52 | """ 53 | 54 | 55 | _T = TypeVar('_T') 56 | _R = TypeVar('_R') 57 | 58 | 59 | class MprGenerator(Generic[_T]): 60 | 61 | @abstractmethod 62 | def __next__(self) -> _T: 63 | pass 64 | 65 | @abstractmethod 66 | def __iter__(self): 67 | pass 68 | 69 | 70 | class MprExecutor(Generic[_T, _R]): 71 | 72 | @abstractmethod 73 | def __call__(self, i: int, t: _T) -> Optional[_R]: 74 | pass 75 | 76 | # noinspection PyMethodMayBeStatic,PyUnusedLocal 77 | def should_early_exit(self, i: int, t: _T) -> bool: 78 | return False 79 | 80 | 81 | class MprExecutorEarlyExit(Exception): pass 82 | 83 | 84 | class MprHandler(Generic[_R]): 85 | 86 | @abstractmethod 87 | def __call__(self, r: Optional[_R]): 88 | pass 89 | 90 | 91 | class KilledByUserSignal(Exception): 92 | 93 | def __init__(self, sig): 94 | self.sig = sig 95 | 96 | 97 | class MultiProcRunner(Generic[_T, _R]): 98 | def __init__(self, 99 | num_proc: int, 100 | generator: MprGenerator[_T], 101 | executor: MprExecutor[_T, _R], 102 | handler: Optional[MprHandler[_R]] = None, 103 | queue_size=128): 104 | self.num_proc = num_proc 105 | self.queue_size = queue_size 106 | self.generator = generator 107 | self.executor = executor 108 | self.handler = handler 109 | self._norm_stop = False 110 | self._started = False 111 | self._stopped = False 112 | 113 | def is_stopped(self): 114 | return self._started and self._stopped 115 | 116 | def is_stopped_normally(self): 117 | script_check(self.is_stopped(), "The runner hasn't started or stopped") 118 | return self._norm_stop 119 | 120 | def run(self, manager): 121 | if self._started: return 122 | else: self._started = True 123 | 124 | # Create a writer for writing result in a single process 125 | if self.handler is not None: 126 | queue = manager.Queue(maxsize=self.queue_size) 127 | hproc = Process(target=self._handle_wrapper, args=(queue,)) 128 | else: 129 | queue = None 130 | hproc = None 131 | 132 | # Workaround: Pool.*_async() functions never block even if all 133 | # process workers are handling their tasks. This is because Pool 134 | # maintains an infinite SimpleQueue instead of a finite Queue, 135 | # making the SimpleQueue to grow unlimitedly. So in here, we use 136 | # a semaphore to ensure only limited tasks are submitted. 137 | pool = Pool(self.num_proc) 138 | sema = manager.BoundedSemaphore(self.num_proc * 2) 139 | 140 | self._norm_stop = False 141 | try: 142 | if hproc is not None: 143 | hproc.start() 144 | 145 | # Register to catch SIGTERM signal. Note, SIGKILL cannot be caught. 146 | def abort_by_signals(sig, _): 147 | raise KilledByUserSignal(sig) 148 | signal.signal(signal.SIGTERM, abort_by_signals) 149 | 150 | for ind, item in zip(itertools.count(start=1, step=1), self.generator): 151 | print(f'< Submit: new item (index: {ind})') 152 | if self.executor.should_early_exit(ind, item): 153 | raise MprExecutorEarlyExit() 154 | sema.acquire() # Make sure only limited tasks are submitted, otherwise wait 155 | res = pool.apply_async(self._execute_wrapper, 156 | args=(ind, item, sema, queue)) 157 | # Work around to let the async task to run. Removing 158 | # the following two lines will make no works to run 159 | try: res.get(timeout=0) 160 | except TimeoutError: pass 161 | 162 | print('* Stopped Normally') 163 | pool.close() 164 | 165 | self._norm_stop = True 166 | except KeyboardInterrupt: 167 | print('* Stopped by KeyboardInterrupt') 168 | except MprExecutorEarlyExit: 169 | print(f'* Stopped by ExecutorEarlyExit') 170 | except KilledByUserSignal as e: 171 | print(f'* Stopped by KilledByUserSignal({signal_name(e.sig)})') 172 | finally: 173 | # Exception happens, terminate the pool directly 174 | if not self._norm_stop: 175 | pool.terminate() 176 | 177 | # Wait until the pool to exit (either all works to finish 178 | # when close(), or immediately passed if terminate()) 179 | print('* Joining the pool, this may take some time') 180 | pool.join() 181 | 182 | if self._norm_stop: 183 | print('* Give 15s to the handler to handler rest results', end='', flush=True) 184 | for i in range(15): 185 | print('.', end='', flush=True) 186 | time.sleep(1) 187 | print() 188 | 189 | print('* Terminating handler process, this may take some time') 190 | if hproc is not None: 191 | hproc.terminate() 192 | 193 | print('* Runner exited') 194 | self._stopped = True 195 | 196 | def _execute_wrapper(self, index: int, item: _T, sema, queue): 197 | try: 198 | print(f'+ Execute: starts to execute item (index: {index})') 199 | queue.put(self.executor(index, item)) 200 | print(f'> Finished: item (index: {index}) is executed') 201 | except KeyboardInterrupt: 202 | pass 203 | except Exception as e: 204 | print(f'! Exception: item (index: {index}): {e}') 205 | finally: 206 | sema.release() 207 | 208 | def _handle_wrapper(self, queue): 209 | try: 210 | while True: 211 | while not queue.empty(): 212 | try: self.handler(queue.get(block=False, timeout=0.1)) 213 | except QueueIsEmpty: break # add this because empty() is not reliable 214 | time.sleep(0.5) 215 | except KeyboardInterrupt: pass 216 | -------------------------------------------------------------------------------- /artemi/uniq_compi_err.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from pathlib import Path 24 | 25 | 26 | FAILURE_FNAME = 'compilation_err.txt' 27 | FAILURES_DIR = Path('./failures') 28 | 29 | failures = [] 30 | 31 | for failure_dir in FAILURES_DIR.iterdir(): 32 | if not failure_dir.is_dir(): continue 33 | for mutant_dir in failure_dir.iterdir(): 34 | if not mutant_dir.is_dir(): continue 35 | failure_file = mutant_dir / FAILURE_FNAME 36 | if not failure_file.is_file(): continue 37 | failures.append(failure_file) 38 | 39 | 40 | def read_error(file): 41 | content = file.read_text().split('\n') 42 | content = content[:-1] # skip N errors 43 | errors, errbuf = [], None 44 | for line in content: 45 | line += '\n' 46 | if line.startswith('/') and 'error: ' in line: 47 | if errbuf is not None: 48 | errors.append(errbuf.strip()) 49 | errbuf = line[line.index('error:'):] 50 | else: 51 | if errbuf is not None: 52 | errbuf += line 53 | if errbuf is not None: 54 | errors.append(errbuf.strip()) 55 | if len(errors) != 0: 56 | return '\n'.join(errors) 57 | else: 58 | return '\n'.join(content) 59 | 60 | 61 | uniq_failures = [] 62 | uniq_errors = set() 63 | 64 | for failure in failures: 65 | err = read_error(failure) 66 | if err in uniq_errors: continue 67 | uniq_failures.append(failure) 68 | uniq_errors.add(err) 69 | 70 | for failure in uniq_failures: 71 | err = read_error(failure) 72 | print(failure.absolute()) 73 | print(err) 74 | print('------------------------------------------------') 75 | print(len(uniq_failures)) 76 | -------------------------------------------------------------------------------- /artemi/uniq_mut_err.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from pathlib import Path 24 | 25 | 26 | FAILURE_FNAME = 'mutation_err.txt' 27 | FAILURES_DIR = Path('./failures') 28 | 29 | failures = [] 30 | 31 | for failure_dir in FAILURES_DIR.iterdir(): 32 | if not failure_dir.is_dir(): continue 33 | for mutant_dir in failure_dir.iterdir(): 34 | if not mutant_dir.is_dir(): continue 35 | failure_file = mutant_dir / FAILURE_FNAME 36 | if not failure_file.is_file(): continue 37 | failures.append(failure_file) 38 | 39 | 40 | def read_exception(file): 41 | content = file.read_text().split('\n') 42 | for ind, line in enumerate(content): 43 | if line.startswith('Exception in thread "main"'): 44 | return '\n'.join(content[ind:]) 45 | return '\n'.join(content) 46 | 47 | 48 | uniq_failures = [] 49 | uniq_exceptions = set() 50 | 51 | for failure in failures: 52 | excp = read_exception(failure) 53 | if excp in uniq_exceptions: continue 54 | uniq_failures.append(failure) 55 | uniq_exceptions.add(excp) 56 | 57 | for failure in uniq_failures: 58 | excp = read_exception(failure) 59 | print(failure.absolute()) 60 | print(excp) 61 | print('------------------------------------------------') 62 | print(len(uniq_failures)) 63 | -------------------------------------------------------------------------------- /artemi/utils.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import os 24 | import shlex 25 | import signal 26 | import sys 27 | import time 28 | from pathlib import Path 29 | from subprocess import Popen, \ 30 | CompletedProcess, \ 31 | TimeoutExpired, \ 32 | PIPE, \ 33 | STDOUT, \ 34 | CalledProcessError 35 | 36 | 37 | # 38 | # Common os/sys utilities 39 | # 40 | 41 | def sys_arch_is_64(): 42 | return sys.maxsize > 2**32 43 | 44 | 45 | def sys_os_type(): 46 | return sys.platform 47 | 48 | 49 | def safe_killpg(pid, sig): 50 | try: os.killpg(pid, sig) 51 | except ProcessLookupError: 52 | pass # Ignore if there is no such process 53 | 54 | 55 | # Fix: subprocess.run(cmd) series methods, when timed out, only sends a SIGTERM 56 | # signal to cmd while does not kill cmd's subprocess. We let each command to run 57 | # in a new process group by adding start_new_session flag, and kill the whole 58 | # process group such that all cmd's subprocess are also killed when timed out. 59 | def run_proc(cmd, stdout, stderr, timeout): 60 | with Popen(cmd, stdout=stdout, stderr=stderr, start_new_session=True) as proc: 61 | try: 62 | output, err_msg = proc.communicate(timeout=timeout) 63 | except: # Including TimeoutExpired, KeyboardInterrupt, communicate handled that. 64 | safe_killpg(os.getpgid(proc.pid), signal.SIGKILL) 65 | # We don't call proc.wait() as .__exit__ does that for us. 66 | raise 67 | retcode = proc.poll() 68 | return CompletedProcess(proc.args, retcode, output, err_msg) 69 | 70 | 71 | # We add this helper function because signal.strsignal() is added from py3.8 72 | def signal_name(sig): 73 | return { 74 | # See `man signal` 75 | signal.SIGKILL: 'Killed', 76 | signal.SIGTERM: 'Terminated', 77 | signal.SIGINT: 'Interrupted', 78 | signal.SIGQUIT: 'Quits' 79 | }[sig] 80 | 81 | 82 | def exec_time(fn, *args, **kwargs): 83 | begin = time.time() 84 | result = fn(*args, **kwargs) 85 | end = time.time() 86 | return result, (end - begin) 87 | 88 | 89 | def format_time(delta: int) -> str: 90 | hour = delta // 3600 91 | minutes = (delta % 3600) // 60 92 | seconds = (delta % 3600) % 60 93 | if hour != 0: 94 | return f'{hour}h,{minutes}m,{seconds}s ({delta}s)' 95 | elif minutes != 0: 96 | return f'{minutes}m,{seconds}s ({delta}s)' 97 | else: 98 | return f'{seconds}s ({delta}s)' 99 | 100 | 101 | # 102 | # Common script utilities 103 | # 104 | 105 | class CheckError(Exception): pass 106 | 107 | 108 | def script_check(pred: bool, msg: str): 109 | if not pred: 110 | raise CheckError(msg) 111 | 112 | 113 | class CommandResult: 114 | 115 | def __init__(self, retcode, output): 116 | # retcode: return code of the command 117 | # output: stdout on success of the command, or stderr on failure 118 | self.retcode = retcode 119 | self.output = output 120 | 121 | 122 | class Command: 123 | 124 | @staticmethod 125 | def run(cmd: str, timeout: int = 5): 126 | try: 127 | proc = run_proc(shlex.split(cmd), 128 | stdout=PIPE, 129 | stderr=STDOUT, 130 | timeout=timeout) 131 | output = proc.stdout 132 | retcode = proc.returncode 133 | except CalledProcessError as x: 134 | output = x.output 135 | retcode = x.returncode 136 | if output is not None: 137 | output = str(output, encoding='utf-8').strip() 138 | return CommandResult(retcode, output) 139 | 140 | @staticmethod 141 | def redirected_run(cmd: str, stdout, stderr, timeout: int = 5): 142 | try: 143 | proc = run_proc(shlex.split(cmd), 144 | stdout=stdout, 145 | stderr=stderr, 146 | timeout=timeout) 147 | retcode = proc.returncode 148 | except CalledProcessError as x: 149 | retcode = x.returncode 150 | return CommandResult(retcode, None) 151 | 152 | @staticmethod 153 | def checked_cmd(cmd: str, *args, **kwargs): 154 | fn = getattr(Command, cmd) 155 | res = fn(*args, kwargs) 156 | script_check(res.retcode == 0, f"Failed to run command Command.{cmd}: {res.output}") 157 | 158 | @classmethod 159 | def copy(cls, source: Path, target: Path, is_dir: bool = False): 160 | if is_dir: 161 | return cls.run(f'cp -r {source.absolute()} {target.absolute()}') 162 | else: 163 | return cls.run(f'cp {source.absolute()} {target.absolute()}') 164 | 165 | @classmethod 166 | def mkdir(cls, target: Path, can_exist: bool = False): 167 | if can_exist: 168 | return cls.run(f'mkdir -p {target.absolute()}') 169 | else: 170 | return cls.run(f'mkdir {target.absolute()}') 171 | 172 | @classmethod 173 | def move(cls, source: Path, target: Path): 174 | return cls.run(f'mv {source.absolute()} {target.absolute()}') 175 | 176 | @classmethod 177 | def remove(cls, path: Path, is_dir: bool, force: bool): 178 | args = '' 179 | if is_dir: 180 | args += '-r ' 181 | if force: 182 | args += '-f ' 183 | return cls.run(f'rm {args} {path.absolute()}') 184 | 185 | 186 | # 187 | # Conf type checking utilities 188 | # 189 | 190 | def check_conf_type(key_path: str, val, typ): 191 | if type(val) != typ: 192 | raise CheckError(f'{key_path} is not a {typ.__name__}: {val}') 193 | return val 194 | 195 | 196 | def check_conf_env_var(key_path: str, var: str) -> str: 197 | val = os.getenv(var[1:]) 198 | script_check(val is not None, f"{key_path}'s environment variable {var} is not set") 199 | return val 200 | 201 | 202 | def check_conf_dir(key_path: str, val: str, should_exist: bool = True) -> Path: 203 | path = Path(val) 204 | if should_exist: 205 | script_check(path.exists(), f'{key_path} does not exist: {val}') 206 | script_check(path.is_dir(), f'{key_path} is not a directory: {val}') 207 | return path 208 | 209 | 210 | def check_conf_file(key_path: str, val: str, should_exist: bool = True) -> Path: 211 | path = Path(val) 212 | if should_exist: 213 | script_check(path.exists(), f'{key_path} does not exist: {val}') 214 | script_check(path.is_file(), f'{key_path} is not a file: {val}') 215 | return path 216 | 217 | 218 | def check_conf_file_or_dir(key_path: str, val: str) -> Path: 219 | path = Path(val) 220 | script_check(path.exists(), f'{key_path} does not exist: {val}') 221 | return path 222 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.github.johnrengelman.shadow' version '6.1.0' 3 | id 'java' 4 | } 5 | 6 | group 'io.artemis' 7 | version '0.1.0' 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | compileJava { 14 | sourceCompatibility = JavaVersion.VERSION_11 15 | targetCompatibility = JavaVersion.VERSION_11 16 | } 17 | 18 | jar { 19 | manifest { 20 | attributes 'Main-Class': 'io.artemis.Artemis' 21 | } 22 | } 23 | 24 | // Creating a zip archive including only the artemis 25 | // jar binary and the testing scripts inside scripts 26 | task archiveArtemis(type: Zip) { 27 | dependsOn shadowJar 28 | 29 | description = "Archive Artemis and the artemi framework" 30 | archiveFileName = "artemis-${project.version}.zip" 31 | 32 | exclude '**/.idea' 33 | 34 | from("$project.rootDir") { 35 | include 'LICENSE' 36 | include 'README.md' 37 | } 38 | 39 | from("$buildDir/libs") { 40 | //noinspection GroovyAssignabilityCheck 41 | include shadowJar.archiveFileName.get() 42 | rename shadowJar.archiveFileName.get(), "artemis.jar" 43 | } 44 | 45 | from('artemi') { 46 | include 'java_fuzzer/**' 47 | include 'jfuzz/**' 48 | include 'requirements.txt' 49 | include 'runner.py', 'jvm.py', 'utils.py' 50 | include 'artemi.py', 'artemi.yaml', 'artemi.ex.yaml' 51 | } 52 | } 53 | 54 | dependencies { 55 | implementation 'fr.inria.gforge.spoon:spoon-core:10.1.1' 56 | // Add the following to resolve "Failed to load class org.slf4j.impl.StaticLoggerBinder" 57 | implementation 'org.slf4j:slf4j-simple:1.7.36' 58 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0' 59 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' 60 | } 61 | 62 | test { 63 | useJUnitPlatform() 64 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/test-jitcomp/Artemis/56417b588d4cd94ce3a8350826a75205dcffea94/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Artemis' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/AxChecker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis; 26 | 27 | public class AxChecker { 28 | 29 | public static class CheckFailError extends Error { 30 | public CheckFailError(String message) { 31 | super("Check fail: " + message); 32 | } 33 | } 34 | 35 | public static void check(boolean value, String message) { 36 | if (!value) { 37 | throw new CheckFailError(message); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/AxLog.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis; 26 | 27 | import java.io.PrintStream; 28 | 29 | public class AxLog { 30 | 31 | public static final int LEVEL_INFO = 1; 32 | public static final int LEVEL_VERBOSE = 2; 33 | public static final int LEVEL_DEBUG = 3; 34 | 35 | private static int sLevel = LEVEL_INFO; 36 | private static PrintStream sStdout = System.out; 37 | private static PrintStream sStderr = System.err; 38 | 39 | public interface LogBlock { 40 | void log(PrintStream out, PrintStream err); 41 | } 42 | 43 | public static void setLevel(int level) { 44 | sLevel = level; 45 | } 46 | 47 | public static void setStdout(PrintStream out) { 48 | sStdout = out; 49 | } 50 | 51 | public static void setStderr(PrintStream err) { 52 | sStderr = err; 53 | } 54 | 55 | public static void e(String msg) { 56 | sStderr.println("***ERROR*** " + msg); 57 | } 58 | 59 | public static void w(String msg) { 60 | sStdout.println("\\\\\\WARN/// " + msg); 61 | } 62 | 63 | public static void i(String msg) { 64 | sStdout.println("[INFO] " + msg); 65 | } 66 | 67 | public static void v(String header, LogBlock b) { 68 | if (sLevel >= LEVEL_VERBOSE) { 69 | v(header + ": "); 70 | v("-----"); 71 | b.log(sStdout, sStderr); 72 | v("-----"); 73 | } 74 | } 75 | 76 | public static void v(String msg) { 77 | if (sLevel >= LEVEL_VERBOSE) { 78 | sStdout.println("[VERB] " + msg); 79 | } 80 | } 81 | 82 | public static void d(String msg) { 83 | if (sLevel >= LEVEL_DEBUG) { 84 | sStdout.println("[·DBG] " + msg); 85 | } 86 | } 87 | 88 | public static void d(String header, LogBlock b) { 89 | if (sLevel >= LEVEL_DEBUG) { 90 | d(header); 91 | d("-----"); 92 | b.log(sStdout, sStderr); 93 | d("-----"); 94 | } 95 | } 96 | 97 | public static void println(String msg) { 98 | sStdout.println(msg); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/AxNames.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis; 26 | 27 | public class AxNames { 28 | private static AxNames sInstance; 29 | private int mCount; 30 | 31 | public static AxNames getInstance() { 32 | if (sInstance == null) { 33 | sInstance = new AxNames(); 34 | } 35 | return sInstance; 36 | } 37 | 38 | public String nextName() { 39 | return "ax$" + (mCount++); 40 | } 41 | 42 | private AxNames() { 43 | mCount = 0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/AxRandom.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis; 26 | 27 | import java.util.Random; 28 | import java.util.stream.DoubleStream; 29 | import java.util.stream.IntStream; 30 | import java.util.stream.LongStream; 31 | 32 | public class AxRandom { 33 | private static AxRandom sInstance; 34 | private final Random mRandom; 35 | 36 | public static AxRandom getInstance() { 37 | if (sInstance == null) { 38 | sInstance = new AxRandom(); 39 | } 40 | return sInstance; 41 | } 42 | 43 | public void setSeed(long seed) { 44 | mRandom.setSeed(seed); 45 | } 46 | 47 | public void nextBytes(byte[] bytes) { 48 | mRandom.nextBytes(bytes); 49 | } 50 | 51 | public int nextInt() { 52 | return mRandom.nextInt(); 53 | } 54 | 55 | public int nextInt(int bound) { 56 | return mRandom.nextInt(bound); 57 | } 58 | 59 | /** 60 | * Return next int between min and max 61 | */ 62 | public int nextInt(int min, int max) { 63 | AxChecker.check(max > min, "Max bound must be greater than min bound"); 64 | return mRandom.nextInt(max - min) + min; 65 | } 66 | 67 | public long nextLong() { 68 | return mRandom.nextLong(); 69 | } 70 | 71 | public boolean nextBoolean() { 72 | return mRandom.nextBoolean(); 73 | } 74 | 75 | public float nextFloat() { 76 | return mRandom.nextFloat(); 77 | } 78 | 79 | public double nextDouble() { 80 | return mRandom.nextDouble(); 81 | } 82 | 83 | public double nextGaussian() { 84 | return mRandom.nextGaussian(); 85 | } 86 | 87 | /** 88 | * Return next index from an array of probabilities. For example, nextIndex([0.1, 0.4, 0.5]) 89 | * returns - 0 with probability 0.1 - 1 with probability 0.4 - 2 with probability 0.5. 90 | */ 91 | public int nextIndex(float[] probs) { 92 | AxChecker.check(probs.length >= 1, "Input probabilities are not sufficient"); 93 | float[] newProbs = new float[probs.length + 1]; 94 | newProbs[0] = 0f; 95 | for (int i = 1; i < newProbs.length; i++) { 96 | newProbs[i] = newProbs[i - 1] + probs[i - 1]; 97 | } 98 | AxChecker.check(Math.abs(1.0f - newProbs[newProbs.length - 1]) <= 1e-5, 99 | "Input probabilities does not sum to 1.0"); 100 | float x = nextFloat(); 101 | for (int i = 0; i < newProbs.length; i++) { 102 | if (newProbs[i] < x && x <= newProbs[i + 1]) { 103 | return i; 104 | } 105 | } 106 | return probs.length - 1; 107 | } 108 | 109 | public IntStream ints(long streamSize) { 110 | return mRandom.ints(streamSize); 111 | } 112 | 113 | public IntStream ints() { 114 | return mRandom.ints(); 115 | } 116 | 117 | public IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) { 118 | return mRandom.ints(streamSize, randomNumberOrigin, randomNumberBound); 119 | } 120 | 121 | public IntStream ints(int randomNumberOrigin, int randomNumberBound) { 122 | return mRandom.ints(randomNumberOrigin, randomNumberBound); 123 | } 124 | 125 | public LongStream longs(long streamSize) { 126 | return mRandom.longs(streamSize); 127 | } 128 | 129 | public LongStream longs() { 130 | return mRandom.longs(); 131 | } 132 | 133 | public LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) { 134 | return mRandom.longs(streamSize, randomNumberOrigin, randomNumberBound); 135 | } 136 | 137 | public LongStream longs(long randomNumberOrigin, long randomNumberBound) { 138 | return mRandom.longs(randomNumberOrigin, randomNumberBound); 139 | } 140 | 141 | public DoubleStream doubles(long streamSize) { 142 | return mRandom.doubles(streamSize); 143 | } 144 | 145 | public DoubleStream doubles() { 146 | return mRandom.doubles(); 147 | } 148 | 149 | public DoubleStream doubles(long streamSize, double randomNumberOrigin, 150 | double randomNumberBound) { 151 | return mRandom.doubles(streamSize, randomNumberOrigin, randomNumberBound); 152 | } 153 | 154 | public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { 155 | return mRandom.doubles(randomNumberOrigin, randomNumberBound); 156 | } 157 | 158 | private AxRandom() { 159 | mRandom = new Random(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/mut/LoopInserter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.mut; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import io.artemis.Artemis; 31 | import io.artemis.AxLog; 32 | import io.artemis.skl.LiLoopSkl; 33 | import io.artemis.syn.PPoint; 34 | import spoon.reflect.code.CtBlock; 35 | import spoon.reflect.code.CtStatement; 36 | import spoon.reflect.declaration.CtImport; 37 | 38 | public class LoopInserter extends StmtMutator { 39 | 40 | public LoopInserter(Artemis ax) { 41 | super(ax); 42 | } 43 | 44 | @Override 45 | protected boolean canMutate(CtStatement stmt) { 46 | return !(stmt instanceof CtBlock); 47 | } 48 | 49 | @Override 50 | protected void mutate(CtStatement stmt) { 51 | PPoint pp = PPoint.beforeStmt(mAx.getTestClass(), stmt); 52 | 53 | AxLog.v("Synthesizing new loops with LoopInserter's skeleton"); 54 | List imports = new ArrayList<>(5); 55 | CtStatement loop = mAx.getCodeSyn().synLoop(pp, new LiLoopSkl(), imports); 56 | 57 | AxLog.v("Inserting the following loop before the statement to mutate", 58 | (out, err) -> out.println(loop)); 59 | stmt.insertBefore(loop); 60 | 61 | // Add required imports to our tests 62 | mAx.getTestCompUnit().getImports().addAll(imports); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/mut/MethInvocator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.mut; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.stream.Collectors; 30 | 31 | import io.artemis.Artemis; 32 | import io.artemis.AxChecker; 33 | import io.artemis.AxLog; 34 | import io.artemis.AxNames; 35 | import io.artemis.AxRandom; 36 | import io.artemis.skl.MiCtrlSeqSkl; 37 | import io.artemis.skl.MiLoopSkl; 38 | import io.artemis.syn.CodeSyn; 39 | import io.artemis.syn.PPoint; 40 | import io.artemis.util.Spoons; 41 | import spoon.reflect.code.CtExpression; 42 | import spoon.reflect.code.CtInvocation; 43 | import spoon.reflect.code.CtReturn; 44 | import spoon.reflect.code.CtStatement; 45 | import spoon.reflect.declaration.CtClass; 46 | import spoon.reflect.declaration.CtField; 47 | import spoon.reflect.declaration.CtImport; 48 | import spoon.reflect.declaration.CtMethod; 49 | import spoon.reflect.declaration.ModifierKind; 50 | import spoon.reflect.factory.Factory; 51 | import spoon.reflect.visitor.filter.TypeFilter; 52 | 53 | public class MethInvocator extends MethMutator { 54 | 55 | public MethInvocator(Artemis ax) { 56 | super(ax); 57 | } 58 | 59 | @Override 60 | protected boolean canMutate(CtMethod meth) { 61 | return meth.getDeclaringType() instanceof CtClass 62 | && meth.getBody().getStatements().size() > 0; 63 | } 64 | 65 | @Override 66 | protected void mutate(CtMethod meth) { 67 | CtClass clazz = (CtClass) meth.getDeclaringType(); 68 | AxChecker.check(clazz != null, "No class found for method " + meth.getSimpleName() + "()"); 69 | 70 | // Find all method calls to meth 71 | List> invocations = clazz.getElements(new TypeFilter<>(CtInvocation.class) { 72 | @Override 73 | public boolean matches(CtInvocation invoc) { 74 | return super.matches(invoc) 75 | && invoc.getExecutable().getExecutableDeclaration() == meth; 76 | } 77 | }); 78 | if (invocations.size() == 0) { 79 | // No invocations found, do nothing 80 | AxLog.v("No method invocations found, discard this mutation"); 81 | return; 82 | } 83 | 84 | Factory fact = mAx.getSpoon().getFactory(); 85 | CodeSyn syn = mAx.getCodeSyn(); 86 | 87 | // Create a control field in the class to control the field 88 | CtField ctrl = fact.createCtField(/* name= */AxNames.getInstance().nextName(), 89 | fact.createCtTypeReference(Boolean.class), "false"); 90 | if (clazz.isStatic() || clazz.isTopLevel()) { 91 | ctrl.addModifier(ModifierKind.STATIC); 92 | } 93 | AxLog.v("Adding control field: " + ctrl.getSimpleName()); 94 | clazz.addField(ctrl); 95 | 96 | List imports = new ArrayList<>(5); 97 | 98 | // Create the control sequence and insert to meth as a prologue 99 | AxLog.v("Synthesizing control prologue controlled by field " + Spoons.getSimpleName(ctrl)); 100 | CtReturn retStmt = fact.createReturn(); 101 | if (!Spoons.isVoidType(meth.getType())) { 102 | retStmt.setReturnedExpression((CtExpression) syn.synExpr(meth.getType())); 103 | } 104 | CtStatement ctrlSeq = MiCtrlSeqSkl.instantiate(mAx, ctrl, 105 | syn.synCodeSeg( 106 | PPoint.beforeStmt(mAx.getTestClass(), meth.getBody().getStatement(0)), 107 | imports), 108 | retStmt); 109 | AxLog.v("Add the following control prologue to method " + Spoons.getSimpleName(meth), 110 | (out, ignoreUnused) -> out.println(ctrlSeq)); 111 | meth.getBody().insertBegin(ctrlSeq); 112 | 113 | // Randomly select an invocation, synthesize and insert a loop before it 114 | CtInvocation invoc = invocations.get(AxRandom.getInstance().nextInt(invocations.size())); 115 | PPoint pp = PPoint.beforeStmt(mAx.getTestClass(), invoc); 116 | 117 | AxLog.v("Synthesizing new loops with MethInvocator's skeleton"); 118 | CtStatement loop = mAx.getCodeSyn().synLoop(pp, new MiLoopSkl(), imports); 119 | 120 | // Substitute placeholders in the skeleton 121 | AxLog.v("Boosting the given by the following loop", 122 | (out, ignoreUnused) -> out.println(loop)); 123 | MiLoopSkl.enableCtrl(loop, ctrl, fact); 124 | MiLoopSkl.disableCtrl(loop, ctrl, fact); 125 | // Let's synthesize an argument list for it 126 | // TODO Reuse existing variables and initializers 127 | List> args = meth.getParameters().stream() 128 | .map(p -> syn.synExpr(p.getType())).collect(Collectors.toList()); 129 | MiLoopSkl.invokeMeth(loop, invoc, args, fact); 130 | // Insert the loop right before the invocation statement 131 | Spoons.insertBeforeStmt(invoc, loop); 132 | 133 | // Add required imports to our tests 134 | mAx.getTestCompUnit().getImports().addAll(imports); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/mut/MethMutator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.mut; 26 | 27 | import io.artemis.Artemis; 28 | import spoon.reflect.declaration.CtElement; 29 | import spoon.reflect.declaration.CtMethod; 30 | 31 | public abstract class MethMutator extends Mutator { 32 | 33 | public MethMutator(Artemis ax) { 34 | super(ax); 35 | } 36 | 37 | @Override 38 | public final boolean canMutate(CtElement element) { 39 | if (!super.canMutate(element) || !(element instanceof CtMethod)) { 40 | return false; 41 | } 42 | CtMethod meth = (CtMethod) element; 43 | return !meth.isAbstract() && canMutate(meth); 44 | } 45 | 46 | @Override 47 | public final void mutate(CtElement element) { 48 | mutate((CtMethod) element); 49 | } 50 | 51 | protected abstract boolean canMutate(CtMethod meth); 52 | 53 | protected abstract void mutate(CtMethod meth); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/mut/Mutator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.mut; 26 | 27 | import io.artemis.Artemis; 28 | import spoon.reflect.declaration.CtElement; 29 | 30 | public abstract class Mutator { 31 | 32 | protected final Artemis mAx; 33 | 34 | public Mutator(Artemis ax) { 35 | mAx = ax; 36 | } 37 | 38 | public void setExtraOptions(Artemis.ExtraOpts opts) {} 39 | 40 | /** 41 | * Test whether the given element can be mutated by this mutator. Always call this method before 42 | * calling mutate(); otherwise, the mutator cannot guarantee the mutation behavior. When 43 | * overriding this method, always call super(). 44 | * 45 | * @param element The element to mutate 46 | * @return Return true if the mutator can mutate element, or false 47 | */ 48 | public boolean canMutate(CtElement element) { 49 | // The element should not be synthetic 50 | return !mAx.getCodeSyn().isSyn(element); 51 | } 52 | 53 | public abstract void mutate(CtElement element); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/mut/StmtMutator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.mut; 26 | 27 | import io.artemis.Artemis; 28 | import spoon.reflect.code.CtStatement; 29 | import spoon.reflect.code.CtStatementList; 30 | import spoon.reflect.declaration.CtElement; 31 | 32 | public abstract class StmtMutator extends Mutator { 33 | 34 | public StmtMutator(Artemis ax) { 35 | super(ax); 36 | } 37 | 38 | @Override 39 | public final boolean canMutate(CtElement element) { 40 | if (!super.canMutate(element) || !(element instanceof CtStatement)) { 41 | return false; 42 | } 43 | CtStatement stmt = (CtStatement) element; 44 | CtElement parent = stmt.getParent(); 45 | // Every statement we mutate should reside in a statement list, thus 46 | // we directly reject those not satisfying this requirement like 47 | // CtUnaryOperator and CtCase. This is a weired design that Spoon treats 48 | // CtUnaryOperator as a subtype of CtStatement. 49 | return parent instanceof CtStatementList && canMutate(stmt); 50 | } 51 | 52 | @Override 53 | public final void mutate(CtElement element) { 54 | mutate((CtStatement) element); 55 | } 56 | 57 | protected abstract boolean canMutate(CtStatement stmt); 58 | 59 | protected abstract void mutate(CtStatement stmt); 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/mut/StmtWrapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.mut; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import io.artemis.Artemis; 31 | import io.artemis.AxLog; 32 | import io.artemis.skl.SwLoopSkl; 33 | import io.artemis.syn.PPoint; 34 | import spoon.reflect.code.CtAbstractInvocation; 35 | import spoon.reflect.code.CtBodyHolder; 36 | import spoon.reflect.code.CtBreak; 37 | import spoon.reflect.code.CtContinue; 38 | import spoon.reflect.code.CtIf; 39 | import spoon.reflect.code.CtNewClass; 40 | import spoon.reflect.code.CtReturn; 41 | import spoon.reflect.code.CtStatement; 42 | import spoon.reflect.code.CtStatementList; 43 | import spoon.reflect.code.CtSwitch; 44 | import spoon.reflect.code.CtSynchronized; 45 | import spoon.reflect.code.CtYieldStatement; 46 | import spoon.reflect.declaration.CtClass; 47 | import spoon.reflect.declaration.CtImport; 48 | import spoon.reflect.declaration.CtInterface; 49 | import spoon.reflect.declaration.CtVariable; 50 | import spoon.reflect.visitor.filter.TypeFilter; 51 | 52 | public class StmtWrapper extends StmtMutator { 53 | 54 | public StmtWrapper(Artemis ax) { 55 | super(ax); 56 | } 57 | 58 | @Override 59 | protected boolean canMutate(CtStatement stmt) { 60 | // We only prefer to wrap single statement. So we don't like block-alike e.g. if/loops/... 61 | // since they often contain unpredictable things like control-flow altering (continue/break) 62 | // which, once wrapped, may break the semantics 63 | if (stmt instanceof CtStatementList || stmt instanceof CtBodyHolder || stmt instanceof CtIf 64 | || stmt instanceof CtSwitch || stmt instanceof CtNewClass 65 | || stmt instanceof CtSynchronized) { 66 | return false; 67 | } 68 | 69 | // Never wrap any variable and type declarations 70 | if ((stmt instanceof CtVariable) || (stmt instanceof CtClass) 71 | || (stmt instanceof CtInterface)) { 72 | return false; 73 | } 74 | 75 | // Never wrap any flow-altering statements 76 | if ((stmt instanceof CtBreak) || (stmt instanceof CtContinue) || (stmt instanceof CtReturn) 77 | || (stmt instanceof CtYieldStatement)) { 78 | return false; 79 | } 80 | 81 | // Never wrap any method and constructor invocations 82 | return stmt.getElements(new TypeFilter<>(CtAbstractInvocation.class)).size() == 0; 83 | } 84 | 85 | @Override 86 | protected void mutate(CtStatement stmt) { 87 | PPoint pp = PPoint.beforeStmt(mAx.getTestClass(), stmt); 88 | 89 | AxLog.v("Synthesizing new loops with StmtWrapper's skeleton"); 90 | List imports = new ArrayList<>(5); 91 | CtStatement loop = mAx.getCodeSyn().synLoop(pp, new SwLoopSkl(), imports); 92 | 93 | AxLog.v("Wrapping the statement by the following loop", (out, err) -> out.println(loop)); 94 | // Replace the statement to wrap stmt by our synthetic loop 95 | stmt.replace(loop); 96 | // Substitute the placeholder by our statement to wrap stmt 97 | SwLoopSkl.wrapStmt(loop, stmt); 98 | 99 | // Add required imports to our tests 100 | mAx.getTestCompUnit().getImports().addAll(imports); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/pol/ArtemisPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.pol; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import io.artemis.Artemis; 31 | import io.artemis.AxChecker; 32 | import io.artemis.AxLog; 33 | import io.artemis.AxRandom; 34 | import io.artemis.mut.LoopInserter; 35 | import io.artemis.mut.MethInvocator; 36 | import io.artemis.mut.MethMutator; 37 | import io.artemis.mut.Mutator; 38 | import io.artemis.mut.StmtMutator; 39 | import io.artemis.mut.StmtWrapper; 40 | import io.artemis.util.Spoons; 41 | import spoon.reflect.code.CtStatement; 42 | import spoon.reflect.declaration.CtClass; 43 | import spoon.reflect.declaration.CtMethod; 44 | import spoon.reflect.visitor.filter.AbstractFilter; 45 | 46 | public class ArtemisPolicy extends MutationPolicy { 47 | 48 | public ArtemisPolicy(Artemis ax, Artemis.ExtraOpts opts) { 49 | super(ax, opts); 50 | } 51 | 52 | @Override 53 | public void apply(CtClass clazz) { 54 | AxRandom rand = AxRandom.getInstance(); 55 | 56 | // We never mutate initializer blocks, either static or not 57 | List> methods = new ArrayList<>(clazz.getMethods()); 58 | AxChecker.check(methods.size() > 0, 59 | "No methods found in the given class: " + clazz.getQualifiedName()); 60 | 61 | for (CtMethod meth : methods) { 62 | // Let's flip a coin to decide whether to mutate meth or not 63 | if (rand.nextBoolean()) { 64 | float prob = rand.nextFloat(); 65 | Mutator mut; 66 | if (prob <= 0.33f) { 67 | mut = new LoopInserter(mAx); 68 | } else if (0.33f < prob && prob <= 0.67f) { 69 | mut = new StmtWrapper(mAx); 70 | } else { 71 | mut = new MethInvocator(mAx); 72 | } 73 | AxLog.v("Flip coin (front): mutating method " + Spoons.getSimpleName(meth) + " by " 74 | + mut.getClass().getSimpleName()); 75 | doApply(mut, meth); 76 | } else { 77 | AxLog.v("Flip coin (back): don't mutate method: " + Spoons.getSimpleName(meth)); 78 | } 79 | } 80 | } 81 | 82 | private void doApply(Mutator mut, CtMethod meth) { 83 | if (mut instanceof StmtMutator) { 84 | doApply((StmtMutator) mut, meth); 85 | } else if (mut instanceof MethMutator) { 86 | doApply((MethMutator) mut, meth); 87 | } 88 | } 89 | 90 | private void doApply(MethMutator mut, CtMethod meth) { 91 | if (mut.canMutate(meth)) { 92 | mut.mutate(meth); 93 | } else { 94 | AxLog.v("The method cannot be mutated, abandon"); 95 | } 96 | } 97 | 98 | private void doApply(StmtMutator mut, CtMethod meth) { 99 | List statements = meth.getElements(new AbstractFilter<>() { 100 | @Override 101 | public boolean matches(CtStatement stmt) { 102 | return super.matches(stmt) && mut.canMutate(stmt); 103 | } 104 | }); 105 | 106 | if (statements.size() == 0) { 107 | AxLog.v("No available statements to mutate, abandon"); 108 | return; 109 | } 110 | 111 | CtStatement stmt = statements.get(AxRandom.getInstance().nextInt(statements.size())); 112 | AxLog.v("Mutating statement", (out, ignoreUnused) -> out.println(stmt)); 113 | mut.mutate(stmt); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/pol/MutationPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.pol; 26 | 27 | import io.artemis.Artemis; 28 | import spoon.reflect.declaration.CtClass; 29 | 30 | public abstract class MutationPolicy { 31 | 32 | protected Artemis mAx; 33 | 34 | public MutationPolicy(Artemis ax, Artemis.ExtraOpts ignoreUnused) { 35 | mAx = ax; 36 | } 37 | 38 | public abstract void apply(CtClass clazz); 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/pol/PolicyFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.pol; 26 | 27 | import io.artemis.Artemis; 28 | import io.artemis.util.CannotReachHereException; 29 | 30 | public class PolicyFactory { 31 | 32 | public enum PolicyName { 33 | ARTEMIS("artemis"); 34 | 35 | public final String name; 36 | 37 | PolicyName(String name) { 38 | this.name = name; 39 | } 40 | } 41 | 42 | public static MutationPolicy create(PolicyName which, Artemis ax, Artemis.ExtraOpts opts) { 43 | switch (which) { 44 | case ARTEMIS: 45 | return new ArtemisPolicy(ax, opts); 46 | default: 47 | throw new CannotReachHereException("Unsupported policy with name: " + which.name); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/skl/ExHandleSkl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.skl; 26 | 27 | import io.artemis.Artemis; 28 | import spoon.reflect.code.CtBlock; 29 | import spoon.reflect.code.CtStatement; 30 | import spoon.template.BlockTemplate; 31 | import spoon.template.Local; 32 | import spoon.template.Parameter; 33 | 34 | public class ExHandleSkl extends BlockTemplate { 35 | /////////////////////////////////////////////////////////////////////////////////////// 36 | /////////////////////////////// SKELETON DEFINITIONS ////////////////////////////////// 37 | /////////////////////////////////////////////////////////////////////////////////////// 38 | // Values to substitute 39 | @Parameter 40 | private CtBlock _TRY_BLOCK_; 41 | @Parameter 42 | private CtBlock _FINALLY_BLOCK_; 43 | 44 | // Names to substitute 45 | @Parameter 46 | private String _EX_NAME_; 47 | 48 | @Override 49 | public void block() throws Throwable { 50 | try { 51 | _TRY_BLOCK_.S(); 52 | } catch (Throwable _EX_NAME_) { 53 | /* DO NOTHING */ 54 | } finally { 55 | _FINALLY_BLOCK_.S(); 56 | } 57 | } 58 | /////////////////////////////////////////////////////////////////////////////////////// 59 | /////////////////////////////////////////////////////////////////////////////////////// 60 | /////////////////////////////////////////////////////////////////////////////////////// 61 | 62 | @Local 63 | public static CtBlock instantiate(Artemis ax, String exName, CtStatement tryStmt) { 64 | return instantiate(ax, exName, ax.getSpoon().getFactory().createCtBlock(tryStmt), null); 65 | } 66 | 67 | @Local 68 | public static CtBlock instantiate(Artemis ax, String exName, CtBlock tryBlock) { 69 | return instantiate(ax, exName, tryBlock, null); 70 | } 71 | 72 | @Local 73 | public static CtBlock instantiate(Artemis ax, String exName, CtStatement tryStmt, 74 | CtStatement finallyStmt) { 75 | return instantiate(ax, exName, ax.getSpoon().getFactory().createCtBlock(tryStmt), 76 | ax.getSpoon().getFactory().createCtBlock(finallyStmt)); 77 | } 78 | 79 | @Local 80 | public static CtBlock instantiate(Artemis ax, String exName, CtBlock tryBlock, 81 | CtBlock finallyBlock) { 82 | ExHandleSkl skl = new ExHandleSkl(); 83 | 84 | skl._EX_NAME_ = exName; 85 | skl._TRY_BLOCK_ = tryBlock; 86 | skl._FINALLY_BLOCK_ = finallyBlock; 87 | 88 | return skl.apply(ax.getTestClass()); 89 | } 90 | 91 | @Local 92 | private ExHandleSkl() {} 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/skl/LiLoopSkl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.skl; 26 | 27 | import io.artemis.Artemis; 28 | import io.artemis.AxChecker; 29 | import io.artemis.syn.LoopSkl; 30 | import spoon.reflect.code.CtBlock; 31 | import spoon.reflect.code.CtLiteral; 32 | import spoon.reflect.code.CtStatement; 33 | import spoon.reflect.factory.Factory; 34 | import spoon.template.Local; 35 | import spoon.template.Parameter; 36 | import spoon.template.StatementTemplate; 37 | 38 | public class LiLoopSkl extends StatementTemplate implements LoopSkl { 39 | /////////////////////////////////////////////////////////////////////////////////////// 40 | /////////////////////////////// SKELETON DEFINITIONS ////////////////////////////////// 41 | /////////////////////////////////////////////////////////////////////////////////////// 42 | // Loop headers to substitute 43 | @Parameter 44 | private CtLiteral _START_; 45 | @Parameter 46 | private CtLiteral _STEP_; 47 | @Parameter 48 | private CtLiteral _TRIP_; 49 | 50 | // Blocks to synthesize and substitute 51 | @Parameter 52 | private CtBlock _BODY_; 53 | 54 | // Names to synthesize and substitute 55 | @Parameter 56 | private String _I_NAME_; 57 | 58 | @Override 59 | public void statement() throws Throwable { 60 | for (int _I_NAME_ = _START_.S(); _I_NAME_ < _START_.S() + _TRIP_.S(); _I_NAME_ += 61 | _STEP_.S()) { 62 | _BODY_.S(); 63 | } 64 | } 65 | /////////////////////////////////////////////////////////////////////////////////////// 66 | /////////////////////////////////////////////////////////////////////////////////////// 67 | /////////////////////////////////////////////////////////////////////////////////////// 68 | 69 | @Local 70 | @Override 71 | public int getBlockCount() { 72 | return 1; 73 | } 74 | 75 | @Local 76 | @Override 77 | public int getNamesCount() { 78 | return 1; 79 | } 80 | 81 | @Local 82 | @Override 83 | public CtStatement instantiate(Artemis ax, int start, int step, int trip, String[] names, 84 | CtBlock[] blocks) { 85 | AxChecker.check(names.length == getNamesCount(), "Insufficient names"); 86 | AxChecker.check(blocks.length == getBlockCount(), "Insufficient blocks"); 87 | 88 | Factory fact = ax.getSpoon().getFactory(); 89 | 90 | _START_ = fact.createLiteral(start); 91 | _STEP_ = fact.createLiteral(step); 92 | _TRIP_ = fact.createLiteral(trip); 93 | _I_NAME_ = names[0]; 94 | _BODY_ = blocks[0]; 95 | 96 | return apply(ax.getTestClass()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/skl/MiCtrlSeqSkl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.skl; 26 | 27 | import io.artemis.Artemis; 28 | import io.artemis.syn.SklPh; 29 | import spoon.reflect.code.CtBlock; 30 | import spoon.reflect.code.CtFieldRead; 31 | import spoon.reflect.code.CtReturn; 32 | import spoon.reflect.code.CtStatement; 33 | import spoon.reflect.declaration.CtField; 34 | import spoon.template.Local; 35 | import spoon.template.Parameter; 36 | import spoon.template.StatementTemplate; 37 | 38 | public class MiCtrlSeqSkl extends StatementTemplate { 39 | /////////////////////////////////////////////////////////////////////////////////////// 40 | /////////////////////////////// SKELETON DEFINITIONS ////////////////////////////////// 41 | /////////////////////////////////////////////////////////////////////////////////////// 42 | // Values to substitute 43 | @Parameter 44 | private CtFieldRead _CTRL_; 45 | @Parameter 46 | private CtBlock _BODY_; 47 | 48 | @Override 49 | public void statement() throws Throwable { 50 | if (_CTRL_.S()) { 51 | _BODY_.S(); 52 | SklPh.placeholder(""); 53 | } 54 | } 55 | /////////////////////////////////////////////////////////////////////////////////////// 56 | /////////////////////////////////////////////////////////////////////////////////////// 57 | /////////////////////////////////////////////////////////////////////////////////////// 58 | 59 | @Local 60 | public static CtStatement instantiate(Artemis ax, CtField ctrl, CtBlock body, 61 | CtReturn retStmt) { 62 | MiCtrlSeqSkl skl = new MiCtrlSeqSkl(); 63 | 64 | skl._CTRL_ = ax.getSpoon().getFactory().createFieldRead(); 65 | skl._CTRL_.setVariable(ctrl.getReference()); 66 | skl._BODY_ = body; 67 | 68 | CtStatement stmt = skl.apply(ax.getTestClass()); 69 | SklPh.substitute(stmt, "", retStmt); 70 | return stmt; 71 | } 72 | 73 | @Local 74 | private MiCtrlSeqSkl() {} 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/skl/MiLoopSkl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.skl; 26 | 27 | import java.util.List; 28 | 29 | import io.artemis.Artemis; 30 | import io.artemis.AxChecker; 31 | import io.artemis.syn.LoopSkl; 32 | import io.artemis.syn.SklPh; 33 | import spoon.reflect.code.CtBlock; 34 | import spoon.reflect.code.CtExpression; 35 | import spoon.reflect.code.CtInvocation; 36 | import spoon.reflect.code.CtLiteral; 37 | import spoon.reflect.code.CtStatement; 38 | import spoon.reflect.declaration.CtField; 39 | import spoon.reflect.factory.Factory; 40 | import spoon.template.BlockTemplate; 41 | import spoon.template.Local; 42 | import spoon.template.Parameter; 43 | 44 | public class MiLoopSkl extends BlockTemplate implements LoopSkl { 45 | /////////////////////////////////////////////////////////////////////////////////////// 46 | /////////////////////////////// SKELETON DEFINITIONS ////////////////////////////////// 47 | /////////////////////////////////////////////////////////////////////////////////////// 48 | // Loop headers to substitute 49 | @Parameter 50 | private CtLiteral _START_; 51 | @Parameter 52 | private CtLiteral _STEP_; 53 | @Parameter 54 | private CtLiteral _TRIP_; 55 | 56 | // Blocks to synthesize and substitute 57 | @Parameter 58 | private CtBlock _PRE_BODY_; 59 | @Parameter 60 | private CtBlock _POST_BODY_; 61 | 62 | // Names to synthesize and substitute 63 | @Parameter 64 | private String _I_NAME_; 65 | 66 | @Override 67 | public void block() throws Throwable { 68 | for (int _I_NAME_ = _START_.S(); _I_NAME_ < _START_.S() + _TRIP_.S(); _I_NAME_ += 69 | _STEP_.S()) { 70 | _PRE_BODY_.S(); 71 | SklPh.placeholder(""); 72 | SklPh.placeholder(""); 73 | SklPh.placeholder(""); 74 | _POST_BODY_.S(); 75 | } 76 | } 77 | /////////////////////////////////////////////////////////////////////////////////////// 78 | /////////////////////////////////////////////////////////////////////////////////////// 79 | /////////////////////////////////////////////////////////////////////////////////////// 80 | 81 | @Local 82 | @Override 83 | public int getBlockCount() { 84 | return 2; 85 | } 86 | 87 | @Local 88 | @Override 89 | public int getNamesCount() { 90 | return 1; 91 | } 92 | 93 | @Local 94 | @Override 95 | public CtStatement instantiate(Artemis ax, int start, int step, int trip, String[] names, 96 | CtBlock[] blocks) { 97 | AxChecker.check(names.length == getNamesCount(), "Insufficient names"); 98 | AxChecker.check(blocks.length == getBlockCount(), "Insufficient blocks"); 99 | 100 | Factory fact = ax.getSpoon().getFactory(); 101 | 102 | _START_ = fact.createLiteral(start); 103 | _STEP_ = fact.createLiteral(step); 104 | _TRIP_ = fact.createLiteral(trip); 105 | _I_NAME_ = names[0]; 106 | _PRE_BODY_ = blocks[0]; 107 | _POST_BODY_ = blocks[1]; 108 | 109 | return apply(ax.getTestClass()); 110 | } 111 | 112 | @Local 113 | public static void enableCtrl(CtStatement loop, CtField ctrl, Factory fact) { 114 | SklPh.substitute(loop, "", fact.createVariableAssignment(ctrl.getReference(), 115 | ctrl.isStatic(), fact.createLiteral(true))); 116 | } 117 | 118 | @Local 119 | public static void disableCtrl(CtStatement loop, CtField ctrl, Factory fact) { 120 | SklPh.substitute(loop, "", fact.createVariableAssignment(ctrl.getReference(), 121 | ctrl.isStatic(), fact.createLiteral(false))); 122 | } 123 | 124 | @Local 125 | public static void invokeMeth(CtStatement loop, CtInvocation invoc, 126 | List> args, Factory fact) { 127 | SklPh.substitute(loop, "", fact.createInvocation(invoc.getTarget().clone(), 128 | invoc.getExecutable().clone(), args)); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/skl/RedirectSkl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.skl; 26 | 27 | import java.io.IOException; 28 | import java.io.OutputStream; 29 | import java.io.PrintStream; 30 | 31 | import io.artemis.Artemis; 32 | import spoon.reflect.code.CtStatement; 33 | import spoon.reflect.declaration.CtClass; 34 | import spoon.reflect.factory.Factory; 35 | import spoon.template.ExtensionTemplate; 36 | import spoon.template.Local; 37 | import spoon.template.Substitution; 38 | 39 | public class RedirectSkl extends ExtensionTemplate { 40 | /////////////////////////////////////////////////////////////////////////////////////// 41 | /////////////////////////////// SKELETON DEFINITIONS ////////////////////////////////// 42 | /////////////////////////////////////////////////////////////////////////////////////// 43 | private static final PrintStream devNull = new PrintStream(new OutputStream() { 44 | @Override 45 | public void write(int i) throws IOException { 46 | /* DO NOTHING */ 47 | } 48 | }); 49 | private static final PrintStream stdOutBk = System.out; 50 | private static final PrintStream stdErrBk = System.err; 51 | 52 | public static void redirect() { 53 | System.setOut(devNull); 54 | System.setErr(devNull); 55 | } 56 | 57 | public static void recover() { 58 | System.setOut(stdOutBk); 59 | System.setErr(stdErrBk); 60 | } 61 | /////////////////////////////////////////////////////////////////////////////////////// 62 | /////////////////////////////////////////////////////////////////////////////////////// 63 | /////////////////////////////////////////////////////////////////////////////////////// 64 | 65 | @Local 66 | public static CtClass instantiate(Artemis ax, String className) { 67 | CtClass rhClass = ax.getSpoon().getFactory().createClass(className); 68 | Substitution.insertAll(rhClass, new RedirectSkl()); 69 | return rhClass; 70 | } 71 | 72 | @Local 73 | public static CtStatement callRedirect(Artemis ax, CtClass rhClass) { 74 | Factory fact = ax.getSpoon().getFactory(); 75 | return fact.createInvocation(fact.createTypeAccess(rhClass.getReference()), 76 | rhClass.getMethod("redirect").getReference()); 77 | } 78 | 79 | @Local 80 | public static CtStatement callRecover(Artemis ax, CtClass rhClass) { 81 | Factory fact = ax.getSpoon().getFactory(); 82 | return fact.createInvocation(fact.createTypeAccess(rhClass.getReference()), 83 | rhClass.getMethod("recover").getReference()); 84 | } 85 | 86 | @Local 87 | private RedirectSkl() {} 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/skl/SwLoopSkl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.skl; 26 | 27 | import io.artemis.Artemis; 28 | import io.artemis.AxChecker; 29 | import io.artemis.syn.LoopSkl; 30 | import io.artemis.syn.SklPh; 31 | import spoon.reflect.code.CtBlock; 32 | import spoon.reflect.code.CtLiteral; 33 | import spoon.reflect.code.CtStatement; 34 | import spoon.reflect.factory.Factory; 35 | import spoon.template.BlockTemplate; 36 | import spoon.template.Local; 37 | import spoon.template.Parameter; 38 | 39 | public class SwLoopSkl extends BlockTemplate implements LoopSkl { 40 | /////////////////////////////////////////////////////////////////////////////////////// 41 | /////////////////////////////// SKELETON DEFINITIONS ////////////////////////////////// 42 | /////////////////////////////////////////////////////////////////////////////////////// 43 | // Loop headers to substitute 44 | @Parameter 45 | private CtLiteral _START_; 46 | @Parameter 47 | private CtLiteral _STEP_; 48 | @Parameter 49 | private CtLiteral _TRIP_; 50 | 51 | // Blocks to synthesize and substitute 52 | @Parameter 53 | private CtBlock _PRE_BODY_; 54 | @Parameter 55 | private CtBlock _POST_BODY_; 56 | 57 | // Names to synthesize and substitute 58 | @Parameter 59 | private String _I_NAME_; 60 | @Parameter 61 | private String _EXEC_NAME_; 62 | 63 | @Override 64 | public void block() throws Throwable { 65 | boolean _EXEC_NAME_ = false; 66 | for (int _I_NAME_ = _START_.S(); _I_NAME_ < _START_.S() + _TRIP_.S(); _I_NAME_ += 67 | _STEP_.S()) { 68 | _PRE_BODY_.S(); 69 | if (!_EXEC_NAME_) { 70 | SklPh.placeholder(""); 71 | _EXEC_NAME_ = true; 72 | } 73 | _POST_BODY_.S(); 74 | } 75 | } 76 | /////////////////////////////////////////////////////////////////////////////////////// 77 | /////////////////////////////////////////////////////////////////////////////////////// 78 | /////////////////////////////////////////////////////////////////////////////////////// 79 | 80 | @Local 81 | @Override 82 | public int getBlockCount() { 83 | return 2; 84 | } 85 | 86 | @Local 87 | @Override 88 | public int getNamesCount() { 89 | return 2; 90 | } 91 | 92 | @Local 93 | @Override 94 | public CtStatement instantiate(Artemis ax, int start, int step, int trip, String[] names, 95 | CtBlock[] blocks) { 96 | AxChecker.check(names.length == getNamesCount(), "Insufficient names"); 97 | AxChecker.check(blocks.length == getBlockCount(), "Insufficient blocks"); 98 | 99 | Factory fact = ax.getSpoon().getFactory(); 100 | 101 | _START_ = fact.createLiteral(start); 102 | _STEP_ = fact.createLiteral(step); 103 | _TRIP_ = fact.createLiteral(trip); 104 | _I_NAME_ = names[0]; 105 | _EXEC_NAME_ = names[1]; 106 | _PRE_BODY_ = blocks[0]; 107 | _POST_BODY_ = blocks[1]; 108 | 109 | return apply(ax.getTestClass()); 110 | } 111 | 112 | public static void wrapStmt(CtStatement loop, CtStatement stmt) { 113 | SklPh.substitute(loop, "", stmt); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/syn/CodeBrick.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.syn; 26 | 27 | import java.util.List; 28 | 29 | import spoon.reflect.code.CtBlock; 30 | import spoon.reflect.declaration.CtImport; 31 | import spoon.reflect.declaration.CtMethod; 32 | import spoon.reflect.declaration.CtParameter; 33 | import spoon.support.util.ModelList; 34 | 35 | /** 36 | * A code brick is a special type of code skeleton which have inputs and a list of statements. To 37 | * use a code brick, synthesize a declaration for each input and link the statement. Sometimes, you 38 | * may need to import the imports (but this often does not need since Spoon can take care of auto 39 | * imports in its setting by Spoon.getEnvironment().setAutoImports(true)). 40 | */ 41 | /* package */ class CodeBrick { 42 | private final int mId; 43 | // The method that hangs the code brick 44 | private final CtMethod mMethod; 45 | // Required imports when using this brick elsewhere 46 | private final ModelList mImports; 47 | 48 | public int getId() { 49 | return mId; 50 | } 51 | 52 | /** 53 | * Get the inputs of this code brick. Just take care. The inputs returned are already linked. So 54 | * please be sure to clone if they are expected to use elsewhere. 55 | * 56 | * @return The inputs of this code brick. 57 | */ 58 | public CtParameter[] unsafeGetInputs() { 59 | List> params = mMethod.getParameters(); 60 | CtParameter[] inputs = new CtParameter[params.size()]; 61 | for (int i = 0; i < inputs.length; i++) { 62 | inputs[i] = params.get(i); 63 | } 64 | return inputs; 65 | } 66 | 67 | /** 68 | * Get all statements of this code brick. Just take care. The statements returned are already 69 | * linked. So please be sure to clone if they are expected to use elsewhere. 70 | * 71 | * @return All statements of this code brick. 72 | */ 73 | public CtBlock unsafeGetStatements() { 74 | return mMethod.getBody(); 75 | } 76 | 77 | /** 78 | * Get required imports if using this code brick elsewhere. The imports returned are already 79 | * linked. So please be sure to clone if they are expected to use elsewhere. 80 | * 81 | * @return Set of imports 82 | */ 83 | public ModelList unsafeGetImports() { 84 | return mImports; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return mMethod.toString(); 90 | } 91 | 92 | /* package */ CodeBrick(int id, CtMethod cbMethod, ModelList imports) { 93 | mId = id; 94 | mMethod = cbMethod; 95 | mImports = imports; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/syn/LoopSkl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.syn; 26 | 27 | import io.artemis.Artemis; 28 | import spoon.reflect.code.CtBlock; 29 | import spoon.reflect.code.CtStatement; 30 | import spoon.template.Local; 31 | 32 | /** 33 | * AbsLoopSkl is the abstract loop skeleton that every loop skeleton needs to implement. A loop 34 | * skeleton should only leave names and blocks for our synthesizer LoopSyn to synthesize. Any other 35 | * are disallowed. The subclass should tell synthesizer how many blocks and names should be 36 | * synthesized and give the exactly same number of names and blocks when instantiating the skeleton. 37 | */ 38 | public interface LoopSkl { 39 | 40 | @Local 41 | int getBlockCount(); 42 | 43 | @Local 44 | int getNamesCount(); 45 | 46 | @Local 47 | CtStatement instantiate(Artemis ax, int start, int step, int trip, String[] names, 48 | CtBlock[] blocks); 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/syn/NewInstance.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.syn; 26 | 27 | import java.lang.reflect.Constructor; 28 | import java.lang.reflect.Modifier; 29 | import java.util.ArrayList; 30 | 31 | import io.artemis.AxRandom; 32 | import io.artemis.util.CannotReachHereException; 33 | import io.artemis.util.Spoons; 34 | import spoon.reflect.code.CtExpression; 35 | import spoon.reflect.code.CtNewArray; 36 | import spoon.reflect.factory.Factory; 37 | import spoon.reflect.reference.CtArrayTypeReference; 38 | import spoon.reflect.reference.CtTypeReference; 39 | import spoon.support.reflect.reference.CtArrayTypeReferenceImpl; 40 | import spoon.support.reflect.reference.CtTypeReferenceImpl; 41 | 42 | /* package */ class NewInstance extends Spoons.TypeSwitch> { 43 | 44 | private Factory mFact; 45 | 46 | public NewInstance() {} 47 | 48 | @Override 49 | protected CtExpression kaseArray(CtArrayTypeReferenceImpl type) { 50 | CtNewArray array = mFact.createNewArray(); 51 | CtArrayTypeReference arrayType = type.clone(); 52 | CtTypeReference arrayFinestType = arrayType.getArrayType(); 53 | // Remove generics (aka type arguments) to avoid generic array creation 54 | if (!arrayFinestType.getActualTypeArguments().isEmpty()) { 55 | arrayFinestType.setActualTypeArguments(new ArrayList<>()); 56 | } 57 | array.setType((CtTypeReference) arrayType); 58 | int dimenCount = type.getDimensionCount(); 59 | if (dimenCount == 1) { 60 | // Don't be too large, otherwise it may occupy too much memory. 61 | int size = AxRandom.getInstance().nextInt(1, 10); 62 | for (int i = 0; i < size; i++) { 63 | array.addElement(svitch(type.getComponentType())); 64 | } 65 | } else { 66 | for (int i = 0; i < type.getDimensionCount(); i++) { 67 | array.addElement(svitch(type.getComponentType())); 68 | } 69 | } 70 | return array; 71 | } 72 | 73 | @Override 74 | protected CtExpression kaseVoid(CtTypeReferenceImpl type) { 75 | throw new CannotReachHereException("Cannot create an instance for void primitive"); 76 | } 77 | 78 | @Override 79 | protected CtExpression kaseBoxedVoid(CtTypeReferenceImpl type) { 80 | return mFact.createLiteral(null); 81 | } 82 | 83 | @Override 84 | protected CtExpression kaseBoolean(CtTypeReferenceImpl type) { 85 | return mFact.createLiteral(AxRandom.getInstance().nextBoolean()); 86 | } 87 | 88 | @Override 89 | protected CtExpression kaseBoxedBoolean(CtTypeReferenceImpl type) { 90 | return kaseBoolean(type); 91 | } 92 | 93 | @Override 94 | protected CtExpression kaseByte(CtTypeReferenceImpl type) { 95 | return mFact.createLiteral((byte) AxRandom.getInstance().nextInt()) 96 | .addTypeCast(mFact.createCtTypeReference(byte.class)); 97 | } 98 | 99 | @Override 100 | protected CtExpression kaseBoxedByte(CtTypeReferenceImpl type) { 101 | return kaseByte(type); 102 | } 103 | 104 | @Override 105 | protected CtExpression kaseShort(CtTypeReferenceImpl type) { 106 | return mFact.createLiteral((short) AxRandom.getInstance().nextInt()) 107 | .addTypeCast(mFact.createCtTypeReference(short.class)); 108 | } 109 | 110 | @Override 111 | protected CtExpression kaseBoxedShort(CtTypeReferenceImpl type) { 112 | return kaseShort(type); 113 | } 114 | 115 | @Override 116 | protected CtExpression kaseChar(CtTypeReferenceImpl type) { 117 | return mFact.createLiteral((char) AxRandom.getInstance().nextInt(0, 0xFFFF)); 118 | } 119 | 120 | @Override 121 | protected CtExpression kaseBoxedChar(CtTypeReferenceImpl type) { 122 | return kaseChar(type); 123 | } 124 | 125 | @Override 126 | protected CtExpression kaseInt(CtTypeReferenceImpl type) { 127 | return mFact.createLiteral(AxRandom.getInstance().nextInt()); 128 | } 129 | 130 | @Override 131 | protected CtExpression kaseBoxedInt(CtTypeReferenceImpl type) { 132 | return kaseInt(type); 133 | } 134 | 135 | @Override 136 | protected CtExpression kaseLong(CtTypeReferenceImpl type) { 137 | return mFact.createLiteral(AxRandom.getInstance().nextLong()); 138 | } 139 | 140 | @Override 141 | protected CtExpression kaseBoxedLong(CtTypeReferenceImpl type) { 142 | return kaseLong(type); 143 | } 144 | 145 | @Override 146 | protected CtExpression kaseFloat(CtTypeReferenceImpl type) { 147 | return mFact.createLiteral(AxRandom.getInstance().nextFloat()); 148 | } 149 | 150 | @Override 151 | protected CtExpression kaseBoxedFloat(CtTypeReferenceImpl type) { 152 | return kaseFloat(type); 153 | } 154 | 155 | @Override 156 | protected CtExpression kaseDouble(CtTypeReferenceImpl type) { 157 | return mFact.createLiteral(AxRandom.getInstance().nextDouble()); 158 | } 159 | 160 | @Override 161 | protected CtExpression kaseBoxedDouble(CtTypeReferenceImpl type) { 162 | return kaseDouble(type); 163 | } 164 | 165 | @Override 166 | protected CtExpression kaseString(CtTypeReferenceImpl type) { 167 | return mFact.createLiteral("s"); 168 | } 169 | 170 | @Override 171 | protected CtExpression kaseRef(CtTypeReferenceImpl type) { 172 | // Let's load the class and check its constructors 173 | Class clazz; 174 | try { 175 | clazz = Class.forName(type.getQualifiedName()); 176 | } catch (ClassNotFoundException e) { 177 | clazz = null; 178 | } 179 | 180 | // No class found. Either not in classpath, or the qualified name is a bit quirky. 181 | if (clazz == null) { 182 | return mFact.createLiteral(null); 183 | } 184 | 185 | // Interfaces and abstract classes cannot be initialized 186 | if (clazz.isInterface()) { 187 | return mFact.createLiteral(null); 188 | } else if (Modifier.isAbstract(clazz.getModifiers())) { 189 | return mFact.createLiteral(null); 190 | } 191 | 192 | // Let's choose only the default constructor, otherwise we have to do some recursive 193 | // synthesis. It's a bit time-consuming so let's disable that temporarily. 194 | // TODO Support recursive new-object synthesis. 195 | Constructor defCtor; 196 | try { 197 | defCtor = clazz.getConstructor(); 198 | } catch (NoSuchMethodException e) { 199 | defCtor = null; 200 | } 201 | 202 | // No default constructor, let's just give it a null 203 | if (defCtor == null) { 204 | return mFact.createLiteral(null); 205 | } 206 | 207 | // If the constructor explicitly throw, give it a null 208 | if (defCtor.getExceptionTypes().length != 0) { 209 | return mFact.createLiteral(null); 210 | } 211 | 212 | // There's a default constructor, then it's safe for us to new an instance 213 | return mFact.createConstructorCall(type); 214 | } 215 | 216 | // Never use the svitch() method 217 | public CtExpression newInstance(Factory fact, CtTypeReference type) { 218 | mFact = fact; 219 | return svitch(type); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/syn/PPoint.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.syn; 26 | 27 | import java.lang.annotation.Annotation; 28 | import java.util.Deque; 29 | import java.util.LinkedList; 30 | import java.util.List; 31 | import java.util.function.Consumer; 32 | 33 | import io.artemis.AxChecker; 34 | import spoon.reflect.code.CtBlock; 35 | import spoon.reflect.code.CtCatch; 36 | import spoon.reflect.code.CtDo; 37 | import spoon.reflect.code.CtFor; 38 | import spoon.reflect.code.CtForEach; 39 | import spoon.reflect.code.CtIf; 40 | import spoon.reflect.code.CtLambda; 41 | import spoon.reflect.code.CtLocalVariable; 42 | import spoon.reflect.code.CtStatement; 43 | import spoon.reflect.code.CtTryWithResource; 44 | import spoon.reflect.code.CtWhile; 45 | import spoon.reflect.declaration.CtAnnotationType; 46 | import spoon.reflect.declaration.CtClass; 47 | import spoon.reflect.declaration.CtConstructor; 48 | import spoon.reflect.declaration.CtElement; 49 | import spoon.reflect.declaration.CtEnum; 50 | import spoon.reflect.declaration.CtField; 51 | import spoon.reflect.declaration.CtInterface; 52 | import spoon.reflect.declaration.CtMethod; 53 | import spoon.reflect.declaration.CtParameter; 54 | import spoon.reflect.declaration.CtVariable; 55 | import spoon.reflect.reference.CtTypeReference; 56 | import spoon.reflect.visitor.CtAbstractVisitor; 57 | import spoon.reflect.visitor.EarlyTerminatingScanner; 58 | 59 | public class PPoint { 60 | 61 | public enum Which { 62 | BEFORE, AFTER 63 | } 64 | 65 | // LimitedLexScope is similar to but different from the lexical scope (i.e., LexicalScope), 66 | // where lexical scope gathers all variables in the scope and its parent scopes. However, 67 | // LimitedLexScope only contains those limited to variables which are accessible to the 68 | // current statement mStmt. 69 | private static class LimitedLexScope { 70 | CtElement element; // Element which created this lexical scope 71 | List> variables; // Accessible variables in this scope 72 | LimitedLexScope link; // Linked parent scope and the accessible variables 73 | 74 | public LimitedLexScope(CtElement ele, LimitedLexScope prev) { 75 | element = ele; 76 | link = prev; 77 | variables = new LinkedList<>(); 78 | } 79 | } 80 | 81 | // Context to consider: in which context we need to consider when computing required information 82 | // for example accessible variables, types, etc. 83 | private final CtClass mContext; 84 | private final Which mWhich; 85 | 86 | private final CtStatement mStmt; 87 | private CtClass mClass; 88 | private CtMethod mMethod; 89 | private LimitedLexScope mScope; 90 | private boolean mScanned; 91 | 92 | public static PPoint beforeStmt(CtClass context, CtStatement stmt) { 93 | return new PPoint(context, stmt, Which.BEFORE); 94 | } 95 | 96 | public static PPoint afterStmt(CtClass context, CtStatement stmt) { 97 | return new PPoint(context, stmt, Which.AFTER); 98 | } 99 | 100 | public CtStatement getStatement() { 101 | return mStmt; 102 | } 103 | 104 | public CtMethod getMethod() { 105 | ensureScanned(); 106 | return mMethod; 107 | } 108 | 109 | public CtClass getClazz() { 110 | ensureScanned(); 111 | return mClass; 112 | } 113 | 114 | public void forEachAccVariable(Consumer> con) { 115 | ensureScanned(); 116 | LimitedLexScope curr = mScope; 117 | while (curr != null) { 118 | curr.variables.forEach(con); 119 | curr = curr.link; 120 | } 121 | } 122 | 123 | public void forEachAccVariable(CtTypeReference type, Consumer> con) { 124 | forEachAccVariable(var -> { 125 | if (type.equals(var.getType())) { 126 | con.accept(var); 127 | } 128 | }); 129 | } 130 | 131 | private void ensureScanned() { 132 | if (!mScanned) { 133 | PPScanner scanner = new PPScanner(); 134 | scanner.scan(mContext); 135 | 136 | mScope = scanner.scopes.peek(); 137 | AxChecker.check(mScope != null, 138 | "Fail to compute the program point of statement: " + mStmt); 139 | 140 | LimitedLexScope curr = mScope; 141 | while (curr != null) { 142 | if (mMethod == null && curr.element instanceof CtMethod) { 143 | mMethod = (CtMethod) curr.element; 144 | } else if (mClass == null && curr.element instanceof CtClass) { 145 | mClass = (CtClass) curr.element; 146 | } 147 | curr = curr.link; 148 | } 149 | AxChecker.check(mMethod != null, "No method found: " + mStmt); 150 | AxChecker.check(mClass != null, "No class found: " + mStmt); 151 | 152 | mScanned = true; 153 | } 154 | } 155 | 156 | private PPoint(CtClass ctx, CtStatement stmt, Which beforeOrAfter) { 157 | mContext = ctx; 158 | mWhich = beforeOrAfter; 159 | mStmt = stmt; 160 | mMethod = null; 161 | mClass = null; 162 | mScanned = false; 163 | mScope = null; 164 | } 165 | 166 | private class PPScanner extends EarlyTerminatingScanner { 167 | final Deque scopes = new LinkedList<>(); 168 | 169 | @Override 170 | protected void enter(CtElement e) { 171 | if (e == mStmt && mWhich == Which.BEFORE) { 172 | terminate(); 173 | } else { 174 | findVariables(scopes.peek(), e); 175 | if (e == mStmt && mWhich == Which.AFTER) { 176 | terminate(); 177 | } 178 | } 179 | } 180 | 181 | @Override 182 | protected void exit(CtElement ele) { 183 | if (isTerminated()) { 184 | return; 185 | } 186 | LimitedLexScope top = scopes.peek(); 187 | if (top != null && top.element == ele) { 188 | scopes.pop(); 189 | } 190 | } 191 | 192 | private void findVariables(LimitedLexScope top, CtElement ele) { 193 | ele.accept(new CtAbstractVisitor() { 194 | 195 | @Override 196 | public void visitCtClass(CtClass clazz) { 197 | scopes.push(new LimitedLexScope(clazz, top)); 198 | } 199 | 200 | @Override 201 | public void visitCtInterface(CtInterface inf) { 202 | scopes.push(new LimitedLexScope(inf, top)); 203 | } 204 | 205 | @Override 206 | public > void visitCtEnum(CtEnum enm) { 207 | scopes.push(new LimitedLexScope(enm, top)); 208 | } 209 | 210 | @Override 211 | public void visitCtAnnotationType(CtAnnotationType anno) { 212 | scopes.push(new LimitedLexScope(anno, top)); 213 | } 214 | 215 | @Override 216 | public void visitCtMethod(CtMethod method) { 217 | scopes.push(new LimitedLexScope(method, top)); 218 | } 219 | 220 | @Override 221 | public void visitCtConstructor(CtConstructor ctor) { 222 | scopes.push(new LimitedLexScope(ctor, top)); 223 | } 224 | 225 | @Override 226 | public void visitCtLambda(CtLambda lambda) { 227 | scopes.push(new LimitedLexScope(lambda, top)); 228 | } 229 | 230 | @Override 231 | public void visitCtTryWithResource(CtTryWithResource tryRes) { 232 | scopes.push(new LimitedLexScope(tryRes, top)); 233 | } 234 | 235 | @Override 236 | public void visitCtCatch(CtCatch katch) { 237 | scopes.push(new LimitedLexScope(katch, top)); 238 | } 239 | 240 | @Override 241 | public void visitCtFor(CtFor forr) { 242 | scopes.push(new LimitedLexScope(forr, top)); 243 | } 244 | 245 | @Override 246 | public void visitCtForEach(CtForEach foreach) { 247 | scopes.push(new LimitedLexScope(foreach, top)); 248 | } 249 | 250 | @Override 251 | public void visitCtWhile(CtWhile whyle) { 252 | scopes.push(new LimitedLexScope(whyle, top)); 253 | } 254 | 255 | @Override 256 | public void visitCtDo(CtDo doo) { 257 | scopes.push(new LimitedLexScope(doo, top)); 258 | } 259 | 260 | @Override 261 | public void visitCtIf(CtIf iff) { 262 | scopes.push(new LimitedLexScope(iff, top)); 263 | } 264 | 265 | @Override 266 | public void visitCtBlock(CtBlock block) { 267 | scopes.push(new LimitedLexScope(block, top)); 268 | } 269 | 270 | @Override 271 | public void visitCtField(CtField field) { 272 | addVariable(field); 273 | } 274 | 275 | @Override 276 | public void visitCtParameter(CtParameter param) { 277 | addVariable(param); 278 | } 279 | 280 | @Override 281 | public void visitCtLocalVariable(CtLocalVariable local) { 282 | addVariable(local); 283 | } 284 | 285 | private void addVariable(CtVariable var) { 286 | if (top != null) { 287 | top.variables.add(var); 288 | } 289 | } 290 | }); 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/syn/SklPh.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.syn; 26 | 27 | import java.util.List; 28 | 29 | import io.artemis.AxChecker; 30 | import spoon.reflect.code.CtExpression; 31 | import spoon.reflect.code.CtInvocation; 32 | import spoon.reflect.code.CtLiteral; 33 | import spoon.reflect.code.CtStatement; 34 | import spoon.reflect.visitor.filter.TypeFilter; 35 | 36 | public final class SklPh { 37 | 38 | public static void placeholder(String id) {} 39 | 40 | public static void substitute(CtStatement from, String id, CtStatement to) { 41 | try { 42 | from.getElements(new TypeFilter<>(CtInvocation.class) { 43 | @Override 44 | public boolean matches(CtInvocation invoc) { 45 | if (!super.matches(invoc) || invoc.getTarget() == null 46 | || !invoc.getTarget().prettyprint().equals("SklPh") 47 | || !invoc.getExecutable().getSimpleName().equals("placeholder")) { 48 | return false; 49 | } 50 | List> args = (List>) invoc.getArguments(); 51 | if (args.size() != 1) { 52 | return false; 53 | } 54 | CtExpression a = args.get(0); 55 | return a instanceof CtLiteral && a.prettyprint().equals("\"" + id + "\""); 56 | } 57 | }).get(0).replace(to); 58 | } catch (IndexOutOfBoundsException ignoreUnused) { 59 | // noinspection ConstantConditions 60 | AxChecker.check(false, "No placeholder namely " + id + " found"); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/util/CannotReachHereException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.util; 26 | 27 | public class CannotReachHereException extends RuntimeException { 28 | 29 | public CannotReachHereException(String message) { 30 | super("Cannot reach here: " + message); 31 | } 32 | 33 | public CannotReachHereException() { 34 | super("Cannot reach here"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/util/DontSupportException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.util; 26 | 27 | public class DontSupportException extends RuntimeException { 28 | 29 | public DontSupportException(String message) { 30 | super("Don't support: " + message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/util/NotImplementedException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.util; 26 | 27 | public class NotImplementedException extends RuntimeException { 28 | 29 | public NotImplementedException(String message) { 30 | super("Not implemented: " + message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/util/Options.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.util; 26 | 27 | import java.io.File; 28 | import java.util.Arrays; 29 | import java.util.HashMap; 30 | import java.util.Iterator; 31 | import java.util.Map; 32 | 33 | public class Options implements Iterable { 34 | 35 | public static class IllegalOptionException extends Exception { 36 | public IllegalOptionException(String opt, String message) { 37 | super("Option \"" + opt + "\" is illegal: " + message); 38 | } 39 | } 40 | 41 | private final String[] mOpts; 42 | private final Map mParsedOpts = new HashMap<>(); 43 | 44 | private int mNextOpt = 0; 45 | private String nCurOptVal = null; 46 | 47 | public static Options parse(String[] opts) { 48 | Options options = new Options(opts); 49 | options.parseOptions(); 50 | return options; 51 | } 52 | 53 | public String[] getUnparsedOptions() { 54 | return mOpts; 55 | } 56 | 57 | public boolean hasOption(String opt) { 58 | return mParsedOpts.containsKey(opt); 59 | } 60 | 61 | public String getOption(String opt, boolean allowNull) throws IllegalOptionException { 62 | String val = mParsedOpts.getOrDefault(opt, null); 63 | if (!allowNull && val == null) { 64 | throw new IllegalOptionException(opt, "Option value is null"); 65 | } 66 | return val; 67 | } 68 | 69 | public String getOption(String opt) throws IllegalOptionException { 70 | return getOption(opt, /* allowNull */ false); 71 | } 72 | 73 | public String getString(String opt, boolean allowEmpty) throws IllegalOptionException { 74 | String val = mParsedOpts.getOrDefault(opt, ""); 75 | if (val == null) { 76 | throw new IllegalOptionException(opt, "Option value is null"); 77 | } else if (!allowEmpty && val.equals("")) { 78 | throw new IllegalOptionException(opt, "Option value is an empty string"); 79 | } 80 | return val; 81 | } 82 | 83 | public String getString(String opt) throws IllegalOptionException { 84 | return getString(opt, /* allowEmpty */ false); 85 | } 86 | 87 | public int getInteger(String opt) throws IllegalOptionException { 88 | try { 89 | return Integer.parseInt(getString(opt)); 90 | } catch (NumberFormatException e) { 91 | throw new IllegalOptionException(opt, e.getMessage()); 92 | } 93 | } 94 | 95 | public long getLong(String opt) throws IllegalOptionException { 96 | try { 97 | return Long.parseLong(getString(opt)); 98 | } catch (NumberFormatException e) { 99 | throw new IllegalOptionException(opt, e.getMessage()); 100 | } 101 | } 102 | 103 | public File getFile(String opt, boolean allowCreating) throws IllegalOptionException { 104 | String path = getString(opt); 105 | File val = new File(path); 106 | if (!allowCreating && !val.exists()) { 107 | throw new IllegalOptionException(opt, "File does not exist: " + path); 108 | } 109 | return val; 110 | } 111 | 112 | public File getFile(String opt) throws IllegalOptionException { 113 | return getFile(opt, /* allowCreating */ false); 114 | } 115 | 116 | @Override 117 | public Iterator iterator() { 118 | return mParsedOpts.keySet().iterator(); 119 | } 120 | 121 | private void parseOptions() { 122 | String opt; 123 | while (true) { 124 | opt = nextOption(); 125 | if (opt == null) { 126 | break; 127 | } 128 | mParsedOpts.put(opt, nextOptionVal()); 129 | } 130 | mNextOpt = -1; 131 | nCurOptVal = null; 132 | } 133 | 134 | /** 135 | * Return the next command line option. This has a number of special cases which closely, but 136 | * not exactly, follow the POSIX command line options patterns: 137 | * 138 | * -- means to stop processing additional options -z means option z -z VAL means option z with 139 | * (non-optional) value VAL -zVAL means option z with (optional) value VAL --zz means option zz 140 | * --zz VAL means option zz with (non-optional) value VAL 141 | * 142 | * Note that you cannot combine single letter options; -abc != -a -b -c 143 | * 144 | * @return Returns the option string, or null if there are no more options. 145 | */ 146 | private String nextOption() { 147 | if (mNextOpt >= mOpts.length) { 148 | return null; 149 | } 150 | 151 | String opt = mOpts[mNextOpt]; 152 | if (!opt.startsWith("-")) { // not an option, stop processing 153 | return null; 154 | } 155 | mNextOpt++; 156 | if (opt.equals("--")) { // --, stop processing 157 | return null; 158 | } 159 | if (opt.length() > 1 && opt.charAt(1) != '-') { // -z, -z VAL, or -zVAL 160 | if (opt.length() > 2) { // -zVAL 161 | nCurOptVal = opt.substring(2); // VAL 162 | return opt.substring(0, 2); // -z 163 | } else { // -z, or -z VAL 164 | nCurOptVal = null; 165 | return opt; // -z 166 | } 167 | } else if (opt.length() > 1) { // --zz, or --zz VAL 168 | nCurOptVal = null; 169 | return opt; // --zz 170 | } else { // -, skip it 171 | nCurOptVal = null; 172 | return nextOption(); 173 | } 174 | } 175 | 176 | /** 177 | * Return the next value associated with the current option. 178 | * 179 | * @return Returns the value string, or null of there are no more options. 180 | */ 181 | private String nextOptionVal() { 182 | if (nCurOptVal != null) { 183 | return nCurOptVal; 184 | } 185 | if (mNextOpt >= mOpts.length) { 186 | return null; 187 | } 188 | String val = mOpts[mNextOpt]; 189 | if (val.startsWith("-")) { // next option 190 | return null; 191 | } else if (val.startsWith("'")) { // next value wrapped by '' 192 | val = nextOptionValWithQuotes("'"); 193 | } else if (val.startsWith("\"")) { // next value wrapped by "" 194 | val = nextOptionValWithQuotes("\""); 195 | } 196 | mNextOpt++; 197 | return val; 198 | } 199 | 200 | private String nextOptionValWithQuotes(String q) { 201 | int start = mNextOpt; 202 | while (!mOpts[mNextOpt].endsWith(q)) { 203 | mNextOpt++; 204 | } 205 | String val = String.join(" ", Arrays.copyOfRange(mOpts, start, mNextOpt + 1)); 206 | return val.substring(1, val.length() - 1); 207 | } 208 | 209 | private Options(String[] opts) { 210 | mOpts = opts; 211 | } 212 | 213 | public static void main(String[] args) throws IllegalOptionException { 214 | args = "-q1 -w_3 --neg \"-12\" -t 2_3 -e - --a --s 23 --sd 24 -- -p 23 -w 3".split(" "); 215 | System.out.println(String.join(" ", args)); 216 | Options parser = new Options(args); 217 | for (String opt : parser) { 218 | System.out.println(opt + " => " + parser.getOption(opt)); 219 | } 220 | 221 | System.out.println(); 222 | 223 | args = "-p com.example.simon.myapplication -s 0 -P dfs -C 10".split(" "); 224 | System.out.println(String.join(" ", args)); 225 | parser = new Options(args); 226 | for (String opt : parser) { 227 | System.out.println(opt + " => " + parser.getOption(opt)); 228 | } 229 | 230 | System.out.println(); 231 | 232 | args = "-x \"208.95996\" -y \"1133.9062\"".split(" "); 233 | System.out.println(String.join(" ", args)); 234 | parser = new Options(args); 235 | for (String opt : parser) { 236 | System.out.println(opt + " => " + parser.getOption(opt)); 237 | } 238 | 239 | System.out.println(); 240 | 241 | args = "--desc \"Advanced Oppos Operations\" -y \"1133.9062\"".split(" "); 242 | System.out.println(String.join(" ", args)); 243 | parser = new Options(args); 244 | for (String opt : parser) { 245 | System.out.println(opt + " => " + parser.getOption(opt)); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/main/java/io/artemis/util/Pair.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Cong Li (congli@smail.nju.edu.cn, cong.li@inf.ethz.ch) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.artemis.util; 26 | 27 | public class Pair { 28 | 29 | public final A a; 30 | public final B b; 31 | 32 | public Pair(A a, B b) { 33 | this.a = a; 34 | this.b = b; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/skeletons: -------------------------------------------------------------------------------- 1 | ../java/io/artemis/skl --------------------------------------------------------------------------------