├── .gitignore ├── LICENSE ├── README.md ├── deployment ├── README.md ├── run.sh └── setup.sh ├── hephaestus.py ├── pylintrc ├── pyproject.toml ├── reported ├── bugs.json ├── fetch_groovy_bugs.py ├── fetch_java_bugs.py ├── fetch_kotlin_bugs.py ├── schema.json ├── stats.py └── supported_features.json ├── requirements.txt ├── src ├── __init__.py ├── analysis │ ├── __init__.py │ ├── call_analysis.py │ ├── type_dependency_analysis.py │ └── use_analysis.py ├── args.py ├── compilers │ ├── __init__.py │ ├── base.py │ ├── groovy.py │ ├── java.py │ ├── kotlin.py │ └── scala.py ├── generators │ ├── __init__.py │ ├── config.py │ ├── generator.py │ ├── generators.py │ └── utils.py ├── graph_utils.py ├── ir │ ├── __init__.py │ ├── ast.py │ ├── builtins.py │ ├── context.py │ ├── groovy_types.py │ ├── java_types.py │ ├── keywords.py │ ├── kotlin_types.py │ ├── node.py │ ├── scala_types.py │ ├── type_utils.py │ ├── types.py │ └── visitors.py ├── modules │ ├── __init__.py │ ├── logging.py │ └── processor.py ├── resources │ ├── __init__.py │ ├── groovy_keywords │ ├── scala_keywords │ └── words ├── transformations │ ├── __init__.py │ ├── base.py │ ├── type_erasure.py │ └── type_overwriting.py ├── translators │ ├── __init__.py │ ├── base.py │ ├── groovy.py │ ├── java.py │ ├── kotlin.py │ └── scala.py └── utils.py └── tests ├── __init__.py ├── resources ├── __init__.py ├── program1.py ├── program10.py ├── program11.py ├── program12.py ├── program2.py ├── program3.py ├── program4.py ├── program5.py ├── program6.py ├── program7.py ├── program8.py ├── program9.py ├── translators │ ├── __init__.py │ ├── groovy │ │ └── program4.py │ ├── program1.groovy │ ├── program1.java │ ├── program1.py │ ├── program2.groovy │ ├── program2.java │ ├── program2.py │ ├── program3.groovy │ ├── program3.java │ ├── program3.py │ ├── program4.groovy │ ├── program4.java │ ├── program4.py │ ├── program5.java │ ├── program5.py │ ├── program6.java │ └── program6.py └── type_analysis_programs.py ├── test_call_analysis.py ├── test_graph_utils.py ├── test_ir.py ├── test_translators.py ├── test_type_dependency_analysis.py ├── test_type_utils.py ├── test_types.py └── test_use_analysis.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | bugs/* 3 | replay_tmp 4 | temp/ 5 | notes/ 6 | *.class 7 | META-INF/ 8 | *.jar 9 | logs 10 | 11 | # Byte-compiled / optimized / DLL files 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | pip-wheel-metadata/ 34 | share/python-wheels/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | MANIFEST 39 | 40 | # PyInstaller 41 | # Usually these files are written by a python script from a template 42 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 43 | *.manifest 44 | *.spec 45 | 46 | # Installer logs 47 | pip-log.txt 48 | pip-delete-this-directory.txt 49 | 50 | # Unit test / coverage reports 51 | htmlcov/ 52 | .tox/ 53 | .nox/ 54 | .coverage 55 | .coverage.* 56 | .cache 57 | nosetests.xml 58 | coverage.xml 59 | *.cover 60 | *.py,cover 61 | .hypothesis/ 62 | .pytest_cache/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | db.sqlite3 72 | db.sqlite3-journal 73 | 74 | # Flask stuff: 75 | instance/ 76 | .webassets-cache 77 | 78 | # Scrapy stuff: 79 | .scrapy 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # PyBuilder 85 | target/ 86 | 87 | # Jupyter Notebook 88 | .ipynb_checkpoints 89 | 90 | # IPython 91 | profile_default/ 92 | ipython_config.py 93 | 94 | # pyenv 95 | .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | *.tasty 141 | -------------------------------------------------------------------------------- /deployment/README.md: -------------------------------------------------------------------------------- 1 | Setup a new machine 2 | =================== 3 | 4 | The machines we use have a single root user. 5 | 6 | ```bash 7 | scp setup.sh run.sh MACHINE: 8 | ssh MACHINE 9 | ssh-keygen 10 | # Add the pub key to your GitHub account if the repo is private 11 | 12 | ./setup.sh -k # or -s or -a 13 | # Setup cron job 14 | apt install postfix python3.9-gdbm # In postfix select only local 15 | crontab -e 16 | 17 | PATH=/sbin:/bin:/usr/sbin:/usr/bin 18 | MAILTO=root 19 | HOME=/root/ 20 | SHELL=/bin/bash 21 | 0 0 1-31/2 * * /root/bin/run.sh -k # or -s or -a 22 | # to check the logs you can run 23 | journalctl -u cron 24 | # to get the output of cron runs 25 | cat /var/mail/root 26 | ``` 27 | -------------------------------------------------------------------------------- /deployment/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | TIME_TO_RUN=$((23 * 60 * 60)) 3 | HOUR=$((60 * 60)) 4 | CORES=$(cat /proc/cpuinfo | grep processor | wc -l) 5 | CORES=$(($CORES - 2)) 6 | TRANSFORMATIONS=1 7 | VERSIONS="1.4.21 1.4.20 1.4.10 1.4.0 1.3.72 1.3.71 1.3.70 1.3.61 1.3.60 1.3.50 1.3.41 1.3.40 1.3.31 1.3.30 1.3.21 1.3.20 1.3.11 1.3.10 1.3.0 1.2.71 1.2.70 1.2.61 1.2.60 1.2.51 1.2.50 1.2.41 1.2.40 1.2.31 1.2.30 1.2.21 1.2.20 1.2.10 1.2.0 1.1.61 1.1.60 1.1.51 1.1.50 1.1.4-3 1.1.4-2 1.1.4 1.1.3-2 1.1.3 1.1.2-5 1.1.2-2 1.1.2 1.1.1 1.1 1.0.7 1.0.6 1.0.5-2 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1-2 1.0.1-1 1.0.1 1.0.0" 8 | source /root/.bashrc 9 | source /root/.bash_profile 10 | 11 | 12 | simple_run_groovy() { 13 | source "$HOME/.sdkman/bin/sdkman-init.sh" 14 | # sdk install groovy 15 | cd $CHECK_TYPE_SYSTEMS 16 | git pull 17 | python3 hephaestus.py -s $TIME_TO_RUN -t $TRANSFORMATIONS -w $CORES --batch 30 -P \ 18 | --language groovy --cast-numbers 19 | } 20 | 21 | simple_run() { 22 | source "$HOME/.sdkman/bin/sdkman-init.sh" 23 | sdk install kotlin 24 | cd $CHECK_TYPE_SYSTEMS 25 | git pull 26 | python3 hephaestus.py -s $TIME_TO_RUN -t $TRANSFORMATIONS -w $CORES --batch 30 -P 27 | } 28 | 29 | run_from_source() { 30 | cd $KOTLIN_INSTALLATION 31 | git pull 32 | ./gradlew clean 33 | ./gradlew -Dhttp.socketTimeout=60000 -Dhttp.connectionTimeout=60000 dist 34 | cd $CHECK_TYPE_SYSTEMS 35 | git pull 36 | python3 hephaestus.py -s $TIME_TO_RUN -t $TRANSFORMATIONS -w $CORES --batch 30 -P 37 | } 38 | 39 | run_groovy_from_source() { 40 | source "$HOME/.sdkman/bin/sdkman-init.sh" 41 | cd $GROOVY_INSTALLATION 42 | git pull 43 | ./gradlew clean dist --continue 44 | cd $CHECK_TYPE_SYSTEMS 45 | git pull 46 | python3 hephaestus.py -s $TIME_TO_RUN -t $TRANSFORMATIONS -w $CORES --batch 30 -P \ 47 | --language groovy --cast-numbers 48 | } 49 | 50 | run_multiple_versions() { 51 | cd $CHECK_TYPE_SYSTEMS 52 | git pull 53 | source "$HOME/.sdkman/bin/sdkman-init.sh" 54 | for i in {1..22}; do 55 | length=$(echo "$VERSIONS" | wc -w) 56 | rnum=$((1 + $RANDOM%$length+1)); 57 | version=$(echo $VERSIONS | cut -d " " -f $rnum) 58 | sdk use kotlin $version 59 | python3 hephaestus.py -s $HOUR -t $TRANSFORMATIONS -w $CORES --batch 30 -P 60 | done 61 | } 62 | 63 | if [ $# -eq 0 ] 64 | then 65 | echo "Missing options!" 66 | echo "(run $0 -h for help)" 67 | echo "" 68 | exit 0 69 | fi 70 | 71 | while getopts "hksagS" OPTION; do 72 | case $OPTION in 73 | 74 | k) 75 | simple_run 76 | ;; 77 | 78 | s) 79 | run_from_source 80 | ;; 81 | 82 | a) 83 | run_multiple_versions 84 | ;; 85 | 86 | g) 87 | simple_run_groovy 88 | ;; 89 | 90 | S) 91 | run_groovy_from_source 92 | ;; 93 | 94 | h) 95 | echo "Usage:" 96 | echo "init.sh -k " 97 | echo "init.sh -s " 98 | echo "init.sh -a " 99 | echo "init.sh -g " 100 | echo "init.sh -S " 101 | echo "" 102 | echo " -k Simple run" 103 | echo " -s Run from source" 104 | echo " -a Run multiple versions" 105 | echo " -g Simple run groovy" 106 | echo " -S Run groovy from source" 107 | echo " -h help (this output)" 108 | exit 0 109 | ;; 110 | 111 | esac 112 | done 113 | -------------------------------------------------------------------------------- /deployment/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | COMMON_PKGS="vim software-properties-common git tmux cron" 3 | SDKMAN_DEPS="curl unzip zip" 4 | export DEBIAN_FRONTEND=noninteractive 5 | 6 | update_and_install_common_pks() { 7 | apt -yqq update && apt -yqq upgrade 8 | apt -yqq install $COMMON_PKGS 9 | add-apt-repository ppa:deadsnakes/ppa 10 | apt -yqq update 11 | apt -yqq install python3.9 python3-pip 12 | update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1 13 | update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1 14 | } 15 | 16 | install_sdkman() { 17 | apt -yqq install $SDKMAN_DEPS 18 | curl -s https://get.sdkman.io | /bin/bash 19 | chmod a+x "$HOME/.sdkman/bin/sdkman-init.sh" 20 | source "$HOME/.sdkman/bin/sdkman-init.sh" 21 | mkdir -p $HOME/.sdkman/etc/ 22 | echo "sdkman_auto_answer=true" >> $HOME/.sdkman/etc/config 23 | echo "SDKMAN_DIR=\"/root/.sdkman\"" >> $HOME/.bash_profile 24 | echo "source \"/root/.sdkman/bin/sdkman-init.sh\"" >> $HOME.bash_profile 25 | } 26 | 27 | install_deps() { 28 | update_and_install_common_pks 29 | install_sdkman 30 | echo "source $HOME/.bash_profile >> $HOME/.bashrc" 31 | } 32 | 33 | install_java() { 34 | sdk install java 8.0.265-open 35 | } 36 | 37 | install_groovy() { 38 | install_java 39 | sdk install groovy 4.0.0-alpha-2 40 | echo "PATH=\"\$PATH:/root/.sdkman/candidates/kotlin/current/bin/\"" >> $HOME/.bash_profile 41 | } 42 | 43 | 44 | install_kotlin_from_source() { 45 | sdk install java 9.0.4-open 46 | sdk install java 8.0.265-open 47 | sdk install gradle 48 | echo "JAVA_HOME=$HOME/.sdkman/candidates/java/8.0.265-open/" >> $HOME/.bash_profile 49 | echo "JDK_16=$HOME/.sdkman/candidates/java/8.0.265-open/" >> $HOME/.bash_profile 50 | echo "JDK_17=$HOME/.sdkman/candidates/java/8.0.265-open/" >> $HOME/.bash_profile 51 | echo "JDK_18=$HOME/.sdkman/candidates/java/8.0.265-open/" >> $HOME/.bash_profile 52 | echo "JDK_9=$HOME/.sdkman/candidates/java/9.0.4-open/" >> $HOME/.bash_profile 53 | source $HOME/.bash_profile 54 | git clone https://github.com/JetBrains/kotlin.git 55 | cd kotlin 56 | ./gradlew -Dhttp.socketTimeout=60000 -Dhttp.connectionTimeout=60000 dist 57 | echo "PATH=\"\$PATH:$HOME/kotlin/dist/kotlinc/bin\"" >> $HOME/.bash_profile 58 | echo "KOTLIN_INSTALLATION=$HOME/kotlin" >> $HOME/.bash_profile 59 | cd .. 60 | source $HOME/.bash_profile 61 | } 62 | 63 | install_groovy_from_source() { 64 | git clone https://github.com/apache/groovy 65 | sdk install gradle 66 | sdk install java 11.0.10-open 67 | cd groovy 68 | gradle -p bootstrap 69 | ./gradlew --write-verification-metadata pgp,sha512 --dry-run 70 | ./gradlew clean dist --continue 71 | echo "#!/bin/bash" >> $HOME/bin/groovyc 72 | echo "java -cp $HOME/groovy/build/libs/groovy-5.0.0-SNAPSHOT.jar org.codehaus.groovy.tools.FileSystemCompiler $@" >> $HOME/bin/groovyc 73 | chmod +x $HOME/bin/groovyc 74 | echo "PATH=$HOME/bin/:$PATH" >> .bash_profile 75 | echo "GROOVY_INSTALLATION=$HOME/groovy" >> $HOME/.bash_profile 76 | cd .. 77 | source $HOME/.bash_profile 78 | } 79 | 80 | install_kotlin() { 81 | install_java 82 | sdk install kotlin 83 | echo "PATH=\"\$PATH:/root/.sdkman/candidates/kotlin/current/bin/\"" >> $HOME/.bash_profile 84 | } 85 | 86 | install_kotlin_all() { 87 | install_java 88 | sdk install kotlin 1.4.21 && \ 89 | sdk install kotlin 1.4.20 && \ 90 | sdk install kotlin 1.4.10 && \ 91 | sdk install kotlin 1.4.0 && \ 92 | sdk install kotlin 1.3.72 && \ 93 | sdk install kotlin 1.3.71 && \ 94 | sdk install kotlin 1.3.70 && \ 95 | sdk install kotlin 1.3.61 && \ 96 | sdk install kotlin 1.3.60 && \ 97 | sdk install kotlin 1.3.50 && \ 98 | sdk install kotlin 1.3.41 && \ 99 | sdk install kotlin 1.3.40 && \ 100 | sdk install kotlin 1.3.31 && \ 101 | sdk install kotlin 1.3.30 && \ 102 | sdk install kotlin 1.3.21 && \ 103 | sdk install kotlin 1.3.20 && \ 104 | sdk install kotlin 1.3.11 && \ 105 | sdk install kotlin 1.3.10 && \ 106 | sdk install kotlin 1.3.0 && \ 107 | sdk install kotlin 1.2.71 && \ 108 | sdk install kotlin 1.2.70 && \ 109 | sdk install kotlin 1.2.61 && \ 110 | sdk install kotlin 1.2.60 && \ 111 | sdk install kotlin 1.2.51 && \ 112 | sdk install kotlin 1.2.50 && \ 113 | sdk install kotlin 1.2.41 && \ 114 | sdk install kotlin 1.2.40 && \ 115 | sdk install kotlin 1.2.31 && \ 116 | sdk install kotlin 1.2.30 && \ 117 | sdk install kotlin 1.2.21 && \ 118 | sdk install kotlin 1.2.20 && \ 119 | sdk install kotlin 1.2.10 && \ 120 | sdk install kotlin 1.2.0 && \ 121 | sdk install kotlin 1.1.61 && \ 122 | sdk install kotlin 1.1.60 && \ 123 | sdk install kotlin 1.1.51 && \ 124 | sdk install kotlin 1.1.50 && \ 125 | sdk install kotlin 1.1.4-3 && \ 126 | sdk install kotlin 1.1.4-2 && \ 127 | sdk install kotlin 1.1.4 && \ 128 | sdk install kotlin 1.1.3-2 && \ 129 | sdk install kotlin 1.1.3 && \ 130 | sdk install kotlin 1.1.2-5 && \ 131 | sdk install kotlin 1.1.2-2 && \ 132 | sdk install kotlin 1.1.2 && \ 133 | sdk install kotlin 1.1.1 && \ 134 | sdk install kotlin 1.1 && \ 135 | sdk install kotlin 1.0.7 && \ 136 | sdk install kotlin 1.0.6 && \ 137 | sdk install kotlin 1.0.5-2 && \ 138 | sdk install kotlin 1.0.5 && \ 139 | sdk install kotlin 1.0.4 && \ 140 | sdk install kotlin 1.0.3 && \ 141 | sdk install kotlin 1.0.2 && \ 142 | sdk install kotlin 1.0.1-2 && \ 143 | sdk install kotlin 1.0.1-1 && \ 144 | sdk install kotlin 1.0.1 && \ 145 | sdk install kotlin 1.0.0 146 | echo "PATH=\"\$PATH:/root/.sdkman/candidates/kotlin/current/bin/\"" >> $HOME/.bash_profile 147 | } 148 | 149 | install_check_type_systems() { 150 | git clone git@github.com:theosotr/check-type-system.git 151 | cd check-type-system 152 | git fetch && git pull 153 | git checkout stable 154 | git pull 155 | echo "CHECK_TYPE_SYSTEMS=$(pwd)" >> $HOME/.bash_profile 156 | cd .. 157 | } 158 | 159 | add_run_script_to_path() { 160 | mkdir bin 161 | cp run.sh bin 162 | echo "PATH=\"\$PATH:$HOME/bin\"" >> $HOME/.bash_profile 163 | } 164 | 165 | if [ $# -eq 0 ] 166 | then 167 | echo "Missing options!" 168 | echo "(run $0 -h for help)" 169 | echo "" 170 | exit 0 171 | fi 172 | 173 | while getopts "hskagS" OPTION; do 174 | case $OPTION in 175 | 176 | k) 177 | install_deps 178 | install_kotlin 179 | install_check_type_systems 180 | add_run_script_to_path 181 | ;; 182 | 183 | s) 184 | install_deps 185 | install_kotlin_from_source 186 | install_check_type_systems 187 | add_run_script_to_path 188 | ;; 189 | 190 | a) 191 | install_deps 192 | install_kotlin_all 193 | install_check_type_systems 194 | add_run_script_to_path 195 | ;; 196 | 197 | g) 198 | install_deps 199 | install_groovy 200 | install_check_type_systems 201 | add_run_script_to_path 202 | ;; 203 | 204 | g) 205 | install_deps 206 | install_groovy_from_source 207 | install_check_type_systems 208 | add_run_script_to_path 209 | ;; 210 | 211 | h) 212 | echo "Usage:" 213 | echo "init.sh -k " 214 | echo "init.sh -s " 215 | echo "init.sh -a " 216 | echo "init.sh -g " 217 | echo "init.sh -S " 218 | echo "" 219 | echo " -k Install latest kotlin version" 220 | echo " -s Install kotlin from source" 221 | echo " -a Install all kotlin versions" 222 | echo " -g Install latest groovy version" 223 | echo " -S Install groovy from source" 224 | echo " -h help (this output)" 225 | exit 0 226 | ;; 227 | 228 | esac 229 | done 230 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "hephaestus" 7 | version = "1.0" 8 | authors = [ 9 | {name = "Thodoris Sotiropoulos", email = "theosotr@windowslive.com"}, 10 | {name = "Stefanos Chaliasos", email = "stefanoschaliassos@gmail.com"}, 11 | ] 12 | description = "A framework for testing compilers' type checkers" 13 | readme = "README.md" 14 | requires-python = ">=3.8" 15 | classifiers = [ 16 | "Development Status :: 3 - Alpha", 17 | "License :: GPL v3.0 License", 18 | "Natural Language :: English", 19 | "Operating System :: OS Independent", 20 | "Programming Language :: Python", 21 | "Programming Language :: Python :: 3.8", 22 | "Programming Language :: Python :: 3.9", 23 | "Programming Language :: Python :: 3.10", 24 | ] 25 | dependencies = [ 26 | "networkx", 27 | ] 28 | 29 | [tool.setuptools] 30 | packages = [ 31 | "src", 32 | "src.analysis", 33 | "src.compilers", 34 | "src.generators", 35 | "src.ir", 36 | "src.modules", 37 | "src.transformations", 38 | "src.translators", 39 | ] 40 | py-modules = ["hephaestus"] 41 | include-package-data = true 42 | 43 | [tool.setuptools.package-data] 44 | src = ["resources/*"] 45 | 46 | [project.scripts] 47 | hephaestus = "hephaestus:main" 48 | 49 | [tool.pytest.ini_options] 50 | addopts = "-vv" 51 | testpaths = [ 52 | "tests", 53 | ] 54 | -------------------------------------------------------------------------------- /reported/fetch_groovy_bugs.py: -------------------------------------------------------------------------------- 1 | """Fetch groovy bugs. 2 | oracle, mutator, symptom must be completed manually 3 | """ 4 | import argparse 5 | import requests 6 | import os 7 | import json 8 | import re 9 | from copy import deepcopy 10 | from datetime import datetime 11 | 12 | 13 | COMPILER = "groovyc" 14 | LANGUAGE = "Groovy" 15 | SCHEMA = { 16 | "date": "", 17 | "language": LANGUAGE, 18 | "compiler": COMPILER, 19 | "version": "", 20 | "bugid": "", 21 | "title": "", 22 | "links": { 23 | "issuetracker": "", 24 | "fix": "" 25 | }, 26 | "oracle": "", 27 | "mutator": "", 28 | "severity": "", 29 | "reporter": "", 30 | "status": "", 31 | "resolution": "", 32 | "resolutiondate": "", 33 | "symptom": "", 34 | "bugtype": "", 35 | "resolvedin": "", 36 | "test": [], 37 | "chars": { 38 | "characteristics": [] 39 | }, 40 | "errormsg": [], 41 | "comment": "" 42 | } 43 | 44 | 45 | def get_code_fragments(text): 46 | matches = re.findall('(?:{code:?(?:java|groovy)?})(.*?)(?:{code})', text, flags=re.I | re.DOTALL) 47 | res = [] 48 | for m in matches[:1]: 49 | res.append([x.replace('\t', ' ') for x in m.splitlines() if x.strip() != '']) 50 | res = [r for r in res if len(r) > 0] 51 | return res 52 | 53 | 54 | def get_data(lookup): 55 | start_at = 0 56 | max_results = 50 57 | total = 0 58 | results = [] 59 | first = True 60 | while start_at < total or first: 61 | first = False 62 | base = "https://issues.apache.org/jira/rest/api/latest/search" 63 | search_terms = [ 64 | 'project = Groovy', 65 | 'AND type = bug', 66 | 'AND reporter = theosot OR reporter = schaliasos' 67 | ] 68 | query = "%20".join(map(lambda x: x.replace(" ", "%20"), search_terms)) 69 | fields = "key,description,resolutiondate,created,reporter,resolution,status,summary" 70 | url = "{base}?jql={query}&fields={fields}&startAt={s}&maxResults={m}" 71 | url = url.format( 72 | base=base, 73 | query=query, 74 | fields=fields, 75 | s=start_at, 76 | m=max_results 77 | ) 78 | print(url) 79 | response = requests.get(url).json() 80 | total = response['total'] 81 | groovy_jira_url = "https://issues.apache.org/jira/browse/" 82 | for item in response['issues']: 83 | created = datetime.strptime( 84 | item['fields']['created'], "%Y-%m-%dT%H:%M:%S.%f%z") 85 | try: 86 | resolution = datetime.strptime( 87 | item['fields']['resolutiondate'], "%Y-%m-%dT%H:%M:%S.%f%z") 88 | except: 89 | resolution = None 90 | passed = resolution - created if resolution else None 91 | reporter = item['fields']['reporter']['name'] 92 | bugid = item['key'] 93 | if bugid in lookup: 94 | bug = lookup[bugid] 95 | else: 96 | bug = deepcopy(SCHEMA) 97 | bug['date'] = str(created) 98 | bug['resolutiondate'] = str(resolution) 99 | bug['resolvedin'] = str(passed) 100 | bug['bugid'] = bugid 101 | bug['title'] = item['fields']['summary'] 102 | bug['links']['issuetracker'] = groovy_jira_url + bugid 103 | bug['reporter'] = reporter 104 | if item['fields']['resolution']: 105 | bug['resolution'] = str(item['fields']['resolution']['name']) 106 | bug['status'] = str(item['fields']['status']['name']) 107 | 108 | description = item['fields']['description'] 109 | code_fragments = get_code_fragments(description) 110 | if len(code_fragments) >= 1: 111 | if not len(lookup.get(bug['bugid'], {}).get('test', [])) >= 1: 112 | bug['test'] = code_fragments[0] 113 | if len(code_fragments) >= 2: 114 | if not len(lookup.get(bug['bugid'], {}).get('errormsg', [])) >= 1: 115 | bug['errormsg'] = code_fragments[1] 116 | if len(code_fragments) != 2: 117 | print("{}: code fragments {}".format( 118 | bug['bugid'], len(code_fragments) 119 | )) 120 | if bug.get('chars', None) is None: 121 | bug['chars'] = SCHEMA['chars'] 122 | results.append(bug) 123 | start_at += max_results 124 | # Add bugs in lookup but not in current set (e.g. from another tracker) 125 | ids = {bug['bugid'] for bug in results} 126 | for bug_id, bug in lookup.items(): 127 | if bug_id not in ids: 128 | if bug.get('chars', None) is None: 129 | bug['chars'] = SCHEMA['chars'] 130 | results.append(bug) 131 | return results 132 | 133 | 134 | def get_args(): 135 | parser = argparse.ArgumentParser( 136 | description='Fetch groovy front-end bugs.') 137 | parser.add_argument("output", help="File to save the bugs.") 138 | return parser.parse_args() 139 | 140 | def main(): 141 | args = get_args() 142 | lookup = {} 143 | if os.path.isfile(args.output): 144 | with open(args.output) as f: 145 | tmp = json.load(f) 146 | for bug in tmp: 147 | lookup[bug['bugid']] = bug 148 | data = get_data(lookup) 149 | with open(args.output, 'w') as f: 150 | json.dump(data, f, indent=4) 151 | 152 | 153 | if __name__ == "__main__": 154 | main() 155 | -------------------------------------------------------------------------------- /reported/fetch_java_bugs.py: -------------------------------------------------------------------------------- 1 | """Fetch java bugs. 2 | """ 3 | import argparse 4 | import requests 5 | import os 6 | import json 7 | import re 8 | from datetime import datetime 9 | from copy import deepcopy 10 | 11 | BUGS = [ 12 | "JDK-8267220", 13 | "JDK-8267610", 14 | "JDK-8268159", 15 | "JDK-8269348", 16 | "JDK-8269386", 17 | "JDK-8269586", 18 | "JDK-8269737", 19 | "JDK-8269738", 20 | "JDK-8272077", 21 | "JDK-8274183" 22 | ] 23 | COMPILER = "javac" 24 | LANGUAGE = "Java" 25 | SCHEMA = { 26 | "date": "", 27 | "language": LANGUAGE, 28 | "compiler": COMPILER, 29 | "version": "", 30 | "bugid": "", 31 | "title": "", 32 | "links": { 33 | "issuetracker": "", 34 | "fix": "" 35 | }, 36 | "oracle": "", 37 | "mutator": "", 38 | "severity": "", 39 | "reporter": "", 40 | "status": "", 41 | "resolution": "", 42 | "resolutiondate": "", 43 | "symptom": "", 44 | "bugtype": "", 45 | "resolvedin": "", 46 | "test": [], 47 | "chars": { 48 | "characteristics": [] 49 | }, 50 | "errormsg": [], 51 | "comment": "" 52 | } 53 | 54 | def get_code_fragments(text): 55 | matches = re.findall('(?:---------- BEGIN SOURCE ----------)(.*?)(?:---------- END SOURCE ----------)', text, flags=re.I | re.DOTALL) 56 | res = [] 57 | for m in matches: 58 | res.append([x.replace('\t', ' ') for x in m.splitlines() if x.strip() != '']) 59 | res = [r for r in res if len(r) > 0] 60 | return res 61 | 62 | def get_data(lookup): 63 | start_at = 0 64 | max_results = 1000 65 | results = [] 66 | # No need for loop, less than 1000 67 | base = "https://bugs.openjdk.java.net/rest/api/latest/search" 68 | search_terms = [ 69 | "project = JDK", 70 | "AND id in ({})".format(", ".join(BUGS)), 71 | ] 72 | query = "%20".join(map(lambda x: x.replace(" ", "%20"), search_terms)) 73 | fields = "key,description,resolutiondate,created,reporter,summary,status,resolution" 74 | url = "{base}?jql={query}&fields={fields}&startAt={s}&maxResults={m}" 75 | url = url.format( 76 | base=base, 77 | query=query, 78 | fields=fields, 79 | s=start_at, 80 | m=max_results 81 | ) 82 | response = requests.get(url).json() 83 | openjdk_url = "https://bugs.openjdk.java.net/browse/" 84 | for item in response['issues']: 85 | created = datetime.strptime( 86 | item['fields']['created'], "%Y-%m-%dT%H:%M:%S.%f%z") 87 | passed = None 88 | try: 89 | resolution = datetime.strptime( 90 | item['fields']['resolutiondate'], "%Y-%m-%dT%H:%M:%S.%f%z") 91 | passed = resolution - created 92 | except: 93 | resolution = None 94 | reporter = item['fields']['reporter']['name'] 95 | bugid = item['key'] 96 | if bugid in lookup: 97 | bug = lookup[bugid] 98 | else: 99 | bug = deepcopy(SCHEMA) 100 | 101 | bug['date'] = str(created) 102 | bug['resolutiondate'] = str(resolution) 103 | bug['resolvedin'] = str(passed) 104 | bug['bugid'] = bugid 105 | bug['title'] = item['fields']['summary'] 106 | bug['links']['issuetracker'] = openjdk_url + bugid 107 | bug['reporter'] = reporter 108 | if item['fields']['resolution']: 109 | bug['resolution'] = str(item['fields']['resolution']['name']) 110 | bug['status'] = str(item['fields']['status']['name']) 111 | if bug.get('chars', None) is None: 112 | bug['chars'] = SCHEMA['chars'] 113 | 114 | description = item['fields']['description'] 115 | code_fragments = get_code_fragments(description) 116 | if len(code_fragments) >= 1: 117 | if not len(lookup.get(bug['bugid'], {}).get('test', [])) >= 1: 118 | bug['test'] = code_fragments[0] 119 | results.append(bug) 120 | # Add bugs in lookup but not in current set (e.g. from another tracker) 121 | ids = {bug['bugid'] for bug in results} 122 | for bug_id, bug in lookup.items(): 123 | if bug_id not in ids: 124 | if bug.get('chars', None) is None: 125 | bug['chars'] = SCHEMA['chars'] 126 | results.append(bug) 127 | return results 128 | 129 | 130 | def get_args(): 131 | parser = argparse.ArgumentParser( 132 | description='Fetch Java bugs.') 133 | parser.add_argument("input", help="File to save read and save bugs.") 134 | return parser.parse_args() 135 | 136 | def main(): 137 | args = get_args() 138 | lookup = {} 139 | if os.path.isfile(args.input): 140 | with open(args.input) as f: 141 | tmp = json.load(f) 142 | for bug in tmp: 143 | lookup[bug['bugid']] = bug 144 | data = get_data(lookup) 145 | with open(args.input, 'w') as f: 146 | json.dump(data, f, indent=4) 147 | 148 | 149 | if __name__ == "__main__": 150 | main() 151 | -------------------------------------------------------------------------------- /reported/fetch_kotlin_bugs.py: -------------------------------------------------------------------------------- 1 | """Fetch kotlin bugs. 2 | oracle, mutator, resolution, status, symptom must be completed manually 3 | """ 4 | import argparse 5 | import requests 6 | import os 7 | import json 8 | import re 9 | from datetime import datetime 10 | from copy import deepcopy 11 | 12 | 13 | COMPILER = "kotlinc" 14 | LANGUAGE = "Kotlin" 15 | SCHEMA = { 16 | "date": "", 17 | "language": LANGUAGE, 18 | "compiler": COMPILER, 19 | "version": "", 20 | "bugid": "", 21 | "title": "", 22 | "links": { 23 | "issuetracker": "", 24 | "fix": "" 25 | }, 26 | "oracle": "", 27 | "mutator": "", 28 | "severity": "", 29 | "reporter": "", 30 | "status": "", 31 | "resolution": "", 32 | "resolutiondate": "", 33 | "symptom": "", 34 | "bugtype": "", 35 | "resolvedin": "", 36 | "test": [], 37 | "chars": { 38 | "characteristics": [] 39 | }, 40 | "errormsg": [], 41 | "comment": "" 42 | } 43 | 44 | 45 | def get_code_fragments(text): 46 | matches = re.findall('(?:```)(.*?)(?:```)', text, flags=re.I | re.DOTALL) 47 | res = [] 48 | for m in matches: 49 | res.append([x.replace('\t', ' ') for x in m.splitlines() 50 | if x.strip() not in ('', 'kotlin')]) 51 | res = [r for r in res if len(r) > 0] 52 | return res 53 | 54 | 55 | def get_data(lookup): 56 | skip = 0 57 | top = 2500 58 | results = [] 59 | 60 | # we don't need to loop (< 2500 bugs) 61 | base = "https://youtrack.jetbrains.com/api/issues" 62 | search_terms = [ 63 | "project: Kotlin", 64 | "Reporter: theosotr, stefanoshaliassos" 65 | ] 66 | query = "%20".join(map(lambda x: x.replace(" ", "%20"), search_terms)) 67 | fields = "idReadable,description,created,reporter(login),resolved," 68 | fields += "summary,fields(value(login))" 69 | url = "{base}?query={query}&fields={fields}&$skip={skip}&$top={top}" 70 | url = url.format( 71 | base=base, 72 | query=query, 73 | fields=fields, 74 | skip=skip, 75 | top=top 76 | ) 77 | response = requests.get(url) 78 | youtrack_url = "https://youtrack.jetbrains.com/issue/" 79 | for item in response.json(): 80 | created = datetime.utcfromtimestamp(int(item['created']) / 1000.0) 81 | try: 82 | resolution = datetime.utcfromtimestamp( 83 | int(item['resolved']) / 1000.0) 84 | except: 85 | resolution = None 86 | passed = resolution - created if resolution else None 87 | reporter = item['reporter']['login'] 88 | 89 | bid = item['idReadable'] 90 | if bid in lookup: 91 | bug = lookup[bid] 92 | else: 93 | bug = deepcopy(SCHEMA) 94 | bug['date'] = str(created) 95 | bug['resolutiondate'] = str(resolution) 96 | bug['resolvedin'] = str(passed) 97 | bug['bugid'] = bid 98 | bug['title'] = item['summary'] 99 | bug['links']['issuetracker'] = youtrack_url + bid 100 | bug['reporter'] = reporter 101 | if bug.get('chars', None) is None: 102 | bug['chars'] = SCHEMA['chars'] 103 | 104 | description = item['description'] 105 | description = description if description is not None else "" 106 | code_fragments = get_code_fragments(description) 107 | if len(code_fragments) >= 1: 108 | if not len(lookup.get(bug['bugid'], {}).get('test', [])) >= 1: 109 | bug['test'] = code_fragments[0] 110 | if len(code_fragments) >= 2: 111 | if not len(lookup.get(bug['bugid'], {}).get('errormsg', [])) >= 1: 112 | bug['errormsg'] = code_fragments[1] 113 | if len(code_fragments) != 2: 114 | print("{}: code fragments {}".format( 115 | bug['bugid'], len(code_fragments) 116 | )) 117 | results.append(bug) 118 | # Add bugs in lookup but not in current set (e.g. from another tracker) 119 | ids = {bug['bugid'] for bug in results} 120 | for bug_id, bug in lookup.items(): 121 | if bug_id not in ids: 122 | if bug.get('chars', None) is None: 123 | bug['chars'] = SCHEMA['chars'] 124 | results.append(bug) 125 | return results 126 | 127 | 128 | def get_args(): 129 | parser = argparse.ArgumentParser( 130 | description='Fetch kotlin front-end bugs.') 131 | parser.add_argument("output", help="File to save the bugs.") 132 | return parser.parse_args() 133 | 134 | def main(): 135 | args = get_args() 136 | lookup = {} 137 | if os.path.isfile(args.output): 138 | with open(args.output) as f: 139 | tmp = json.load(f) 140 | for bug in tmp: 141 | lookup[bug['bugid']] = bug 142 | data = get_data(lookup) 143 | with open(args.output, 'w') as f: 144 | json.dump(data, f, indent=4) 145 | 146 | 147 | if __name__ == "__main__": 148 | main() 149 | -------------------------------------------------------------------------------- /reported/schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "", 4 | "language": "", 5 | "compiler": "", 6 | "version": "", 7 | "bugid": "", 8 | "title": "", 9 | "links": { 10 | "issuetracker": "", 11 | "fix": "" 12 | }, 13 | "oracle": "", 14 | "mutator": "", 15 | "severity": "", 16 | "reporter": "", 17 | "status": "", 18 | "resolution": "", 19 | "resolutiondate": "", 20 | "symptom": "", 21 | "bugtype": "", 22 | "resolvedin": "", 23 | "test": "", 24 | "chars": { 25 | "characteristics": [] 26 | }, 27 | "errormsg": "", 28 | "comment": "" 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /reported/supported_features.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parametric polymorphism": [ 3 | "Parameterized class", 4 | "Parameterized type", 5 | "Parameterized function", 6 | "Use-site variance", 7 | "Bounded type parameter", 8 | "Declaration-site variance" 9 | ], 10 | "OOP features": [ 11 | "Inheritance", 12 | "Overriding", 13 | ], 14 | "Type system-related features": [ 15 | "Subtyping", 16 | "Primitive type", 17 | "Wildcard type", 18 | "Nothing" 19 | ] 20 | "Functional programming": [ 21 | "Lambda", 22 | "Function reference", 23 | "SAM type", 24 | "Function type" 25 | ] 26 | "Standard language features": [ 27 | "Conditionals", 28 | "Array", 29 | "Cast", 30 | "Variable arguments" 31 | ] 32 | "Type inference": [ 33 | "Type argument inference", 34 | "Variable type inference", 35 | "Parameter type inference", 36 | "Flow typing", 37 | "Return type inference" 38 | ] 39 | "Other": [ 40 | "Named arguments", 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tqdm 2 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/src/__init__.py -------------------------------------------------------------------------------- /src/analysis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/src/analysis/__init__.py -------------------------------------------------------------------------------- /src/analysis/call_analysis.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=inherit-non-class,pointless-statement,expression-not-assigned 2 | from typing import Tuple, NamedTuple 3 | from collections import defaultdict 4 | 5 | import src.utils as ut 6 | from src.ir import ast 7 | from src.ir import types as tp 8 | from src.ir.visitors import DefaultVisitor 9 | from src.transformations.base import change_namespace 10 | from src.analysis.use_analysis import UseAnalysis, GNode, get_decl 11 | 12 | 13 | class CNode(NamedTuple): 14 | namespace: Tuple[str, ...] 15 | 16 | def __str__(self): 17 | return "/".join(self.namespace) 18 | 19 | def is_none(self): 20 | return self.namespace is None 21 | 22 | def __repr__(self): 23 | return self.__str__() 24 | 25 | 26 | def get_gnode_type(gnode, namespace, context): 27 | # TODO Do we need to add limit? 28 | decl = get_decl(context, namespace, gnode.name) 29 | if decl: 30 | return decl[1].get_type() 31 | return None 32 | 33 | 34 | def find_gnode_type(gnode, namespace, context, use_graph): 35 | """Find the type of a gnode 36 | 37 | If gnode is a declaration return its type, otherwise return the type 38 | of an adjacent node. 39 | """ 40 | def get_adj(gnode): 41 | return [v for v, e in use_graph.items if gnode in e] 42 | 43 | gnode_type = get_gnode_type(gnode, namespace, context) 44 | if gnode_type: 45 | return gnode_type 46 | 47 | visited = {v: False for v in use_graph.keys()} 48 | 49 | queue = get_adj(gnode) 50 | 51 | while len(queue) > 0: 52 | adjacent = queue.pop(0) 53 | if not visited[adjacent]: 54 | visited[adjacent] = True 55 | adj_type = get_gnode_type(adjacent, namespace, context) 56 | if adj_type: 57 | return adj_type 58 | queue.extend(get_adj(adjacent)) 59 | return None 60 | 61 | 62 | def namespaces_reduction(namespace, all_namespaces): 63 | """Try to find filter out namespaces that cannot be called. 64 | """ 65 | declared_inside_namespace = [ns for ns in all_namespaces 66 | if ut.prefix_lst(namespace, ns)] 67 | if len(declared_inside_namespace) > 0: 68 | return declared_inside_namespace 69 | tmp_namespace = namespace 70 | while len(tmp_namespace) > 0: 71 | tmp_namespace = tmp_namespace[:-1] 72 | same_prefix_ns = [ns for ns in all_namespaces 73 | if ut.prefix_lst(tmp_namespace, ns)] 74 | if len(same_prefix_ns) > 0: 75 | # Consider we having those functions: 76 | # [('global', 'First', 'foo'), ('global', 'foo')] 77 | # and we have called foo from ('global', 'bar') 78 | # then the function that will be called is ('global', 'foo') 79 | return list(filter( 80 | lambda x: len(x) <= len(namespace), 81 | same_prefix_ns)) 82 | return all_namespaces 83 | 84 | 85 | class CallAnalysis(DefaultVisitor): 86 | """Get the call graph of a program. 87 | 88 | Currently we only handle simple function calls. 89 | For example we don't support multi functions. 90 | We only support user defined functions. 91 | In case a function is not declared in the context, we simple ignore it. 92 | 93 | Graphs: 94 | * _call_graph: caller => callee 95 | * _calls: callee => call sites (FunctionCall objects) 96 | 97 | To employ CallAnalysis use the following instructions. 98 | 99 | analysis = CallAnalysis(self.program) 100 | call_graph, calls = analysis.result() 101 | """ 102 | def __init__(self, program): 103 | # The type of each node is: CNode 104 | self._call_graph = defaultdict(set) # namespace => [CNode] 105 | # All call sites of a function 106 | self._calls = defaultdict(set) # namespace => [FunctionCall] 107 | self._namespace = ast.GLOBAL_NAMESPACE 108 | # We compute the use_graph for each top level declaration. 109 | self._use_graph = None 110 | self.program = program 111 | self.visit(self.program) 112 | 113 | def result(self): 114 | return self._call_graph, self._calls 115 | 116 | def _get_func_namespace(self, func: str, receiver: ast.Expr = None): 117 | def get_filtered_or_all(namespace, namespaces): 118 | res = [] 119 | for ns in namespaces: 120 | if ut.prefix_lst(namespace, ns): 121 | res.append(ns) 122 | if len(res) == 0: 123 | return namespaces 124 | return res 125 | 126 | funcs = self.program.context.get_namespaces_decls( 127 | self._namespace, func, 'funcs') 128 | 129 | # Not a user defined function 130 | if len(funcs) == 0: 131 | return None 132 | 133 | if len(funcs) == 1: 134 | namespace, _ = list(funcs)[0] 135 | return [namespace] 136 | 137 | all_namespaces = [func[0] for func in funcs] 138 | 139 | # There are multiple functions defined with the same name. 140 | # Select the namespace that is closer to current namespace. 141 | if receiver is None: 142 | return namespaces_reduction(self._namespace, all_namespaces) 143 | 144 | # Handle receiver 145 | if isinstance(receiver, ast.Variable): 146 | gnode = GNode(self._namespace, receiver.name) 147 | gnode_type = find_gnode_type( 148 | gnode, self._namespace, self.program.context, self._use_graph) 149 | if isinstance(gnode_type, tp.Builtin) or gnode_type is None: 150 | return all_namespaces 151 | # Get the namespace of source_type 152 | # It is not possible to have multiple classes with the same name 153 | namespace, _ = list(self.program.context.get_namespaces_decls( 154 | self._namespace, gnode_type.name, 'classes'))[0] 155 | return get_filtered_or_all(namespace, all_namespaces) 156 | if isinstance(receiver, ast.New): 157 | # There should be only one class declaration per name. 158 | new_namespace, _ = list(self.program.context.get_namespaces_decls( 159 | self._namespace, 160 | receiver.class_type.name, 161 | 'classes'))[0] 162 | return namespaces_reduction(new_namespace, all_namespaces) 163 | if isinstance(receiver, ast.FunctionCall): 164 | if receiver.receiver: 165 | # TODO 166 | return all_namespaces 167 | # Possible Function declaration 168 | function_decls = self.program.context.get_namespaces_decls( 169 | self._namespace, receiver.func, 'funcs') 170 | function_namespaces = list(zip(*function_decls))[0] 171 | # FIXME if it has a receiver we must be more precise 172 | function_namespace = namespaces_reduction( 173 | self._namespace, all_namespaces) 174 | # We cannot find the exact function declaration 175 | if len(function_namespace) != 1: 176 | return all_namespaces 177 | function_namespace = function_namespace[0] 178 | function_decl = self.program.context.get_decl( 179 | function_namespace[:-1], function_namespace[-1]) 180 | function_type = function_decl.inferred_type 181 | if isinstance(function_type, tp.Builtin) or function_type is None: 182 | return all_namespaces 183 | # It is not possible to have multiple classes with the same name 184 | namespace, _ = list(self.program.context.get_namespaces_decls( 185 | self._namespace, function_type.name, 'classes'))[0] 186 | return get_filtered_or_all(namespace, all_namespaces) 187 | # TODO 188 | # if isinstance(receiver, ast.FieldAccess): 189 | return all_namespaces 190 | 191 | def _compute_use_graph(self, node): 192 | if len(self._namespace) == 2: 193 | analysis = UseAnalysis(self.program) 194 | analysis.visit(node) 195 | self._use_graph = analysis.result() 196 | 197 | @change_namespace 198 | def visit_class_decl(self, node): 199 | self._compute_use_graph(node) 200 | super().visit_class_decl(node) 201 | 202 | @change_namespace 203 | def visit_func_decl(self, node): 204 | self._compute_use_graph(node) 205 | self._call_graph[CNode(self._namespace)] 206 | self._calls[CNode(self._namespace)] 207 | super().visit_func_decl(node) 208 | 209 | def visit_func_call(self, node): 210 | super().visit_func_call(node) 211 | callee_ns = self._get_func_namespace(node.func, node.receiver) 212 | if callee_ns: 213 | for ns in callee_ns: 214 | self._call_graph[CNode(self._namespace)].add(CNode(ns)) 215 | self._calls[CNode(ns)].add(node) 216 | -------------------------------------------------------------------------------- /src/analysis/use_analysis.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=pointless-statement 2 | from typing import Tuple, NamedTuple 3 | from collections import defaultdict 4 | 5 | from src.ir import ast 6 | from src.ir.context import get_decl 7 | from src.ir.visitors import DefaultVisitor 8 | from src.transformations.base import change_namespace 9 | 10 | 11 | class GNode(NamedTuple): 12 | namespace: Tuple[str, ...] 13 | name: str 14 | 15 | def __str__(self): 16 | if self.name is None: 17 | return "NONE" 18 | return "/".join(self.namespace + (self.name,)) 19 | 20 | def is_none(self): 21 | return self.name is None 22 | 23 | def __repr__(self): 24 | return self.__str__() 25 | 26 | 27 | NONE_NODE = GNode(None, None) 28 | FUNC_RET = '__RET__' 29 | 30 | 31 | class UseAnalysis(DefaultVisitor): 32 | """Get the use graph for a node. 33 | 34 | To employ UseAnalysis use the following instructions. 35 | 36 | analysis = UseAnalysis(self.program) 37 | analysis.visit(node) 38 | use_graph = analysis.result() 39 | """ 40 | def __init__(self, program): 41 | # The type of each node is: GNode 42 | self._use_graph = defaultdict(set) # node => [node] 43 | self._use_graph[NONE_NODE] 44 | self._namespace = ast.GLOBAL_NAMESPACE 45 | self.program = program 46 | self.add_none_to_call = True 47 | self._ret_vars = set() 48 | self._selected_namespace = None 49 | 50 | def set_namespace(self, namespace): 51 | self._namespace = namespace 52 | 53 | def result(self): 54 | return self._use_graph 55 | 56 | def _flow_ret_to_callee(self, expr: ast.FunctionCall, target_node: GNode): 57 | fun_nsdecl = get_decl( 58 | self.program.context, self._namespace, expr.func, 59 | limit=self._selected_namespace) 60 | if not fun_nsdecl: 61 | self._use_graph[target_node].add(NONE_NODE) 62 | return 63 | callee_node = GNode(fun_nsdecl[0] + (fun_nsdecl[1].name,), 64 | FUNC_RET) 65 | if target_node: 66 | self._use_graph[target_node] 67 | self._use_graph[callee_node].add(target_node) 68 | 69 | def _flow_var_to_ref(self, expr: ast.Variable, target_node: GNode): 70 | var_node = get_decl(self.program.context, 71 | self._namespace, expr.name, 72 | limit=self._selected_namespace) 73 | if not var_node: 74 | # If node is None, this means that we referring to a variable 75 | # outside the context of class. 76 | self._use_graph[target_node].add(NONE_NODE) 77 | return 78 | var_node = GNode(var_node[0], var_node[1].name) 79 | if target_node: 80 | self._use_graph[target_node] 81 | self._use_graph[var_node].add(target_node) 82 | 83 | @change_namespace 84 | def visit_class_decl(self, node): 85 | self._selected_namespace = self._namespace 86 | super().visit_class_decl(node) 87 | 88 | def visit_field_decl(self, node): 89 | gnode = GNode(self._namespace, node.name) 90 | self._use_graph[gnode] # initialize the node 91 | 92 | def visit_param_decl(self, node): 93 | gnode = GNode(self._namespace, node.name) 94 | self._use_graph[gnode] # initialize the node 95 | 96 | def visit_variable(self, node): 97 | gnode = get_decl(self.program.context, 98 | self._namespace, node.name, 99 | limit=self._selected_namespace) 100 | if not gnode: 101 | return 102 | # If this variable reference is not in the return of the 103 | # current function, we add an edge from this variable to NONE. 104 | # 105 | # For example: 106 | # * return x == "foo" => We add the edge x -> NONE 107 | # * return x => We don't add any edge. 108 | ret_node = GNode(self._namespace, FUNC_RET) 109 | gnode = GNode(gnode[0], gnode[1].name) 110 | nodes = self._use_graph[gnode] 111 | if ret_node not in nodes or node.name not in self._ret_vars: 112 | self._use_graph[gnode].add(NONE_NODE) 113 | else: 114 | self._ret_vars.discard(node.name) 115 | 116 | def visit_var_decl(self, node): 117 | """Add variable to _var_decl_stack to add flows from it to other 118 | variables in visit_variable. 119 | """ 120 | gnode = GNode(self._namespace, node.name) 121 | self._use_graph[gnode] # initialize the node 122 | if isinstance(node.expr, ast.Variable): 123 | self._flow_var_to_ref(node.expr, gnode) 124 | elif isinstance(node.expr, ast.FunctionCall): 125 | self._flow_ret_to_callee(node.expr, gnode) 126 | prev = self.add_none_to_call 127 | self.add_none_to_call = False 128 | self.visit(node.expr) 129 | self.add_none_to_call = prev 130 | else: 131 | self._use_graph[gnode].add(NONE_NODE) 132 | super().visit_var_decl(node) 133 | 134 | def visit_assign(self, node): 135 | self._flow_var_to_ref(node, NONE_NODE) 136 | super().visit_assign(node) 137 | 138 | @change_namespace 139 | def visit_func_decl(self, node): 140 | """NOTE that you should set the namespace (use set_namespace), in case 141 | the function is not a top_level declaration. 142 | """ 143 | ret_node = None 144 | if node.get_type() != self.program.bt_factory.get_void_type(): 145 | # We add a special node for representing the return of a function. 146 | ret_node = GNode(self._namespace, FUNC_RET) 147 | self._use_graph[ret_node] 148 | expr = None 149 | if isinstance(node.body, ast.Block): 150 | expr = node.body.body[-1] if node.body.body else None 151 | else: 152 | expr = node.body 153 | if not expr: 154 | return super().visit_func_decl(node) 155 | if isinstance(expr, ast.Variable): 156 | self._ret_vars.add(expr.name) 157 | self._flow_var_to_ref(expr, ret_node) 158 | elif isinstance(expr, ast.FunctionCall): 159 | self._flow_ret_to_callee(expr, ret_node) 160 | else: 161 | if ret_node: 162 | self._use_graph[ret_node].add(NONE_NODE) 163 | super().visit_func_decl(node) 164 | 165 | def visit_func_call(self, node): 166 | """Add flows from function call arguments to function declaration 167 | parameters. 168 | """ 169 | # Find the namespace and the declaration of the functions that is 170 | # being called. 171 | fun_nsdecl = get_decl( 172 | self.program.context, self._namespace, node.func, 173 | limit=self._selected_namespace) 174 | add_none = False 175 | if not fun_nsdecl: 176 | # The function is outer, so, if we pass a variable to this function 177 | # we must add an edge from this variable to the None node. 178 | add_none = True 179 | 180 | for i, arg in enumerate(node.args): 181 | if add_none: 182 | param_node = NONE_NODE 183 | else: 184 | len_p = len(fun_nsdecl[1].params) 185 | if i < len_p: 186 | param_index = i 187 | else: 188 | # Ok, the formal parameter is a vararg, and we have 189 | # provided more than one arguments. 190 | param_index = len_p - 1 191 | assert fun_nsdecl[1].params[param_index].vararg 192 | 193 | param_node = GNode(fun_nsdecl[0] + (fun_nsdecl[1].name,), 194 | fun_nsdecl[1].params[param_index].name) 195 | if isinstance(arg, ast.Variable): 196 | # The argument is a variable reference. So add edge from the 197 | # variable to the corresponding functions's parameter. 198 | self._flow_var_to_ref(arg, param_node) 199 | continue 200 | if isinstance(arg, ast.FunctionCall): 201 | # The argument is a function call. So depending on the callee 202 | # function, we might add an edge from the callee's function 203 | # return node to the corresponding function's parameter. 204 | self._flow_ret_to_callee(arg, param_node) 205 | # The ret variable of this function should not point to None. 206 | prev = self.add_none_to_call 207 | self.add_none_to_call = False 208 | self.visit(arg) 209 | self.add_none_to_call = prev 210 | continue 211 | if param_node is not NONE_NODE: 212 | # The argument is other than a variable reference or function 213 | # call. So we add an edge from NONE to the corresponding 214 | # function's parameter. 215 | self._use_graph[param_node] 216 | self._use_graph[NONE_NODE].add(param_node) 217 | self.visit(arg) 218 | if fun_nsdecl: 219 | # If this function call is part of an expression other than the 220 | # return expression of the current function, then we add an edge 221 | # from the callee's ret node to NONE. 222 | # For example: 223 | # * return if (cond) callee(x) else y => 224 | # We add the edge callee_ret -> NONE 225 | # * return callee(x) => We don't add any edge. 226 | namespace, fun_decl = fun_nsdecl 227 | gnode = GNode(namespace + (fun_decl.name,), FUNC_RET) 228 | ret_node = GNode(self._namespace, FUNC_RET) 229 | nodes = self._use_graph[gnode] 230 | if ret_node not in nodes and self.add_none_to_call: 231 | self._use_graph[gnode].add(NONE_NODE) 232 | 233 | if node.receiver: 234 | self.visit(node.receiver) 235 | -------------------------------------------------------------------------------- /src/args.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | from src.utils import random, mkdir 5 | from src.modules.processor import ProgramProcessor 6 | from src.generators.config import cfg 7 | 8 | 9 | cwd = os.getcwd() 10 | 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument( 13 | "-s", "--seconds", 14 | type=int, 15 | help="Timeout in seconds" 16 | ) 17 | parser.add_argument( 18 | "-i", "--iterations", 19 | type=int, 20 | help="Iterations to run (default: 3)" 21 | ) 22 | parser.add_argument( 23 | "-t", "--transformations", 24 | type=int, 25 | default=0, 26 | help="Number of transformations in each round" 27 | ) 28 | parser.add_argument( 29 | "--batch", 30 | type=int, 31 | default=1, 32 | help='Number of programs to generate before invoking the compiler' 33 | ) 34 | parser.add_argument( 35 | "-b", "--bugs", 36 | default=os.path.join(cwd, "bugs"), 37 | help="Set bug directory (default: " + str(os.path.join(cwd, "bugs")) + ")" 38 | ) 39 | parser.add_argument( 40 | "-n", "--name", 41 | default=random.str(), 42 | help="Set name of this testing instance (default: random string)" 43 | ) 44 | parser.add_argument( 45 | "-T", "--transformation-types", 46 | default=ProgramProcessor.CP_TRANSFORMATIONS.keys(), 47 | nargs="*", 48 | choices=ProgramProcessor.CP_TRANSFORMATIONS.keys(), 49 | help="Select specific transformations to perform" 50 | ) 51 | parser.add_argument( 52 | "--transformation-schedule", 53 | default=None, 54 | type=str, 55 | help="A file containing the schedule of transformations" 56 | ) 57 | parser.add_argument( 58 | "-R", "--replay", 59 | help="Give a program to use instead of a randomly generated (pickled)" 60 | ) 61 | parser.add_argument( 62 | "-e", "--examine", 63 | action="store_true", 64 | help="Open ipdb for a program (can be used only with --replay option)" 65 | ) 66 | parser.add_argument( 67 | "-k", "--keep-all", 68 | action="store_true", 69 | help="Save all programs" 70 | ) 71 | parser.add_argument( 72 | "-S", "--print-stacktrace", 73 | action="store_true", 74 | help="When an error occurs print stack trace" 75 | ) 76 | parser.add_argument( 77 | "-w", "--workers", 78 | type=int, 79 | default=None, 80 | help="Number of workers for processing test programs" 81 | ) 82 | parser.add_argument( 83 | "-d", "--debug", 84 | action="store_true" 85 | ) 86 | parser.add_argument( 87 | "-r", "--rerun", 88 | action="store_true", 89 | help=("Run only the last transformation. If failed, start from the last " 90 | "and go back until the transformation introduces the error") 91 | ) 92 | parser.add_argument( 93 | "-F", "--log-file", 94 | default=os.path.join(cwd, "logs"), 95 | help="Set log file (default: " + str(os.path.join(cwd, "logs")) + ")" 96 | ) 97 | parser.add_argument( 98 | "-L", "--log", 99 | action="store_true", 100 | help="Keep logs for each transformation (bugs/session/logs)" 101 | ) 102 | parser.add_argument( 103 | "-N", "--dry-run", 104 | action="store_true", 105 | help="Do not compile the programs" 106 | ) 107 | parser.add_argument( 108 | "--language", 109 | default="kotlin", 110 | choices=['kotlin', 'groovy', 'java', 'scala'], 111 | help="Select specific language" 112 | ) 113 | parser.add_argument( 114 | "--max-type-params", 115 | type=int, 116 | default=3, 117 | help="Maximum number of type parameters to generate" 118 | ) 119 | parser.add_argument( 120 | "--max-depth", 121 | type=int, 122 | default=6, 123 | help="Generate programs up to the given depth" 124 | ) 125 | parser.add_argument( 126 | "-P", 127 | "--only-correctness-preserving-transformations", 128 | action="store_true", 129 | help="Use only correctness-preserving transformations" 130 | ) 131 | parser.add_argument( 132 | "--timeout", 133 | type=int, 134 | default=600, 135 | help="Timeout for transformations (in seconds)" 136 | ) 137 | parser.add_argument( 138 | "--cast-numbers", 139 | action="store_true", 140 | help=("Cast numeric constants to their actual type" 141 | " (this option is used to avoid re-occurrence of" 142 | " a specific Groovy bug)") 143 | ) 144 | parser.add_argument( 145 | "--disable-use-site-variance", 146 | action="store_true", 147 | help="Disable use-site variance" 148 | ) 149 | parser.add_argument( 150 | "--disable-contravariance-use-site", 151 | action="store_true", 152 | help="Disable contravariance in use-site variance" 153 | ) 154 | parser.add_argument( 155 | "--disable-bounded-type-parameters", 156 | action="store_true", 157 | help="Disable bounded type parameters" 158 | ) 159 | parser.add_argument( 160 | "--disable-parameterized-functions", 161 | action="store_true", 162 | help="Disable parameterized functions" 163 | ) 164 | parser.add_argument( 165 | "--error-filter-patterns", 166 | default='', 167 | type=str, 168 | help=("A file containing regular expressions for filtering compiler error " 169 | "messages") 170 | ) 171 | 172 | 173 | args = parser.parse_args() 174 | 175 | 176 | args.test_directory = os.path.join(args.bugs, args.name) 177 | args.stop_cond = "timeout" if args.seconds else "iterations" 178 | args.temp_directory = os.path.join(cwd, "temp") 179 | args.options = { 180 | "Generator": { 181 | }, 182 | 'Translator': { 183 | 'cast_numbers': args.cast_numbers, 184 | }, 185 | "TypeErasure": { 186 | "timeout": args.timeout 187 | }, 188 | "TypeOverwriting": { 189 | "timeout": args.timeout 190 | } 191 | } 192 | random.remove_reserved_words(args.language) 193 | 194 | 195 | # Set configurations 196 | 197 | cfg.dis.use_site_variance = args.disable_use_site_variance 198 | cfg.dis.use_site_contravariance = args.disable_contravariance_use_site 199 | cfg.limits.max_depth = args.max_depth 200 | if args.disable_bounded_type_parameters: 201 | cfg.prob.bounded_type_parameters = 0 202 | if args.disable_parameterized_functions: 203 | cfg.prob.parameterized_functions = 0 204 | 205 | 206 | def validate_args(args): 207 | # CHECK ARGUMENTS 208 | 209 | if args.seconds and args.iterations: 210 | sys.exit("Error: you should only set --seconds or --iterations") 211 | 212 | if os.path.isdir(args.bugs) and args.name in os.listdir(args.bugs): 213 | sys.exit("Error: --name {} already exists".format(args.name)) 214 | 215 | if args.transformation_schedule and args.transformations: 216 | sys.exit("Options --transformation-schedule and --transformations" 217 | " are mutually exclusive. You can't use both.") 218 | 219 | if not args.transformation_schedule and args.transformations is None: 220 | sys.exit("You have to provide one of --transformation-schedule or" 221 | " --transformations.") 222 | 223 | if args.transformation_schedule and ( 224 | not os.path.isfile(args.transformation_schedule)): 225 | sys.exit("You have to provide a valid file in --transformation-schedule") 226 | 227 | if args.rerun and args.workers: 228 | sys.exit('You cannot use -r option in parallel mode') 229 | 230 | if args.rerun and not args.keep_all: 231 | sys.exit("The -r option only works with the option -k") 232 | 233 | if args.rerun and args.batch: 234 | sys.exit("You cannot use -r option with the option --batch") 235 | 236 | if args.examine and not args.replay: 237 | sys.exit("You cannot use --examine option without the --replay option") 238 | 239 | 240 | def pre_process_args(args): 241 | # PRE-PROCESSING 242 | 243 | if not os.path.isdir(args.bugs): 244 | mkdir(args.bugs) 245 | -------------------------------------------------------------------------------- /src/compilers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/src/compilers/__init__.py -------------------------------------------------------------------------------- /src/compilers/base.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import re 3 | 4 | 5 | class BaseCompiler(): 6 | ERROR_REGEX = None 7 | CRASH_REGEX = None 8 | 9 | def __init__(self, input_name, filter_patterns=None): 10 | self.input_name = input_name 11 | self.filter_patterns = filter_patterns or [] 12 | self.crash_msg = None 13 | 14 | @classmethod 15 | def get_compiler_version(cls): 16 | raise NotImplementedError('get_compiler_version() must be implemented') 17 | 18 | def get_compiler_cmd(self): 19 | raise NotImplementedError('get_compiler_cmd() must be implemented') 20 | 21 | def get_filename(self, match): 22 | raise NotImplementedError('get_filename() must be implemented') 23 | 24 | def get_error_msg(self, match): 25 | raise NotImplementedError('get_error_msg() must be implemented') 26 | 27 | def analyze_compiler_output(self, output): 28 | crash_match = re.search(self.CRASH_REGEX, output) 29 | if crash_match: 30 | self.crash_msg = output 31 | return None, [] 32 | failed = defaultdict(list) 33 | filtered_output = output 34 | for p in self.filter_patterns: 35 | filtered_output = re.sub(p, '', filtered_output) 36 | matches = re.findall(self.ERROR_REGEX, filtered_output) 37 | for match in matches: 38 | filename = self.get_filename(match) 39 | error_msg = self.get_error_msg(match) 40 | failed[filename].append(error_msg) 41 | return failed, matches 42 | -------------------------------------------------------------------------------- /src/compilers/groovy.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | 4 | from src.compilers.base import BaseCompiler 5 | 6 | 7 | class GroovyCompiler(BaseCompiler): 8 | # Match (example.groovy):(error message until empty line) 9 | ERROR_REGEX = re.compile(r'([a-zA-Z0-9\\/_]+.groovy):([\s\S]*?(?=\n{2,}))') 10 | 11 | CRASH_REGEX = re.compile(r'(at org.codehaus.groovy)(.*)') 12 | 13 | STACKOVERFLOW_REGEX = re.compile(r'(.*java.lang.StackOverflowError)(.*)') 14 | 15 | def __init__(self, input_name, filter_patterns=None): 16 | input_name = os.path.join(input_name, '*', '*.groovy') 17 | super().__init__(input_name, filter_patterns) 18 | 19 | @classmethod 20 | def get_compiler_version(cls): 21 | return ['groovyc-l', '-version'] 22 | 23 | def get_compiler_cmd(self): 24 | return ['groovyc-l', '--compile-static', self.input_name] 25 | 26 | def get_filename(self, match): 27 | return match[0] 28 | 29 | def get_error_msg(self, match): 30 | return match[1] 31 | 32 | def analyze_compiler_output(self, output): 33 | failed, matches = super().analyze_compiler_output(output) 34 | stack_overflow = re.search(self.STACKOVERFLOW_REGEX, output) 35 | if stack_overflow and not matches: 36 | self.crash_msg = output 37 | return None, matches 38 | 39 | return failed, matches 40 | -------------------------------------------------------------------------------- /src/compilers/java.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | 4 | from src.compilers.base import BaseCompiler 5 | 6 | 7 | class JavaCompiler(BaseCompiler): 8 | # Match (example.groovy):(error message until empty line) 9 | ERROR_REGEX = re.compile( 10 | r'([a-zA-Z0-9\/_]+.java):(\d+:[ ]+error:[ ]+.*)(.*?(?=\n{1,}))') 11 | 12 | CRASH_REGEX = re.compile(r'(java\.lang.*)\n(.*)') 13 | 14 | def __init__(self, input_name, filter_patterns=None): 15 | input_name = os.path.join(input_name, '*', '*.java') 16 | super().__init__(input_name, filter_patterns) 17 | 18 | @classmethod 19 | def get_compiler_version(cls): 20 | return ['javac', '-version'] 21 | 22 | def get_compiler_cmd(self): 23 | return ['javac', '-nowarn', self.input_name] 24 | 25 | def get_filename(self, match): 26 | return match[0] 27 | 28 | def get_error_msg(self, match): 29 | return match[1] 30 | -------------------------------------------------------------------------------- /src/compilers/kotlin.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from src.compilers.base import BaseCompiler 4 | 5 | 6 | class KotlinCompiler(BaseCompiler): 7 | ERROR_REGEX = re.compile( 8 | r'([a-zA-Z0-9\/_]+.kt):\d+:\d+:[ ]+error:[ ]+(.*)') 9 | CRASH_REGEX = re.compile( 10 | r'(org\.jetbrains\..*)\n(.*)', 11 | re.MULTILINE 12 | ) 13 | 14 | def __init__(self, input_name, filter_patterns=None): 15 | super().__init__(input_name, filter_patterns) 16 | 17 | @classmethod 18 | def get_compiler_version(cls): 19 | return ['kotlinc', '-version'] 20 | 21 | def get_compiler_cmd(self): 22 | return ['kotlinc', self.input_name, '-include-runtime', '-d', 23 | 'program.jar'] 24 | 25 | def get_filename(self, match): 26 | return match[0] 27 | 28 | def get_error_msg(self, match): 29 | return match[1] 30 | -------------------------------------------------------------------------------- /src/compilers/scala.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | from src.compilers.base import BaseCompiler 5 | 6 | 7 | class ScalaCompiler(BaseCompiler): 8 | ERROR_REGEX = re.compile( 9 | r"-- .*Error: (.*\.scala):\d+:\d+ -+\n((?:[^-]+))", re.MULTILINE) 10 | CRASH_REGEX = re.compile(r".*at dotty(.*)") 11 | 12 | def __init__(self, input_name, filter_patterns=None): 13 | input_name = os.path.join(input_name, '*', '*.scala') 14 | super().__init__(input_name, filter_patterns) 15 | 16 | @classmethod 17 | def get_compiler_version(cls): 18 | return ['scalac', '-version'] 19 | 20 | def get_compiler_cmd(self): 21 | return ['scalac', '-color', 'never', '-nowarn', self.input_name] 22 | 23 | def get_filename(self, match): 24 | return match[0] 25 | 26 | def get_error_msg(self, match): 27 | return match[1] 28 | -------------------------------------------------------------------------------- /src/generators/__init__.py: -------------------------------------------------------------------------------- 1 | from src.generators.generator import * 2 | -------------------------------------------------------------------------------- /src/generators/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file contains the classes that are responsible for configuring the 3 | generation policies. 4 | """ 5 | import json 6 | from dataclasses import dataclass 7 | 8 | 9 | class Singleton(type): 10 | _instances = {} 11 | def __call__(cls, *args, **kwargs): 12 | if cls not in cls._instances: 13 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 14 | return cls._instances[cls] 15 | 16 | 17 | def process_arg(config, name, value): 18 | assert hasattr(config, name), \ 19 | f"{type(config).__name__} has not {name} argument" 20 | if isinstance(getattr(config, name), (int, float)): 21 | assert isinstance(value, (int, float)), \ 22 | f"{name}={value} is not int or float" 23 | setattr(config, name, value) 24 | else: 25 | for key, val in value.items(): 26 | process_arg(getattr(config, name), key, val) 27 | 28 | 29 | @dataclass 30 | class ClassLimits: 31 | max_fields: int 32 | max_funcs: int 33 | 34 | 35 | @dataclass 36 | class FunctionLimits: 37 | max_side_effects: int 38 | max_params: int 39 | 40 | 41 | @dataclass 42 | class GenLimits: 43 | cls: ClassLimits 44 | fn: FunctionLimits 45 | max_var_decls: int # max variable declarations in a scope. 46 | max_type_params: int # max type parameters in parameterized classes and functions 47 | max_functional_params: int # max number of parameters in functional interfaces 48 | max_top_level: int # max number of top-level declarations 49 | min_top_level: int # min number of top-level declarations 50 | max_depth: int # max depth of leaves in programs 51 | 52 | 53 | # In many scenarios like func_ref_call, there may be a slighter change that 54 | # we will generate the specified expression based on the current program 55 | @dataclass 56 | class Probabilities: 57 | function_expr: float # functions that their body are expressions 58 | bounded_type_parameters: float 59 | parameterized_functions: float 60 | func_ref_call: float # use function reference call instead of function call 61 | func_ref: float # generate func_ref instead of lambda 62 | sam_coercion: float # perform sam coercion whenever possible 63 | 64 | 65 | # Features that we want to either disable or enable 66 | # If something is set to True then it means it is disabled. 67 | @dataclass 68 | class Disabled: 69 | use_site_variance: bool 70 | use_site_contravariance: bool 71 | 72 | 73 | class GenConfig(metaclass=Singleton): 74 | def __init__(self): 75 | self.limits = GenLimits( 76 | cls=ClassLimits( 77 | max_fields=2, 78 | max_funcs=2 79 | ), 80 | fn=FunctionLimits( 81 | max_side_effects=1, 82 | max_params=2 83 | ), 84 | max_var_decls=3, 85 | max_type_params=3, 86 | max_functional_params=3, 87 | max_top_level=10, 88 | min_top_level=5, 89 | max_depth=6 90 | ) 91 | self.prob=Probabilities( 92 | function_expr=1.0, 93 | bounded_type_parameters=0.5, 94 | parameterized_functions=0.3, 95 | func_ref_call=1.0, 96 | func_ref=0.5, 97 | sam_coercion=1.0, 98 | ) 99 | self.dis=Disabled( 100 | use_site_variance=False, 101 | use_site_contravariance=False 102 | ) 103 | 104 | def json_config(self, kwargs): 105 | for key, value in kwargs.items(): 106 | process_arg(self, key, value) 107 | 108 | def to_json(self): 109 | return json.dumps(self, default=lambda o: o.__dict__) 110 | 111 | 112 | cfg = GenConfig() 113 | 114 | 115 | def main(): 116 | __import__('pprint').pprint(cfg.to_json()) 117 | 118 | 119 | if __name__ == "__main__": 120 | main() 121 | -------------------------------------------------------------------------------- /src/generators/generators.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file includes various generators that do not require the state of the 3 | generator. 4 | Currently, it contains only generators for constant AST nodes. 5 | 6 | NOTE: generators should declare a parameter named `expr_type`, 7 | even if they don't need it, for API compatibility reasons. 8 | """ 9 | from src import utils 10 | from src.ir import ast 11 | from src.generators import utils as gu 12 | 13 | 14 | # pylint: disable=unused-argument 15 | def gen_string_constant(expr_type=None) -> ast.StringConstant: 16 | """Generate a string constant. 17 | """ 18 | return ast.StringConstant(gu.gen_identifier()) 19 | 20 | 21 | # pylint: disable=unused-argument 22 | def gen_integer_constant(expr_type=None) -> ast.IntegerConstant: 23 | """Generate an integer constant. 24 | 25 | The generated integers are between -100 and 100. 26 | """ 27 | return ast.IntegerConstant(utils.random.integer(-100, 100), expr_type) 28 | 29 | 30 | def gen_real_constant(expr_type=None) -> ast.RealConstant: 31 | """Generate a real constant. 32 | 33 | The generated reals are between `-100.1000` and `100.1000`. 34 | """ 35 | prefix = str(utils.random.integer(0, 100)) 36 | suffix = str(utils.random.integer(0, 1000)) 37 | sign = utils.random.choice(['', '-']) 38 | return ast.RealConstant(sign + prefix + "." + suffix, expr_type) 39 | 40 | 41 | # pylint: disable=unused-argument 42 | def gen_bool_constant(expr_type=None) -> ast.BooleanConstant: 43 | """Generate a boolean constant. 44 | """ 45 | return ast.BooleanConstant(utils.random.choice(['true', 'false'])) 46 | 47 | 48 | # pylint: disable=unused-argument 49 | def gen_char_constant(expr_type=None) -> ast.CharConstant: 50 | """Generate a character constant. 51 | """ 52 | return ast.CharConstant(utils.random.char()) 53 | -------------------------------------------------------------------------------- /src/generators/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file includes utility functions for the generator module. 3 | """ 4 | from dataclasses import dataclass 5 | 6 | from src import utils as ut 7 | from src.ir import ast 8 | from src.ir import types as tp 9 | from src.ir import type_utils as tu 10 | 11 | 12 | ### Data Classes ### 13 | 14 | @dataclass 15 | class SuperClassInfo: 16 | """ 17 | A data class for storing information regarding a super class. 18 | """ 19 | super_cls: ast.ClassDeclaration 20 | type_var_map: tu.TypeVarMap 21 | super_inst: ast.SuperClassInstantiation 22 | 23 | 24 | @dataclass 25 | class AttrAccessInfo: 26 | """ 27 | A data class for storing information regarding a specific attribute 28 | access (either function or field). 29 | 30 | Attributes: 31 | receiver_t: The type of the receiver. 32 | receiver_inst: TypeVarMap for receiver_t in case receiver_t is a 33 | parameterized type. 34 | attr_decl: The declaration corresponding to the attribute (either field 35 | declaration or a function declaration). 36 | attr_inst: TypeVarMap for attr_decl if attr_decl is a parameterized 37 | function 38 | """ 39 | receiver_t: tp.Type 40 | receiver_inst: tu.TypeVarMap 41 | attr_decl: ast.Declaration 42 | attr_inst: tu.TypeVarMap 43 | 44 | 45 | @dataclass 46 | class AttrReceiverInfo: 47 | """ 48 | A data class for storing information regarding a receiver object and one 49 | of its attributes. 50 | 51 | Attributes: 52 | receiver_expr: The receiver expression (usually a Variable). 53 | receiver_inst: TypeVarMap for receiver in case receiver is a 54 | parameterized type. 55 | attr_decl: The declaration corresponding to the attribute (either a field 56 | declaration or a function declaration). 57 | attr_inst: TypeVarMap for attr_decl if attr_decl is a parameterized 58 | function. 59 | """ 60 | receiver_expr: ast.Expr 61 | receiver_inst: tu.TypeVarMap 62 | attr_decl: ast.Declaration 63 | attr_inst: tu.TypeVarMap 64 | 65 | 66 | ### Utility functions ### 67 | 68 | # NOTE maybe me can create an enum for class types 69 | def select_class_type(contain_fields: bool): 70 | """Select class type for a class declaration. 71 | 72 | Args: 73 | contain_fields: set to True if the class contains a field. 74 | 75 | Returns: 76 | ast.ClassDeclaration.{REGULAR, ABSTRACT, INTERFACE} 77 | """ 78 | # TODO probabilities table 79 | # there's higher probability to generate a regular class. 80 | if ut.random.bool(): 81 | return ast.ClassDeclaration.REGULAR 82 | 83 | candidates = [ast.ClassDeclaration.ABSTRACT] 84 | if not contain_fields: 85 | candidates.append(ast.ClassDeclaration.INTERFACE) 86 | return ut.random.choice(candidates) 87 | 88 | 89 | def init_variance_choices(type_var_map: tu.TypeVarMap) -> tu.VarianceChoices: 90 | """Generate variance_choices for the type variables of type_var_map 91 | """ 92 | variance_choices = {} 93 | for type_var in type_var_map.keys(): 94 | variance_choices[type_var] = (False, False) 95 | # If disable variance on specific type parameters, then we have to 96 | # do the same on its bound (assuming it is another type variable). 97 | while type_var.bound and type_var.bound.is_type_var(): 98 | type_var = type_var.bound 99 | variance_choices[type_var] = (False, False) 100 | return variance_choices 101 | 102 | 103 | def gen_identifier(ident_type:str=None) -> str: 104 | """Generate an identifier name. 105 | 106 | Args: 107 | ident_type: None or 'capitalize' or 'lower' 108 | 109 | Raises: 110 | AssertionError: Raises an AssertionError if the ident_type is neither 111 | 'capitalize' nor 'lower'. 112 | """ 113 | word = ut.random.word() 114 | if ident_type is None: 115 | return word 116 | if ident_type == 'lower': 117 | return word.lower() 118 | if ident_type == 'capitalize': 119 | return word.capitalize() 120 | raise AssertionError("ident_type should be 'capitalize' or 'lower'") 121 | -------------------------------------------------------------------------------- /src/graph_utils.py: -------------------------------------------------------------------------------- 1 | from src.analysis.use_analysis import NONE_NODE 2 | 3 | 4 | class Node(): 5 | def is_none(self): 6 | raise NotImplementedError("is_none must be implemented") 7 | 8 | 9 | def reachable(graph, start_vertex, dest_vertex): 10 | """Find if a start_vertex can reach dest_vertex with BFS.""" 11 | visited = {v: False for v in graph.keys()} 12 | 13 | queue = [] 14 | queue.append(start_vertex) 15 | if start_vertex not in visited: 16 | return False 17 | visited[start_vertex] = True 18 | 19 | while queue: 20 | next_v = queue.pop(0) 21 | 22 | if next_v == dest_vertex: 23 | return True 24 | 25 | for vertex in graph[next_v]: 26 | if vertex in visited and not visited[vertex]: 27 | queue.append(vertex) 28 | visited[vertex] = True 29 | return False 30 | 31 | 32 | def dfs(graph, source): 33 | visited = {k: False for k in graph.keys()} 34 | 35 | def _dfs(n): 36 | visited[n] = True 37 | for e in graph.get(n, []): 38 | if not visited.get(e.target, False): 39 | _dfs(e.target) 40 | _dfs(source) 41 | return { 42 | n 43 | for n, is_visited in visited.items() 44 | if is_visited and n != source 45 | } 46 | 47 | 48 | def bi_reachable(graph, start_vertex, dest_vertex): 49 | """Bidirectional reachable""" 50 | # pylint: disable=arguments-out-of-order 51 | return (reachable(graph, start_vertex, dest_vertex) or 52 | reachable(graph, dest_vertex, start_vertex)) 53 | 54 | 55 | def connected(graph, start_vertex, dest_vertex): 56 | """Find if a start_vertex can connect to dest_vertex. 57 | 58 | The following example is neither reachable nor bi-reachable, but it is 59 | connected: 60 | G: 0 -> 1 -> 2, 4 -> 2 61 | 62 | Connected: 4->0, 0->4, 1->4, etc. and all bi-reachable 63 | """ 64 | # TODO optimize 65 | visited = {v: False for v in graph.keys()} 66 | 67 | queue = [] 68 | queue.append(start_vertex) 69 | if start_vertex not in visited: 70 | return False 71 | visited[start_vertex] = True 72 | 73 | while queue: 74 | next_v = queue.pop(0) 75 | 76 | if next_v == dest_vertex: 77 | return True 78 | 79 | for node, adjs in graph.items(): 80 | if next_v == node: 81 | for vertex in adjs: 82 | if vertex in visited and not visited[vertex]: 83 | queue.append(vertex) 84 | visited[vertex] = True 85 | if next_v in adjs and not visited[node]: 86 | queue.append(node) 87 | visited[node] = True 88 | 89 | return False 90 | 91 | 92 | def none_reachable(graph, vertex, none_node=NONE_NODE): 93 | reachable_nodes = find_all_bi_reachable(graph, vertex) 94 | if any(bi_reachable(graph, v, none_node) for v in reachable_nodes): 95 | return True 96 | return False 97 | 98 | 99 | def none_connected(graph, vertex, none_node=NONE_NODE): 100 | connected_nodes = find_all_connected(graph, vertex) 101 | if any(connected(graph, v, none_node) for v in connected_nodes): 102 | return True 103 | return False 104 | 105 | 106 | def find_all_paths(graph, start, path=None): 107 | """Find all the paths of graph from start.""" 108 | path = path if path is not None else [] 109 | path = path + [start] 110 | if start not in graph: 111 | return [path] 112 | paths = [path] 113 | for node in graph[start]: 114 | if node not in path: 115 | newpaths = find_all_paths(graph, node, path) 116 | for newpath in newpaths: 117 | paths.append(newpath) 118 | return paths 119 | 120 | 121 | def find_longest_paths(graph, vertex): 122 | def exist(x, y): 123 | """Checks if x is in y with the same order""" 124 | return x == y[:len(y)-len(x)+1] 125 | paths = find_all_paths(graph, vertex) 126 | if len(paths) == 1: 127 | return paths 128 | return [x for x in paths if not any(exist(x, p) for p in paths)] 129 | 130 | 131 | def find_all_reachable(graph, vertex): 132 | res = set() 133 | for path in find_longest_paths(graph, vertex): 134 | res.update(path) 135 | return res 136 | 137 | 138 | # TODO optimize 139 | def find_all_bi_reachable(graph, vertex): 140 | return {n for n in graph if bi_reachable(graph, vertex, n)} 141 | 142 | 143 | # TODO optimize 144 | def find_all_connected(graph, vertex): 145 | return {n for n in graph if connected(graph, vertex, n)} 146 | 147 | 148 | def find_sources(graph, vertex): 149 | sources = [] 150 | visited = {v: False for v in graph.keys()} 151 | 152 | stack = [vertex] 153 | 154 | while len(stack) > 0: 155 | source = stack.pop() 156 | 157 | if not visited[source]: 158 | visited[source] = True 159 | s_sources = [n for n in graph.keys() if source in graph[n]] 160 | if not s_sources: 161 | sources.append(source) 162 | continue 163 | stack.extend(s_sources) 164 | return sources 165 | -------------------------------------------------------------------------------- /src/ir/__init__.py: -------------------------------------------------------------------------------- 1 | from src.ir.kotlin_types import KotlinBuiltinFactory 2 | from src.ir.groovy_types import GroovyBuiltinFactory 3 | from src.ir.java_types import JavaBuiltinFactory 4 | from src.ir.scala_types import ScalaBuiltinFactory 5 | 6 | 7 | BUILTIN_FACTORIES = { 8 | "kotlin": KotlinBuiltinFactory(), 9 | "groovy": GroovyBuiltinFactory(), 10 | "java": JavaBuiltinFactory(), 11 | "scala": ScalaBuiltinFactory() 12 | } 13 | -------------------------------------------------------------------------------- /src/ir/builtins.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=abstract-method 2 | from abc import ABC, abstractmethod 3 | 4 | from src.ir.types import Builtin, TypeConstructor, TypeParameter 5 | 6 | 7 | class BuiltinFactory(ABC): 8 | @abstractmethod 9 | def get_language(self): 10 | pass 11 | 12 | @abstractmethod 13 | def get_builtin(self): 14 | pass 15 | 16 | @abstractmethod 17 | def get_void_type(self): 18 | pass 19 | 20 | @abstractmethod 21 | def get_any_type(self): 22 | pass 23 | 24 | @abstractmethod 25 | def get_number_type(self): 26 | pass 27 | 28 | @abstractmethod 29 | def get_integer_type(self): 30 | pass 31 | 32 | @abstractmethod 33 | def get_byte_type(self): 34 | pass 35 | 36 | @abstractmethod 37 | def get_short_type(self): 38 | pass 39 | 40 | @abstractmethod 41 | def get_long_type(self): 42 | pass 43 | 44 | @abstractmethod 45 | def get_float_type(self): 46 | pass 47 | 48 | @abstractmethod 49 | def get_double_type(self): 50 | pass 51 | 52 | @abstractmethod 53 | def get_big_decimal_type(self): 54 | pass 55 | 56 | @abstractmethod 57 | def get_big_integer_type(self): 58 | pass 59 | 60 | @abstractmethod 61 | def get_boolean_type(self): 62 | pass 63 | 64 | @abstractmethod 65 | def get_char_type(self): 66 | pass 67 | 68 | @abstractmethod 69 | def get_string_type(self): 70 | pass 71 | 72 | @abstractmethod 73 | def get_array_type(self): 74 | pass 75 | 76 | @abstractmethod 77 | def get_function_type(self, nr_parameters=0): 78 | pass 79 | 80 | def get_non_nothing_types(self): 81 | return [ 82 | self.get_any_type(), 83 | self.get_number_type(), 84 | self.get_integer_type(), 85 | self.get_byte_type(), 86 | self.get_short_type(), 87 | self.get_long_type(), 88 | self.get_float_type(), 89 | self.get_double_type(), 90 | self.get_big_decimal_type(), 91 | self.get_big_integer_type(), 92 | self.get_boolean_type(), 93 | self.get_char_type(), 94 | self.get_string_type(), 95 | self.get_array_type() 96 | ] 97 | 98 | def get_number_types(self): 99 | return [ 100 | self.get_byte_type(), 101 | self.get_short_type(), 102 | self.get_integer_type(), 103 | self.get_long_type(), 104 | self.get_float_type(), 105 | self.get_double_type(), 106 | self.get_big_decimal_type(), 107 | self.get_big_integer_type(), 108 | ] 109 | 110 | def get_function_types(self, max_parameters): 111 | return [self.get_function_type(i) for i in range(0, max_parameters+1)] 112 | 113 | def get_nothing(self): 114 | raise NotImplementedError 115 | 116 | 117 | class AnyType(Builtin): 118 | def __init__(self, name="Any"): 119 | super().__init__(name) 120 | 121 | 122 | class NothingType(Builtin): 123 | def __init__(self, name="Nothing"): 124 | super().__init__(name) 125 | 126 | def is_subtype(self, other): 127 | return True 128 | 129 | 130 | class VoidType(AnyType): 131 | def __init__(self, name="Void"): 132 | super().__init__(name) 133 | self.supertypes.append(AnyType()) 134 | 135 | 136 | class NumberType(AnyType): 137 | def __init__(self, name="Number"): 138 | super().__init__(name) 139 | self.supertypes.append(AnyType()) 140 | 141 | 142 | class IntegerType(NumberType): 143 | def __init__(self, name="Int"): 144 | super().__init__(name) 145 | self.supertypes.append(NumberType()) 146 | 147 | 148 | class BigIntegerType(NumberType): 149 | def __init__(self, name="BigInteger"): 150 | super().__init__(name) 151 | self.supertypes.append(NumberType()) 152 | 153 | 154 | class ShortType(NumberType): 155 | def __init__(self, name="Short"): 156 | super().__init__(name) 157 | self.supertypes.append(NumberType()) 158 | 159 | 160 | class LongType(NumberType): 161 | def __init__(self, name="Long"): 162 | super().__init__(name) 163 | self.supertypes.append(NumberType()) 164 | 165 | 166 | class ByteType(NumberType): 167 | def __init__(self, name="Byte"): 168 | super().__init__(name) 169 | self.supertypes.append(NumberType()) 170 | 171 | 172 | class FloatType(NumberType): 173 | def __init__(self, name="Float"): 174 | super().__init__(name) 175 | self.supertypes.append(NumberType()) 176 | 177 | 178 | class DoubleType(NumberType): 179 | def __init__(self, name="Double"): 180 | super().__init__(name) 181 | self.supertypes.append(NumberType()) 182 | 183 | 184 | class BigDecimalType(NumberType): 185 | def __init__(self, name="BigDecimal"): 186 | super().__init__(name) 187 | self.supertypes.append(NumberType()) 188 | 189 | 190 | class CharType(AnyType): 191 | def __init__(self, name="Char"): 192 | super().__init__(name) 193 | self.supertypes.append(AnyType()) 194 | 195 | 196 | class StringType(AnyType): 197 | def __init__(self, name="String"): 198 | super().__init__(name) 199 | self.supertypes.append(AnyType()) 200 | 201 | 202 | class BooleanType(AnyType): 203 | def __init__(self, name="Boolean"): 204 | super().__init__(name) 205 | self.supertypes.append(AnyType()) 206 | 207 | 208 | class ArrayType(TypeConstructor): 209 | def __init__(self, name="Array"): 210 | super().__init__(name, [TypeParameter("T")]) 211 | self.supertypes.append(AnyType()) 212 | 213 | 214 | class FunctionType(TypeConstructor): 215 | def __init__(self, nr_type_parameters: int): 216 | name = "Function" + str(nr_type_parameters) 217 | type_parameters = [ 218 | TypeParameter("A" + str(i)) 219 | for i in range(1, nr_type_parameters + 1) 220 | ] + [TypeParameter("R")] 221 | self.nr_type_parameters = nr_type_parameters 222 | super().__init__(name, type_parameters) 223 | self.supertypes.append(AnyType()) 224 | 225 | 226 | ### WARNING: use them only for testing ### 227 | Any = AnyType() 228 | Nothing = NothingType() 229 | Void = VoidType() 230 | Number = NumberType() 231 | Integer = IntegerType() 232 | Short = ShortType() 233 | Long = LongType() 234 | Byte = ByteType() 235 | Float = FloatType() 236 | Double = DoubleType() 237 | BigDecimal = BigDecimalType() 238 | BigInteger = BigIntegerType() 239 | Char = CharType() 240 | String = StringType() 241 | Boolean = BooleanType() 242 | Array = ArrayType() 243 | NonNothingTypes = [Any, Number, Integer, Short, Long, Byte, Float, 244 | Double, Char, String, Boolean, BigDecimal, BigInteger, 245 | Array] 246 | -------------------------------------------------------------------------------- /src/ir/context.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | from src import utils 4 | from src.ir import ast 5 | 6 | 7 | class Context(): 8 | 9 | def __init__(self): 10 | self._context = {} 11 | # A lookup from declarations to namespaces 12 | self._namespaces = {} 13 | 14 | def _add_entity(self, namespace, entity, name, value): 15 | if namespace in self._context: 16 | self._context[namespace][entity][name] = value 17 | else: 18 | self._context[namespace] = { 19 | 'types': {}, 20 | 'funcs': {}, 21 | 'lambdas': {}, 22 | 'vars': {}, 23 | 'classes': {}, 24 | 'decls': OrderedDict() # Here we keep the declaration order 25 | } 26 | self._context[namespace][entity][name] = value 27 | self._namespaces[value] = namespace 28 | 29 | def _remove_entity(self, namespace, entity, name): 30 | if namespace not in self._context: 31 | return 32 | if name in self._context[namespace][entity]: 33 | decl = self._context[namespace][entity][name] 34 | if decl in self._namespaces: 35 | del self._namespaces[decl] 36 | del self._context[namespace][entity][name] 37 | 38 | def add_type(self, namespace, type_name, t): 39 | self._add_entity(namespace, 'types', type_name, t) 40 | 41 | def add_func(self, namespace, func_name, func): 42 | self._add_entity(namespace, 'funcs', func_name, func) 43 | self._add_entity(namespace, 'decls', func_name, func) 44 | 45 | def add_lambda(self, namespace, shadow_name, lmd): 46 | self._add_entity(namespace, 'lambdas', shadow_name, lmd) 47 | 48 | def add_var(self, namespace, var_name, var): 49 | self._add_entity(namespace, 'vars', var_name, var) 50 | self._add_entity(namespace, 'decls', var_name, var) 51 | 52 | def add_class(self, namespace, class_name, cls): 53 | self._add_entity(namespace, 'classes', class_name, cls) 54 | self._add_entity(namespace, 'decls', class_name, cls) 55 | 56 | def remove_type(self, namespace, type_name): 57 | self._remove_entity(namespace, 'types', type_name) 58 | 59 | def remove_var(self, namespace, var_name): 60 | self._remove_entity(namespace, 'vars', var_name) 61 | self._remove_entity(namespace, 'decls', var_name) 62 | 63 | def remove_func(self, namespace, func_name): 64 | self._remove_entity(namespace, 'funcs', func_name) 65 | self._remove_entity(namespace, 'decls', func_name) 66 | 67 | def remove_lambda(self, namespace, shadow_name): 68 | self._remove_entity(namespace, 'lambdas', shadow_name) 69 | 70 | def remove_class(self, namespace, class_name): 71 | self._remove_entity(namespace, 'classes', class_name) 72 | self._remove_entity(namespace, 'decls', class_name) 73 | 74 | def _get_declarations_glob(self, namespace, decl_type): 75 | decls = OrderedDict({}) 76 | namespaces = [(namespace[0],)] 77 | while namespaces: 78 | namespace = namespaces.pop() 79 | decl = self._context.get(namespace, {}).get(decl_type) 80 | if decl is not None: 81 | decls.update(decl) 82 | namespaces.extend(self.find_namespaces(namespace, True)) 83 | return decls 84 | 85 | def _get_declarations(self, namespace, decl_type, only_current, glob, none): 86 | len_namespace = len(namespace) 87 | assert len_namespace >= 1 88 | decls = {} 89 | if glob: 90 | decls = self._get_declarations_glob(namespace, decl_type) 91 | elif len_namespace == 1 or only_current: 92 | decls = self._context.get(namespace, {}).get(decl_type, {}) 93 | else: 94 | start = (namespace[0],) 95 | decls = OrderedDict(self._context.get(start, {}).get(decl_type) or {}) 96 | for ns in namespace[1:]: 97 | start = start + (ns,) 98 | decl = self._context.get(start, {}).get(decl_type) 99 | if decl is not None: 100 | decls.update(decl) 101 | if not none: 102 | # Do not return artificial nodes 103 | decls = {k: v for k, v in decls.items() if v is not None} 104 | return decls 105 | 106 | def find_namespaces(self, namespace, none): 107 | func_namespaces = [namespace + (fname,) 108 | for fname in self.get_funcs(namespace, True, none=none)] 109 | class_namespaces = [namespace + (cname,) 110 | for cname in self.get_classes(namespace, True, none=none)] 111 | return func_namespaces + class_namespaces 112 | 113 | def get_namespaces_decls(self, namespace, name, decl_type, glob=True): 114 | """Return a set of tuples of namespace, decl. Note that namespace 115 | includes the name of the decl. 116 | """ 117 | namespaces_decls = set() # Set of tuples of namespace, decl 118 | if glob: 119 | namespaces = [(namespace[0],)] 120 | else: 121 | namespaces = [namespace] 122 | while namespaces: 123 | namespace = namespaces.pop() 124 | decls = self._context.get(namespace, {}).get(decl_type) 125 | if decls is not None: 126 | for decl_name, decl in decls.items(): 127 | if decl_name == name: 128 | ns = namespace + (name,) 129 | namespaces_decls.add((ns, decl)) 130 | namespaces.extend(self.find_namespaces(namespace, none=False)) 131 | return namespaces_decls 132 | 133 | def get_decl(self, namespace, name): 134 | return self._context.get(namespace, {}).get('decls', {}).get( 135 | name, None) 136 | 137 | def get_lambda(self, namespace, name): 138 | return self._context.get(namespace, {}).get('lambdas', {}).get( 139 | name, None) 140 | 141 | def get_types(self, namespace, only_current=False, glob=False, none=False): 142 | return self._get_declarations(namespace, 'types', only_current, glob, none) 143 | 144 | def get_funcs(self, namespace, only_current=False, glob=False, none=False): 145 | return self._get_declarations(namespace, 'funcs', only_current, glob, none) 146 | 147 | def get_lambdas(self, namespace, only_current=False, glob=False, none=False): 148 | return self._get_declarations(namespace, 'lambdas', only_current, glob, none) 149 | 150 | def get_vars(self, namespace, only_current=False, glob=False, none=False): 151 | return self._get_declarations(namespace, 'vars', only_current, glob, none) 152 | 153 | def get_classes(self, namespace, only_current=False, glob=False, none=False): 154 | return self._get_declarations(namespace, 'classes', only_current, glob, none) 155 | 156 | def get_declarations(self, namespace, only_current=False, glob=False, none=False): 157 | return self._get_declarations(namespace, 'decls', only_current, glob, none) 158 | 159 | def remove_namespace(self, namespace): 160 | if namespace in self._context: 161 | self._context.pop(namespace) 162 | 163 | def get_declarations_in(self, namespace): 164 | decls = {} 165 | for ns, entities in self._context.items(): 166 | if utils.prefix_lst(namespace, ns): 167 | decls[ns] = entities['decls'] 168 | return decls 169 | 170 | def get_decl_type(self, namespace, name): 171 | return type(self.get_decl(namespace, name)) 172 | 173 | def get_namespace(self, decl): 174 | return self._namespaces.get(decl, None) 175 | 176 | def get_parent(self, namespace): 177 | if len(namespace) < 2: 178 | return None 179 | parent_namespace = namespace[:-1] 180 | return self.get_decl(parent_namespace[:-1], parent_namespace[-1]) 181 | 182 | def get_parent_class(self, namespace): 183 | parent = self.get_parent(namespace) 184 | if parent is None and not (len(namespace) > 2 and 185 | 'lambda_' in namespace[-2]): 186 | return None 187 | if isinstance(parent, ast.ClassDeclaration): 188 | return parent 189 | return self.get_parent_class(namespace[:-1]) 190 | 191 | 192 | def get_decl(context, namespace, decl_name: str, limit=None): 193 | """ 194 | We search the context for a declaration with the given name (`decl_name`). 195 | 196 | The search begins from the given namespace `namespace` up to the namespace 197 | given by `limit`. 198 | """ 199 | def stop_cond(ns): 200 | # If 'limit' is provided, we search the given declaration 'node' 201 | # up to a certain namespace. 202 | return (len(ns) 203 | if limit is None 204 | else utils.prefix_lst(limit, ns)) 205 | 206 | while stop_cond(namespace): 207 | decls = context.get_declarations(namespace, True) 208 | decl = decls.get(decl_name) 209 | if decl: 210 | return namespace, decl 211 | namespace = namespace[:-1] 212 | return None 213 | -------------------------------------------------------------------------------- /src/ir/keywords.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | Keywords = Enum('Keywords', 'public final static override') 4 | -------------------------------------------------------------------------------- /src/ir/kotlin_types.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=abstract-method, useless-super-delegation,too-many-ancestors 2 | import src.ir.types as tp 3 | 4 | import src.ir.builtins as bt 5 | 6 | 7 | class KotlinBuiltinFactory(bt.BuiltinFactory): 8 | def get_language(self): 9 | return "kotlin" 10 | 11 | def get_builtin(self): 12 | return KotlinBuiltin 13 | 14 | def get_void_type(self): 15 | return UnitType() 16 | 17 | def get_any_type(self): 18 | return AnyType() 19 | 20 | def get_number_type(self): 21 | return NumberType() 22 | 23 | def get_integer_type(self): 24 | return IntegerType() 25 | 26 | def get_byte_type(self): 27 | return ByteType() 28 | 29 | def get_short_type(self): 30 | return ShortType() 31 | 32 | def get_long_type(self): 33 | return LongType() 34 | 35 | def get_float_type(self): 36 | return FloatType() 37 | 38 | def get_double_type(self): 39 | return DoubleType() 40 | 41 | def get_big_decimal_type(self): 42 | return DoubleType() 43 | 44 | def get_big_integer_type(self): 45 | # FIXME 46 | return IntegerType() 47 | 48 | def get_boolean_type(self): 49 | return BooleanType() 50 | 51 | def get_char_type(self): 52 | return CharType() 53 | 54 | def get_string_type(self): 55 | return StringType() 56 | 57 | def get_array_type(self): 58 | return ArrayType() 59 | 60 | def get_function_type(self, nr_parameters=0): 61 | return FunctionType(nr_parameters) 62 | 63 | def get_nothing(self): 64 | return NothingType() 65 | 66 | def get_non_nothing_types(self): 67 | types = super().get_non_nothing_types() 68 | types.extend([ 69 | DoubleArray, 70 | FloatArray, 71 | LongArray, 72 | IntegerArray, 73 | ShortArray, 74 | ByteArray, 75 | CharArray, 76 | BooleanArray 77 | ]) 78 | return types 79 | 80 | 81 | class KotlinBuiltin(tp.Builtin): 82 | def __str__(self): 83 | return str(self.name) + "(kotlin-builtin)" 84 | 85 | def is_primitive(self): 86 | return False 87 | 88 | 89 | class AnyType(KotlinBuiltin, ): 90 | def __init__(self, name="Any"): 91 | super().__init__(name) 92 | 93 | def get_builtin_type(self): 94 | return bt.Any 95 | 96 | 97 | class NothingType(KotlinBuiltin): 98 | def __init__(self, name="Nothing"): 99 | super().__init__(name) 100 | 101 | def is_subtype(self, other): 102 | return True 103 | 104 | def get_builtin_type(self): 105 | return bt.Nothing 106 | 107 | 108 | class UnitType(AnyType): 109 | def __init__(self, name="Unit"): 110 | super().__init__(name) 111 | self.supertypes.append(AnyType()) 112 | 113 | def get_builtin_type(self): 114 | return bt.Void 115 | 116 | 117 | class NumberType(AnyType): 118 | def __init__(self, name="Number"): 119 | super().__init__(name) 120 | self.supertypes.append(AnyType()) 121 | 122 | def get_builtin_type(self): 123 | return bt.Number 124 | 125 | 126 | class IntegerType(NumberType): 127 | def __init__(self, name="Int"): 128 | super().__init__(name) 129 | self.supertypes.append(NumberType()) 130 | 131 | def get_builtin_type(self): 132 | return bt.Integer 133 | 134 | 135 | class ShortType(NumberType): 136 | def __init__(self, name="Short"): 137 | super().__init__(name) 138 | self.supertypes.append(NumberType()) 139 | 140 | def get_builtin_type(self): 141 | return bt.Short 142 | 143 | 144 | class LongType(NumberType): 145 | def __init__(self, name="Long"): 146 | super().__init__(name) 147 | self.supertypes.append(NumberType()) 148 | 149 | def get_builtin_type(self): 150 | return bt.Long 151 | 152 | 153 | class ByteType(NumberType): 154 | def __init__(self, name="Byte"): 155 | super().__init__(name) 156 | self.supertypes.append(NumberType()) 157 | 158 | def get_builtin_type(self): 159 | return bt.Byte 160 | 161 | 162 | class FloatType(NumberType): 163 | def __init__(self, name="Float"): 164 | super().__init__(name) 165 | self.supertypes.append(NumberType()) 166 | 167 | def get_builtin_type(self): 168 | return bt.Float 169 | 170 | 171 | class DoubleType(NumberType): 172 | def __init__(self, name="Double"): 173 | super().__init__(name) 174 | self.supertypes.append(NumberType()) 175 | 176 | def get_builtin_type(self): 177 | return bt.Double 178 | 179 | 180 | class CharType(AnyType): 181 | def __init__(self, name="Char"): 182 | super().__init__(name) 183 | self.supertypes.append(AnyType()) 184 | 185 | def get_builtin_type(self): 186 | return bt.Char 187 | 188 | 189 | class StringType(AnyType): 190 | def __init__(self, name="String"): 191 | super().__init__(name) 192 | self.supertypes.append(AnyType()) 193 | 194 | def get_builtin_type(self): 195 | return bt.String 196 | 197 | 198 | class BooleanType(AnyType): 199 | def __init__(self, name="Boolean"): 200 | super().__init__(name) 201 | self.supertypes.append(AnyType()) 202 | 203 | def get_builtin_type(self): 204 | return bt.Boolean 205 | 206 | 207 | class ArrayType(tp.TypeConstructor, AnyType): 208 | def __init__(self, name="Array"): 209 | # In Kotlin, arrays are invariant. 210 | super().__init__(name, [tp.TypeParameter("T")]) 211 | self.supertypes.append(AnyType()) 212 | 213 | 214 | class SpecializedArrayType(tp.TypeConstructor, AnyType): 215 | def __init__(self, name="Array"): 216 | # In Kotlin, arrays are invariant. 217 | super().__init__(name, [tp.TypeParameter("T")]) 218 | self.supertypes.append(AnyType()) 219 | 220 | 221 | class FunctionType(tp.TypeConstructor, AnyType): 222 | def __init__(self, nr_type_parameters: int): 223 | name = "Function" + str(nr_type_parameters) 224 | # We can have decl-variance in Kotlin 225 | type_parameters = [ 226 | tp.TypeParameter("A" + str(i), tp.Contravariant) 227 | for i in range(1, nr_type_parameters + 1) 228 | ] + [tp.TypeParameter("R", tp.Covariant)] 229 | self.nr_type_parameters = nr_type_parameters 230 | super().__init__(name, type_parameters) 231 | self.supertypes.append(AnyType()) 232 | 233 | 234 | ### WARNING: use them only for testing ### 235 | Any = AnyType() 236 | Nothing = NothingType() 237 | Unit = UnitType() 238 | Number = NumberType() 239 | Integer = IntegerType() 240 | Short = ShortType() 241 | Long = LongType() 242 | Byte = ByteType() 243 | Float = FloatType() 244 | Double = DoubleType() 245 | Char = CharType() 246 | String = StringType() 247 | Boolean = BooleanType() 248 | Array = ArrayType() 249 | 250 | # Specialized arrays, see https://kotlinlang.org/spec/type-system.html#array-types 251 | DoubleArray = SpecializedArrayType().new([Double]) 252 | FloatArray = SpecializedArrayType().new([Float]) 253 | LongArray = SpecializedArrayType().new([Long]) 254 | IntegerArray = SpecializedArrayType().new([Integer]) 255 | ShortArray = SpecializedArrayType().new([Short]) 256 | ByteArray = SpecializedArrayType().new([Byte]) 257 | CharArray = SpecializedArrayType().new([Char]) 258 | BooleanArray = SpecializedArrayType().new([Boolean]) 259 | 260 | NonNothingTypes = [Any, Number, Integer, Short, Long, Byte, Float, 261 | Double, Char, String, Boolean, Array, 262 | DoubleArray, FloatArray, LongArray, IntegerArray, 263 | ShortArray, ByteArray, CharArray, BooleanArray] 264 | -------------------------------------------------------------------------------- /src/ir/node.py: -------------------------------------------------------------------------------- 1 | class Node(): 2 | 3 | def accept(self, visitor): 4 | return visitor.visit(self) 5 | 6 | def children(self): 7 | raise NotImplementedError('children() must be implemented') 8 | 9 | def update_children(self, children): 10 | assert len(children) == len(self.children()), ( 11 | 'The number of the given children is not compatible' 12 | ' with the number of the node\'s children.') 13 | 14 | def is_bottom(self): 15 | return False 16 | -------------------------------------------------------------------------------- /src/ir/scala_types.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=abstract-method, useless-super-delegation,too-many-ancestors 2 | import src.ir.types as tp 3 | 4 | import src.ir.builtins as bt 5 | 6 | 7 | class ScalaBuiltinFactory(bt.BuiltinFactory): 8 | def get_language(self): 9 | return "scala" 10 | 11 | def get_builtin(self): 12 | return ScalaBuiltin 13 | 14 | def get_void_type(self): 15 | return UnitType() 16 | 17 | def get_any_type(self): 18 | return AnyType() 19 | 20 | def get_anyref_type(self): 21 | return AnyRefType() 22 | 23 | def get_number_type(self): 24 | return NumberType() 25 | 26 | def get_integer_type(self, primitive=False): 27 | return IntegerType() 28 | 29 | def get_byte_type(self, primitive=False): 30 | return ByteType() 31 | 32 | def get_short_type(self, primitive=False): 33 | return ShortType() 34 | 35 | def get_long_type(self, primitive=False): 36 | return LongType() 37 | 38 | def get_float_type(self, primitive=False): 39 | return FloatType() 40 | 41 | def get_double_type(self, primitive=False): 42 | return DoubleType() 43 | 44 | def get_big_decimal_type(self): 45 | return DoubleType() 46 | 47 | def get_big_integer_type(self): 48 | return IntegerType() 49 | 50 | def get_boolean_type(self, primitive=False): 51 | return BooleanType() 52 | 53 | def get_char_type(self, primitive=False): 54 | return CharType() 55 | 56 | def get_string_type(self): 57 | return StringType() 58 | 59 | def get_array_type(self): 60 | return ArrayType() 61 | 62 | def get_function_type(self, nr_parameters=0): 63 | return FunctionType(nr_parameters) 64 | 65 | def get_nothing(self): 66 | return NothingType() 67 | 68 | def get_non_nothing_types(self): 69 | types = super().get_non_nothing_types() 70 | types.extend([ 71 | SeqType() 72 | ]) 73 | return types 74 | 75 | 76 | class ScalaBuiltin(tp.Builtin): 77 | def __str__(self): 78 | return str(self.name) + "(scala-builtin)" 79 | 80 | def is_primitive(self): 81 | return False 82 | 83 | 84 | class AnyType(ScalaBuiltin): 85 | def __init__(self, name="Any"): 86 | super().__init__(name) 87 | 88 | def get_builtin_type(self): 89 | return bt.Any 90 | 91 | 92 | class AnyRefType(AnyType): 93 | def __init__(self, name="AnyRef"): 94 | super().__init__(name) 95 | self.supertypes.append(AnyType()) 96 | 97 | 98 | class NothingType(ScalaBuiltin): 99 | def __init__(self, name="Nothing"): 100 | super().__init__(name) 101 | 102 | def is_subtype(self, other): 103 | return True 104 | 105 | def get_builtin_type(self): 106 | return bt.Nothing 107 | 108 | 109 | class UnitType(AnyType): 110 | def __init__(self, name="Unit"): 111 | super().__init__(name) 112 | self.supertypes.append(AnyType()) 113 | 114 | def get_builtin_type(self): 115 | return bt.Void 116 | 117 | 118 | class NumberType(AnyRefType): 119 | def __init__(self, name="Number"): 120 | super().__init__(name) 121 | self.supertypes.append(AnyRefType()) 122 | 123 | def get_builtin_type(self): 124 | return bt.Number 125 | 126 | 127 | class IntegerType(AnyType): 128 | def __init__(self, name="Int"): 129 | super().__init__(name) 130 | self.supertypes.append(AnyType()) 131 | 132 | def get_builtin_type(self): 133 | return bt.Integer 134 | 135 | 136 | class ShortType(AnyType): 137 | def __init__(self, name="Short"): 138 | super().__init__(name) 139 | self.supertypes.append(AnyType()) 140 | 141 | def get_builtin_type(self): 142 | return bt.Short 143 | 144 | 145 | class LongType(AnyType): 146 | def __init__(self, name="Long"): 147 | super().__init__(name) 148 | self.supertypes.append(AnyType()) 149 | 150 | def get_builtin_type(self): 151 | return bt.Long 152 | 153 | 154 | class ByteType(AnyType): 155 | def __init__(self, name="Byte"): 156 | super().__init__(name) 157 | self.supertypes.append(AnyType()) 158 | 159 | def get_builtin_type(self): 160 | return bt.Byte 161 | 162 | 163 | class FloatType(AnyType): 164 | def __init__(self, name="Float"): 165 | super().__init__(name) 166 | self.supertypes.append(AnyType()) 167 | 168 | def get_builtin_type(self): 169 | return bt.Float 170 | 171 | 172 | class DoubleType(AnyType): 173 | def __init__(self, name="Double"): 174 | super().__init__(name) 175 | self.supertypes.append(AnyType()) 176 | 177 | def get_builtin_type(self): 178 | return bt.Double 179 | 180 | 181 | class CharType(AnyType): 182 | def __init__(self, name="Char"): 183 | super().__init__(name) 184 | self.supertypes.append(AnyType()) 185 | 186 | def get_builtin_type(self): 187 | return bt.Char 188 | 189 | 190 | class StringType(AnyRefType): 191 | def __init__(self, name="String"): 192 | super().__init__(name) 193 | self.supertypes.append(AnyRefType()) 194 | 195 | def get_builtin_type(self): 196 | return bt.String 197 | 198 | 199 | class BooleanType(AnyType): 200 | def __init__(self, name="Boolean"): 201 | super().__init__(name) 202 | self.supertypes.append(AnyType()) 203 | 204 | def get_builtin_type(self): 205 | return bt.Boolean 206 | 207 | 208 | class ArrayType(tp.TypeConstructor, AnyRefType): 209 | def __init__(self, name="Array"): 210 | # In Scala, arrays are invariant. 211 | super().__init__(name, [tp.TypeParameter("T")]) 212 | self.supertypes.append(AnyRefType()) 213 | 214 | 215 | class SeqType(tp.TypeConstructor, AnyRefType): 216 | def __init__(self, name="Seq"): 217 | super().__init__(name, [tp.TypeParameter("T", variance=tp.Covariant)]) 218 | self.supertypes.append(AnyRefType()) 219 | 220 | 221 | class FunctionType(tp.TypeConstructor, AnyRefType): 222 | def __init__(self, nr_type_parameters: int): 223 | name = "Function" + str(nr_type_parameters) 224 | # We can have decl-variance in Scala 225 | type_parameters = [ 226 | tp.TypeParameter("A" + str(i), tp.Contravariant) 227 | for i in range(1, nr_type_parameters + 1) 228 | ] + [tp.TypeParameter("R", tp.Covariant)] 229 | self.nr_type_parameters = nr_type_parameters 230 | super().__init__(name, type_parameters) 231 | self.supertypes.append(AnyRefType()) 232 | 233 | 234 | class TupleType(tp.TypeConstructor, AnyRefType): 235 | def __init__(self, n_type_parameters: int): 236 | name = "Tuple" + str(n_type_parameters) 237 | # We can have decl-variance in Scala 238 | type_parameters = [ 239 | tp.TypeParameter("A" + str(i + 1), tp.Contravariant) 240 | for i in range(n_type_parameters) 241 | ] 242 | self.nr_type_parameters = n_type_parameters 243 | super().__init__(name, type_parameters) 244 | self.supertypes.append(AnyRefType()) 245 | 246 | 247 | Any = AnyType() 248 | Nothing = NothingType() 249 | Unit = UnitType() 250 | Number = NumberType() 251 | Integer = IntegerType() 252 | Short = ShortType() 253 | Long = LongType() 254 | Byte = ByteType() 255 | Float = FloatType() 256 | Double = DoubleType() 257 | Char = CharType() 258 | String = StringType() 259 | Boolean = BooleanType() 260 | Array = ArrayType() 261 | Seq = SeqType() 262 | AnyRef = AnyRefType() 263 | 264 | NonNothingTypes = [Any, Number, Integer, Short, Long, Byte, Float, 265 | Double, Char, String, Boolean, Array, Seq] 266 | -------------------------------------------------------------------------------- /src/ir/visitors.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast 2 | from src.ir import types 3 | 4 | 5 | class ASTVisitor(): 6 | 7 | def result(self): 8 | raise NotImplementedError('result() must be implemented') 9 | 10 | def visit(self, node): 11 | visitors = { 12 | ast.SuperClassInstantiation: self.visit_super_instantiation, 13 | ast.ClassDeclaration: self.visit_class_decl, 14 | types.TypeParameter: self.visit_type_param, 15 | ast.CallArgument: self.visit_call_argument, 16 | ast.FieldDeclaration: self.visit_field_decl, 17 | ast.VariableDeclaration: self.visit_var_decl, 18 | ast.ParameterDeclaration: self.visit_param_decl, 19 | ast.FunctionDeclaration: self.visit_func_decl, 20 | ast.Lambda: self.visit_lambda, 21 | ast.FunctionReference: self.visit_func_ref, 22 | ast.BottomConstant: self.visit_bottom_constant, 23 | ast.IntegerConstant: self.visit_integer_constant, 24 | ast.RealConstant: self.visit_real_constant, 25 | ast.CharConstant: self.visit_char_constant, 26 | ast.StringConstant: self.visit_string_constant, 27 | ast.ArrayExpr: self.visit_array_expr, 28 | ast.BooleanConstant: self.visit_boolean_constant, 29 | ast.Variable: self.visit_variable, 30 | ast.LogicalExpr: self.visit_logical_expr, 31 | ast.EqualityExpr: self.visit_equality_expr, 32 | ast.ComparisonExpr: self.visit_comparison_expr, 33 | ast.ArithExpr: self.visit_arith_expr, 34 | ast.Conditional: self.visit_conditional, 35 | ast.Is: self.visit_is, 36 | ast.New: self.visit_new, 37 | ast.FieldAccess: self.visit_field_access, 38 | ast.FunctionCall: self.visit_func_call, 39 | ast.Assignment: self.visit_assign, 40 | ast.Program: self.visit_program, 41 | ast.Block: self.visit_block, 42 | } 43 | visitor = visitors.get(node.__class__) 44 | if visitor is None: 45 | raise Exception( 46 | "Cannot find visitor for instance node " + str(node.__class__)) 47 | return visitor(node) 48 | 49 | def visit_program(self, node): 50 | raise NotImplementedError('visit_program() must be implemented') 51 | 52 | def visit_block(self, node): 53 | raise NotImplementedError('visit_block() must be implemented') 54 | 55 | def visit_super_instantiation(self, node): 56 | raise NotImplementedError( 57 | 'visit_super_instantiation() must be implemented') 58 | 59 | def visit_class_decl(self, node): 60 | raise NotImplementedError('visit_class_decl() must be implemented') 61 | 62 | def visit_type_param(self, node): 63 | raise NotImplementedError('visit_type_param() must be implemented') 64 | 65 | def visit_var_decl(self, node): 66 | raise NotImplementedError('visit_var_decl() must be implemented') 67 | 68 | def visit_call_argument(self, node): 69 | raise NotImplementedError('visit_call_argument() must be implemented') 70 | 71 | def visit_field_decl(self, node): 72 | raise NotImplementedError('visit_field_decl() must be implemented') 73 | 74 | def visit_param_decl(self, node): 75 | raise NotImplementedError('visit_param_decl() must be implemented') 76 | 77 | def visit_func_decl(self, node): 78 | raise NotImplementedError('visit_func_decl() must be implemented') 79 | 80 | def visit_lambda(self, node): 81 | raise NotImplementedError('visit_lambda() must be implemented') 82 | 83 | def visit_func_ref(self, node): 84 | raise NotImplementedError('visit_func_ref() must be implemented') 85 | 86 | def visit_bottom_constant(self, node): 87 | raise NotImplementedError("visit_bottom_constant() must be implemented") 88 | 89 | def visit_integer_constant(self, node): 90 | raise NotImplementedError( 91 | 'visit_integer_constant() must be implemented') 92 | 93 | def visit_real_constant(self, node): 94 | raise NotImplementedError('visit_real_constant() must be implemented') 95 | 96 | def visit_char_constant(self, node): 97 | raise NotImplementedError('visit_char_constant() must be implemented') 98 | 99 | def visit_string_constant(self, node): 100 | raise NotImplementedError( 101 | 'visit_string_constant() must be implemented') 102 | 103 | def visit_array_expr(self, node): 104 | raise NotImplementedError( 105 | 'visit_array_expr() must be implemented') 106 | 107 | def visit_boolean_constant(self, node): 108 | raise NotImplementedError( 109 | 'visit_boolean_constant() must be implemented') 110 | 111 | def visit_variable(self, node): 112 | raise NotImplementedError('visit_variable() must be implemented') 113 | 114 | def visit_logical_expr(self, node): 115 | raise NotImplementedError('visit_logical_expr() must be implemented') 116 | 117 | def visit_equality_expr(self, node): 118 | raise NotImplementedError('visit_equality_expr() must be implemented') 119 | 120 | def visit_comparison_expr(self, node): 121 | raise NotImplementedError( 122 | 'visit_comparison_expr() must be implemented') 123 | 124 | def visit_arith_expr(self, node): 125 | raise NotImplementedError('visit_arith_expr() must be implemented') 126 | 127 | def visit_conditional(self, node): 128 | raise NotImplementedError('visit_conditional() must be implemented') 129 | 130 | def visit_is(self, node): 131 | raise NotImplementedError('visit_is() must be implemented') 132 | 133 | def visit_new(self, node): 134 | raise NotImplementedError('visit_new() must be implemented') 135 | 136 | def visit_field_access(self, node): 137 | raise NotImplementedError('visit_field_access() must be implemented') 138 | 139 | def visit_func_call(self, node): 140 | raise NotImplementedError('visit_func_call() must be implemented') 141 | 142 | def visit_assign(self, node): 143 | raise NotImplementedError('visit_assign() must be implemented') 144 | 145 | 146 | class DefaultVisitor(ASTVisitor): 147 | 148 | def result(self): 149 | raise NotImplementedError('result() must be implemented') 150 | 151 | def _visit_node(self, node): 152 | children = node.children() 153 | for c in children: 154 | c.accept(self) 155 | 156 | def visit_program(self, node): 157 | return self._visit_node(node) 158 | 159 | def visit_block(self, node): 160 | return self._visit_node(node) 161 | 162 | def visit_super_instantiation(self, node): 163 | return self._visit_node(node) 164 | 165 | def visit_class_decl(self, node): 166 | return self._visit_node(node) 167 | 168 | def visit_type_param(self, node): 169 | return self._visit_node(node) 170 | 171 | def visit_var_decl(self, node): 172 | return self._visit_node(node) 173 | 174 | def visit_call_argument(self, node): 175 | return self._visit_node(node) 176 | 177 | def visit_field_decl(self, node): 178 | return self._visit_node(node) 179 | 180 | def visit_param_decl(self, node): 181 | return self._visit_node(node) 182 | 183 | def visit_func_decl(self, node): 184 | return self._visit_node(node) 185 | 186 | def visit_lambda(self, node): 187 | return self._visit_node(node) 188 | 189 | def visit_func_ref(self, node): 190 | return self._visit_node(node) 191 | 192 | def visit_bottom_constant(self, node): 193 | return self._visit_node(node) 194 | 195 | def visit_integer_constant(self, node): 196 | return self._visit_node(node) 197 | 198 | def visit_real_constant(self, node): 199 | return self._visit_node(node) 200 | 201 | def visit_char_constant(self, node): 202 | return self._visit_node(node) 203 | 204 | def visit_string_constant(self, node): 205 | return self._visit_node(node) 206 | 207 | def visit_array_expr(self, node): 208 | return self._visit_node(node) 209 | 210 | def visit_boolean_constant(self, node): 211 | return self._visit_node(node) 212 | 213 | def visit_variable(self, node): 214 | return self._visit_node(node) 215 | 216 | def visit_logical_expr(self, node): 217 | return self._visit_node(node) 218 | 219 | def visit_equality_expr(self, node): 220 | return self._visit_node(node) 221 | 222 | def visit_comparison_expr(self, node): 223 | return self._visit_node(node) 224 | 225 | def visit_arith_expr(self, node): 226 | return self._visit_node(node) 227 | 228 | def visit_conditional(self, node): 229 | return self._visit_node(node) 230 | 231 | def visit_is(self, node): 232 | return self._visit_node(node) 233 | 234 | def visit_new(self, node): 235 | return self._visit_node(node) 236 | 237 | def visit_field_access(self, node): 238 | return self._visit_node(node) 239 | 240 | def visit_func_call(self, node): 241 | return self._visit_node(node) 242 | 243 | def visit_assign(self, node): 244 | return self._visit_node(node) 245 | 246 | 247 | class DefaultVisitorUpdate(DefaultVisitor): 248 | 249 | def result(self): 250 | raise NotImplementedError('result() must be implemented') 251 | 252 | def _visit_node(self, node): 253 | children = node.children() 254 | new_children = [] 255 | for c in children: 256 | new_children.append(c.accept(self)) 257 | node.update_children(new_children) 258 | return node 259 | -------------------------------------------------------------------------------- /src/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/src/modules/__init__.py -------------------------------------------------------------------------------- /src/modules/logging.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from src.utils import mkdir 4 | 5 | 6 | class Logger(): 7 | def __init__(self, session, test_directory, iteration, name, number, 8 | stdout=False): 9 | self.session = session 10 | self.test_directory = test_directory 11 | self.iteration = iteration 12 | self.transformation_name = name 13 | self.transformation_number = number 14 | self.stdout = stdout 15 | if not self.stdout: 16 | self.directory = os.path.join(self.test_directory, "logs") 17 | mkdir(self.directory) 18 | self.filename = os.path.join(self.directory, str(self.iteration)) 19 | 20 | def log_info(self): 21 | msg = "\n{}\nTransformation name:{}\nTransformation No: {}\n\n".format( 22 | 10*"=", 23 | self.transformation_name, 24 | self.transformation_number 25 | ) 26 | self.log(msg) 27 | 28 | def log(self, msg): 29 | if self.stdout: 30 | print(msg) 31 | else: 32 | with open(self.filename, 'a') as out: 33 | out.write(str(msg)) 34 | out.write('\n') 35 | 36 | 37 | def log(logger: Logger, msg: str): 38 | if logger is not None: 39 | logger.log(msg) 40 | -------------------------------------------------------------------------------- /src/modules/processor.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=too-few-public-methods 2 | import sys 3 | 4 | from src.generators.generator import Generator 5 | from src.transformations.type_erasure import TypeErasure 6 | from src.transformations.type_overwriting import TypeOverwriting 7 | from src.utils import random, read_lines, load_program 8 | from src.modules.logging import Logger 9 | 10 | 11 | class ProgramProcessor(): 12 | 13 | # Correctness-preserving transformations 14 | CP_TRANSFORMATIONS = { 15 | 'TypeErasure': TypeErasure, 16 | } 17 | 18 | # Non correctness-preserving transformations 19 | NCP_TRANSFORMATIONS = { 20 | 'TypeOverwriting': TypeOverwriting, 21 | } 22 | 23 | def __init__(self, proc_id, args): 24 | self.proc_id = proc_id 25 | self.args = args 26 | self.transformations = [ 27 | ProgramProcessor.CP_TRANSFORMATIONS[t] 28 | for t in self.args.transformation_types 29 | ] 30 | self.ncp_transformations = list( 31 | ProgramProcessor.NCP_TRANSFORMATIONS.values()) 32 | self.transformation_schedule = self._get_transformation_schedule() 33 | self.current_transformation = 0 34 | 35 | def _apply_transformation(self, transformation_cls, 36 | transformation_number, program): 37 | if self.args.log: 38 | logger = Logger(self.args.name, self.args.test_directory, 39 | self.proc_id, transformation_cls.get_name(), 40 | transformation_number) 41 | else: 42 | logger = None 43 | transformer = transformation_cls( 44 | program, self.args.language, logger, 45 | self.args.options[transformation_cls.get_name()]) 46 | if self.args.debug: 47 | print("Transformation " + str(transformation_number) + ": " + 48 | transformer.get_name()) 49 | transformer.transform() 50 | program = transformer.result() 51 | return program, transformer 52 | 53 | def _get_transformation_schedule(self): 54 | if self.args.transformations is not None: 55 | # Randomly generate a transformation schedule. 56 | return [ 57 | random.choice(self.transformations) 58 | for i in range(self.args.transformations) 59 | ] 60 | # Get transformation schedule from file. 61 | lines = read_lines(self.args.transformation_schedule) 62 | schedule = [] 63 | for line in lines: 64 | transformation = self.CP_TRANSFORMATIONS.get(line) 65 | if transformation is None: 66 | sys.exit( 67 | "Transformation " + line 68 | + " found in schedule is not valid.") 69 | schedule.append(transformation) 70 | return schedule 71 | 72 | def get_program(self): 73 | if self.args.replay: 74 | if self.args.debug: 75 | print("\nLoading program: " + self.args.replay) 76 | # Load the program from the given file. 77 | return load_program(self.args.replay), True 78 | else: 79 | # Generate a new program. 80 | return self.generate_program() 81 | 82 | def get_transformations(self): 83 | return self.transformation_schedule[:self.current_transformation] 84 | 85 | def generate_program(self): 86 | if self.args.debug: 87 | print("\nGenerating program: " + str(self.proc_id)) 88 | if self.args.log: 89 | logger = Logger(self.args.name, self.args.test_directory, 90 | self.proc_id, "Generator", 91 | self.proc_id) 92 | else: 93 | logger = None 94 | generator = Generator( 95 | language=self.args.language, 96 | logger=logger, 97 | options=self.args.options["Generator"]) 98 | program = generator.generate() 99 | return program, True 100 | 101 | def can_transform(self): 102 | return self.current_transformation < len(self.transformation_schedule) 103 | 104 | def transform_program(self, program): 105 | transformer_cls = ( 106 | self.transformation_schedule[self.current_transformation]) 107 | program, transformer = self._apply_transformation( 108 | transformer_cls, self.current_transformation + 1, program) 109 | self.current_transformation += 1 110 | if not transformer.is_transformed: 111 | return None 112 | return program, transformer.preserve_correctness() 113 | 114 | def inject_fault(self, program): 115 | transformer_cls = random.choice(self.ncp_transformations) 116 | program, transformer = self._apply_transformation( 117 | transformer_cls, self.current_transformation + 1, program) 118 | self.current_transformation += 1 119 | if not transformer.is_transformed: 120 | return None 121 | return program, transformer.error_injected 122 | -------------------------------------------------------------------------------- /src/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/src/resources/__init__.py -------------------------------------------------------------------------------- /src/resources/groovy_keywords: -------------------------------------------------------------------------------- 1 | abstract 2 | Array 3 | as 4 | assert 5 | BigDecimal 6 | boolean 7 | break 8 | byte 9 | case 10 | catch 11 | char 12 | character 13 | class 14 | Closure 15 | closure 16 | const 17 | continue 18 | Date 19 | DATEFORMAT_DEFAULT 20 | DAYS 21 | def 22 | default 23 | DEFAULTLANG 24 | do 25 | double 26 | else 27 | enum 28 | Exception 29 | extends 30 | false 31 | final 32 | finalize 33 | finally 34 | float 35 | for 36 | Geocode 37 | goto 38 | HOURS 39 | if 40 | implements 41 | import 42 | in 43 | instanceof 44 | int 45 | integer 46 | interface 47 | InvokerHelper 48 | long 49 | Math 50 | MILLISECONDS 51 | MINUTES 52 | MONTHS 53 | native 54 | new 55 | notify 56 | null 57 | Object 58 | package 59 | private 60 | protected 61 | public 62 | return 63 | Rowbinding 64 | SECONDS 65 | Set 66 | short 67 | Short 68 | static 69 | strictfp 70 | string 71 | super 72 | switch 73 | synchronized 74 | this 75 | threadsafe 76 | throw 77 | throws 78 | transient 79 | true 80 | try 81 | void 82 | volatile 83 | WEEKS 84 | while 85 | YEARS 86 | wait 87 | -------------------------------------------------------------------------------- /src/resources/scala_keywords: -------------------------------------------------------------------------------- 1 | abstract 2 | case 3 | catch 4 | class 5 | console 6 | def 7 | do 8 | either 9 | else 10 | extends 11 | false 12 | final 13 | finally 14 | for 15 | forSome 16 | given 17 | if 18 | implicit 19 | import 20 | iterator 21 | lazy 22 | macro 23 | match 24 | math 25 | new 26 | null 27 | object 28 | override 29 | package 30 | private 31 | protected 32 | proxy 33 | readable 34 | return 35 | sealed 36 | some 37 | super 38 | then 39 | this 40 | throw 41 | trait 42 | try 43 | true 44 | type 45 | val 46 | var 47 | while 48 | with 49 | yield 50 | -------------------------------------------------------------------------------- /src/transformations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/src/transformations/__init__.py -------------------------------------------------------------------------------- /src/transformations/base.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=protected-access,dangerous-default-value 2 | import time 3 | import threading 4 | import sys 5 | 6 | from src.ir.visitors import DefaultVisitorUpdate 7 | 8 | 9 | def timeout_function(log, entity, timeouted): 10 | log("{}: took too long (timeout)".format(entity)) 11 | sys.stderr.flush() # Python 3 stderr is likely buffered. 12 | timeouted[0] = True 13 | 14 | 15 | def visitor_logging_and_timeout_with_args(*args): 16 | def wrap_visitor_func(visitor_func): 17 | def wrapped_visitor(self, node): 18 | if len(args) > 0: 19 | self.log(*args) 20 | transformation_name = self.__class__.__name__ 21 | visitor_name = visitor_func.__name__ 22 | entity = transformation_name + "-" + visitor_name 23 | self.log("{}: Begin".format(entity)) 24 | 25 | timeouted = [False] 26 | start = time.time() 27 | try: 28 | timer = threading.Timer( 29 | self.timeout, timeout_function, 30 | args=[self.log, entity, timeouted]) 31 | timer.start() 32 | new_node = visitor_func(self, node) 33 | finally: 34 | timer.cancel() 35 | end = time.time() 36 | if timeouted[0]: 37 | new_node = node 38 | self.log("{}: {} elapsed time".format(entity, str(end - start))) 39 | self.log("{}: End".format(entity)) 40 | return new_node 41 | return wrapped_visitor 42 | return wrap_visitor_func 43 | 44 | 45 | def change_namespace(visit): 46 | def inner(self, node): 47 | initial_namespace = self._namespace 48 | self._namespace += (node.name,) 49 | new_node = visit(self, node) 50 | self._namespace = initial_namespace 51 | return new_node 52 | return inner 53 | 54 | 55 | def change_depth(visit): 56 | def inner(self, node): 57 | initial_depth = self.depth 58 | self.depth += 1 59 | new_node = visit(self, node) 60 | self.depth = initial_depth 61 | return new_node 62 | return inner 63 | 64 | 65 | class Transformation(DefaultVisitorUpdate): 66 | CORRECTNESS_PRESERVING = None 67 | 68 | def __init__(self, program, language, logger=None, options={}): 69 | assert program is not None, 'The given program must not be None' 70 | self.is_transformed = False 71 | self.language = language 72 | self.program = program 73 | self.types = self.program.get_types() 74 | self.logger = logger 75 | self.options = options 76 | self.timeout = options.get("timeout", 600) 77 | if self.logger: 78 | self.logger.log_info() 79 | 80 | def transform(self): 81 | self.program = self.visit(self.program) 82 | 83 | def result(self): 84 | return self.program 85 | 86 | def log(self, msg): 87 | if self.logger is None: 88 | pass 89 | else: 90 | self.logger.log(msg) 91 | 92 | @classmethod 93 | def get_name(cls): 94 | return cls.__name__ 95 | 96 | @classmethod 97 | def preserve_correctness(cls): 98 | return cls.CORRECTNESS_PRESERVING 99 | 100 | @visitor_logging_and_timeout_with_args() 101 | def visit_program(self, node): 102 | return super().visit_program(node) 103 | -------------------------------------------------------------------------------- /src/transformations/type_erasure.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=too-many-instance-attributes,dangerous-default-value 2 | from copy import copy 3 | import itertools 4 | 5 | from src.ir import ast 6 | from src.transformations.base import Transformation, change_namespace 7 | from src.analysis import type_dependency_analysis as tda 8 | 9 | 10 | class TypeErasure(Transformation): 11 | CORRECTNESS_PRESERVING = True 12 | 13 | def __init__(self, program, language, logger=None, options={}): 14 | super().__init__(program, language, logger, options) 15 | self._namespace = ast.GLOBAL_NAMESPACE 16 | self.max_combinations = options.get( 17 | 'max_combinations', 500000 18 | ) 19 | self.global_type_graph = {} 20 | 21 | @change_namespace 22 | def visit_class_decl(self, node): 23 | return super().visit_class_decl(node) 24 | 25 | def visit_var_decl(self, node): 26 | if self._namespace != ast.GLOBAL_NAMESPACE: 27 | return super().visit_var_decl(node) 28 | 29 | # We need this analysis, we have to include type information of global 30 | # variables. 31 | t_an = tda.TypeDependencyAnalysis(self.program, 32 | namespace=ast.GLOBAL_NAMESPACE) 33 | t_an.visit(node) 34 | self.global_type_graph.update(t_an.result()) 35 | return node 36 | 37 | @change_namespace 38 | def visit_func_decl(self, node): 39 | t_an = tda.TypeDependencyAnalysis(self.program, 40 | namespace=self._namespace[:-1], 41 | type_graph=None) 42 | t_an.visit(node) 43 | type_graph = t_an.result() 44 | type_graph.update(self.global_type_graph) 45 | omittable_nodes = [n for n in type_graph.keys() 46 | if n.is_omittable()] 47 | omittable_nodes = [ 48 | n 49 | for n in omittable_nodes 50 | if tda.is_combination_feasible(type_graph, (n,)) 51 | ] 52 | # We compute the powerset of omittable nodes. 53 | combinations = itertools.chain.from_iterable( 54 | itertools.combinations(omittable_nodes, r) 55 | for r in range(len(omittable_nodes), 0, -1) 56 | ) 57 | for i, combination in enumerate(combinations): 58 | if self.max_combinations and i > self.max_combinations: 59 | break 60 | c_type_graph = copy(type_graph) 61 | # We are trying to find the maximal combination that is feasible. 62 | if tda.is_combination_feasible(c_type_graph, combination): 63 | for g_node in combination: 64 | self.is_transformed = True 65 | if isinstance(g_node, tda.DeclarationNode): 66 | g_node.decl.omit_type() 67 | if isinstance(g_node, 68 | tda.TypeConstructorInstantiationCallNode): 69 | g_node.t.can_infer_type_args = True 70 | break 71 | return node 72 | -------------------------------------------------------------------------------- /src/transformations/type_overwriting.py: -------------------------------------------------------------------------------- 1 | from src import utils 2 | from src.ir import ast, type_utils as tu, types as tp 3 | from src.transformations.base import Transformation, change_namespace 4 | from src.analysis import type_dependency_analysis as tda 5 | 6 | 7 | class TypeOverwriting(Transformation): 8 | CORRECTNESS_PRESERVING = False 9 | 10 | def __init__(self, program, language, logger=None, options={}): 11 | super().__init__(program, language, logger, options) 12 | self._namespace = ast.GLOBAL_NAMESPACE 13 | self.global_type_graph = {} 14 | self.types = program.get_types() 15 | self.bt_factory = program.bt_factory 16 | self.error_injected = None 17 | self._method_selection = True 18 | self._candidate_methods = [] 19 | self._selected_method = None 20 | 21 | def visit_program(self, node): 22 | super().visit_program(node) 23 | self._method_selection = False 24 | if not self._candidate_methods: 25 | return node 26 | self._selected_method = utils.random.choice(self._candidate_methods) 27 | return super().visit_program(node) 28 | 29 | @change_namespace 30 | def visit_class_decl(self, node): 31 | return super().visit_class_decl(node) 32 | 33 | def visit_var_decl(self, node): 34 | if self._namespace != ast.GLOBAL_NAMESPACE: 35 | return super().visit_var_decl(node) 36 | 37 | # We need this analysis, we have to include type information of global 38 | # variables. 39 | t_an = tda.TypeDependencyAnalysis(self.program, 40 | namespace=ast.GLOBAL_NAMESPACE) 41 | t_an.visit(node) 42 | self.global_type_graph.update(t_an.result()) 43 | return node 44 | 45 | def _add_candidate_method(self, node): 46 | t_an = tda.TypeDependencyAnalysis(self.program, 47 | namespace=self._namespace[:-1], 48 | type_graph=None) 49 | t_an.visit(node) 50 | type_graph = t_an.result() 51 | type_graph.update(self.global_type_graph) 52 | candidate_nodes = [ 53 | n 54 | for n in type_graph.keys() 55 | if n.is_omittable() and not ( 56 | isinstance(n, tda.DeclarationNode) and n.decl.name == tda.RET 57 | ) 58 | ] 59 | if not candidate_nodes: 60 | return node 61 | self._candidate_methods.append((self._namespace, candidate_nodes, 62 | type_graph)) 63 | return node 64 | 65 | @change_namespace 66 | def visit_func_decl(self, node): 67 | if self._method_selection: 68 | return self._add_candidate_method(node) 69 | namespace, candidate_nodes, type_graph = self._selected_method 70 | if namespace != self._namespace: 71 | return node 72 | 73 | # Generate a type that is irrelevant 74 | n = utils.random.choice(candidate_nodes) 75 | if isinstance(n, tda.TypeConstructorInstantiationCallNode): 76 | type_params = [ 77 | n.target for n in type_graph[n] 78 | if any(e.is_inferred() for e in type_graph[n.target]) 79 | ] 80 | if not type_params: 81 | return node 82 | type_param = utils.random.choice(type_params) 83 | node_type = n.t.get_type_variable_assignments()[type_param.t] 84 | old_type = node_type 85 | else: 86 | node_type = n.decl.get_type() 87 | old_type = node_type 88 | if node_type.name in ["Boolean", "String", "BigInteger"]: 89 | return node 90 | ir_type = tu.find_irrelevant_type(node_type, self.types, 91 | self.bt_factory) 92 | if ir_type is None: 93 | return node 94 | 95 | # Perform the mutation 96 | if isinstance(n, tda.DeclarationNode): 97 | if isinstance(n.decl, ast.VariableDeclaration): 98 | n.decl.var_type = ir_type 99 | else: 100 | n.decl.ret_type = ir_type 101 | n.decl.inferred_type = ir_type 102 | t = getattr(n, 't', None) 103 | if t is not None: 104 | type_parameters = ( 105 | t.t_constructor.type_parameters 106 | if isinstance(t, tp.ParameterizedType) 107 | else t.type_parameters 108 | ) 109 | if isinstance(n, tda.TypeConstructorInstantiationCallNode): 110 | indexes = { 111 | t_param: i 112 | for i, t_param in enumerate(type_parameters) 113 | } 114 | n.t.type_args[indexes[type_param.t]] = ir_type 115 | self.is_transformed = True 116 | self.error_injected = "{} expected but {} found in node {}".format( 117 | str(old_type), str(ir_type), n.node_id) 118 | return node 119 | -------------------------------------------------------------------------------- /src/translators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/src/translators/__init__.py -------------------------------------------------------------------------------- /src/translators/base.py: -------------------------------------------------------------------------------- 1 | from src.ir import types as tp 2 | from src.ir.visitors import ASTVisitor 3 | 4 | 5 | class BaseTranslator(ASTVisitor): 6 | 7 | def __init__(self, package=None, options={}): 8 | self.program = None 9 | self.package = package 10 | 11 | def result(self) -> str: 12 | if self.program is None: 13 | raise Exception('You have to translate the program first') 14 | return self.program 15 | 16 | def get_type_name(self, t: tp.Type) -> str: 17 | raise NotImplementedError('get_type_name() must be implemented') 18 | -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import random 3 | import string 4 | import pickle 5 | import os 6 | import sys 7 | 8 | 9 | class Singleton(type): 10 | _instances = {} 11 | 12 | def __call__(cls, *args, **kwargs): 13 | if cls not in cls._instances: 14 | cls._instances[cls] = super(Singleton, cls).__call__( 15 | *args, **kwargs) 16 | return cls._instances[cls] 17 | 18 | 19 | def prefix_lst(prefix, lst): 20 | return any(prefix == lst[:i] 21 | for i in range(1, len(prefix) + 1)) 22 | 23 | 24 | def is_number(string_var): 25 | try: 26 | float(string_var) 27 | return True 28 | except ValueError: 29 | return False 30 | 31 | 32 | def lst_get(lst, index=0, default=None): 33 | """Safely get an element from a list""" 34 | try: 35 | return lst[index] 36 | except IndexError: 37 | return default 38 | 39 | 40 | def leading_spaces(input_string): 41 | """Count leading spaces of a string""" 42 | return len(input_string) - len(input_string.lstrip(' ')) 43 | 44 | 45 | def add_string_at(input_string, substring, pos): 46 | """Add a substring to the given position of the string""" 47 | return input_string[:pos] + substring + input_string[pos:] 48 | 49 | 50 | def read_lines(path): 51 | lines = [] 52 | with open(path, 'r') as infile: 53 | for line in infile: 54 | lines.append(line.rstrip('\n')) 55 | return lines 56 | 57 | 58 | def mkdir(directory_name): 59 | """Safe mkdir 60 | """ 61 | try: 62 | os.makedirs(directory_name, exist_ok=True) 63 | except Exception as e: 64 | print(e) 65 | sys.exit(0) 66 | 67 | 68 | def fprint(text): 69 | """Full screen print""" 70 | try: 71 | terminal_width = os.get_terminal_size().columns 72 | print(text.center(int(terminal_width), "=")) 73 | except OSError: # This error may occur when run under cron 74 | print(text) 75 | 76 | 77 | def translate_program(translator, program): 78 | translator.visit(program) 79 | return translator.result() 80 | 81 | 82 | def load_program(path): 83 | with open(path, 'rb') as initial_bin: 84 | return pickle.load(initial_bin) 85 | 86 | 87 | def dump_program(path, program): 88 | with open(path, 'wb') as out: 89 | pickle.dump(program, out) 90 | 91 | 92 | def save_text(path, text): 93 | with open(path, 'w') as out: 94 | out.write(text) 95 | 96 | 97 | def path2set(path): 98 | if os.path.isfile(path): 99 | with open(path, 'r') as f: 100 | return { 101 | line.strip() 102 | for line in f.readlines() 103 | } 104 | else: 105 | return set() 106 | 107 | 108 | def get_reserved_words(resource_path, language): 109 | filename = "{}_keywords".format(language) 110 | path = os.path.join(resource_path, filename) 111 | return path2set(path) 112 | 113 | 114 | class RandomUtils(): 115 | 116 | resource_path = os.path.join(os.path.split(__file__)[0], "resources") 117 | 118 | WORD_POOL_LEN = 10000 119 | # Construct a random word pool of size 'WORD_POOL_LEN'. 120 | WORDS = set(random.sample( 121 | read_lines(os.path.join(resource_path, 'words')), WORD_POOL_LEN)) 122 | INITIAL_WORDS = set(WORDS) 123 | 124 | def __init__(self, seed=None): 125 | self.r = random.Random(seed) 126 | 127 | def reset_word_pool(self): 128 | self.WORDS = set(self.INITIAL_WORDS) 129 | 130 | def bool(self, prob=0.5): 131 | return self.r.random() < prob 132 | 133 | def word(self): 134 | word = self.r.choice(tuple(self.WORDS)) 135 | self.WORDS.remove(word) 136 | return word 137 | 138 | def remove_reserved_words(self, language): 139 | reserved_words = get_reserved_words(self.resource_path, language) 140 | self.INITIAL_WORDS = self.INITIAL_WORDS - reserved_words 141 | self.WORDS = self.WORDS - reserved_words 142 | 143 | def integer(self, min_int=0, max_int=10): 144 | return self.r.randint(min_int, max_int) 145 | 146 | def char(self): 147 | return self.r.choice(string.ascii_letters + string.digits) 148 | 149 | def choice(self, choices): 150 | return self.r.choice(choices) 151 | 152 | def sample(self, choices, k=None): 153 | k = k or self.integer(0, len(choices)) 154 | return self.r.sample(choices, k) 155 | 156 | def str(self, length=5): 157 | return ''.join(self.r.sample( 158 | string.ascii_letters + string.digits, length)) 159 | 160 | def caps(self, length=1, blacklist=None): 161 | blacklist = blacklist if blacklist is not None else [] 162 | while True: 163 | res = ''.join(self.r.sample(string.ascii_uppercase, length)) 164 | if res not in blacklist: 165 | return res 166 | 167 | def range(self, from_value, to_value): 168 | return range(0, self.integer(from_value, to_value)) 169 | 170 | 171 | random = RandomUtils() 172 | 173 | 174 | class IdGen(): 175 | def __init__(self): 176 | self._cache = defaultdict(lambda: 1) 177 | 178 | def get_node_id(self, node_id): 179 | if node_id not in self._cache: 180 | self._cache[node_id] 181 | return node_id, None 182 | else: 183 | value = self._cache[node_id] 184 | self._cache[node_id] += 1 185 | return node_id, str(value) 186 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/tests/__init__.py -------------------------------------------------------------------------------- /tests/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/tests/resources/__init__.py -------------------------------------------------------------------------------- /tests/resources/program1.py: -------------------------------------------------------------------------------- 1 | from src.ir.ast import * 2 | from src.ir.kotlin_types import * 3 | from src.ir.context import * 4 | 5 | # class Bam 6 | # 7 | # open class A(val x: String) { 8 | # open fun foo(y: String, z: String): bam { 9 | # val q: String = z 10 | # val x: String = q 11 | # return bar(y, "foo", q) 12 | # } 13 | # 14 | # open fun bar(arg: String, y: String, z: String): bam { 15 | # return bam(arg) 16 | # } 17 | # 18 | # open fun buz(): String { 19 | # return x 20 | # } 21 | # 22 | # open fun spam(): String { 23 | # return (if (true) { x } else { "foo" }) 24 | # } 25 | # } 26 | 27 | bam_cls = ClassDeclaration("Bam", []) 28 | 29 | xA_field = FieldDeclaration( 30 | "x", 31 | StringType(), 32 | is_final=True, 33 | override=False 34 | ) 35 | 36 | foo_y = ParameterDeclaration("y", StringType()) 37 | foo_z = ParameterDeclaration("z", StringType()) 38 | foo_q = VariableDeclaration("q", Variable("z"), var_type=StringType()) 39 | foo_x = VariableDeclaration("x", Variable("q"), var_type=StringType()) 40 | foo_func = FunctionDeclaration( 41 | "foo", 42 | params=[foo_y, foo_z], 43 | ret_type=bam_cls.get_type(), 44 | func_type=FunctionDeclaration.CLASS_METHOD, 45 | is_final=False, 46 | body=Block([ 47 | foo_q, 48 | foo_x, 49 | FunctionCall("bar", [Variable("y"), StringConstant("foo"), Variable("q")]) 50 | ]) 51 | ) 52 | 53 | bar_arg = ParameterDeclaration("arg", StringType()) 54 | bar_y = ParameterDeclaration("y", StringType()) 55 | bar_z = ParameterDeclaration("z", StringType()) 56 | bar_func = FunctionDeclaration( 57 | "bar", 58 | params=[ 59 | ParameterDeclaration("arg", StringType()), 60 | ParameterDeclaration("y", StringType()), 61 | ParameterDeclaration("z", StringType()) 62 | ], 63 | ret_type=bam_cls.get_type(), 64 | func_type=FunctionDeclaration.CLASS_METHOD, 65 | is_final=False, 66 | body=Block([ 67 | New(bam_cls.get_type(), [Variable("arg")]) 68 | 69 | ]) 70 | ) 71 | 72 | buz_func = FunctionDeclaration( 73 | "buz", 74 | params=[], 75 | ret_type=StringType(), 76 | func_type=FunctionDeclaration.CLASS_METHOD, 77 | is_final=False, 78 | body=Block([ 79 | Variable(xA_field.name) 80 | ]) 81 | ) 82 | 83 | spam_func = FunctionDeclaration( 84 | "spam", 85 | params=[], 86 | ret_type=StringType(), 87 | func_type=FunctionDeclaration.CLASS_METHOD, 88 | is_final=False, 89 | body=Block([ 90 | Conditional(BooleanConstant("true"), 91 | Block([Variable(xA_field.name)]), 92 | Block([StringConstant("foo")]), StringType()) 93 | ]) 94 | ) 95 | 96 | a_cls = ClassDeclaration( 97 | "A", 98 | superclasses=[], 99 | class_type=ClassDeclaration.REGULAR, 100 | is_final=False, 101 | fields=[xA_field], 102 | # functions=[foo_func, bar_func, buz_func] 103 | functions=[foo_func, bar_func, buz_func, spam_func] 104 | ) 105 | 106 | ctx = Context() 107 | ctx.add_class(GLOBAL_NAMESPACE, bam_cls.name, bam_cls) 108 | ctx.add_class(GLOBAL_NAMESPACE, a_cls.name, a_cls) 109 | ctx.add_var(GLOBAL_NAMESPACE + ('A',), 'x', xA_field) 110 | ctx.add_var(GLOBAL_NAMESPACE + ('A', 'foo'), 'y', foo_y) 111 | ctx.add_var(GLOBAL_NAMESPACE + ('A', 'foo'), 'z', foo_z) 112 | ctx.add_var(GLOBAL_NAMESPACE + ('A', 'foo'), 'q', foo_q) 113 | ctx.add_var(GLOBAL_NAMESPACE + ('A', 'foo'), 'x', foo_x) 114 | ctx.add_var(GLOBAL_NAMESPACE + ('A', 'bar'), 'arg', bar_arg) 115 | ctx.add_var(GLOBAL_NAMESPACE + ('A', 'bar'), 'y', bar_y) 116 | ctx.add_var(GLOBAL_NAMESPACE + ('A', 'bar'), 'z', bar_z) 117 | ctx.add_func(GLOBAL_NAMESPACE + ('A',), bar_func.name, bar_func) 118 | ctx.add_func(GLOBAL_NAMESPACE + ('A',), foo_func.name, foo_func) 119 | ctx.add_func(GLOBAL_NAMESPACE + ('A',), buz_func.name, buz_func) 120 | ctx.add_func(GLOBAL_NAMESPACE + ('A',), spam_func.name, spam_func) 121 | program = Program(ctx, language="kotlin") 122 | -------------------------------------------------------------------------------- /tests/resources/program10.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # open class First { 5 | # open fun foo() {} 6 | # } 7 | # 8 | # class Second(): First() { 9 | # override fun foo() {} 10 | # } 11 | # 12 | # fun foo(x: First) { 13 | # x.foo() 14 | # } 15 | # 16 | # fun bar() { 17 | # val y: Second = Second() 18 | # y.foo() 19 | # } 20 | # 21 | # class Third(val z: First) { 22 | # fun foo() { 23 | # val k = z 24 | # z.foo() 25 | # } 26 | # } 27 | 28 | first_foo = ast.FunctionDeclaration( 29 | "foo", 30 | params=[], 31 | ret_type=kt.Unit, 32 | body=ast.Block([]), 33 | func_type=ast.FunctionDeclaration.CLASS_METHOD, 34 | is_final=False 35 | ) 36 | cls_first = ast.ClassDeclaration( 37 | "First", 38 | [], 39 | ast.ClassDeclaration.REGULAR, 40 | fields=[], 41 | functions=[first_foo], 42 | is_final=False 43 | ) 44 | 45 | second_foo = ast.FunctionDeclaration( 46 | "foo", 47 | params=[], 48 | ret_type=kt.Unit, 49 | body=ast.Block([]), 50 | func_type=ast.FunctionDeclaration.CLASS_METHOD, 51 | is_final=True, 52 | override=True 53 | ) 54 | cls_second = ast.ClassDeclaration( 55 | "Second", 56 | [ast.SuperClassInstantiation(cls_first.get_type(), [])], 57 | ast.ClassDeclaration.REGULAR, 58 | fields=[], 59 | functions=[second_foo] 60 | ) 61 | 62 | foo_x = ast.ParameterDeclaration("x", cls_first.get_type()) 63 | fun_foo = ast.FunctionDeclaration( 64 | "foo", 65 | params=[foo_x], 66 | ret_type=kt.Unit, 67 | body=ast.Block([ast.FunctionCall("foo", [], ast.Variable("x"))]), 68 | func_type=ast.FunctionDeclaration.FUNCTION 69 | ) 70 | 71 | 72 | bar_y = ast.VariableDeclaration( 73 | "y", 74 | ast.New(cls_second.get_type(), []), 75 | var_type = cls_second.get_type() 76 | ) 77 | fun_bar = ast.FunctionDeclaration( 78 | "bar", 79 | params=[], 80 | ret_type=kt.Unit, 81 | body=ast.Block([ 82 | bar_y, 83 | ast.FunctionCall("foo", [], ast.Variable("y")) 84 | ]), 85 | func_type=ast.FunctionDeclaration.FUNCTION 86 | ) 87 | 88 | third_z = ast.FieldDeclaration( 89 | "z", 90 | cls_first.get_type() 91 | ) 92 | third_foo_k = ast.VariableDeclaration( 93 | "k", 94 | ast.Variable("z"), 95 | inferred_type=third_z.get_type() 96 | ) 97 | third_foo = ast.FunctionDeclaration( 98 | "foo", 99 | params=[], 100 | ret_type=kt.Unit, 101 | body=ast.Block([ 102 | third_foo_k, 103 | ast.FunctionCall("foo", [], ast.Variable("k")) 104 | ]), 105 | func_type=ast.FunctionDeclaration.CLASS_METHOD 106 | ) 107 | cls_third = ast.ClassDeclaration( 108 | "Third", 109 | [], 110 | ast.ClassDeclaration.REGULAR, 111 | fields=[third_z], 112 | functions=[third_foo], 113 | is_final=False 114 | ) 115 | 116 | ctx = Context() 117 | ctx.add_class(ast.GLOBAL_NAMESPACE, "First", cls_first) 118 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("First",), first_foo.name, first_foo) 119 | ctx.add_class(ast.GLOBAL_NAMESPACE, "Second", cls_second) 120 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("Second",), second_foo.name, second_foo) 121 | ctx.add_func(ast.GLOBAL_NAMESPACE, "foo", fun_foo) 122 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo",), foo_x.name, foo_x) 123 | ctx.add_func(ast.GLOBAL_NAMESPACE, "bar", fun_bar) 124 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("bar",), bar_y.name, bar_y) 125 | ctx.add_class(ast.GLOBAL_NAMESPACE, "Third", cls_third) 126 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("Third",), third_z.name, third_z) 127 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("Third",), third_foo.name, third_foo) 128 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("Third", "foo"), third_foo_k.name, third_foo_k) 129 | program = ast.Program(ctx, language="kotlin") 130 | -------------------------------------------------------------------------------- /tests/resources/program11.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # open class First { 5 | # open fun foo() {} 6 | # } 7 | # 8 | # class Second(): First() { 9 | # override fun foo() {} 10 | # } 11 | # 12 | # fun foo(x: First): First { 13 | # return x 14 | # } 15 | # 16 | # fun bar() { 17 | # val y = foo() 18 | # y.foo() 19 | # } 20 | 21 | first_foo = ast.FunctionDeclaration( 22 | "foo", 23 | params=[], 24 | ret_type=kt.Unit, 25 | body=ast.Block([]), 26 | func_type=ast.FunctionDeclaration.CLASS_METHOD, 27 | is_final=False 28 | ) 29 | cls_first = ast.ClassDeclaration( 30 | "First", 31 | [], 32 | ast.ClassDeclaration.REGULAR, 33 | fields=[], 34 | functions=[first_foo], 35 | is_final=False 36 | ) 37 | 38 | second_foo = ast.FunctionDeclaration( 39 | "foo", 40 | params=[], 41 | ret_type=kt.Unit, 42 | body=ast.Block([]), 43 | func_type=ast.FunctionDeclaration.CLASS_METHOD, 44 | is_final=True, 45 | override=True 46 | ) 47 | cls_second = ast.ClassDeclaration( 48 | "Second", 49 | [ast.SuperClassInstantiation(cls_first.get_type(), [])], 50 | ast.ClassDeclaration.REGULAR, 51 | fields=[], 52 | functions=[second_foo] 53 | ) 54 | 55 | foo_x = ast.ParameterDeclaration("x", cls_first.get_type()) 56 | fun_foo = ast.FunctionDeclaration( 57 | "foo", 58 | params=[foo_x], 59 | ret_type=cls_first.get_type(), 60 | body=ast.Variable("x"), 61 | func_type=ast.FunctionDeclaration.FUNCTION 62 | ) 63 | 64 | 65 | bar_y = ast.VariableDeclaration( 66 | "y", 67 | ast.FunctionCall("foo", [ast.New(cls_second.get_type(), [])]), 68 | inferred_type = cls_first.get_type() 69 | ) 70 | fun_bar = ast.FunctionDeclaration( 71 | "bar", 72 | params=[], 73 | ret_type=kt.Unit, 74 | body=ast.Block([ 75 | bar_y, 76 | ast.FunctionCall("foo", [], ast.Variable("y")), 77 | ]), 78 | func_type=ast.FunctionDeclaration.FUNCTION 79 | ) 80 | 81 | ctx = Context() 82 | ctx.add_class(ast.GLOBAL_NAMESPACE, "First", cls_first) 83 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("First",), first_foo.name, first_foo) 84 | ctx.add_class(ast.GLOBAL_NAMESPACE, "Second", cls_second) 85 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("Second",), second_foo.name, second_foo) 86 | ctx.add_func(ast.GLOBAL_NAMESPACE, "foo", fun_foo) 87 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo",), foo_x.name, foo_x) 88 | ctx.add_func(ast.GLOBAL_NAMESPACE, "bar", fun_bar) 89 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("bar",), bar_y.name, bar_y) 90 | program = ast.Program(ctx, language="kotlin") 91 | -------------------------------------------------------------------------------- /tests/resources/program12.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # open class First { 5 | # open fun foo() {} 6 | # } 7 | # 8 | # class Second(): First() { 9 | # override fun foo() {} 10 | # } 11 | # 12 | # fun foo(x: First): First { 13 | # return x 14 | # } 15 | # 16 | # fun bar() { 17 | # B().foo() 18 | # foo(A()).foo() 19 | # } 20 | 21 | first_foo = ast.FunctionDeclaration( 22 | "foo", 23 | params=[], 24 | ret_type=kt.Unit, 25 | body=ast.Block([]), 26 | func_type=ast.FunctionDeclaration.CLASS_METHOD, 27 | is_final=False 28 | ) 29 | cls_first = ast.ClassDeclaration( 30 | "First", 31 | [], 32 | ast.ClassDeclaration.REGULAR, 33 | fields=[], 34 | functions=[first_foo], 35 | is_final=False 36 | ) 37 | 38 | second_foo = ast.FunctionDeclaration( 39 | "foo", 40 | params=[], 41 | ret_type=kt.Unit, 42 | body=ast.Block([]), 43 | func_type=ast.FunctionDeclaration.CLASS_METHOD, 44 | is_final=True, 45 | override=True 46 | ) 47 | cls_second = ast.ClassDeclaration( 48 | "Second", 49 | [ast.SuperClassInstantiation(cls_first.get_type(), [])], 50 | ast.ClassDeclaration.REGULAR, 51 | fields=[], 52 | functions=[second_foo] 53 | ) 54 | 55 | foo_x = ast.ParameterDeclaration("x", cls_first.get_type()) 56 | fun_foo = ast.FunctionDeclaration( 57 | "foo", 58 | params=[foo_x], 59 | ret_type=cls_first.get_type(), 60 | body=ast.Variable("x"), 61 | func_type=ast.FunctionDeclaration.FUNCTION 62 | ) 63 | 64 | 65 | fun_bar = ast.FunctionDeclaration( 66 | "bar", 67 | params=[], 68 | ret_type=kt.Unit, 69 | body=ast.Block([ 70 | ast.FunctionCall( 71 | "foo", [], 72 | ast.New(cls_second.get_type(), []) 73 | ), 74 | ast.FunctionCall( 75 | "foo", [], 76 | ast.FunctionCall("foo", [ast.New(cls_first.get_type(), [])]), 77 | ) 78 | ]), 79 | func_type=ast.FunctionDeclaration.FUNCTION 80 | ) 81 | 82 | ctx = Context() 83 | ctx.add_class(ast.GLOBAL_NAMESPACE, "First", cls_first) 84 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("First",), first_foo.name, first_foo) 85 | ctx.add_class(ast.GLOBAL_NAMESPACE, "Second", cls_second) 86 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("Second",), second_foo.name, second_foo) 87 | ctx.add_func(ast.GLOBAL_NAMESPACE, "foo", fun_foo) 88 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo",), foo_x.name, foo_x) 89 | ctx.add_func(ast.GLOBAL_NAMESPACE, "bar", fun_bar) 90 | program = ast.Program(ctx, language="kotlin") 91 | -------------------------------------------------------------------------------- /tests/resources/program2.py: -------------------------------------------------------------------------------- 1 | from src.ir.ast import * 2 | from src.ir.kotlin_types import * 3 | from src.ir.context import * 4 | 5 | # open class Bam(val x: String) { 6 | # open fun getX(z: String): String { 7 | # return x 8 | # } 9 | # } 10 | 11 | xB_field = FieldDeclaration( 12 | "x", 13 | StringType(), 14 | is_final=True, 15 | override=False 16 | ) 17 | 18 | z_get_param = ParameterDeclaration("z", StringType()) 19 | getX_func = FunctionDeclaration( 20 | "getX", 21 | params=[ 22 | z_get_param 23 | ], 24 | func_type=FunctionDeclaration.CLASS_METHOD, 25 | is_final=False, 26 | ret_type=StringType(), 27 | body=Block([ 28 | Variable(xB_field.name) 29 | 30 | ]) 31 | ) 32 | 33 | bam_cls = ClassDeclaration( 34 | "Bam", 35 | superclasses=[], 36 | class_type=ClassDeclaration.REGULAR, 37 | is_final=False, 38 | fields=[xB_field], 39 | functions=[getX_func] 40 | ) 41 | 42 | ctx = Context() 43 | ctx.add_class(GLOBAL_NAMESPACE, bam_cls.name, bam_cls) 44 | ctx.add_var(GLOBAL_NAMESPACE + ('Bam',), 'x', xB_field) 45 | ctx.add_var(GLOBAL_NAMESPACE + ('Bam', 'getX'), 'z', z_get_param) 46 | ctx.add_func(GLOBAL_NAMESPACE + ('Bam',), getX_func.name, getX_func) 47 | program = Program(ctx, language="kotlin") 48 | -------------------------------------------------------------------------------- /tests/resources/program3.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # class A { 5 | # fun foo(x: String) { 6 | # val y = if (true) { 7 | # val z = x 8 | # x 9 | # } else "foo" 10 | # } 11 | # 12 | # fun bar(x: String) { 13 | # val y = if (true) { 14 | # val z = x 15 | # "bar" 16 | # } else "foo" 17 | # } 18 | # } 19 | 20 | foo_x = ast.ParameterDeclaration("x", kt.String) 21 | foo_z = ast.VariableDeclaration("z", ast.Variable("x"), var_type=kt.String) 22 | if_cond1 = ast.Conditional( 23 | ast.BooleanConstant("true"), 24 | ast.Block([foo_z, ast.Variable("z")]), 25 | ast.StringConstant("str"), 26 | kt.String 27 | ) 28 | foo_y = ast.VariableDeclaration("y", if_cond1, var_type=kt.String) 29 | fun_body = ast.Block([foo_y]) 30 | fun_foo = ast.FunctionDeclaration( 31 | "foo", 32 | params=[foo_x], 33 | ret_type=kt.Unit, 34 | body=fun_body, 35 | func_type=ast.FunctionDeclaration.CLASS_METHOD 36 | ) 37 | 38 | 39 | bar_x = ast.ParameterDeclaration("x", kt.String) 40 | bar_z = ast.VariableDeclaration("z", ast.Variable("x"), var_type=kt.String) 41 | if_cond2 = ast.Conditional( 42 | ast.BooleanConstant("true"), 43 | ast.Block([bar_z, ast.StringConstant("bar")]), 44 | ast.StringConstant("foo"), 45 | kt.String 46 | ) 47 | bar_y = ast.VariableDeclaration("y", if_cond2, var_type=kt.String) 48 | fun_body = ast.Block([bar_y]) 49 | fun_bar = ast.FunctionDeclaration( 50 | "bar", 51 | params=[bar_x], 52 | ret_type=kt.Unit, 53 | body=fun_body, 54 | func_type=ast.FunctionDeclaration.CLASS_METHOD 55 | ) 56 | 57 | cls = ast.ClassDeclaration( 58 | "A", 59 | [], 60 | ast.ClassDeclaration.REGULAR, 61 | fields=[], 62 | functions=[fun_bar, fun_foo] 63 | ) 64 | 65 | 66 | ctx = Context() 67 | ctx.add_class(ast.GLOBAL_NAMESPACE, "A", cls) 68 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_bar.name, fun_bar) 69 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_foo.name, fun_foo) 70 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "foo"), foo_x.name, foo_x) 71 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "foo"), foo_z.name, foo_z) 72 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "foo"), foo_y.name, foo_y) 73 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "bar"), bar_x.name, bar_x) 74 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "bar"), bar_z.name, bar_z) 75 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "bar"), bar_y.name, bar_y) 76 | program = ast.Program(ctx, language="kotlin") 77 | -------------------------------------------------------------------------------- /tests/resources/program4.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # class A (val x: String) { 5 | # 6 | # fun foo(): String { 7 | # fun bar() = x 8 | # return bar() 9 | # } 10 | # } 11 | 12 | field_x = ast.FieldDeclaration("x", kt.String) 13 | fun_bar = ast.FunctionDeclaration( 14 | "bar", 15 | params=[], 16 | ret_type=kt.String, 17 | body=ast.Variable("x"), 18 | func_type=ast.FunctionDeclaration.FUNCTION 19 | ) 20 | fun_foo = ast.FunctionDeclaration( 21 | "foo", 22 | params=[], 23 | ret_type=kt.String, 24 | body=ast.Block([fun_bar, ast.FunctionCall("bar", [])]), 25 | func_type=ast.FunctionDeclaration.CLASS_METHOD 26 | ) 27 | cls = ast.ClassDeclaration( 28 | "A", 29 | [], 30 | ast.ClassDeclaration.REGULAR, 31 | fields=[field_x], 32 | functions=[fun_foo] 33 | ) 34 | 35 | 36 | ctx = Context() 37 | ctx.add_class(ast.GLOBAL_NAMESPACE, "A", cls) 38 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_foo.name, fun_foo) 39 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A",), field_x.name, field_x) 40 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A", "foo"), fun_bar.name, fun_bar) 41 | program = ast.Program(ctx, language="kotlin") 42 | -------------------------------------------------------------------------------- /tests/resources/program5.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # class A (val x: String) { 5 | # 6 | # fun foo(y: String): String { 7 | # return y 8 | # } 9 | # 10 | # fun bar(y: Int): Int { 11 | # fun baz(): String = x 12 | # val z = baz() 13 | # return quz(foo(z)) 14 | # } 15 | # 16 | # fun quz(y: String): Int = 1 17 | # } 18 | 19 | foo_y = ast.ParameterDeclaration("y", kt.String) 20 | foo_body = ast.Variable("y") 21 | foo_fun = ast.FunctionDeclaration( 22 | "foo", 23 | params=[foo_y], 24 | ret_type=kt.String, 25 | body=foo_body, 26 | func_type=ast.FunctionDeclaration.CLASS_METHOD 27 | ) 28 | 29 | bar_y = ast.ParameterDeclaration("y", kt.Integer) 30 | baz_fun = ast.FunctionDeclaration( 31 | "baz", 32 | params=[], 33 | ret_type=kt.String, 34 | body=ast.Variable("x"), 35 | func_type=ast.FunctionDeclaration.FUNCTION 36 | ) 37 | bar_z = ast.VariableDeclaration("z", ast.FunctionCall("baz", []), 38 | var_type=kt.String) 39 | 40 | bar_fun = ast.FunctionDeclaration( 41 | "bar", 42 | params=[bar_y], 43 | ret_type=kt.Integer, 44 | body=ast.Block([ 45 | baz_fun, 46 | bar_z, 47 | ast.FunctionCall("quz", [ast.FunctionCall("foo", [ast.Variable("z")])]) 48 | ]), 49 | func_type=ast.FunctionDeclaration.CLASS_METHOD 50 | ) 51 | 52 | quz_y = ast.ParameterDeclaration("y", kt.String) 53 | quz_fun = ast.FunctionDeclaration( 54 | "quz", 55 | params=[quz_y], 56 | ret_type=kt.Integer, 57 | body=ast.IntegerConstant(1, kt.Integer), 58 | func_type=ast.FunctionDeclaration.CLASS_METHOD 59 | ) 60 | 61 | field_x = ast.FieldDeclaration("x", kt.String) 62 | cls = ast.ClassDeclaration( 63 | "A", 64 | [], 65 | ast.ClassDeclaration.REGULAR, 66 | fields=[field_x], 67 | functions=[foo_fun, bar_fun, quz_fun] 68 | ) 69 | 70 | 71 | ctx = Context() 72 | ctx.add_class(ast.GLOBAL_NAMESPACE, "A", cls) 73 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), foo_fun.name, foo_fun) 74 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), bar_fun.name, bar_fun) 75 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), quz_fun.name, quz_fun) 76 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A",), field_x.name, field_x) 77 | 78 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "foo"), foo_y.name, foo_y) 79 | 80 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A", "bar"), baz_fun.name, baz_fun) 81 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "bar"), bar_y.name, bar_y) 82 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "bar"), bar_z.name, bar_z) 83 | 84 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "quz"), quz_y.name, quz_y) 85 | program = ast.Program(ctx, language="kotlin") 86 | -------------------------------------------------------------------------------- /tests/resources/program6.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # val y = "foo" 5 | # fun foo(x: String) = x 6 | # class A (val x: String) { 7 | # 8 | # fun bar(): String { 9 | # val y = x 10 | # return foo(y) 11 | # } 12 | # 13 | # fun baz(): String = y 14 | # } 15 | 16 | val_y = ast.VariableDeclaration("y", ast.StringConstant("foo"), 17 | var_type=kt.String) 18 | foo_x = ast.ParameterDeclaration("x", kt.String) 19 | fun_foo = ast.FunctionDeclaration( 20 | "foo", 21 | params=[foo_x], 22 | ret_type=kt.String, 23 | body=ast.Variable("x"), 24 | func_type=ast.FunctionDeclaration.FUNCTION 25 | ) 26 | 27 | bar_y = ast.VariableDeclaration("y", ast.Variable("x"), var_type=kt.String) 28 | fun_bar = ast.FunctionDeclaration( 29 | "bar", 30 | params=[], 31 | ret_type=kt.String, 32 | body=ast.Block([bar_y, ast.FunctionCall("foo", [ast.Variable("y")])]), 33 | func_type=ast.FunctionDeclaration.CLASS_METHOD 34 | ) 35 | fun_baz = ast.FunctionDeclaration( 36 | "baz", 37 | params=[], 38 | ret_type=kt.String, 39 | body=ast.Variable("y"), 40 | func_type=ast.FunctionDeclaration.CLASS_METHOD 41 | ) 42 | 43 | field_x = ast.FieldDeclaration("x", kt.String) 44 | cls = ast.ClassDeclaration( 45 | "A", 46 | [], 47 | ast.ClassDeclaration.REGULAR, 48 | fields=[field_x], 49 | functions=[fun_bar, fun_baz] 50 | ) 51 | 52 | 53 | ctx = Context() 54 | ctx.add_class(ast.GLOBAL_NAMESPACE, "A", cls) 55 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_foo.name, fun_foo) 56 | ctx.add_func(ast.GLOBAL_NAMESPACE, val_y.name, val_y) 57 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("foo",), foo_x.name, foo_x) 58 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_bar.name, fun_bar) 59 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_baz.name, fun_baz) 60 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A",), field_x.name, field_x) 61 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "bar"), bar_y.name, bar_y) 62 | program = ast.Program(ctx, language="kotlin") 63 | -------------------------------------------------------------------------------- /tests/resources/program7.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # val y = "foo" 5 | # class A (val x: String) { 6 | # 7 | # fun foo(x: String): String = x 8 | # 9 | # fun bar(): String = foo(y) 10 | # } 11 | 12 | val_y = ast.VariableDeclaration("y", ast.StringConstant("foo"), 13 | var_type=kt.String) 14 | foo_x = ast.ParameterDeclaration("x", kt.String) 15 | fun_foo = ast.FunctionDeclaration( 16 | "foo", 17 | params=[foo_x], 18 | ret_type=kt.String, 19 | body=ast.Variable("x"), 20 | func_type=ast.FunctionDeclaration.CLASS_METHOD 21 | ) 22 | 23 | fun_bar = ast.FunctionDeclaration( 24 | "bar", 25 | params=[], 26 | ret_type=kt.String, 27 | body=ast.FunctionCall("foo", [ast.Variable("y")]), 28 | func_type=ast.FunctionDeclaration.CLASS_METHOD 29 | ) 30 | cls = ast.ClassDeclaration( 31 | "A", 32 | [], 33 | ast.ClassDeclaration.REGULAR, 34 | fields=[], 35 | functions=[fun_bar, fun_foo] 36 | ) 37 | 38 | 39 | ctx = Context() 40 | ctx.add_class(ast.GLOBAL_NAMESPACE, "A", cls) 41 | ctx.add_var(ast.GLOBAL_NAMESPACE, val_y.name, val_y) 42 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_bar.name, fun_bar) 43 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_foo.name, fun_foo) 44 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "foo",), foo_x.name, foo_x) 45 | program = ast.Program(ctx, language="kotlin") 46 | -------------------------------------------------------------------------------- /tests/resources/program8.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # class A (val x: String) { 5 | # 6 | # fun foo(x: String): String = { 7 | # x.length() 8 | # x 9 | # } 10 | # } 11 | foo_x = ast.ParameterDeclaration("x", kt.String) 12 | fun_foo = ast.FunctionDeclaration( 13 | "foo", 14 | params=[foo_x], 15 | ret_type=kt.String, 16 | body=ast.Block([ 17 | ast.FunctionCall("length", args=[], receiver=ast.Variable("x")), 18 | ast.Variable("x") 19 | ]), 20 | func_type=ast.FunctionDeclaration.CLASS_METHOD 21 | ) 22 | 23 | cls = ast.ClassDeclaration( 24 | "A", 25 | [], 26 | ast.ClassDeclaration.REGULAR, 27 | fields=[], 28 | functions=[fun_foo] 29 | ) 30 | 31 | 32 | ctx = Context() 33 | ctx.add_class(ast.GLOBAL_NAMESPACE, "A", cls) 34 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("A",), fun_foo.name, fun_foo) 35 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("A", "foo",), foo_x.name, foo_x) 36 | program = ast.Program(ctx, language="kotlin") 37 | -------------------------------------------------------------------------------- /tests/resources/program9.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, kotlin_types as kt 2 | from src.ir.context import Context 3 | 4 | # open class First 5 | # class Second(val x: First): First() 6 | # 7 | # fun foo(lank: First): Second = 8 | # Second(First()) 9 | # 10 | # fun bar(): Second { 11 | # val cinches: Second = foo(First()) 12 | # return cinches 13 | # } 14 | 15 | cls_first = ast.ClassDeclaration( 16 | "First", 17 | [], 18 | ast.ClassDeclaration.REGULAR, 19 | fields=[], 20 | functions=[], 21 | is_final=False 22 | ) 23 | 24 | second_x = ast.FieldDeclaration("x", cls_first.get_type()) 25 | cls_second = ast.ClassDeclaration( 26 | "Second", 27 | [ast.SuperClassInstantiation(cls_first.get_type(), [])], 28 | ast.ClassDeclaration.REGULAR, 29 | fields=[second_x], 30 | functions=[] 31 | ) 32 | 33 | foo_lank = ast.ParameterDeclaration("lank", cls_first.get_type()) 34 | fun_foo = ast.FunctionDeclaration( 35 | "foo", 36 | params=[foo_lank], 37 | ret_type=cls_second.get_type(), 38 | body=ast.New(cls_second.get_type(), [ast.Variable("lank")]), 39 | func_type=ast.FunctionDeclaration.FUNCTION 40 | ) 41 | 42 | 43 | bar_cinches = ast.VariableDeclaration( 44 | "cinches", 45 | ast.FunctionCall("foo", [ast.New(cls_first.get_type(), [])]), 46 | var_type = cls_second.get_type() 47 | ) 48 | fun_bar = ast.FunctionDeclaration( 49 | "bar", 50 | params=[], 51 | ret_type=cls_second.get_type(), 52 | body=ast.Block([ 53 | bar_cinches, ast.Variable("cinches") 54 | ]), 55 | func_type=ast.FunctionDeclaration.FUNCTION 56 | ) 57 | 58 | ctx = Context() 59 | ctx.add_class(ast.GLOBAL_NAMESPACE, "First", cls_first) 60 | ctx.add_class(ast.GLOBAL_NAMESPACE, "Second", cls_second) 61 | ctx.add_class(ast.GLOBAL_NAMESPACE + ("Second",), second_x.name, second_x) 62 | ctx.add_func(ast.GLOBAL_NAMESPACE, "foo", fun_foo) 63 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo",), foo_lank.name, foo_lank) 64 | ctx.add_func(ast.GLOBAL_NAMESPACE, "bar", fun_bar) 65 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("bar",), bar_cinches.name, bar_cinches) 66 | program = ast.Program(ctx, language="kotlin") 67 | -------------------------------------------------------------------------------- /tests/resources/translators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hephaestus-compiler-project/hephaestus/18ee48b48949710fa0504b7d56a4b700ee591577/tests/resources/translators/__init__.py -------------------------------------------------------------------------------- /tests/resources/translators/groovy/program4.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, types, groovy_types as gt 2 | from src.ir.context import Context 3 | 4 | tp_T = types.TypeParameter("T") 5 | field_x = ast.FieldDeclaration("x", tp_T) 6 | fun_tmp = ast.FunctionDeclaration( 7 | "tmp", 8 | params=[], 9 | ret_type=gt.String, 10 | body=ast.Block([ast.StringConstant("xxx")]), 11 | func_type=ast.FunctionDeclaration.CLASS_METHOD 12 | ) 13 | cls_a = ast.ClassDeclaration( 14 | "AClass", 15 | [], 16 | ast.ClassDeclaration.REGULAR, 17 | fields=[field_x], 18 | functions=[fun_tmp], 19 | type_parameters=[tp_T] 20 | ) 21 | cls_a_str = cls_a.get_type().new([gt.String.to_type_arg()]) 22 | cls_a_any = cls_a.get_type().new([gt.Object.to_type_arg()]) 23 | 24 | tp_X = types.TypeParameter("X") 25 | tp_Y = types.TypeParameter("Y", bound=gt.Object) 26 | cls_b = ast.ClassDeclaration( 27 | "BClass", 28 | [], 29 | ast.ClassDeclaration.REGULAR, 30 | fields=[], 31 | functions=[], 32 | type_parameters=[tp_X, tp_Y] 33 | ) 34 | cls_b_double_integer = cls_b.get_type().new([gt.Double.to_type_arg(), 35 | gt.Integer.to_type_arg()]) 36 | cls_b_any_integer = cls_b.get_type().new([gt.Object.to_type_arg(), 37 | gt.Integer.to_type_arg()]) 38 | 39 | y_param = ast.ParameterDeclaration("y", cls_b_double_integer) 40 | fun_bar = ast.FunctionDeclaration( 41 | "bar", 42 | params=[y_param], 43 | ret_type=gt.Void, 44 | body=ast.Block([]), 45 | func_type=ast.FunctionDeclaration.FUNCTION 46 | ) 47 | 48 | fun_buz = ast.FunctionDeclaration( 49 | "buz", 50 | params=[], 51 | ret_type=cls_b_any_integer, 52 | body=ast.New(cls_b_any_integer, []), 53 | func_type=ast.FunctionDeclaration.FUNCTION 54 | ) 55 | 56 | k_param = ast.ParameterDeclaration("k", cls_a_str) 57 | fun_foo1 = ast.FunctionDeclaration( 58 | "foo1", 59 | params=[k_param], 60 | ret_type=gt.Void, 61 | body=ast.Block([]), 62 | func_type=ast.FunctionDeclaration.FUNCTION 63 | ) 64 | 65 | n_param = ast.ParameterDeclaration("n", cls_a_any) 66 | fun_foo2 = ast.FunctionDeclaration( 67 | "foo2", 68 | params=[n_param], 69 | ret_type=gt.Void, 70 | body=ast.Block([]), 71 | func_type=ast.FunctionDeclaration.FUNCTION 72 | ) 73 | 74 | var_a1 = ast.VariableDeclaration( 75 | "a1", 76 | ast.New(cls_a_str, [ast.StringConstant("a1")]), 77 | inferred_type=cls_a_str 78 | ) 79 | var_a2 = ast.VariableDeclaration( 80 | "a2", 81 | ast.New(cls_a_any, [ast.StringConstant("a2")]), 82 | inferred_type=cls_a_any 83 | ) 84 | var_a3 = ast.VariableDeclaration( 85 | "a3", 86 | ast.New(cls_a_any, [ast.StringConstant("a3")]), 87 | inferred_type=cls_a_any 88 | ) 89 | var_b = ast.VariableDeclaration( 90 | "b", 91 | ast.New(cls_b_any_integer, []), 92 | var_type=cls_b_any_integer 93 | ) 94 | var_c = ast.VariableDeclaration( 95 | "c", 96 | ast.New(cls_b_double_integer, []), 97 | var_type=cls_b_double_integer 98 | ) 99 | main_body = ast.Block( 100 | body=[ 101 | var_a1, var_a2, var_a3, var_b, var_c, 102 | ast.FunctionCall( 103 | "bar", 104 | [ast.Variable('c')] 105 | ), 106 | ast.FunctionCall( 107 | "bar", 108 | [ast.New(cls_b_double_integer, [])] 109 | ), 110 | ast.FunctionCall( 111 | "foo1", 112 | [ast.Variable("a1")] 113 | ), 114 | ast.FunctionCall( 115 | "foo2", 116 | [ast.Variable("a2")] 117 | ), 118 | ] 119 | ) 120 | main_func = ast.FunctionDeclaration( 121 | "main", 122 | params=[], 123 | ret_type=gt.Void, 124 | func_type=ast.FunctionDeclaration.FUNCTION, 125 | body=main_body, 126 | is_final=False 127 | ) 128 | 129 | 130 | ctx = Context() 131 | ctx.add_class(ast.GLOBAL_NAMESPACE, "AClass", cls_a) 132 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("AClass",), fun_tmp.name, fun_tmp) 133 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("AClass",), field_x.name, field_x) 134 | ctx.add_class(ast.GLOBAL_NAMESPACE, "BClass", cls_b) 135 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_bar.name, fun_bar) 136 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("bar",), y_param.name, y_param) 137 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_buz.name, fun_buz) 138 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_foo1.name, fun_foo1) 139 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo1",), k_param.name, k_param) 140 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_foo2.name, fun_foo2) 141 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo2",), n_param.name, n_param) 142 | ctx.add_func(ast.GLOBAL_NAMESPACE, main_func.name, main_func) 143 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_a1.name, var_a1) 144 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_a2.name, var_a2) 145 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_a3.name, var_a3) 146 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_b.name, var_b) 147 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_c.name, var_c) 148 | program = ast.Program(ctx, language="groovy") 149 | -------------------------------------------------------------------------------- /tests/resources/translators/program1.groovy: -------------------------------------------------------------------------------- 1 | class Main { 2 | public static final void main() { 3 | B b = new B("b") 4 | A ba = new B("ba") 5 | } 6 | } 7 | 8 | class A { 9 | public final String a 10 | 11 | public A(String a) { 12 | this.a = a 13 | } 14 | 15 | } 16 | 17 | class B extends A { 18 | public final String a 19 | public B(String a) { 20 | this.a = a 21 | } 22 | } 23 | 24 | final class C {} 25 | -------------------------------------------------------------------------------- /tests/resources/translators/program1.java: -------------------------------------------------------------------------------- 1 | class Main { 2 | static public final void main() { 3 | B b = new B("b"); 4 | A ba = new B("ba"); 5 | } 6 | } 7 | 8 | interface Function0 { 9 | public A1 apply(); 10 | } 11 | 12 | class A { 13 | public final String a; 14 | 15 | public A(String a) { 16 | this.a = a; 17 | } 18 | 19 | } 20 | 21 | class B extends A { 22 | public final String a; 23 | public B(String a) { 24 | super("b"); 25 | this.a = a; 26 | } 27 | } 28 | 29 | final class C {} 30 | -------------------------------------------------------------------------------- /tests/resources/translators/program1.py: -------------------------------------------------------------------------------- 1 | from src.ir.ast import * 2 | from src.ir.groovy_types import * 3 | from src.ir.context import * 4 | 5 | 6 | def produce_program(lang, types): 7 | a_a_field = FieldDeclaration("a", types.StringType(), is_final=True) 8 | a_cls = ClassDeclaration( 9 | "A", 10 | [], 11 | ClassDeclaration.REGULAR, 12 | [a_a_field], 13 | is_final=False 14 | ) 15 | 16 | b_a_field = FieldDeclaration( 17 | "a", 18 | types.StringType(), 19 | is_final=True, 20 | override=True 21 | ) 22 | b_cls = ClassDeclaration( 23 | "B", 24 | [SuperClassInstantiation(a_cls.get_type(), [StringConstant("b")])], 25 | ClassDeclaration.REGULAR, 26 | [b_a_field], 27 | is_final=False 28 | ) 29 | 30 | c_cls = ClassDeclaration("C", []) 31 | 32 | foo_b = VariableDeclaration( 33 | "b", 34 | New(b_cls.get_type(), [StringConstant("b")]), 35 | is_final=False, 36 | var_type=b_cls.get_type() 37 | ) 38 | foo_a = VariableDeclaration( 39 | "ba", 40 | New(b_cls.get_type(), [StringConstant("ba")]), 41 | is_final=False, 42 | var_type=a_cls.get_type() 43 | ) 44 | main_fun = FunctionDeclaration( 45 | "main", 46 | [], 47 | types.VoidType(), 48 | Block([foo_b, foo_a]), 49 | FunctionDeclaration.FUNCTION 50 | ) 51 | 52 | 53 | ctx = Context() 54 | ctx.add_class(GLOBAL_NAMESPACE, a_cls.name, a_cls) 55 | ctx.add_var(GLOBAL_NAMESPACE + ('A',), a_a_field.name, a_a_field) 56 | ctx.add_class(GLOBAL_NAMESPACE, b_cls.name, b_cls) 57 | ctx.add_var(GLOBAL_NAMESPACE + ('B',), b_a_field.name, b_a_field) 58 | ctx.add_class(GLOBAL_NAMESPACE, c_cls.name, c_cls) 59 | ctx.add_func(GLOBAL_NAMESPACE, main_fun.name, main_fun) 60 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_b.name, foo_b) 61 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_a.name, foo_a) 62 | program = Program(ctx, language=lang) 63 | return program 64 | -------------------------------------------------------------------------------- /tests/resources/translators/program2.groovy: -------------------------------------------------------------------------------- 1 | class Main { 2 | static final String z = "z" 3 | static final String y = "y" 4 | static final String bar(String y) { 5 | (Main.z + y) 6 | } 7 | static final A buz() { 8 | new A("a") 9 | } 10 | public static final void main() { 11 | A a = new A("a") 12 | a.foo() 13 | Main.buz().foo() 14 | } 15 | } 16 | 17 | class A { 18 | public final String a 19 | 20 | public A(String a) { 21 | this.a = a 22 | } 23 | 24 | void foo() { 25 | println(Main.bar(a)) 26 | println(Main.bar(Main.z)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/resources/translators/program2.java: -------------------------------------------------------------------------------- 1 | class Main { 2 | static final String z = "z"; 3 | static final String y = "y"; 4 | static public final String bar(String y) { 5 | return (Main.z + y); 6 | } 7 | static public final A buz() { 8 | return new A("a"); 9 | } 10 | static public final void main() { 11 | A a = new A("a"); 12 | a.foo(); 13 | Main.buz().foo(); 14 | } 15 | } 16 | 17 | interface Function0 { 18 | public A1 apply(); 19 | } 20 | 21 | class A { 22 | public final String a; 23 | 24 | public A(String a) { 25 | this.a = a; 26 | } 27 | 28 | public void foo() { 29 | println(Main.bar(a)); 30 | println(Main.bar(Main.z)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/resources/translators/program2.py: -------------------------------------------------------------------------------- 1 | from src.ir.ast import * 2 | from src.ir.context import * 3 | 4 | 5 | def produce_program(lang, types): 6 | a_a_field = FieldDeclaration("a", types.StringType(), is_final=True) 7 | a_foo = FunctionDeclaration( 8 | "foo", 9 | [], 10 | types.Void, 11 | Block([ 12 | FunctionCall("println", [FunctionCall("bar", [Variable("a")])]), 13 | FunctionCall("println", [FunctionCall("bar", [Variable("z")])]) 14 | ]), 15 | FunctionDeclaration.CLASS_METHOD, 16 | is_final=False 17 | ) 18 | a_cls = ClassDeclaration( 19 | "A", 20 | [], 21 | ClassDeclaration.REGULAR, 22 | fields=[a_a_field], 23 | functions=[a_foo], 24 | is_final=False 25 | ) 26 | 27 | var_z = VariableDeclaration("z", StringConstant("z"), 28 | var_type=types.StringType()) 29 | var_y = VariableDeclaration("y", StringConstant("y"), 30 | var_type=types.StringType()) 31 | 32 | param_y = ParameterDeclaration("y", types.String) 33 | fun_bar = FunctionDeclaration( 34 | "bar", 35 | [param_y], 36 | types.String, 37 | ArithExpr(Variable("z"), Variable("y"), Operator('+')), 38 | FunctionDeclaration.FUNCTION 39 | ) 40 | 41 | fun_buz = FunctionDeclaration( 42 | "buz", 43 | [], 44 | a_cls.get_type(), 45 | New(a_cls.get_type(), [StringConstant("a")]), 46 | FunctionDeclaration.FUNCTION 47 | ) 48 | 49 | main_a = VariableDeclaration( 50 | "a", 51 | New(a_cls.get_type(), [StringConstant("a")]), 52 | is_final=False, 53 | var_type=a_cls.get_type() 54 | ) 55 | main_fun = FunctionDeclaration( 56 | "main", 57 | [], 58 | types.VoidType(), 59 | Block([ 60 | main_a, 61 | FunctionCall("foo", [], Variable("a")), 62 | FunctionCall("foo", [], FunctionCall("buz", [])) 63 | ]), 64 | FunctionDeclaration.FUNCTION 65 | ) 66 | 67 | 68 | ctx = Context() 69 | ctx.add_class(GLOBAL_NAMESPACE, a_cls.name, a_cls) 70 | ctx.add_var(GLOBAL_NAMESPACE + ('A',), a_a_field.name, a_a_field) 71 | ctx.add_func(GLOBAL_NAMESPACE + ('A',), a_foo.name, a_foo) 72 | ctx.add_var(GLOBAL_NAMESPACE, var_z.name, var_z) 73 | ctx.add_var(GLOBAL_NAMESPACE, var_y.name, var_y) 74 | ctx.add_func(GLOBAL_NAMESPACE, fun_bar.name, fun_bar) 75 | ctx.add_var(GLOBAL_NAMESPACE + ('bar',), param_y.name, param_y) 76 | ctx.add_func(GLOBAL_NAMESPACE, fun_buz.name, fun_buz) 77 | ctx.add_func(GLOBAL_NAMESPACE, main_fun.name, main_fun) 78 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), main_a.name, main_a) 79 | program = Program(ctx, language=lang) 80 | return program 81 | -------------------------------------------------------------------------------- /tests/resources/translators/program3.groovy: -------------------------------------------------------------------------------- 1 | class Main { 2 | public static final void main() { 3 | A a = new A("a") 4 | a.foo() 5 | } 6 | } 7 | 8 | class A { 9 | public final String a 10 | 11 | public A(String a) { 12 | this.a = a 13 | } 14 | 15 | void foo() { 16 | Closure clos = { String i -> { 17 | String s = "s" 18 | s 19 | }} 20 | println(clos(a)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/resources/translators/program3.java: -------------------------------------------------------------------------------- 1 | class Main { 2 | static public final void main() { 3 | A a = new A("a"); 4 | a.foo(); 5 | } 6 | } 7 | 8 | interface Function0 { 9 | public A1 apply(); 10 | } 11 | 12 | interface Function1 { 13 | public A2 apply(A1 a1); 14 | } 15 | 16 | class A { 17 | public final String a; 18 | 19 | public A(String a) { 20 | this.a = a; 21 | } 22 | 23 | public void foo() { 24 | Function1 clos = (i) -> { 25 | String s = "s"; 26 | return s; 27 | }; 28 | println(clos.apply(a)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/resources/translators/program3.py: -------------------------------------------------------------------------------- 1 | from src.ir.ast import * 2 | from src.ir.context import * 3 | 4 | 5 | def produce_program(lang, types): 6 | a_a_field = FieldDeclaration("a", types.StringType(), is_final=True) 7 | 8 | param_i = ParameterDeclaration("i", types.String) 9 | a_foo_clos_s = VariableDeclaration( 10 | "s", StringConstant("s"), var_type=types.String, is_final=False) 11 | a_foo_clos = FunctionDeclaration( 12 | "clos", 13 | [param_i], 14 | types.String, 15 | Block([a_foo_clos_s, Variable("s")]), 16 | FunctionDeclaration.FUNCTION 17 | ) 18 | 19 | a_foo = FunctionDeclaration( 20 | "foo", 21 | [], 22 | types.Void, 23 | Block([ 24 | a_foo_clos, 25 | FunctionCall("println", [FunctionCall("clos", [Variable("a")])]) 26 | ]), 27 | FunctionDeclaration.CLASS_METHOD, 28 | is_final=False 29 | ) 30 | a_cls = ClassDeclaration( 31 | "A", 32 | [], 33 | ClassDeclaration.REGULAR, 34 | fields=[a_a_field], 35 | functions=[a_foo], 36 | is_final=False 37 | ) 38 | 39 | main_a = VariableDeclaration( 40 | "a", 41 | New(a_cls.get_type(), [StringConstant("a")]), 42 | is_final=False, 43 | var_type=a_cls.get_type() 44 | ) 45 | main_fun = FunctionDeclaration( 46 | "main", 47 | [], 48 | types.VoidType(), 49 | Block([ 50 | main_a, 51 | FunctionCall("foo", [], Variable("a")), 52 | ]), 53 | FunctionDeclaration.FUNCTION 54 | ) 55 | 56 | 57 | ctx = Context() 58 | ctx.add_class(GLOBAL_NAMESPACE, a_cls.name, a_cls) 59 | ctx.add_var(GLOBAL_NAMESPACE + ('A',), a_a_field.name, a_a_field) 60 | ctx.add_func(GLOBAL_NAMESPACE + ('A',), a_foo.name, a_foo) 61 | ctx.add_func(GLOBAL_NAMESPACE + ('A', 'foo'), a_foo_clos.name, a_foo_clos) 62 | ctx.add_func(GLOBAL_NAMESPACE + ('A', 'foo'), a_foo_clos.name, a_foo_clos) 63 | ctx.add_func(GLOBAL_NAMESPACE + ('A', 'foo', 'clos'), param_i.name, param_i) 64 | ctx.add_func(GLOBAL_NAMESPACE + ('A', 'foo', 'clos'), a_foo_clos_s.name, a_foo_clos_s) 65 | ctx.add_func(GLOBAL_NAMESPACE, main_fun.name, main_fun) 66 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), main_a.name, main_a) 67 | program = Program(ctx, language=lang) 68 | return program 69 | -------------------------------------------------------------------------------- /tests/resources/translators/program4.groovy: -------------------------------------------------------------------------------- 1 | class Main { 2 | static final void bar(BClass y) { 3 | 4 | } 5 | static final BClass buz() { 6 | new BClass() 7 | } 8 | static final void foo1(AClass k) { 9 | 10 | } 11 | static final void foo2(AClass n) { 12 | 13 | } 14 | public static void main() { 15 | final def a1 = new AClass("a1") 16 | final def a2 = new AClass("a2") 17 | final def a3 = new AClass("a3") 18 | final BClass b = new BClass() 19 | final BClass c = new BClass() 20 | Main.bar(c) 21 | Main.bar(new BClass()) 22 | Main.foo1(a1) 23 | Main.foo2(a2) 24 | } 25 | } 26 | 27 | final class AClass { 28 | public final T x 29 | public AClass(T x) { 30 | this.x = x 31 | } 32 | 33 | final String tmp() { 34 | "xxx" 35 | } 36 | } 37 | 38 | final class BClass {} 39 | -------------------------------------------------------------------------------- /tests/resources/translators/program4.java: -------------------------------------------------------------------------------- 1 | class Main { 2 | static public final void bar(BClass y) { 3 | 4 | } 5 | static public final BClass buz() { 6 | return new BClass(); 7 | } 8 | static public final void foo1(AClass k) { 9 | 10 | } 11 | static public final void foo2(AClass n) { 12 | 13 | } 14 | static public void main() { 15 | final var a1 = new AClass("a1"); 16 | final var a2 = new AClass("a2"); 17 | final var a3 = new AClass("a3"); 18 | final BClass b = new BClass(); 19 | final BClass c = new BClass(); 20 | Main.bar(c); 21 | Main.bar(new BClass()); 22 | Main.foo1(a1); 23 | Main.foo2(a2); 24 | } 25 | } 26 | 27 | interface Function0 { 28 | public A1 apply(); 29 | } 30 | 31 | final class AClass { 32 | public final T x; 33 | public AClass(T x) { 34 | this.x = x; 35 | } 36 | 37 | public final String tmp() { 38 | return "xxx"; 39 | } 40 | } 41 | 42 | final class BClass {} 43 | -------------------------------------------------------------------------------- /tests/resources/translators/program4.py: -------------------------------------------------------------------------------- 1 | from src.ir import ast, types as tp 2 | from src.ir.context import Context 3 | 4 | 5 | def produce_program(lang, types): 6 | tp_T = tp.TypeParameter("T") 7 | field_x = ast.FieldDeclaration("x", tp_T) 8 | fun_tmp = ast.FunctionDeclaration( 9 | "tmp", 10 | params=[], 11 | ret_type=types.String, 12 | body=ast.Block([ast.StringConstant("xxx")]), 13 | func_type=ast.FunctionDeclaration.CLASS_METHOD 14 | ) 15 | cls_a = ast.ClassDeclaration( 16 | "AClass", 17 | [], 18 | ast.ClassDeclaration.REGULAR, 19 | fields=[field_x], 20 | functions=[fun_tmp], 21 | type_parameters=[tp_T] 22 | ) 23 | cls_a_str = cls_a.get_type().new([types.String]) 24 | cls_a_any = cls_a.get_type().new([types.Object]) 25 | 26 | tp_X = tp.TypeParameter("X") 27 | tp_Y = tp.TypeParameter("Y", bound=types.Object) 28 | cls_b = ast.ClassDeclaration( 29 | "BClass", 30 | [], 31 | ast.ClassDeclaration.REGULAR, 32 | fields=[], 33 | functions=[], 34 | type_parameters=[tp_X, tp_Y] 35 | ) 36 | cls_b_double_integer = cls_b.get_type().new([types.Double, types.Integer]) 37 | cls_b_any_integer = cls_b.get_type().new([types.Object, types.Integer]) 38 | 39 | y_param = ast.ParameterDeclaration("y", cls_b_double_integer) 40 | fun_bar = ast.FunctionDeclaration( 41 | "bar", 42 | params=[y_param], 43 | ret_type=types.Void, 44 | body=ast.Block([]), 45 | func_type=ast.FunctionDeclaration.FUNCTION 46 | ) 47 | 48 | fun_buz = ast.FunctionDeclaration( 49 | "buz", 50 | params=[], 51 | ret_type=cls_b_any_integer, 52 | body=ast.New(cls_b_any_integer, []), 53 | func_type=ast.FunctionDeclaration.FUNCTION 54 | ) 55 | 56 | k_param = ast.ParameterDeclaration("k", cls_a_str) 57 | fun_foo1 = ast.FunctionDeclaration( 58 | "foo1", 59 | params=[k_param], 60 | ret_type=types.Void, 61 | body=ast.Block([]), 62 | func_type=ast.FunctionDeclaration.FUNCTION 63 | ) 64 | 65 | n_param = ast.ParameterDeclaration("n", cls_a_any) 66 | fun_foo2 = ast.FunctionDeclaration( 67 | "foo2", 68 | params=[n_param], 69 | ret_type=types.Void, 70 | body=ast.Block([]), 71 | func_type=ast.FunctionDeclaration.FUNCTION 72 | ) 73 | 74 | var_a1 = ast.VariableDeclaration( 75 | "a1", 76 | ast.New(cls_a_str, [ast.StringConstant("a1")]), 77 | inferred_type=cls_a_str 78 | ) 79 | var_a2 = ast.VariableDeclaration( 80 | "a2", 81 | ast.New(cls_a_any, [ast.StringConstant("a2")]), 82 | inferred_type=cls_a_any 83 | ) 84 | var_a3 = ast.VariableDeclaration( 85 | "a3", 86 | ast.New(cls_a_any, [ast.StringConstant("a3")]), 87 | inferred_type=cls_a_any 88 | ) 89 | var_b = ast.VariableDeclaration( 90 | "b", 91 | ast.New(cls_b_any_integer, []), 92 | var_type=cls_b_any_integer 93 | ) 94 | var_c = ast.VariableDeclaration( 95 | "c", 96 | ast.New(cls_b_double_integer, []), 97 | var_type=cls_b_double_integer 98 | ) 99 | main_body = ast.Block( 100 | body=[ 101 | var_a1, var_a2, var_a3, var_b, var_c, 102 | ast.FunctionCall( 103 | "bar", 104 | [ast.Variable('c')] 105 | ), 106 | ast.FunctionCall( 107 | "bar", 108 | [ast.New(cls_b_double_integer, [])] 109 | ), 110 | ast.FunctionCall( 111 | "foo1", 112 | [ast.Variable("a1")] 113 | ), 114 | ast.FunctionCall( 115 | "foo2", 116 | [ast.Variable("a2")] 117 | ), 118 | ] 119 | ) 120 | main_func = ast.FunctionDeclaration( 121 | "main", 122 | params=[], 123 | ret_type=types.Void, 124 | func_type=ast.FunctionDeclaration.FUNCTION, 125 | body=main_body, 126 | is_final=False 127 | ) 128 | 129 | 130 | ctx = Context() 131 | ctx.add_class(ast.GLOBAL_NAMESPACE, "AClass", cls_a) 132 | ctx.add_func(ast.GLOBAL_NAMESPACE + ("AClass",), fun_tmp.name, fun_tmp) 133 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("AClass",), field_x.name, field_x) 134 | ctx.add_class(ast.GLOBAL_NAMESPACE, "BClass", cls_b) 135 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_bar.name, fun_bar) 136 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("bar",), y_param.name, y_param) 137 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_buz.name, fun_buz) 138 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_foo1.name, fun_foo1) 139 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo1",), k_param.name, k_param) 140 | ctx.add_func(ast.GLOBAL_NAMESPACE, fun_foo2.name, fun_foo2) 141 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("foo2",), n_param.name, n_param) 142 | ctx.add_func(ast.GLOBAL_NAMESPACE, main_func.name, main_func) 143 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_a1.name, var_a1) 144 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_a2.name, var_a2) 145 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_a3.name, var_a3) 146 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_b.name, var_b) 147 | ctx.add_var(ast.GLOBAL_NAMESPACE + ("main",), var_c.name, var_c) 148 | program = ast.Program(ctx, language=lang) 149 | return program 150 | -------------------------------------------------------------------------------- /tests/resources/translators/program5.java: -------------------------------------------------------------------------------- 1 | class Main { 2 | static public final void main() { 3 | Object b = new B("b"); 4 | B ba = new B("ba"); 5 | B bb = ((b instanceof A b_is) ? 6 | ((b_is instanceof B b_is_is) ? 7 | b_is_is : 8 | ba) : 9 | ba); 10 | } 11 | } 12 | 13 | interface Function0 { 14 | public A1 apply(); 15 | } 16 | 17 | class A { 18 | public final String a; 19 | 20 | public A(String a) { 21 | this.a = a; 22 | } 23 | 24 | } 25 | 26 | class B extends A { 27 | public final String a; 28 | public B(String a) { 29 | super("b"); 30 | this.a = a; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/resources/translators/program5.py: -------------------------------------------------------------------------------- 1 | from src.ir.ast import * 2 | from src.ir.groovy_types import * 3 | from src.ir.context import * 4 | 5 | 6 | def produce_program(lang, types): 7 | a_a_field = FieldDeclaration("a", types.StringType(), is_final=True) 8 | a_cls = ClassDeclaration( 9 | "A", 10 | [], 11 | ClassDeclaration.REGULAR, 12 | [a_a_field], 13 | is_final=False 14 | ) 15 | 16 | b_a_field = FieldDeclaration( 17 | "a", 18 | types.StringType(), 19 | is_final=True, 20 | override=True 21 | ) 22 | b_cls = ClassDeclaration( 23 | "B", 24 | [SuperClassInstantiation(a_cls.get_type(), [StringConstant("b")])], 25 | ClassDeclaration.REGULAR, 26 | [b_a_field], 27 | is_final=False 28 | ) 29 | 30 | foo_b = VariableDeclaration( 31 | "b", 32 | New(b_cls.get_type(), [StringConstant("b")]), 33 | is_final=False, 34 | var_type=types.Object 35 | ) 36 | foo_a = VariableDeclaration( 37 | "ba", 38 | New(b_cls.get_type(), [StringConstant("ba")]), 39 | is_final=False, 40 | var_type=b_cls.get_type() 41 | ) 42 | foo_bb = VariableDeclaration( 43 | "bb", 44 | Conditional(Is(Variable("b"), a_cls.get_type()), 45 | Conditional(Is(Variable("b"), b_cls.get_type()), 46 | Variable("b"), Variable("ba"), b_cls.get_type()), 47 | Variable("ba"), b_cls.get_type()), 48 | is_final=False, 49 | var_type=b_cls.get_type() 50 | ) 51 | main_fun = FunctionDeclaration( 52 | "main", 53 | [], 54 | types.VoidType(), 55 | Block([foo_b, foo_a, foo_bb]), 56 | FunctionDeclaration.FUNCTION 57 | ) 58 | 59 | 60 | ctx = Context() 61 | ctx.add_class(GLOBAL_NAMESPACE, a_cls.name, a_cls) 62 | ctx.add_var(GLOBAL_NAMESPACE + ('A',), a_a_field.name, a_a_field) 63 | ctx.add_class(GLOBAL_NAMESPACE, b_cls.name, b_cls) 64 | ctx.add_var(GLOBAL_NAMESPACE + ('B',), b_a_field.name, b_a_field) 65 | ctx.add_func(GLOBAL_NAMESPACE, main_fun.name, main_fun) 66 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_b.name, foo_b) 67 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_a.name, foo_a) 68 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_bb.name, foo_bb) 69 | program = Program(ctx, language=lang) 70 | return program 71 | -------------------------------------------------------------------------------- /tests/resources/translators/program6.java: -------------------------------------------------------------------------------- 1 | class Main { 2 | static public final void main() { 3 | Object b = new B("b"); 4 | A ba = new B("ba"); 5 | A bb = ((b instanceof A b_is) ? 6 | b_is : 7 | ((Function0) (() -> { 8 | A bc = new B("bc"); 9 | return bc; 10 | })).apply()); 11 | } 12 | } 13 | 14 | interface Function0 { 15 | public A1 apply(); 16 | } 17 | 18 | class A { 19 | public final String a; 20 | 21 | public A(String a) { 22 | this.a = a; 23 | } 24 | 25 | } 26 | 27 | class B extends A { 28 | public final String a; 29 | public B(String a) { 30 | super("b"); 31 | this.a = a; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/resources/translators/program6.py: -------------------------------------------------------------------------------- 1 | from src.ir.ast import * 2 | from src.ir.groovy_types import * 3 | from src.ir.context import * 4 | 5 | 6 | def produce_program(lang, types): 7 | a_a_field = FieldDeclaration("a", types.StringType(), is_final=True) 8 | a_cls = ClassDeclaration( 9 | "A", 10 | [], 11 | ClassDeclaration.REGULAR, 12 | [a_a_field], 13 | is_final=False 14 | ) 15 | 16 | b_a_field = FieldDeclaration( 17 | "a", 18 | types.StringType(), 19 | is_final=True, 20 | override=True 21 | ) 22 | b_cls = ClassDeclaration( 23 | "B", 24 | [SuperClassInstantiation(a_cls.get_type(), [StringConstant("b")])], 25 | ClassDeclaration.REGULAR, 26 | [b_a_field], 27 | is_final=False 28 | ) 29 | 30 | foo_b = VariableDeclaration( 31 | "b", 32 | New(b_cls.get_type(), [StringConstant("b")]), 33 | is_final=False, 34 | var_type=types.Object 35 | ) 36 | foo_a = VariableDeclaration( 37 | "ba", 38 | New(b_cls.get_type(), [StringConstant("ba")]), 39 | is_final=False, 40 | var_type=a_cls.get_type() 41 | ) 42 | foo_bc = VariableDeclaration("bc", 43 | New(b_cls.get_type(), 44 | [StringConstant("bc")]), 45 | is_final=False, 46 | var_type=a_cls.get_type()) 47 | foo_bb = VariableDeclaration( 48 | "bb", 49 | Conditional(Is(Variable("b"), a_cls.get_type()), 50 | Variable("b"), 51 | Block([foo_bc, Variable("bc")]), a_cls.get_type()), 52 | is_final=False, 53 | var_type=a_cls.get_type() 54 | ) 55 | main_fun = FunctionDeclaration( 56 | "main", 57 | [], 58 | types.VoidType(), 59 | Block([foo_b, foo_a, foo_bb]), 60 | FunctionDeclaration.FUNCTION 61 | ) 62 | 63 | 64 | ctx = Context() 65 | ctx.add_class(GLOBAL_NAMESPACE, a_cls.name, a_cls) 66 | ctx.add_var(GLOBAL_NAMESPACE + ('A',), a_a_field.name, a_a_field) 67 | ctx.add_class(GLOBAL_NAMESPACE, b_cls.name, b_cls) 68 | ctx.add_var(GLOBAL_NAMESPACE + ('B',), b_a_field.name, b_a_field) 69 | ctx.add_func(GLOBAL_NAMESPACE, main_fun.name, main_fun) 70 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_b.name, foo_b) 71 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_a.name, foo_a) 72 | ctx.add_var(GLOBAL_NAMESPACE + ('main',), foo_bc.name, foo_bc) 73 | program = Program(ctx, language=lang) 74 | return program 75 | -------------------------------------------------------------------------------- /tests/test_call_analysis.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | from src.ir import ast 4 | from src.analysis.call_analysis import CallAnalysis, CNode 5 | from tests.resources import (program1, program4, program5, program8, program9, 6 | program10, program11, program12) 7 | 8 | 9 | def str2node(string): 10 | segs = tuple(string.split("/")) 11 | return CNode(segs) 12 | 13 | 14 | def assert_nodes(nodes, expected_nodes, calls, calls_number): 15 | assert len(nodes) == len(expected_nodes) 16 | assert (nodes - expected_nodes) == set() 17 | assert len(calls) == calls_number 18 | 19 | 20 | # Simple function call from method to method declared in the same class 21 | def test_program1(): 22 | ca = CallAnalysis(program1.program) 23 | cg, calls = ca.result() 24 | 25 | foo = str2node("global/A/foo") 26 | bar = str2node("global/A/bar") 27 | buz = str2node("global/A/buz") 28 | spam = str2node("global/A/spam") 29 | 30 | assert_nodes(cg[foo], {bar}, calls[foo], 0) 31 | assert_nodes(cg[bar], set(), calls[bar], 1) 32 | assert_nodes(cg[buz], set(), calls[buz], 0) 33 | assert_nodes(cg[spam], set(), calls[spam], 0) 34 | 35 | 36 | # Inner method call 37 | def test_program4(): 38 | ca = CallAnalysis(program4.program) 39 | cg, calls = ca.result() 40 | 41 | foo = str2node("global/A/foo") 42 | bar = str2node("global/A/foo/bar") 43 | 44 | assert_nodes(cg[foo], {bar}, calls[foo], 0) 45 | assert_nodes(cg[bar], set(), calls[bar], 1) 46 | 47 | 48 | # Multiple method calls 49 | def test_program5(): 50 | ca = CallAnalysis(program5.program) 51 | cg, calls = ca.result() 52 | 53 | 54 | foo = str2node("global/A/foo") 55 | bar = str2node("global/A/bar") 56 | baz = str2node("global/A/bar/baz") 57 | quz = str2node("global/A/quz") 58 | 59 | assert_nodes(cg[foo], set(), calls[foo], 1) 60 | assert_nodes(cg[bar], {foo, baz, quz}, calls[bar], 0) 61 | assert_nodes(cg[baz], set(), calls[baz], 1) 62 | assert_nodes(cg[quz], set(), calls[quz], 1) 63 | 64 | 65 | # There is a call from foo to x.length() which is not a user defined function. 66 | # Currently, we skip such calls. 67 | def test_program8(): 68 | ca = CallAnalysis(program8.program) 69 | cg, calls = ca.result() 70 | 71 | foo = str2node("global/A/foo") 72 | 73 | assert_nodes(cg[foo], set(), calls[foo], 0) 74 | 75 | 76 | # Simple function call 77 | def test_program9(): 78 | ca = CallAnalysis(program9.program) 79 | cg, calls = ca.result() 80 | 81 | foo = str2node("global/foo") 82 | bar = str2node("global/bar") 83 | 84 | assert_nodes(cg[foo], set(), calls[foo], 1) 85 | assert_nodes(cg[bar], {foo}, calls[bar], 0) 86 | 87 | 88 | # Variable receivers 89 | def test_program10(): 90 | ca = CallAnalysis(program10.program) 91 | cg, calls = ca.result() 92 | 93 | first_foo = str2node("global/First/foo") 94 | second_foo = str2node("global/Second/foo") 95 | third_foo = str2node("global/Third/foo") 96 | global_foo = str2node("global/foo") 97 | global_bar = str2node("global/bar") 98 | 99 | assert_nodes(cg[first_foo], set(), calls[first_foo], 2) 100 | assert_nodes(cg[second_foo], set(), calls[second_foo], 1) 101 | assert_nodes(cg[third_foo], {first_foo}, calls[third_foo], 0) 102 | assert_nodes(cg[global_foo], {first_foo}, calls[global_foo], 0) 103 | assert_nodes(cg[global_bar], {second_foo}, calls[global_bar], 0) 104 | 105 | # Variable receivers with function in variable declaration 106 | def test_program11(): 107 | ca = CallAnalysis(program11.program) 108 | cg, calls = ca.result() 109 | 110 | first_foo = str2node("global/First/foo") 111 | second_foo = str2node("global/Second/foo") 112 | global_foo = str2node("global/foo") 113 | global_bar = str2node("global/bar") 114 | 115 | assert_nodes(cg[first_foo], set(), calls[first_foo], 1) 116 | assert_nodes(cg[second_foo], set(), calls[second_foo], 0) 117 | assert_nodes(cg[global_foo], set(), calls[global_foo], 1) 118 | assert_nodes(cg[global_bar], {global_foo, first_foo}, calls[global_bar], 0) 119 | 120 | # Function call and New receivers 121 | def test_program12(): 122 | ca = CallAnalysis(program12.program) 123 | cg, calls = ca.result() 124 | 125 | first_foo = str2node("global/First/foo") 126 | second_foo = str2node("global/Second/foo") 127 | global_foo = str2node("global/foo") 128 | global_bar = str2node("global/bar") 129 | 130 | assert_nodes(cg[first_foo], set(), calls[first_foo], 1) 131 | assert_nodes(cg[second_foo], set(), calls[second_foo], 1) 132 | assert_nodes(cg[global_foo], set(), calls[global_foo], 1) 133 | assert_nodes(cg[global_bar], {global_foo, first_foo, second_foo}, calls[global_bar], 0) 134 | -------------------------------------------------------------------------------- /tests/test_graph_utils.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from src.analysis.use_analysis import GNode, NONE_NODE 3 | from src.graph_utils import * 4 | 5 | 6 | NONE = NONE_NODE 7 | X = GNode(('global', 'A'), 'x') 8 | BAR_Y = GNode(('global', 'A', 'bar'), 'y') 9 | BAR_Z = GNode(('global', 'A', 'bar'), 'z') 10 | BAR_ARG = GNode(('global', 'A', 'bar'), 'arg') 11 | FOO_Q = GNode(('global', 'A', 'foo'), 'q') 12 | FOO_X = GNode(('global', 'A', 'foo'), 'x') 13 | FOO_Y = GNode(('global', 'A', 'foo'), 'y') 14 | FOO_Z = GNode(('global', 'A', 'foo'), 'z') 15 | BUZ_K = GNode(('global', 'A', 'buz'), 'k') 16 | GRAPH = {NONE: {GNode(('global', 'A', 'bar'), 'y')}, 17 | X: {NONE}, 18 | BAR_ARG: {NONE}, 19 | BAR_Y: {BUZ_K}, 20 | BAR_Z: set(), 21 | FOO_Q: {BAR_Z, FOO_X}, 22 | FOO_X: set(), 23 | FOO_Y: {BAR_ARG}, 24 | FOO_Z: {FOO_Q}, 25 | BUZ_K: set() 26 | } 27 | 28 | N0 = "N0" 29 | N1 = "N1" 30 | N2 = "N2" 31 | N3 = "N3" 32 | N4 = "N4" 33 | GRAPH2 = {N0: {N1}, 34 | N1: {N2}, 35 | N2: {N3}, 36 | N4: {N3}, 37 | N3: {} 38 | } 39 | # N4 40 | # \ 41 | # -> N3 42 | # / 43 | # N0 -> N1 -> N2 44 | GRAPH3 = {X: {NONE}} 45 | 46 | 47 | def test_reachable(): 48 | assert reachable(GRAPH, X, X) 49 | assert reachable(GRAPH, X, NONE) 50 | assert reachable(GRAPH, X, BAR_Y) 51 | assert reachable(GRAPH, BAR_ARG, NONE) 52 | assert reachable(GRAPH, FOO_Q, FOO_X) 53 | assert reachable(GRAPH, FOO_Q, BAR_Z) 54 | assert reachable(GRAPH, FOO_Y, BAR_ARG) 55 | assert reachable(GRAPH, FOO_Y, NONE) 56 | assert reachable(GRAPH, FOO_Z, FOO_Q) 57 | assert reachable(GRAPH, FOO_Q, BAR_Z) 58 | assert not reachable(GRAPH, BAR_Y, NONE) 59 | assert not reachable(GRAPH, BAR_Y, X) 60 | assert not reachable(GRAPH, BUZ_K, NONE) 61 | assert not reachable(GRAPH, BAR_Z, FOO_Q) 62 | assert not reachable(GRAPH, BAR_Z, FOO_Z) 63 | assert not reachable(GRAPH, BAR_Z, FOO_X) 64 | assert not reachable(GRAPH, BAR_ARG, FOO_Z) 65 | assert reachable(GRAPH2, N0, N1) 66 | assert reachable(GRAPH2, N0, N3) 67 | assert not reachable(GRAPH2, N3, N0) 68 | assert not reachable(GRAPH2, N3, N2) 69 | assert not reachable(GRAPH2, N3, N4) 70 | assert not reachable(GRAPH2, N4, N2) 71 | assert not reachable(GRAPH2, N0, N4) 72 | assert not reachable(GRAPH3, X, BAR_ARG) 73 | 74 | 75 | def test_bi_reachable(): 76 | assert bi_reachable(GRAPH, X, X) 77 | assert bi_reachable(GRAPH, X, NONE) 78 | assert bi_reachable(GRAPH, X, BAR_Y) 79 | assert bi_reachable(GRAPH, BAR_ARG, NONE) 80 | assert bi_reachable(GRAPH, FOO_Q, FOO_X) 81 | assert bi_reachable(GRAPH, FOO_Q, BAR_Z) 82 | assert bi_reachable(GRAPH, FOO_Y, BAR_ARG) 83 | assert bi_reachable(GRAPH, FOO_Y, NONE) 84 | assert bi_reachable(GRAPH, FOO_Z, FOO_Q) 85 | assert bi_reachable(GRAPH, FOO_Q, BAR_Z) 86 | assert bi_reachable(GRAPH, BAR_Y, NONE) 87 | assert bi_reachable(GRAPH, BAR_Y, X) 88 | assert bi_reachable(GRAPH, BUZ_K, NONE) 89 | assert bi_reachable(GRAPH, BAR_Z, FOO_Q) 90 | assert bi_reachable(GRAPH, BAR_Z, FOO_Z) 91 | assert not bi_reachable(GRAPH, BAR_Z, FOO_X) 92 | assert not bi_reachable(GRAPH, BAR_ARG, FOO_Z) 93 | assert bi_reachable(GRAPH2, N0, N1) 94 | assert bi_reachable(GRAPH2, N0, N3) 95 | assert bi_reachable(GRAPH2, N3, N0) 96 | assert bi_reachable(GRAPH2, N3, N2) 97 | assert bi_reachable(GRAPH2, N3, N4) 98 | assert not bi_reachable(GRAPH2, N4, N2) 99 | assert not bi_reachable(GRAPH2, N0, N4) 100 | assert not bi_reachable(GRAPH3, X, BAR_ARG) 101 | 102 | 103 | def test_none_reachable(): 104 | assert none_reachable(GRAPH, X) 105 | assert none_reachable(GRAPH, FOO_Y) 106 | assert none_reachable(GRAPH, BAR_Y) 107 | assert none_reachable(GRAPH, BUZ_K) 108 | assert none_reachable(GRAPH, BUZ_K) 109 | assert not none_reachable(GRAPH, FOO_Q) 110 | assert not none_reachable(GRAPH, BAR_Z) 111 | 112 | 113 | def test_connected(): 114 | assert connected(GRAPH, X, X) 115 | assert connected(GRAPH, X, NONE) 116 | assert connected(GRAPH, X, BAR_Y) 117 | assert connected(GRAPH, BAR_ARG, NONE) 118 | assert connected(GRAPH, FOO_Q, FOO_X) 119 | assert connected(GRAPH, FOO_Q, BAR_Z) 120 | assert connected(GRAPH, FOO_Y, BAR_ARG) 121 | assert connected(GRAPH, FOO_Y, NONE) 122 | assert connected(GRAPH, FOO_Z, FOO_Q) 123 | assert connected(GRAPH, FOO_Q, BAR_Z) 124 | assert connected(GRAPH, BAR_Y, NONE) 125 | assert connected(GRAPH, BAR_Y, X) 126 | assert connected(GRAPH, BUZ_K, NONE) 127 | assert connected(GRAPH, BAR_Z, FOO_Q) 128 | assert connected(GRAPH, BAR_Z, FOO_Z) 129 | assert connected(GRAPH, BAR_Z, FOO_X) 130 | assert not connected(GRAPH, BAR_ARG, FOO_Z) 131 | assert connected(GRAPH2, N0, N1) 132 | assert connected(GRAPH2, N0, N3) 133 | assert connected(GRAPH2, N3, N0) 134 | assert connected(GRAPH2, N3, N2) 135 | assert connected(GRAPH2, N3, N4) 136 | assert connected(GRAPH2, N4, N2) 137 | assert connected(GRAPH2, N0, N4) 138 | assert not connected(GRAPH3, X, BAR_ARG) 139 | 140 | 141 | def test_none_connected(): 142 | assert none_connected(GRAPH, X) 143 | assert none_connected(GRAPH, FOO_Y) 144 | assert none_connected(GRAPH, BAR_Y) 145 | assert none_connected(GRAPH, BUZ_K) 146 | assert none_connected(GRAPH, BUZ_K) 147 | assert not none_connected(GRAPH, FOO_Q) 148 | assert not none_connected(GRAPH, BAR_Z) 149 | 150 | 151 | def compare_lists(a, b): 152 | return sorted(a) == sorted(b) 153 | 154 | 155 | def test_find_all_paths(): 156 | assert compare_lists(find_all_paths(GRAPH, BAR_Z), [[BAR_Z]]) 157 | assert compare_lists(find_all_paths(GRAPH, FOO_Q), 158 | [ 159 | [FOO_Q], 160 | [FOO_Q, BAR_Z], 161 | [FOO_Q, FOO_X] 162 | ]) 163 | assert compare_lists(find_all_paths(GRAPH, FOO_Z), 164 | [ 165 | [FOO_Z], 166 | [FOO_Z, FOO_Q], 167 | [FOO_Z, FOO_Q, BAR_Z], 168 | [FOO_Z, FOO_Q, FOO_X] 169 | ]) 170 | 171 | 172 | def test_find_longest_paths(): 173 | assert find_longest_paths(GRAPH, BAR_Z) == [[BAR_Z]] 174 | assert compare_lists(find_longest_paths(GRAPH, FOO_Q), 175 | [ 176 | [FOO_Q, BAR_Z], 177 | [FOO_Q, FOO_X] 178 | ]) 179 | assert compare_lists(find_longest_paths(GRAPH, FOO_Z), 180 | [ 181 | [FOO_Z, FOO_Q, BAR_Z], 182 | [FOO_Z, FOO_Q, FOO_X] 183 | ]) 184 | 185 | 186 | def test_find_all_reachable(): 187 | assert find_all_reachable(GRAPH, BAR_Z) == {BAR_Z} 188 | assert find_all_reachable(GRAPH, FOO_Q) == {FOO_Q, BAR_Z, FOO_X} 189 | assert find_all_reachable(GRAPH, FOO_Z) == {FOO_Z, FOO_Q, BAR_Z, FOO_X} 190 | 191 | 192 | def test_find_all_bi_reachable(): 193 | assert find_all_bi_reachable(GRAPH, BAR_Z) == {BAR_Z, FOO_Q, FOO_Z} 194 | assert find_all_bi_reachable(GRAPH, FOO_Q) == {BAR_Z, FOO_Q, FOO_Z, FOO_X} 195 | assert find_all_bi_reachable(GRAPH, FOO_Z) == {BAR_Z, FOO_Q, FOO_Z, FOO_X} 196 | 197 | 198 | def test_find_all_connected(): 199 | assert find_all_connected(GRAPH, BAR_Z) == {BAR_Z, FOO_Q, FOO_Z, FOO_X} 200 | assert find_all_connected(GRAPH, FOO_Q) == {BAR_Z, FOO_Q, FOO_Z, FOO_X} 201 | assert find_all_connected(GRAPH, FOO_Z) == {BAR_Z, FOO_Q, FOO_Z, FOO_X} 202 | assert find_all_connected(GRAPH2, N0) == {N0, N1, N2, N3, N4} 203 | 204 | 205 | def test_find_sources(): 206 | assert find_sources(GRAPH2, N0) == [N0] 207 | assert find_sources(GRAPH2, N1) == [N0] 208 | assert find_sources(GRAPH2, N2) == [N0] 209 | assert find_sources(GRAPH2, N3) == [N4, N0] 210 | assert find_sources(GRAPH2, N4) == [N4] 211 | -------------------------------------------------------------------------------- /tests/test_translators.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | 4 | 5 | from src.ir import groovy_types, java_types 6 | from src.translators.groovy import GroovyTranslator 7 | from src.translators.java import JavaTranslator 8 | from tests.resources.translators import (program1, program2, program3, 9 | program4, program5, program6) 10 | 11 | 12 | LANG_LOOKUP = { 13 | "groovy": (GroovyTranslator, groovy_types, "groovy"), 14 | "java": (JavaTranslator, java_types, "java") 15 | } 16 | TEST_DIR = "tests/resources/translators/" 17 | 18 | 19 | def translate(translator_cls, program): 20 | translator = translator_cls() 21 | translator.visit(program) 22 | return translator.result() 23 | 24 | 25 | def read_expected(path): 26 | with open(path, 'r') as expf: 27 | return expf.read() 28 | 29 | 30 | def find_targets(program): 31 | return [f for f in os.listdir(TEST_DIR) 32 | if f.startswith(program) and f != program + ".py"] 33 | 34 | 35 | def run_test(program_name, program): 36 | return 37 | #for target_program in find_targets(program_name): 38 | # expected = os.path.join(TEST_DIR, target_program) 39 | # translator, types, lang = LANG_LOOKUP[target_program.split(".")[-1]] 40 | # ast = program.produce_program(lang, types) 41 | # res = translate(translator, ast) 42 | # expected_res = read_expected(expected) 43 | # res = re.sub('\s+', ' ', res) 44 | # expected_res = re.sub('\s+', ' ', expected_res) 45 | # assert res.strip() == expected_res.strip() 46 | 47 | 48 | def test_cls(): 49 | run_test("program1", program1) 50 | 51 | 52 | def test_global(): 53 | run_test("program2", program2) 54 | 55 | 56 | def test_closures(): 57 | run_test("program3", program3) 58 | 59 | 60 | def test_generics(): 61 | run_test("program4", program4) 62 | 63 | 64 | def test_is(): 65 | run_test("program5", program5) 66 | 67 | 68 | def test_condition_block(): 69 | run_test("program6", program6) 70 | --------------------------------------------------------------------------------