[Auto-Sync] LLVM 18 update (#2296)

Refactor auto-sync updater

This refactors the auto-sync updater scripts, adds multiple tests and some other smaller things:

- Converts the updater in a proper Python package.
- Renaming was done to fit this new package structure.
- Format code with usort and black and enforce it with the CI.
- Add license information to auto-sync scripts.
- Update tree-sitter-cpp to v20.0.5
- Fix py-tree-sitter version to `< 0.22.0` due to https://github.com/tree-sitter/tree-sitter-cpp/issues/250
- Allow file/dir creation of non existing paths.
- Add CI tests for Patch, inc gen, translation and diff persistence testing.
- Implement editing of diffs with an editor.
- Fix: Add Namespace id also to anonymous enumeration members.
This commit is contained in:
Rot127 2024-04-22 03:55:44 +00:00 committed by GitHub
parent 24d99a907b
commit 7746648f0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
104 changed files with 2393 additions and 547 deletions

66
.github/workflows/auto-sync.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: Auto-Sync
on:
push:
paths:
- "suite/auto-sync/**"
pull_request:
jobs:
check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: suite/auto-sync/
steps:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: true
- name: Install auto-sync package
run: |
pip install .
- name: Check formatting
run: |
python3.11 -m black --check src/autosync
- name: Build llvm-tblgen
run: |
git clone https://github.com/capstone-engine/llvm-capstone.git vendor/llvm_root
cd vendor/llvm_root
mkdir build
cd build
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ../llvm
cmake --build . --target llvm-tblgen --config Debug
cd ../../../
- name: Test generation of inc files
run: |
./src/autosync/ASUpdater.py -d -a AArch64 -s IncGen
./src/autosync/ASUpdater.py -d -a Alpha -s IncGen
./src/autosync/ASUpdater.py -d -a ARM -s IncGen
./src/autosync/ASUpdater.py -d -a PPC -s IncGen
- name: CppTranslator - Patch tests
run: |
python -m unittest src/autosync/cpptranslator/Tests/test_patches.py
- name: CppTranslator - Differ tests
run: |
python -m unittest src/autosync/cpptranslator/Tests/test_differ.py
- name: CppTranslator - Test translation
run: |
./src/autosync/ASUpdater.py --ci -d -a AArch64 -s Translate
./src/autosync/ASUpdater.py --ci -d -a ARM -s Translate
./src/autosync/ASUpdater.py --ci -d -a PPC -s Translate
- name: Test Header patcher
run: |
python -m unittest src/autosync/Tests/test_header_patcher.py

3
.gitignore vendored
View File

@ -143,3 +143,6 @@ android-ndk-*
# python virtual env # python virtual env
.venv/ .venv/
# Auto-sync files
suite/auto-sync/src/autosync.egg-info

20
.reuse/dep5 Normal file
View File

@ -0,0 +1,20 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: CapstoneEngine
Source: https://github.com/capstone-engine/capstone
Files: src/autosync/cpptranslator/Tests/test_config.json
Copyright: 2022 Rot127 <unisono@quyllur.org>
License: BSD-3
Files: src/autosync/cpptranslator/arch_config.json
Copyright: 2022 Rot127 <unisono@quyllur.org>
License: BSD-3
Files: src/autosync/cpptranslator/saved_patches.json
Copyright: 2022 Rot127 <unisono@quyllur.org>
License: BSD-3
Files: src/autosync/path_vars.json
Copyright: 2022 Rot127 <unisono@quyllur.org>
License: BSD-3

View File

@ -0,0 +1,9 @@
{% for copyright_line in copyright_lines %}
{{ copyright_line }}
{% endfor %}
{% for contributor_line in contributor_lines %}
SPDX-FileContributor: {{ contributor_line }}
{% endfor %}
{% for expression in spdx_expressions %}
SPDX-License-Identifier: {{ expression }}
{% endfor %}

View File

@ -0,0 +1,11 @@
Copyright (c) <year> <owner>.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -96,7 +96,7 @@ def copy_sources():
src.extend(glob.glob(os.path.join(BUILD_DIR, "*.mk"))) src.extend(glob.glob(os.path.join(BUILD_DIR, "*.mk")))
src.extend(glob.glob(os.path.join(BUILD_DIR, "Makefile"))) src.extend(glob.glob(os.path.join(BUILD_DIR, "Makefile")))
src.extend(glob.glob(os.path.join(BUILD_DIR, "LICENSE*"))) src.extend(glob.glob(os.path.join(BUILD_DIR, "LICENSES/*")))
src.extend(glob.glob(os.path.join(BUILD_DIR, "README"))) src.extend(glob.glob(os.path.join(BUILD_DIR, "README")))
src.extend(glob.glob(os.path.join(BUILD_DIR, "*.TXT"))) src.extend(glob.glob(os.path.join(BUILD_DIR, "*.TXT")))
src.extend(glob.glob(os.path.join(BUILD_DIR, "RELEASE_NOTES"))) src.extend(glob.glob(os.path.join(BUILD_DIR, "RELEASE_NOTES")))

View File

@ -1,4 +0,0 @@
tree-sitter==0.20.1
termcolor==2.2.0
cmake==3.27.9
ninja==1.11.1.1

View File

@ -1,5 +1,7 @@
build/ build/
vendor/llvm_root vendor/llvm_root
*/.idea */.idea
Updater/config.json src/auto-sync/config.json
src/autosync/cpptranslator/Tests/Differ/test_saved_patches.json
src/autosync.egg-info

View File

@ -1,3 +1,9 @@
<!--
Copyright © 2022 Rot127 <unisono@quyllur.org>
Copyright © 2024 2022 Rot127 <unisono@quyllur.org>
SPDX-License-Identifier: BSD-3
-->
# Architecture updater # Architecture updater
This is Capstones updater for some architectures. This is Capstones updater for some architectures.
@ -5,12 +11,6 @@ Unfortunately not all architectures are supported yet.
## Install dependencies ## Install dependencies
Install clang-format
```
sudo apt install clang-format-18
```
Setup Python environment and Tree-sitter Setup Python environment and Tree-sitter
``` ```
@ -20,7 +20,6 @@ sudo apt install python3-venv
# Setup virtual environment in Capstone root dir # Setup virtual environment in Capstone root dir
python3 -m venv ./.venv python3 -m venv ./.venv
source ./.venv/bin/activate source ./.venv/bin/activate
pip3 install -r dev_requirements.txt
``` ```
Clone C++ grammar Clone C++ grammar
@ -28,6 +27,7 @@ Clone C++ grammar
``` ```
cd suite/auto-sync/ cd suite/auto-sync/
git submodule update --init --recursive ./vendor/ git submodule update --init --recursive ./vendor/
pip install -e .
``` ```
## Update ## Update
@ -35,13 +35,13 @@ git submodule update --init --recursive ./vendor/
Check if your architecture is supported. Check if your architecture is supported.
``` ```
./Updater/ASUpdater.py -h ./src/autosync/ASUpdater.py -h
``` ```
Clone Capstones LLVM fork and build `llvm-tblgen` Clone Capstones LLVM fork and build `llvm-tblgen`
``` ```
git clone https://github.com/capstone-engine/llvm-capstone git clone https://github.com/capstone-engine/llvm-capstone vendor/llvm_root/
cd llvm-capstone cd llvm-capstone
git checkout auto-sync git checkout auto-sync
mkdir build mkdir build
@ -55,7 +55,7 @@ cd ../../
Run the updater Run the updater
``` ```
./Updater/ASUpdater.py -a <ARCH> ./src/autosync/ASUpdater.py -a <ARCH>
``` ```
## Post-processing steps ## Post-processing steps

View File

@ -1,2 +0,0 @@
*/.idea/

View File

@ -1,67 +0,0 @@
import json
import logging as log
import re
import subprocess
from pathlib import Path
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class PathVarHandler(metaclass=Singleton):
paths = {}
def __init__(self) -> None:
try:
res = subprocess.run(["git", "rev-parse", "--show-toplevel"], check=True, stdout=subprocess.PIPE)
except subprocess.CalledProcessError:
log.fatal("Could not get repository top level directory.")
exit(1)
repo_root = res.stdout.decode("utf8").strip("\n")
# The main directories
self.paths["{CS_ROOT}"] = Path(repo_root)
self.paths["{AUTO_SYNC_ROOT}"] = Path(repo_root).joinpath("suite/auto-sync/")
self.paths["{AUTO_SYNC_UPDATER_DIR}"] = self.paths["{AUTO_SYNC_ROOT}"].joinpath("Updater/")
path_config_file = self.paths["{AUTO_SYNC_UPDATER_DIR}"].joinpath("path_vars.json")
# Load variables
with open(path_config_file) as f:
vars = json.load(f)
missing = list()
for p_name, path in vars.items():
resolved = path
for var_id in re.findall(r"\{.+}", resolved):
if var_id not in self.paths:
log.fatal(
f"{var_id} hasn't been added to the PathVarsHandler, yet. The var must be defined in a previous entry."
)
exit(1)
resolved = re.sub(var_id, str(self.paths[var_id]), resolved)
log.debug(f"Set {p_name} = {resolved}")
if not Path(resolved).exists():
missing.append(resolved)
self.paths[p_name] = resolved
if len(missing) > 0:
log.fatal(f"Some paths from config file are missing!")
for m in missing:
log.fatal(f"\t{m}")
exit(1)
def get_path(self, name: str) -> Path:
if name not in self.paths:
raise ValueError(f"Path variable {name} has no path saved.")
return self.paths[name]
def complete_path(self, path_str: str) -> Path:
resolved = path_str
for p_name in re.findall(r"\{.+}", path_str):
resolved = re.sub(p_name, self.get_path(p_name), resolved)
return Path(resolved)

View File

@ -1,7 +0,0 @@
# The update scripts for auto-sync
## Updating only the `inc` files
```cmd
> ./IncGenerator.py ...
```

View File

@ -1,22 +0,0 @@
{
"{LLVM_ROOT}": "{AUTO_SYNC_ROOT}/llvm-capstone/",
"{LLVM_TARGET_DIR}": "{AUTO_SYNC_ROOT}/llvm-capstone/llvm/lib/Target/",
"{LLVM_TBLGEN_BIN}": "{AUTO_SYNC_ROOT}/llvm-capstone/build/bin/llvm-tblgen",
"{LLVM_INCLUDE_DIR}": "{AUTO_SYNC_ROOT}/llvm-capstone/llvm/include",
"{VENDOR_DIR}": "{AUTO_SYNC_ROOT}/vendor/",
"{AUTO_SYNC_UPDATER_DIR}": "{AUTO_SYNC_ROOT}/Updater/",
"{CPP_TRANSLATOR_DIR}": "{AUTO_SYNC_ROOT}/Updater/CppTranslator/",
"{CPP_TRANSLATOR_CONFIG}": "{CPP_TRANSLATOR_DIR}/arch_config.json",
"{INC_PATCH_DIR}": "{AUTO_SYNC_ROOT}/inc_patches/",
"{CS_INCLUDE_DIR}": "{CS_ROOT}/include/capstone/",
"{CS_ARCH_MODULE_DIR}": "{CS_ROOT}/arch/",
"{CS_CLANG_FORMAT_FILE}": "{CS_ROOT}/.clang-format",
"{BUILD_DIR}": "{AUTO_SYNC_ROOT}/build/",
"{C_INC_OUT_DIR}": "{BUILD_DIR}/llvm_c_inc/",
"{CPP_INC_OUT_DIR}": "{BUILD_DIR}/llvm_cpp_inc/"
}

View File

@ -1,2 +0,0 @@
termcolor>=2.3.0
tree_sitter>=0.20.2

4
suite/auto-sync/format_py.sh Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/bash
python3.11 -m usort format src/autosync
python3.11 -m black src/autosync

View File

@ -0,0 +1,23 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# Copyright © 2024 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
[project]
name = "autosync"
version = "0.1.0"
dependencies = [
"termcolor >= 2.3.0",
"tree_sitter < 0.22.0",
"black >= 24.3.0",
"usort >= 1.0.8",
"setuptools >= 69.2.0",
"ninja >= 1.11.1.1",
"cmake >= 3.28.3",
"reuse >= 3.0.1",
"clang-format >= 18.1.1",
]
requires-python = ">= 3.11"
[tool.setuptools]
packages = ["autosync", "autosync.cpptranslator", "autosync.cpptranslator.patches"]
package-dir = {"" = "src"}

View File

@ -1,21 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import argparse import argparse
import logging as log
import os import os
import shutil import shutil
import subprocess import subprocess
import sys import sys
import logging as log
from enum import StrEnum from enum import StrEnum
from CppTranslator.Configurator import Configurator
from CppTranslator.CppTranslator import Translator
from IncGenerator import IncGenerator
from Helper import get_path, convert_loglevel, check_py_version, fail_exit
from HeaderPatcher import HeaderPatcher
from pathlib import Path from pathlib import Path
from autosync.cpptranslator.Configurator import Configurator
from autosync.cpptranslator.CppTranslator import Translator
from autosync.HeaderPatcher import HeaderPatcher
from autosync.Helper import check_py_version, convert_loglevel, fail_exit, get_path
from autosync.IncGenerator import IncGenerator
class USteps(StrEnum): class USteps(StrEnum):
INC_GEN = "IncGen" INC_GEN = "IncGen"
@ -38,11 +42,13 @@ class ASUpdater:
no_clean: bool, no_clean: bool,
refactor: bool, refactor: bool,
differ_no_auto_apply: bool, differ_no_auto_apply: bool,
wait_for_user: bool = True,
) -> None: ) -> None:
self.arch = arch self.arch = arch
self.write = write self.write = write
self.no_clean_build = no_clean self.no_clean_build = no_clean
self.inc_list = inc_list self.inc_list = inc_list
self.wait_for_user = wait_for_user
if USteps.ALL in steps: if USteps.ALL in steps:
self.steps = [USteps.INC_GEN, USteps.TRANS, USteps.DIFF] self.steps = [USteps.INC_GEN, USteps.TRANS, USteps.DIFF]
else: else:
@ -108,20 +114,22 @@ class ASUpdater:
ts_dir = get_path("{VENDOR_DIR}").joinpath("tree-sitter-cpp") ts_dir = get_path("{VENDOR_DIR}").joinpath("tree-sitter-cpp")
if not ts_dir.exists(): if not ts_dir.exists():
log.info("tree-sitter was not fetched. Cloning it now...") log.info("tree-sitter was not fetched. Cloning it now...")
subprocess.run(["git", "submodule", "update", "--init", "--recursive"], check=True) subprocess.run(
["git", "submodule", "update", "--init", "--recursive"], check=True
)
def translate(self) -> None: def translate(self) -> None:
self.check_tree_sitter() self.check_tree_sitter()
translator_config = get_path("{CPP_TRANSLATOR_CONFIG}") translator_config = get_path("{CPP_TRANSLATOR_CONFIG}")
configurator = Configurator(self.arch, translator_config) configurator = Configurator(self.arch, translator_config)
translator = Translator(configurator) translator = Translator(configurator, self.wait_for_user)
translator.translate() translator.translate()
translator.remark_manual_files() translator.remark_manual_files()
def diff(self) -> None: def diff(self) -> None:
translator_config = get_path("{CPP_TRANSLATOR_CONFIG}") translator_config = get_path("{CPP_TRANSLATOR_CONFIG}")
configurator = Configurator(self.arch, translator_config) configurator = Configurator(self.arch, translator_config)
from CppTranslator.Differ import Differ from autosync.cpptranslator.Differ import Differ
differ = Differ(configurator, self.differ_no_auto_apply) differ = Differ(configurator, self.differ_no_auto_apply)
differ.diff() differ.diff()
@ -151,11 +159,23 @@ def parse_args() -> argparse.Namespace:
description="Capstones architecture module updater.", description="Capstones architecture module updater.",
) )
parser.add_argument( parser.add_argument(
"-a", dest="arch", help="Name of target architecture.", choices=["ARM", "PPC", "AArch64", "Alpha"], required=True "-a",
dest="arch",
help="Name of target architecture.",
choices=["ARM", "PPC", "AArch64", "Alpha"],
required=True,
) )
parser.add_argument("-d", dest="no_clean", help="Don't clean build dir before updating.", action="store_true")
parser.add_argument( parser.add_argument(
"-w", dest="write", help="Write generated/translated files to arch/<ARCH>/", action="store_true" "-d",
dest="no_clean",
help="Don't clean build dir before updating.",
action="store_true",
)
parser.add_argument(
"-w",
dest="write",
help="Write generated/translated files to arch/<ARCH>/",
action="store_true",
) )
parser.add_argument( parser.add_argument(
"-v", "-v",
@ -205,9 +225,15 @@ def parse_args() -> argparse.Namespace:
parser.add_argument( parser.add_argument(
"--refactor", "--refactor",
dest="refactor", dest="refactor",
help="Sets change update behavior to ease refacotring and new implementations.", help="Sets change update behavior to ease refactoring and new implementations.",
action="store_true", action="store_true",
) )
parser.add_argument(
"--ci",
dest="wait_for_user",
help="The translator will not wait for user input when printing important logs.",
action="store_false",
)
arguments = parser.parse_args() arguments = parser.parse_args()
return arguments return arguments
@ -223,6 +249,13 @@ if __name__ == "__main__":
) )
Updater = ASUpdater( Updater = ASUpdater(
args.arch, args.write, args.steps, args.inc_list, args.no_clean, args.refactor, args.no_auto_apply args.arch,
args.write,
args.steps,
args.inc_list,
args.no_clean,
args.refactor,
args.no_auto_apply,
args.wait_for_user,
) )
Updater.update() Updater.update()

View File

@ -1,5 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import argparse import argparse
import logging as log import logging as log
import re import re
@ -12,8 +15,12 @@ def parse_args() -> argparse.Namespace:
prog="PatchHeaders", prog="PatchHeaders",
description="Patches generated enums into the main arch header file.", description="Patches generated enums into the main arch header file.",
) )
parser.add_argument("--header", dest="header", help="Path header file.", type=Path, required=True) parser.add_argument(
parser.add_argument("--inc", dest="inc", help="Path inc file.", type=Path, required=True) "--header", dest="header", help="Path header file.", type=Path, required=True
)
parser.add_argument(
"--inc", dest="inc", help="Path inc file.", type=Path, required=True
)
arguments = parser.parse_args() arguments = parser.parse_args()
return arguments return arguments
@ -24,9 +31,13 @@ def error_exit(msg: str) -> None:
class HeaderPatcher: class HeaderPatcher:
def __init__(self, header: Path, inc: Path) -> None: def __init__(self, header: Path, inc: Path, write_file: bool = True) -> None:
self.header = header self.header = header
self.inc = inc self.inc = inc
self.inc_content: str = ""
self.write_file = write_file
# Gets set to the patched file content if writing to the file is disabled.
self.patched_header_content: str = ""
def patch_header(self) -> bool: def patch_header(self) -> bool:
if not (self.header.exists() or self.header.is_file()): if not (self.header.exists() or self.header.is_file()):
@ -69,7 +80,7 @@ class HeaderPatcher:
header_enum_id = f":{ev_id}" if ev_id != "NOTGIVEN" else "" header_enum_id = f":{ev_id}" if ev_id != "NOTGIVEN" else ""
regex = ( regex = (
rf"\s*// generated content <{self.inc.name}{header_enum_id}> begin.*(\n)" rf"\s*// generated content <{self.inc.name}{header_enum_id}> begin.*(\n)"
rf"(.*\n)+" rf"(.*\n)*"
rf"\s*// generated content <{self.inc.name}{header_enum_id}> end.*(\n)" rf"\s*// generated content <{self.inc.name}{header_enum_id}> end.*(\n)"
) )
if not re.search(regex, header_content): if not re.search(regex, header_content):
@ -84,8 +95,11 @@ class HeaderPatcher:
) )
header_content = re.sub(regex, new_content, header_content) header_content = re.sub(regex, new_content, header_content)
with open(self.header, "w") as f: if self.write_file:
f.write(header_content) with open(self.header, "w") as f:
f.write(header_content)
else:
self.patched_header_content = header_content
log.info(f"Patched {self.inc.name} into {self.header.name}") log.info(f"Patched {self.inc.name} into {self.header.name}")
return True return True

View File

@ -1,3 +1,6 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import hashlib import hashlib
import logging as log import logging as log
import shutil import shutil
@ -6,10 +9,10 @@ import sys
from pathlib import Path from pathlib import Path
import termcolor import termcolor
from PathVarHandler import PathVarHandler
from tree_sitter import Node from tree_sitter import Node
from autosync.PathVarHandler import PathVarHandler
def convert_loglevel(level: str) -> int: def convert_loglevel(level: str) -> int:
if level == "debug": if level == "debug":
@ -79,23 +82,25 @@ def find_id_by_type(node: Node, node_types: [str], type_must_match: bool) -> byt
return b"" return b""
def print_prominent_warning(msg: str) -> None: def print_prominent_warning(msg: str, wait_for_user: bool = True) -> None:
print("\n" + separator_line_1("yellow")) print("\n" + separator_line_1("yellow"))
print(termcolor.colored("WARNING", "yellow", attrs=["bold"]) + "\n") print(termcolor.colored("WARNING", "yellow", attrs=["bold"]) + "\n")
print(msg) print(msg)
print(separator_line_1("yellow")) print(separator_line_1("yellow"))
input("Press enter to continue...\n") if wait_for_user:
input("Press enter to continue...\n")
def term_width() -> int: def term_width() -> int:
return shutil.get_terminal_size()[0] return shutil.get_terminal_size()[0]
def print_prominent_info(msg: str) -> None: def print_prominent_info(msg: str, wait_for_user: bool = True) -> None:
print("\n" + separator_line_1("blue")) print("\n" + separator_line_1("blue"))
print(msg) print(msg)
print(separator_line_1("blue")) print(separator_line_1("blue"))
input("Press enter to continue...\n") if wait_for_user:
input("Press enter to continue...\n")
def bold(msg: str, color: str = None) -> str: def bold(msg: str, color: str = None) -> str:
@ -140,7 +145,14 @@ def get_header() -> str:
def run_clang_format(out_paths: list[Path]): def run_clang_format(out_paths: list[Path]):
for out_file in out_paths: for out_file in out_paths:
log.info(f"Format {out_file}") log.info(f"Format {out_file}")
subprocess.run(["clang-format-18", f"-style=file:{get_path('{CS_CLANG_FORMAT_FILE}')}", "-i", out_file]) subprocess.run(
[
"clang-format",
f"-style=file:{get_path('{CS_CLANG_FORMAT_FILE}')}",
"-i",
out_file,
]
)
def get_path(config_path: str) -> Path: def get_path(config_path: str) -> Path:

View File

@ -1,15 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import os import os
import re import re
import shutil import shutil
import subprocess import subprocess
import logging as log
from Helper import fail_exit, get_path
from pathlib import Path from pathlib import Path
from autosync.Helper import fail_exit, get_path
inc_tables = [ inc_tables = [
{ {
"name": "Disassembler", "name": "Disassembler",
@ -71,7 +73,9 @@ class IncGenerator:
self.patches_dir_path: Path = get_path("{INC_PATCH_DIR}") self.patches_dir_path: Path = get_path("{INC_PATCH_DIR}")
self.llvm_include_dir: Path = get_path("{LLVM_INCLUDE_DIR}") self.llvm_include_dir: Path = get_path("{LLVM_INCLUDE_DIR}")
self.output_dir: Path = get_path("{BUILD_DIR}") self.output_dir: Path = get_path("{BUILD_DIR}")
self.llvm_target_dir: Path = get_path("{LLVM_TARGET_DIR}").joinpath(f"{self.arch_dir_name}") self.llvm_target_dir: Path = get_path("{LLVM_TARGET_DIR}").joinpath(
f"{self.arch_dir_name}"
)
self.llvm_tblgen: Path = get_path("{LLVM_TBLGEN_BIN}") self.llvm_tblgen: Path = get_path("{LLVM_TBLGEN_BIN}")
self.output_dir_c_inc = get_path("{C_INC_OUT_DIR}") self.output_dir_c_inc = get_path("{C_INC_OUT_DIR}")
self.output_dir_cpp_inc = get_path("{CPP_INC_OUT_DIR}") self.output_dir_cpp_inc = get_path("{CPP_INC_OUT_DIR}")
@ -106,16 +110,26 @@ class IncGenerator:
for file in Path.cwd().iterdir(): for file in Path.cwd().iterdir():
if re.search(rf"{self.arch}Gen.*\.inc", file.name): if re.search(rf"{self.arch}Gen.*\.inc", file.name):
log.debug(f"Move {file} to {self.output_dir_c_inc}") log.debug(f"Move {file} to {self.output_dir_c_inc}")
if self.output_dir_c_inc.joinpath(file.name).exists():
os.remove(self.output_dir_c_inc.joinpath(file.name))
shutil.move(file, self.output_dir_c_inc) shutil.move(file, self.output_dir_c_inc)
if self.arch == "AArch64": if self.arch == "AArch64":
# We have to rename the file SystemRegister -> SystemOperands # We have to rename the file SystemRegister -> SystemOperands
sys_ops_table_file = self.output_dir_c_inc.joinpath("AArch64GenSystemRegister.inc") sys_ops_table_file = self.output_dir_c_inc.joinpath(
new_sys_ops_file = self.output_dir_c_inc.joinpath("AArch64GenSystemOperands.inc") "AArch64GenSystemRegister.inc"
)
new_sys_ops_file = self.output_dir_c_inc.joinpath(
"AArch64GenSystemOperands.inc"
)
if "SystemOperand" not in self.inc_list: if "SystemOperand" not in self.inc_list:
return return
elif not sys_ops_table_file.exists(): elif not sys_ops_table_file.exists():
fail_exit(f"{sys_ops_table_file} does not exist. But it should have been generated.") fail_exit(
f"{sys_ops_table_file} does not exist. But it should have been generated."
)
if new_sys_ops_file.exists():
os.remove(new_sys_ops_file)
shutil.move(sys_ops_table_file, new_sys_ops_file) shutil.move(sys_ops_table_file, new_sys_ops_file)
def gen_incs(self) -> None: def gen_incs(self) -> None:
@ -178,6 +192,6 @@ class IncGenerator:
check=True, check=True,
) )
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
log.warn(f"Patch {patch.name} did not apply correctly!") log.warning(f"Patch {patch.name} did not apply correctly!")
log.warn(f"git apply returned: {e}") log.warning(f"git apply returned: {e}")
return return

View File

@ -0,0 +1,106 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import json
import logging as log
import re
import subprocess
from pathlib import Path
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class PathVarHandler(metaclass=Singleton):
def __init__(self) -> None:
try:
res = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
check=True,
stdout=subprocess.PIPE,
)
except subprocess.CalledProcessError:
log.fatal("Could not get repository top level directory.")
exit(1)
repo_root = res.stdout.decode("utf8").strip("\n")
# The main directories
self.paths: dict[str:Path] = dict()
self.paths["{CS_ROOT}"] = Path(repo_root)
self.paths["{AUTO_SYNC_ROOT}"] = Path(repo_root).joinpath("suite/auto-sync/")
self.paths["{AUTO_SYNC_SRC}"] = self.paths["{AUTO_SYNC_ROOT}"].joinpath(
"src/autosync/"
)
path_config_file = self.paths["{AUTO_SYNC_SRC}"].joinpath("path_vars.json")
# Load variables
with open(path_config_file) as f:
vars = json.load(f)
paths = vars["paths"]
self.create_during_runtime = vars["create_during_runtime"]
missing = list()
for p_name, path in paths.items():
resolved = path
for var_id in re.findall(r"\{.+}", resolved):
if var_id not in self.paths:
log.fatal(
f"{var_id} hasn't been added to the PathVarsHandler, yet. The var must be defined in a previous entry."
)
exit(1)
resolved: str = re.sub(var_id, str(self.paths[var_id]), resolved)
log.debug(f"Set {p_name} = {resolved}")
if not Path(resolved).exists() and (
p_name not in self.create_during_runtime
and p_name not in vars["ignore_missing"]
):
missing.append(resolved)
elif var_id in self.create_during_runtime:
self.create_path(var_id, resolved)
self.paths[p_name] = Path(resolved)
if len(missing) > 0:
log.fatal(f"Some paths from config file are missing!")
for m in missing:
log.fatal(f"\t{m}")
exit(1)
def get_path(self, name: str) -> Path:
if name not in self.paths:
raise ValueError(f"Path variable {name} has no path saved.")
if name in self.create_during_runtime:
self.create_path(name, self.paths[name])
return self.paths[name]
def complete_path(self, path_str: str) -> Path:
resolved = path_str
for p_name in re.findall(r"\{.+}", path_str):
resolved = re.sub(p_name, str(self.get_path(p_name)), resolved)
return Path(resolved)
@staticmethod
def create_path(var_id: str, path: str):
pp = Path(path)
if pp.exists():
return
log.debug(f"Create path {var_id} @ {path}")
postfix = var_id.strip("}").split("_")[-1]
if postfix == "FILE":
if not pp.parent.exists():
pp.parent.mkdir(parents=True)
pp.touch()
elif postfix == "DIR":
pp.mkdir(parents=True)
else:
from autosync.Helper import fail_exit
fail_exit(
f"The var_id: {var_id} must end in _FILE or _DIR. It ends in '{postfix}'"
)

View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
// Include the whole file
// generated content <test_include.inc> begin
// generated content <test_include.inc> end
// Include only a part of the file.
// generated content <test_include.inc:GUARD> begin
// generated content <test_include.inc:GUARD> end

View File

@ -0,0 +1,47 @@
# SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import unittest
from autosync.HeaderPatcher import HeaderPatcher
from autosync.Helper import get_path
class TestHeaderPatcher(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.hpatcher = HeaderPatcher(
get_path("{HEADER_PATCHER_TEST_HEADER_FILE}"),
get_path("{HEADER_PATCHER_TEST_INC_FILE}"),
write_file=False,
)
def test_header_patching(self):
self.hpatcher.patch_header()
self.assertEqual(
self.hpatcher.patched_header_content,
(
"// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>\n"
"// SPDX-License-Identifier: BSD-3\n"
"\n"
"\n"
" // Include the whole file\n"
" // generated content <test_include.inc> begin\n"
" // clang-format off\n"
"\n"
"This part should be included if the whole file is included.\n"
"\n"
" // clang-format on\n"
" // generated content <test_include.inc> end\n"
"\n"
" // Include only a part of the file.\n"
" // generated content <test_include.inc:GUARD> begin\n"
" // clang-format off\n"
"\n"
" Partial include of something\n"
"\n"
" // clang-format on\n"
" // generated content <test_include.inc:GUARD> end\n"
"\n"
),
)

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
#ifdef GUARD
#undef GUARD
Partial include of something
#endif
This part should be included if the whole file is included.

View File

@ -1,9 +1,13 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import json import json
import logging as log
from pathlib import Path from pathlib import Path
from Helper import get_path, fail_exit
from tree_sitter import Language, Parser from tree_sitter import Language, Parser
import logging as log
from autosync.Helper import fail_exit, get_path
class Configurator: class Configurator:
@ -60,7 +64,9 @@ class Configurator:
with open(self.config_path) as f: with open(self.config_path) as f:
conf = json.loads(f.read()) conf = json.loads(f.read())
if self.arch not in conf: if self.arch not in conf:
fail_exit(f"{self.arch} has no configuration. Please add them in {self.config_path}!") fail_exit(
f"{self.arch} has no configuration. Please add them in {self.config_path}!"
)
self.config = conf self.config = conf
def ts_compile_cpp(self) -> None: def ts_compile_cpp(self) -> None:
@ -68,13 +74,17 @@ class Configurator:
ts_grammar_path = get_path("{VENDOR_DIR}").joinpath("tree-sitter-cpp") ts_grammar_path = get_path("{VENDOR_DIR}").joinpath("tree-sitter-cpp")
if not Path.exists(ts_grammar_path): if not Path.exists(ts_grammar_path):
fail_exit(f"Could not load the tree-sitter grammar at '{ts_grammar_path}'") fail_exit(f"Could not load the tree-sitter grammar at '{ts_grammar_path}'")
Language.build_library(str(self.ts_shared_object), [ts_grammar_path]) # build_library wll be deprecated in 0.22.0. But CPP tree-sitter doesn't have Python bindings.
# So we stick with it.
Language.build_library(str(self.ts_shared_object), [str(ts_grammar_path)])
def ts_set_cpp_language(self) -> None: def ts_set_cpp_language(self) -> None:
log.info(f"Load language '{self.ts_shared_object}'") log.info(f"Load language '{self.ts_shared_object}'")
if not Path.exists(self.ts_shared_object): if not Path.exists(self.ts_shared_object):
fail_exit(f"Could not load the tree-sitter language shared object at '{self.ts_shared_object}'") fail_exit(
self.ts_cpp_lang = Language(self.ts_shared_object, "cpp") f"Could not load the tree-sitter language shared object at '{self.ts_shared_object}'"
)
self.ts_cpp_lang = Language(str(self.ts_shared_object), "cpp")
def init_parser(self) -> None: def init_parser(self) -> None:
log.debug("Init parser") log.debug("Init parser")

View File

@ -1,77 +1,89 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path
import termcolor # Copyright © 2022 Rot127 <unisono@quyllur.org>
from tree_sitter import Language, Parser, Tree, Node # SPDX-License-Identifier: BSD-3
import argparse import argparse
import logging as log import logging as log
import sys import sys
from pathlib import Path
from tree_sitter.binding import Query import termcolor
from tree_sitter import Language, Node, Parser, Query, Tree
from CppTranslator.Configurator import Configurator from autosync.cpptranslator.Configurator import Configurator
from Helper import convert_loglevel, print_prominent_warning, get_header, run_clang_format, get_path from autosync.cpptranslator.patches.AddCSDetail import AddCSDetail
from CppTranslator.Patches.GetRegFromClass import GetRegFromClass from autosync.cpptranslator.patches.AddOperand import AddOperand
from CppTranslator.Patches.AddCSDetail import AddCSDetail from autosync.cpptranslator.patches.Assert import Assert
from CppTranslator.Patches.AddOperand import AddOperand from autosync.cpptranslator.patches.BitCastStdArray import BitCastStdArray
from CppTranslator.Patches.Assert import Assert from autosync.cpptranslator.patches.CheckDecoderStatus import CheckDecoderStatus
from CppTranslator.Patches.BitCastStdArray import BitCastStdArray from autosync.cpptranslator.patches.ClassConstructorDef import ClassConstructorDef
from CppTranslator.Patches.CheckDecoderStatus import CheckDecoderStatus from autosync.cpptranslator.patches.ClassesDef import ClassesDef
from CppTranslator.Patches.ClassConstructorDef import ClassConstructorDef from autosync.cpptranslator.patches.ConstMCInstParameter import ConstMCInstParameter
from CppTranslator.Patches.ClassesDef import ClassesDef from autosync.cpptranslator.patches.ConstMCOperand import ConstMCOperand
from CppTranslator.Patches.ConstMCInstParameter import ConstMCInstParameter from autosync.cpptranslator.patches.CppInitCast import CppInitCast
from CppTranslator.Patches.ConstMCOperand import ConstMCOperand from autosync.cpptranslator.patches.CreateOperand0 import CreateOperand0
from CppTranslator.Patches.CppInitCast import CppInitCast from autosync.cpptranslator.patches.CreateOperand1 import CreateOperand1
from CppTranslator.Patches.CreateOperand0 import CreateOperand0 from autosync.cpptranslator.patches.DeclarationInConditionClause import (
from CppTranslator.Patches.CreateOperand1 import CreateOperand1 DeclarationInConditionalClause,
from CppTranslator.Patches.DeclarationInConditionClause import DeclarationInConditionalClause )
from CppTranslator.Patches.DecodeInstruction import DecodeInstruction from autosync.cpptranslator.patches.DecodeInstruction import DecodeInstruction
from CppTranslator.Patches.DecoderCast import DecoderCast from autosync.cpptranslator.patches.DecoderCast import DecoderCast
from CppTranslator.Patches.DecoderParameter import DecoderParameter from autosync.cpptranslator.patches.DecoderParameter import DecoderParameter
from CppTranslator.Patches.FallThrough import FallThrough from autosync.cpptranslator.patches.FallThrough import FallThrough
from CppTranslator.Patches.FeatureBits import FeatureBits from autosync.cpptranslator.patches.FeatureBits import FeatureBits
from CppTranslator.Patches.FeatureBitsDecl import FeatureBitsDecl from autosync.cpptranslator.patches.FeatureBitsDecl import FeatureBitsDecl
from CppTranslator.Patches.FieldFromInstr import FieldFromInstr from autosync.cpptranslator.patches.FieldFromInstr import FieldFromInstr
from CppTranslator.Patches.GetNumOperands import GetNumOperands from autosync.cpptranslator.patches.GetNumOperands import GetNumOperands
from CppTranslator.Patches.GetOpcode import GetOpcode from autosync.cpptranslator.patches.GetOpcode import GetOpcode
from CppTranslator.Patches.GetOperandRegImm import GetOperandRegImm from autosync.cpptranslator.patches.GetOperand import GetOperand
from CppTranslator.Patches.GetOperand import GetOperand from autosync.cpptranslator.patches.GetOperandRegImm import GetOperandRegImm
from CppTranslator.Patches.GetRegClass import GetRegClass from autosync.cpptranslator.patches.GetRegClass import GetRegClass
from CppTranslator.Patches.GetSubReg import GetSubReg from autosync.cpptranslator.patches.GetRegFromClass import GetRegFromClass
from CppTranslator.Patches.Includes import Includes from autosync.cpptranslator.patches.GetSubReg import GetSubReg
from CppTranslator.Patches.InlineToStaticInline import InlineToStaticInline from autosync.cpptranslator.patches.Includes import Includes
from CppTranslator.Patches.IsRegImm import IsOperandRegImm from autosync.cpptranslator.patches.InlineToStaticInline import InlineToStaticInline
from CppTranslator.Patches.IsOptionalDef import IsOptionalDef from autosync.cpptranslator.patches.IsOptionalDef import IsOptionalDef
from CppTranslator.Patches.IsPredicate import IsPredicate from autosync.cpptranslator.patches.IsPredicate import IsPredicate
from CppTranslator.Patches.LLVMFallThrough import LLVMFallThrough from autosync.cpptranslator.patches.IsRegImm import IsOperandRegImm
from CppTranslator.Patches.LLVMunreachable import LLVMUnreachable from autosync.cpptranslator.patches.LLVMFallThrough import LLVMFallThrough
from CppTranslator.Patches.MethodToFunctions import MethodToFunction from autosync.cpptranslator.patches.LLVMunreachable import LLVMUnreachable
from CppTranslator.Patches.MethodTypeQualifier import MethodTypeQualifier from autosync.cpptranslator.patches.MethodToFunctions import MethodToFunction
from CppTranslator.Patches.NamespaceLLVM import NamespaceLLVM from autosync.cpptranslator.patches.MethodTypeQualifier import MethodTypeQualifier
from CppTranslator.Patches.NamespaceAnon import NamespaceAnon from autosync.cpptranslator.patches.NamespaceAnon import NamespaceAnon
from CppTranslator.Patches.NamespaceArch import NamespaceArch from autosync.cpptranslator.patches.NamespaceArch import NamespaceArch
from CppTranslator.Patches.OutStreamParam import OutStreamParam from autosync.cpptranslator.patches.NamespaceLLVM import NamespaceLLVM
from CppTranslator.Patches.PredicateBlockFunctions import PredicateBlockFunctions from autosync.cpptranslator.patches.OutStreamParam import OutStreamParam
from CppTranslator.Patches.PrintAnnotation import PrintAnnotation from autosync.cpptranslator.patches.Patch import Patch
from CppTranslator.Patches.PrintRegImmShift import PrintRegImmShift from autosync.cpptranslator.patches.PredicateBlockFunctions import (
from CppTranslator.Patches.QualifiedIdentifier import QualifiedIdentifier PredicateBlockFunctions,
from CppTranslator.Patches.Patch import Patch )
from CppTranslator.Patches.ReferencesDecl import ReferencesDecl from autosync.cpptranslator.patches.PrintAnnotation import PrintAnnotation
from CppTranslator.Patches.RegClassContains import RegClassContains from autosync.cpptranslator.patches.PrintRegImmShift import PrintRegImmShift
from CppTranslator.Patches.STIArgument import STIArgument from autosync.cpptranslator.patches.QualifiedIdentifier import QualifiedIdentifier
from CppTranslator.Patches.STIFeatureBits import STIFeatureBits from autosync.cpptranslator.patches.ReferencesDecl import ReferencesDecl
from CppTranslator.Patches.STParameter import SubtargetInfoParam from autosync.cpptranslator.patches.RegClassContains import RegClassContains
from CppTranslator.Patches.SetOpcode import SetOpcode from autosync.cpptranslator.patches.SetOpcode import SetOpcode
from CppTranslator.Patches.SignExtend import SignExtend from autosync.cpptranslator.patches.SignExtend import SignExtend
from CppTranslator.Patches.SizeAssignments import SizeAssignment from autosync.cpptranslator.patches.SizeAssignments import SizeAssignment
from CppTranslator.Patches.StreamOperation import StreamOperations from autosync.cpptranslator.patches.STIArgument import STIArgument
from CppTranslator.Patches.TemplateDeclaration import TemplateDeclaration from autosync.cpptranslator.patches.STIFeatureBits import STIFeatureBits
from CppTranslator.Patches.TemplateDefinition import TemplateDefinition from autosync.cpptranslator.patches.STParameter import SubtargetInfoParam
from CppTranslator.Patches.TemplateParamDecl import TemplateParamDecl from autosync.cpptranslator.patches.StreamOperation import StreamOperations
from CppTranslator.Patches.TemplateRefs import TemplateRefs from autosync.cpptranslator.patches.TemplateDeclaration import TemplateDeclaration
from CppTranslator.Patches.UseMarkup import UseMarkup from autosync.cpptranslator.patches.TemplateDefinition import TemplateDefinition
from CppTranslator.Patches.UsingDeclaration import UsingDeclaration from autosync.cpptranslator.patches.TemplateParamDecl import TemplateParamDecl
from CppTranslator.TemplateCollector import TemplateCollector from autosync.cpptranslator.patches.TemplateRefs import TemplateRefs
from autosync.cpptranslator.patches.UseMarkup import UseMarkup
from autosync.cpptranslator.patches.UsingDeclaration import UsingDeclaration
from autosync.cpptranslator.TemplateCollector import TemplateCollector
from autosync.Helper import (
convert_loglevel,
get_header,
get_path,
print_prominent_warning,
run_clang_format,
)
class Translator: class Translator:
@ -162,17 +174,22 @@ class Translator:
TemplateDefinition.__name__: 6, TemplateDefinition.__name__: 6,
} }
def __init__(self, configure: Configurator): def __init__(self, configure: Configurator, wait_for_user: bool = False):
self.configurator = configure self.configurator = configure
self.wait_for_user = wait_for_user
self.arch = self.configurator.get_arch() self.arch = self.configurator.get_arch()
self.conf = self.configurator.get_arch_config() self.conf = self.configurator.get_arch_config()
self.conf_general = self.configurator.get_general_config() self.conf_general = self.configurator.get_general_config()
self.ts_cpp_lang = self.configurator.get_cpp_lang() self.ts_cpp_lang = self.configurator.get_cpp_lang()
self.parser = self.configurator.get_parser() self.parser = self.configurator.get_parser()
self.src_paths: [Path] = [get_path(sp["in"]) for sp in self.conf["files_to_translate"]] self.src_paths: [Path] = [
t_out_dir: Path = get_path(self.conf_general["translation_out_dir"]) get_path(sp["in"]) for sp in self.conf["files_to_translate"]
self.out_paths: [Path] = [t_out_dir.joinpath(sp["out"]) for sp in self.conf["files_to_translate"]] ]
t_out_dir: Path = get_path("{CPP_TRANSLATOR_TRANSLATION_OUT_DIR}")
self.out_paths: [Path] = [
t_out_dir.joinpath(sp["out"]) for sp in self.conf["files_to_translate"]
]
self.collect_template_instances() self.collect_template_instances()
self.init_patches() self.init_patches()
@ -188,7 +205,9 @@ class Translator:
def init_patches(self): def init_patches(self):
log.debug("Init patches") log.debug("Init patches")
priorities = dict(sorted(self.patch_priorities.items(), key=lambda item: item[1])) priorities = dict(
sorted(self.patch_priorities.items(), key=lambda item: item[1])
)
for ptype, p in priorities.items(): for ptype, p in priorities.items():
match ptype: match ptype:
case RegClassContains.__name__: case RegClassContains.__name__:
@ -351,8 +370,13 @@ class Translator:
def apply_patch(self, patch: Patch) -> bool: def apply_patch(self, patch: Patch) -> bool:
"""Tests if the given patch should be applied for the current architecture or file.""" """Tests if the given patch should be applied for the current architecture or file."""
has_apply_only = len(patch.apply_only_to["files"]) > 0 or len(patch.apply_only_to["archs"]) > 0 has_apply_only = (
has_do_not_apply = len(patch.do_not_apply["files"]) > 0 or len(patch.do_not_apply["archs"]) > 0 len(patch.apply_only_to["files"]) > 0
or len(patch.apply_only_to["archs"]) > 0
)
has_do_not_apply = (
len(patch.do_not_apply["files"]) > 0 or len(patch.do_not_apply["archs"]) > 0
)
if not (has_apply_only or has_do_not_apply): if not (has_apply_only or has_do_not_apply):
# Lists empty. # Lists empty.
@ -374,7 +398,9 @@ class Translator:
exit(1) exit(1)
def translate(self) -> None: def translate(self) -> None:
for self.current_src_path_in, self.current_src_path_out in zip(self.src_paths, self.out_paths): for self.current_src_path_in, self.current_src_path_out in zip(
self.src_paths, self.out_paths
):
log.info(f"Translate '{self.current_src_path_in}'") log.info(f"Translate '{self.current_src_path_in}'")
self.parse(self.current_src_path_in) self.parse(self.current_src_path_in)
patch: Patch patch: Patch
@ -398,7 +424,9 @@ class Translator:
# Add it to the bundle. # Add it to the bundle.
captures_bundle[-1].append(q) captures_bundle[-1].append(q)
log.debug(f"Patch {patch.__class__.__name__} (to patch: {len(captures_bundle)}).") log.debug(
f"Patch {patch.__class__.__name__} (to patch: {len(captures_bundle)})."
)
p_list: (bytes, Node) = list() p_list: (bytes, Node) = list()
cb: [(Node, str)] cb: [(Node, str)]
@ -420,8 +448,12 @@ class Translator:
def collect_template_instances(self): def collect_template_instances(self):
search_paths = [get_path(p) for p in self.conf["files_for_template_search"]] search_paths = [get_path(p) for p in self.conf["files_for_template_search"]]
temp_arg_deduction = [p.encode("utf8") for p in self.conf["templates_with_arg_deduction"]] temp_arg_deduction = [
self.template_collector = TemplateCollector(self.parser, self.ts_cpp_lang, search_paths, temp_arg_deduction) p.encode("utf8") for p in self.conf["templates_with_arg_deduction"]
]
self.template_collector = TemplateCollector(
self.parser, self.ts_cpp_lang, search_paths, temp_arg_deduction
)
self.template_collector.collect() self.template_collector.collect()
def get_patch_kwargs(self, patch): def get_patch_kwargs(self, patch):
@ -435,7 +467,8 @@ class Translator:
if len(manual_edited) > 0: if len(manual_edited) > 0:
msg += ( msg += (
termcolor.colored( termcolor.colored(
"The following files are too complex to translate! Please check them by hand.", attrs=["bold"] "The following files are too complex to translate! Please check them by hand.",
attrs=["bold"],
) )
+ "\n" + "\n"
) )
@ -443,7 +476,7 @@ class Translator:
return return
for f in manual_edited: for f in manual_edited:
msg += get_path(f).name + "\n" msg += get_path(f).name + "\n"
print_prominent_warning(msg) print_prominent_warning(msg, self.wait_for_user)
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
@ -452,7 +485,11 @@ def parse_args() -> argparse.Namespace:
description="Capstones C++ to C translator for LLVM source files", description="Capstones C++ to C translator for LLVM source files",
) )
parser.add_argument( parser.add_argument(
"-a", dest="arch", help="Name of target architecture.", choices=["ARM", "PPC", "AArch64", "Alpha"], required=True "-a",
dest="arch",
help="Name of target architecture.",
choices=["ARM", "PPC", "AArch64", "Alpha"],
required=True,
) )
parser.add_argument( parser.add_argument(
"-v", "-v",
@ -462,7 +499,11 @@ def parse_args() -> argparse.Namespace:
default="info", default="info",
) )
parser.add_argument( parser.add_argument(
"-c", dest="config_path", help="Config file for architectures.", default="arch_config.json", type=Path "-c",
dest="config_path",
help="Config file for architectures.",
default="arch_config.json",
type=Path,
) )
arguments = parser.parse_args() arguments = parser.parse_args()
return arguments return arguments

View File

@ -1,29 +1,34 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json
from enum import StrEnum
from pathlib import Path
from typing import Iterator
from tree_sitter import Language, Parser, Tree, Node # Copyright © 2022 Rot127 <unisono@quyllur.org>
from shutil import copy2 # SPDX-License-Identifier: BSD-3
import argparse import argparse
import difflib as dl import difflib as dl
import json
import logging as log import logging as log
import subprocess
import sys import sys
import tempfile
from enum import StrEnum
from pathlib import Path
from shutil import copy2
from CppTranslator.Configurator import Configurator from tree_sitter import Language, Node, Parser, Tree
from Helper import (
convert_loglevel, from autosync.cpptranslator.Configurator import Configurator
find_id_by_type, from autosync.Helper import (
print_prominent_info,
bold, bold,
colored, colored,
convert_loglevel,
find_id_by_type,
get_path,
get_sha256,
print_prominent_info,
print_prominent_warning,
run_clang_format,
separator_line_1, separator_line_1,
separator_line_2, separator_line_2,
print_prominent_warning,
get_sha256,
run_clang_format,
get_path,
) )
@ -35,7 +40,13 @@ class PatchCoord:
start_point: tuple[int, int] start_point: tuple[int, int]
end_point: tuple[int, int] end_point: tuple[int, int]
def __init__(self, start_byte: int, end_byte: int, start_point: tuple[int, int], end_point: tuple[int, int]): def __init__(
self,
start_byte: int,
end_byte: int,
start_point: tuple[int, int],
end_point: tuple[int, int],
):
self.start_byte = start_byte self.start_byte = start_byte
self.end_byte = end_byte self.end_byte = end_byte
self.start_point = start_point self.start_point = start_point
@ -58,7 +69,9 @@ class PatchCoord:
@staticmethod @staticmethod
def get_coordinates_from_node(node: Node): def get_coordinates_from_node(node: Node):
return PatchCoord(node.start_byte, node.end_byte, node.start_point, node.end_point) return PatchCoord(
node.start_byte, node.end_byte, node.start_point, node.end_point
)
class ApplyType(StrEnum): class ApplyType(StrEnum):
@ -66,6 +79,7 @@ class ApplyType(StrEnum):
NEW = "NEW" # Apply version from new file (leave unchanged) NEW = "NEW" # Apply version from new file (leave unchanged)
SAVED = "SAVED" # Use saved resolution SAVED = "SAVED" # Use saved resolution
EDIT = "EDIT" # Edit patch and apply EDIT = "EDIT" # Edit patch and apply
SHOW_EDIT = "SHOW_EDIT" # Show the saved edited text.
OLD_ALL = "OLD_ALL" # Apply all versions from old file. OLD_ALL = "OLD_ALL" # Apply all versions from old file.
PREVIOUS = "PREVIOUS" # Ignore diff and go to previous PREVIOUS = "PREVIOUS" # Ignore diff and go to previous
@ -81,7 +95,13 @@ class Patch:
new_hash: str new_hash: str
def __init__( def __init__(
self, node_id: str, old: bytes, new: bytes, coord: PatchCoord, apply: ApplyType, edit: bytes = None self,
node_id: str,
old: bytes,
new: bytes,
coord: PatchCoord,
apply: ApplyType,
edit: bytes = None,
) -> None: ) -> None:
if apply == ApplyType.SAVED: if apply == ApplyType.SAVED:
raise NotImplementedError("Not yet implemented.") raise NotImplementedError("Not yet implemented.")
@ -133,6 +153,41 @@ class Patch:
class Differ: class Differ:
""" """
Diffs the newly translated C++ files against the old version. Diffs the newly translated C++ files against the old version.
The general diffing works like this:
The old and the new file get parsed with tree sitter into an AST.
Then, we extract all nodes of a specific type out of this AST.
Which nodes specifically is defined in "arch_config.json::General::nodes_to_diff".
These nodes (old and new separately) are than sorted descending by their coordinates.
Meaning, nodes at the end in the file come first.
The identifiers of those nodes are saved in a single list.
Now we iterate over this list of identifiers. Now we make decisions:
The node id is present as:
old node & new node => Text matches?
yes => Continue
no => Add new node as Patch (see below)
only old node => We save all consecutive old nodes, which have _no_ equivalent new node
and add them as single patch
only new node => Add patch
Now we have the patch. We have a persistence file which saved previous decisions, on which patch to choose.
We take the node text of the old and new node (or only from a single one) and compare them to our previous decision.
If the text of the nodes didn't change since the last run, we auto-apply the patch.
Otherwise, the user decides:
- Choose the old node text
- Choose the new node text
- Open the editor to edit the patch and apply it.
- Use the stored previous decision.
- Select always the old nodes.
- Go back and decide on node before.
Each decision is saved to the persistence file for later.
Last (optional) step is to write the patches to the new file.
Please note that we always write to the new file in the current version.
""" """
ts_cpp_lang: Language = None ts_cpp_lang: Language = None
@ -148,11 +203,13 @@ class Differ:
patches: list[Patch] patches: list[Patch]
current_patch: Patch current_patch: Patch
cur_old_node: Node = None cur_old_node: Node | None = None
cur_new_node: Node = None cur_new_node: Node | None = None
cur_nid: str = None cur_nid: str = None
def __init__(self, configurator: Configurator, no_auto_apply: bool): def __init__(
self, configurator: Configurator, no_auto_apply: bool, testing: bool = False
):
self.configurator = configurator self.configurator = configurator
self.no_auto_apply = no_auto_apply self.no_auto_apply = no_auto_apply
self.arch = self.configurator.get_arch() self.arch = self.configurator.get_arch()
@ -161,18 +218,41 @@ class Differ:
self.ts_cpp_lang = self.configurator.get_cpp_lang() self.ts_cpp_lang = self.configurator.get_cpp_lang()
self.parser = self.configurator.get_parser() self.parser = self.configurator.get_parser()
self.differ = dl.Differ() self.differ = dl.Differ()
self.testing = testing
t_out_dir: Path = get_path(self.conf_general["translation_out_dir"]) self.diff_out_dir = get_path("{CPP_TRANSLATOR_DIFF_OUT_DIR}")
self.translated_files = [t_out_dir.joinpath(sp["out"]) for sp in self.conf_arch["files_to_translate"]] if self.testing:
cs_arch_src: Path = get_path("{CS_ARCH_MODULE_DIR}") t_out_dir: Path = get_path("{DIFFER_TEST_NEW_SRC_DIR}")
cs_arch_src = cs_arch_src.joinpath(self.arch if self.arch != "PPC" else "PowerPC") self.translated_files = [
self.old_files = [ t_out_dir.joinpath(sp["out"])
cs_arch_src.joinpath(f"{cs_arch_src}/" + sp["out"]) for sp in self.conf_arch["files_to_translate"] for sp in self.conf_arch["files_to_translate"]
] ]
self.load_persistence_file() self.old_files = [
get_path("{DIFFER_TEST_OLD_SRC_DIR}").joinpath(sp["out"])
for sp in self.conf_arch["files_to_translate"]
]
self.load_persistence_file()
else:
t_out_dir: Path = get_path("{CPP_TRANSLATOR_TRANSLATION_OUT_DIR}")
self.translated_files = [
t_out_dir.joinpath(sp["out"])
for sp in self.conf_arch["files_to_translate"]
]
cs_arch_src: Path = get_path("{CS_ARCH_MODULE_DIR}")
cs_arch_src = cs_arch_src.joinpath(
self.arch if self.arch != "PPC" else "PowerPC"
)
self.old_files = [
cs_arch_src.joinpath(f"{cs_arch_src}/" + sp["out"])
for sp in self.conf_arch["files_to_translate"]
]
self.load_persistence_file()
def load_persistence_file(self) -> None: def load_persistence_file(self) -> None:
self.persistence_filepath = get_path(self.conf_general["patch_persistence_file"]) if self.testing:
self.persistence_filepath = get_path("{DIFFER_TEST_PERSISTENCE_FILE}")
else:
self.persistence_filepath = get_path("{DIFFER_PERSISTENCE_FILE}")
if not self.persistence_filepath.exists(): if not self.persistence_filepath.exists():
self.saved_patches = dict() self.saved_patches = dict()
return return
@ -181,7 +261,9 @@ class Differ:
try: try:
self.saved_patches = json.load(f) self.saved_patches = json.load(f)
except json.decoder.JSONDecodeError as e: except json.decoder.JSONDecodeError as e:
log.fatal(f"Persistence file {bold(self.persistence_filepath.name)} corrupt.") log.fatal(
f"Persistence file {bold(self.persistence_filepath.name)} corrupt."
)
log.fatal("Delete it or fix it by hand.") log.fatal("Delete it or fix it by hand.")
log.fatal(f"JSON Exception: {e}") log.fatal(f"JSON Exception: {e}")
exit(1) exit(1)
@ -191,6 +273,10 @@ class Differ:
json.dump(self.saved_patches, f, indent=2) json.dump(self.saved_patches, f, indent=2)
def persist_patch(self, filename: Path, patch: Patch) -> None: def persist_patch(self, filename: Path, patch: Patch) -> None:
"""
:param filename: The filename this patch is saved for.
:param patch: The patch to apply.
"""
if filename.name not in self.saved_patches: if filename.name not in self.saved_patches:
self.saved_patches[filename.name] = dict() self.saved_patches[filename.name] = dict()
log.debug(f"Save: {patch.get_persist_info()}") log.debug(f"Save: {patch.get_persist_info()}")
@ -201,14 +287,19 @@ class Differ:
Copy translated files to diff directory for editing. Copy translated files to diff directory for editing.
""" """
log.info("Copy files for editing") log.info("Copy files for editing")
diff_dir: Path = get_path(self.conf_general["diff_out_dir"]) diff_dir: Path = self.diff_out_dir
for f in self.translated_files: for f in self.translated_files:
dest = diff_dir.joinpath(f.name) dest = diff_dir.joinpath(f.name)
copy2(f, dest) copy2(f, dest)
self.diff_dest_files.append(dest) self.diff_dest_files.append(dest)
def get_diff_intro_msg( def get_diff_intro_msg(
self, old_filename: Path, new_filename: Path, current: int, total: int, num_diffs: int self,
old_filename: Path,
new_filename: Path,
current: int,
total: int,
num_diffs: int,
) -> str: ) -> str:
color_new = self.conf_general["diff_color_new"] color_new = self.conf_general["diff_color_new"]
color_old = self.conf_general["diff_color_old"] color_old = self.conf_general["diff_color_old"]
@ -229,7 +320,9 @@ class Differ:
if n["node_type"] == node.type: if n["node_type"] == node.type:
id_types = n["identifier_node_type"] id_types = n["identifier_node_type"]
if not id_types: if not id_types:
log.fatal(f"Diffing: Node of type {node.type} has not identifier type specified.") log.fatal(
f"Diffing: Node of type {node.type} has not identifier type specified."
)
exit(1) exit(1)
identifier = "" identifier = ""
for id_type in id_types: for id_type in id_types:
@ -241,7 +334,7 @@ class Differ:
exit(1) exit(1)
return identifier return identifier
def parse_file(self, file: Path) -> dict: def parse_file(self, file: Path) -> dict[str:Node]:
""" """
Parse a files and return all nodes which should be diffed. Parse a files and return all nodes which should be diffed.
Nodes are indexed by a unique identifier. Nodes are indexed by a unique identifier.
@ -251,7 +344,9 @@ class Differ:
tree: Tree = self.parser.parse(content, keep_text=True) tree: Tree = self.parser.parse(content, keep_text=True)
node_types_to_diff = [n["node_type"] for n in self.conf_general["nodes_to_diff"]] node_types_to_diff = [
n["node_type"] for n in self.conf_general["nodes_to_diff"]
]
content = None content = None
if file.suffix == ".h": if file.suffix == ".h":
# Header file. Get the content in between the include guard # Header file. Get the content in between the include guard
@ -283,11 +378,13 @@ class Differ:
def print_diff(self, diff_lines: list[str], node_id: str, current: int, total: int): def print_diff(self, diff_lines: list[str], node_id: str, current: int, total: int):
new_color = self.conf_general["diff_color_new"] new_color = self.conf_general["diff_color_new"]
old_color = self.conf_general["diff_color_old"] old_color = self.conf_general["diff_color_old"]
print(separator_line_1()) print(separator_line_2())
print(f"{bold('Patch:')} {current}/{total}\n") print(f"{bold('Patch:')} {current}/{total}\n")
print(f"{bold('Node:')} {node_id}") print(f"{bold('Node:')} {node_id}")
print(f"{bold('Color:')} {colored('NEW FILE - (Just translated)', new_color)}") print(f"{bold('Color:')} {colored('NEW FILE - (Just translated)', new_color)}")
print(f"{bold('Color:')} {colored('OLD FILE - (Currently in Capstone)', old_color)}\n") print(
f"{bold('Color:')} {colored('OLD FILE - (Currently in Capstone)', old_color)}\n"
)
print(separator_line_1()) print(separator_line_1())
for line in diff_lines: for line in diff_lines:
if line[0] == "+": if line[0] == "+":
@ -301,13 +398,15 @@ class Differ:
print(separator_line_2()) print(separator_line_2())
@staticmethod @staticmethod
def no_difference(diff_lines: Iterator[str]) -> bool: def no_difference(diff_lines: list[str]) -> bool:
for line in diff_lines: for line in diff_lines:
if line[0] != " ": if line[0] != " ":
return False return False
return True return True
def print_prompt(self, saved_diff_present: bool = False, saved_choice: ApplyType = None) -> str: def print_prompt(
self, saved_diff_present: bool = False, saved_choice: ApplyType = None
) -> str:
new_color = self.conf_general["diff_color_new"] new_color = self.conf_general["diff_color_new"]
old_color = self.conf_general["diff_color_old"] old_color = self.conf_general["diff_color_old"]
edited_color = self.conf_general["diff_color_edited"] edited_color = self.conf_general["diff_color_edited"]
@ -315,15 +414,19 @@ class Differ:
choice = input( choice = input(
f"Choice: {colored('O', old_color)}, {bold('o', old_color)}, {bold('n', new_color)}, " f"Choice: {colored('O', old_color)}, {bold('o', old_color)}, {bold('n', new_color)}, "
f"{saved_selection}, {colored('e', edited_color)}, p, q, ? > " f"{saved_selection}, {colored('e', edited_color)}, {colored('E', edited_color)}, p, q, ? > "
) )
return choice return choice
def get_saved_choice_prompt(self, saved_diff_present: bool = False, saved_choice: ApplyType = None): def get_saved_choice_prompt(
self, saved_diff_present: bool = False, saved_choice: ApplyType = None
):
new_color = self.conf_general["diff_color_new"] new_color = self.conf_general["diff_color_new"]
old_color = self.conf_general["diff_color_old"] old_color = self.conf_general["diff_color_old"]
edited_color = self.conf_general["diff_color_edited"] edited_color = self.conf_general["diff_color_edited"]
saved_color = self.conf_general["diff_color_saved"] if saved_diff_present else "dark_grey" saved_color = (
self.conf_general["diff_color_saved"] if saved_diff_present else "dark_grey"
)
saved_selection = f"{bold('s', saved_color)}" saved_selection = f"{bold('s', saved_color)}"
if saved_choice == ApplyType.OLD: if saved_choice == ApplyType.OLD:
saved_selection += f" ({colored('old', old_color)}) " saved_selection += f" ({colored('old', old_color)}) "
@ -335,7 +438,9 @@ class Differ:
saved_selection += f" ({colored('none', 'dark_grey')}) " saved_selection += f" ({colored('none', 'dark_grey')}) "
return saved_selection return saved_selection
def print_prompt_help(self, saved_diff_present: bool = False, saved_choice: ApplyType = None) -> None: def print_prompt_help(
self, saved_diff_present: bool = False, saved_choice: ApplyType = None
) -> None:
new_color = self.conf_general["diff_color_new"] new_color = self.conf_general["diff_color_new"]
old_color = self.conf_general["diff_color_old"] old_color = self.conf_general["diff_color_old"]
edited_color = self.conf_general["diff_color_edited"] edited_color = self.conf_general["diff_color_edited"]
@ -352,10 +457,12 @@ class Differ:
f"?\t\t- Show this help\n\n" f"?\t\t- Show this help\n\n"
) )
def get_user_choice(self, saved_diff_present: bool, saved_choice: ApplyType) -> ApplyType: def get_user_choice(
self, saved_diff_present: bool, saved_choice: ApplyType
) -> ApplyType:
while True: while True:
choice = self.print_prompt(saved_diff_present, saved_choice) choice = self.print_prompt(saved_diff_present, saved_choice)
if choice not in ["O", "o", "n", "e", "s", "p", "q", "?", "help"]: if choice not in ["O", "o", "n", "e", "E", "s", "p", "q", "?", "help"]:
print(f"{bold(choice)} is not valid.") print(f"{bold(choice)} is not valid.")
self.print_prompt_help(saved_diff_present, saved_choice) self.print_prompt_help(saved_diff_present, saved_choice)
continue continue
@ -372,6 +479,8 @@ class Differ:
return ApplyType.OLD_ALL return ApplyType.OLD_ALL
elif choice == "e": elif choice == "e":
return ApplyType.EDIT return ApplyType.EDIT
elif choice == "E":
return ApplyType.SHOW_EDIT
elif choice == "s": elif choice == "s":
return ApplyType.SAVED return ApplyType.SAVED
elif choice in ["?", "help"]: elif choice in ["?", "help"]:
@ -391,10 +500,23 @@ class Differ:
new_hash = "" new_hash = ""
return saved["old_hash"] == old_hash and saved["new_hash"] == new_hash return saved["old_hash"] == old_hash and saved["new_hash"] == new_hash
def create_patch(self, coord: PatchCoord, choice: ApplyType, saved_patch: dict = None): def create_patch(
self,
coord: PatchCoord,
choice: ApplyType,
saved_patch: dict = None,
edited_text: bytes = None,
):
old = self.cur_old_node.text if self.cur_old_node else b"" old = self.cur_old_node.text if self.cur_old_node else b""
new = self.cur_new_node.text if self.cur_new_node else b"" new = self.cur_new_node.text if self.cur_new_node else b""
return Patch(self.cur_nid, old, new, coord, saved_patch["apply_type"] if saved_patch else choice) return Patch(
self.cur_nid,
old,
new,
coord,
saved_patch["apply_type"] if saved_patch else choice,
edit=edited_text,
)
def add_patch( def add_patch(
self, self,
@ -402,8 +524,12 @@ class Differ:
consec_old: int, consec_old: int,
old_filepath: Path, old_filepath: Path,
patch_coord: PatchCoord, patch_coord: PatchCoord,
saved_patch: dict | None = None,
edited_text: bytes | None = None,
) -> None: ) -> None:
self.current_patch = self.create_patch(patch_coord, apply_type) self.current_patch = self.create_patch(
patch_coord, apply_type, saved_patch, edited_text
)
self.persist_patch(old_filepath, self.current_patch) self.persist_patch(old_filepath, self.current_patch)
if consec_old > 1: if consec_old > 1:
# Two or more old nodes are not present in the new file. # Two or more old nodes are not present in the new file.
@ -412,18 +538,34 @@ class Differ:
else: else:
self.patches.append(self.current_patch) self.patches.append(self.current_patch)
def diff_nodes(self, old_filepath: Path, new_nodes: dict[bytes, Node], old_nodes: dict[bytes, Node]) -> list[Patch]: def diff_nodes(
self,
old_filepath: Path,
new_nodes: dict[bytes, Node],
old_nodes: dict[bytes, Node],
) -> list[Patch]:
""" """
Asks the user for each different node, which version should be written. Asks the user for each different node, which version should be written.
It writes the choice to a file, so the previous choice can be applied again if nothing changed. It writes the choice to a file, so the previous choice can be applied again if nothing changed.
""" """
# Sort list of nodes descending. # Sort list of nodes descending.
# This is necessary because # This is necessary because
# a) we need to apply the patches backwards (so the coordinates in the file don't change. # a) we need to apply the patches backwards (starting from the end of the file,
# so the coordinates in the file don't change, when replace text).
# b) If there is an old node, which is not present in the new file, we search for # b) If there is an old node, which is not present in the new file, we search for
# a node which is adjacent (random node order wouldn't allow this). # a node which is adjacent (random node order wouldn't allow this).
new_nodes = {k: v for k, v in sorted(new_nodes.items(), key=lambda item: item[1].start_byte, reverse=True)} new_nodes = {
old_nodes = {k: v for k, v in sorted(old_nodes.items(), key=lambda item: item[1].start_byte, reverse=True)} k: v
for k, v in sorted(
new_nodes.items(), key=lambda item: item[1].start_byte, reverse=True
)
}
old_nodes = {
k: v
for k, v in sorted(
old_nodes.items(), key=lambda item: item[1].start_byte, reverse=True
)
}
# Collect all node ids of this file # Collect all node ids of this file
node_ids = set() node_ids = set()
@ -433,36 +575,48 @@ class Differ:
# The initial patch coordinates point after the last node in the file. # The initial patch coordinates point after the last node in the file.
n0 = new_nodes[list(new_nodes.keys())[0]] n0 = new_nodes[list(new_nodes.keys())[0]]
patch_coord = PatchCoord(n0.end_byte, n0.end_byte, n0.end_point, n0.end_point) PatchCoord(n0.end_byte, n0.end_byte, n0.end_point, n0.end_point)
node_ids = sorted(node_ids) node_ids = sorted(node_ids)
self.patches = list() self.patches = list()
matching_nodes_count = 0 matching_nodes_count = 0
# Counts the number of old nodes which have no equivalent new node. # Counts the number of _consecutive_ old nodes which have no equivalent new node.
# They will be merged to a single patch later
consec_old = 0 consec_old = 0
choice: ApplyType = None choice: ApplyType | None = None
i = 0 idx = 0
while i < len(node_ids): while idx < len(node_ids):
self.cur_nid = node_ids[i] self.cur_nid = node_ids[idx]
self.cur_new_node = None self.cur_new_node = (
if self.cur_nid in new_nodes: None if self.cur_nid not in new_nodes else new_nodes[self.cur_nid]
self.cur_new_node = new_nodes[self.cur_nid] )
self.cur_old_node = None self.cur_old_node = (
if self.cur_nid in old_nodes: None if self.cur_nid not in old_nodes else old_nodes[self.cur_nid]
self.cur_old_node = old_nodes[self.cur_nid] )
n = self.cur_new_node.text.decode("utf8").splitlines() if self.cur_new_node else [""] n = (
o = self.cur_old_node.text.decode("utf8").splitlines() if self.cur_old_node else [""] self.cur_new_node.text.decode("utf8").splitlines()
if self.cur_new_node
else [""]
)
o = (
self.cur_old_node.text.decode("utf8").splitlines()
if self.cur_old_node
else [""]
)
diff_lines = list(self.differ.compare(o, n)) diff_lines = list(self.differ.compare(o, n))
if self.no_difference(diff_lines): if self.no_difference(diff_lines):
log.debug(f"Nodes {bold(self.cur_nid)} match.") log.info(
f"{bold('Patch:')} {idx + 1}/{len(node_ids)} - Nodes {bold(self.cur_nid)} match."
)
matching_nodes_count += 1 matching_nodes_count += 1
i += 1 idx += 1
continue continue
if self.cur_new_node: if self.cur_new_node:
consec_old = 0 consec_old = 0
# We always write to the new file. So we always take he coordinates form it.
patch_coord = PatchCoord.get_coordinates_from_node(self.cur_new_node) patch_coord = PatchCoord.get_coordinates_from_node(self.cur_new_node)
else: else:
consec_old += 1 consec_old += 1
@ -473,38 +627,50 @@ class Differ:
j = old_node_ids.index(self.cur_nid) j = old_node_ids.index(self.cur_nid)
while j >= 0 and (old_node_ids[j] not in new_nodes.keys()): while j >= 0 and (old_node_ids[j] not in new_nodes.keys()):
j -= 1 j -= 1
ref_new: Node = new_nodes[old_node_ids[j]] if old_node_ids[j] in new_nodes.keys() else new_nodes[0] ref_new: Node = (
new_nodes[old_node_ids[j]]
if old_node_ids[j] in new_nodes.keys()
else new_nodes[0]
)
ref_end_byte = ref_new.start_byte ref_end_byte = ref_new.start_byte
# We always write to the new file. So we always take he coordinates form it.
patch_coord = PatchCoord( patch_coord = PatchCoord(
ref_end_byte - 1, ref_end_byte - 1,
ref_end_byte - 1, ref_end_byte - 1,
ref_end_byte, ref_new.start_point,
ref_end_byte, ref_new.start_point,
) )
save_exists = False save_exists = False
saved = None saved: dict | None = None
if old_filepath.name in self.saved_patches and self.cur_nid in self.saved_patches[old_filepath.name]: if (
saved: dict = self.saved_patches[old_filepath.name][self.cur_nid] old_filepath.name in self.saved_patches
and self.cur_nid in self.saved_patches[old_filepath.name]
):
saved = self.saved_patches[old_filepath.name][self.cur_nid]
save_exists = True save_exists = True
if self.saved_patch_matches(saved) and not self.no_auto_apply: if self.saved_patch_matches(saved) and not self.no_auto_apply:
apply_type = ApplyType(saved["apply_type"]) apply_type = ApplyType(saved["apply_type"])
self.add_patch(apply_type, consec_old, old_filepath, patch_coord) self.add_patch(apply_type, consec_old, old_filepath, patch_coord)
log.info(f"Auto apply patch for {bold(self.cur_nid)}") log.info(
i += 1 f"{bold('Patch:')} {idx + 1}/{len(node_ids)} - Auto apply patch for {bold(self.cur_nid)}"
)
idx += 1
continue continue
if choice == ApplyType.OLD_ALL: if choice == ApplyType.OLD_ALL:
self.add_patch(ApplyType.OLD, consec_old, old_filepath, patch_coord) self.add_patch(ApplyType.OLD, consec_old, old_filepath, patch_coord)
i += 1 idx += 1
continue continue
self.print_diff(diff_lines, self.cur_nid, i + 1, len(node_ids)) self.print_diff(diff_lines, self.cur_nid, idx + 1, len(node_ids))
choice = self.get_user_choice(save_exists, None if not saved else saved["apply_type"]) choice = self.get_user_choice(
save_exists, None if not saved else saved["apply_type"]
)
if choice == ApplyType.OLD: if choice == ApplyType.OLD:
if not self.cur_old_node: if not self.cur_old_node:
# No data in old node. Skip # No data in old node. Skip
i += 1 idx += 1
continue continue
self.add_patch(ApplyType.OLD, consec_old, old_filepath, patch_coord) self.add_patch(ApplyType.OLD, consec_old, old_filepath, patch_coord)
elif choice == ApplyType.NEW: elif choice == ApplyType.NEW:
@ -514,20 +680,43 @@ class Differ:
if not save_exists: if not save_exists:
print(bold("Save does not exist.")) print(bold("Save does not exist."))
continue continue
self.add_patch(saved["apply_type"], consec_old, old_filepath, patch_coord) self.add_patch(
saved["apply_type"],
consec_old,
old_filepath,
patch_coord,
saved_patch=saved,
edited_text=saved["edit"].encode(),
)
elif choice == ApplyType.SHOW_EDIT:
if not saved or not saved["edit"]:
print(bold("No edited text was saved before."))
input("Press enter to continue...\n")
continue
saved_edited_text = colored(
f'\n{saved["edit"]}\n', self.conf_general["diff_color_edited"]
)
print(saved_edited_text)
input("Press enter to continue...\n")
continue
elif choice == ApplyType.OLD_ALL: elif choice == ApplyType.OLD_ALL:
self.add_patch(ApplyType.OLD, consec_old, old_filepath, patch_coord) self.add_patch(ApplyType.OLD, consec_old, old_filepath, patch_coord)
elif choice == ApplyType.EDIT: elif choice == ApplyType.EDIT:
print(f"{bold('Editing not yet implemented.', 'light_red')}") edited_text = self.edit_patch(diff_lines)
continue if not edited_text:
continue
self.persist_patch(
old_filepath,
self.create_patch(patch_coord, choice, edited_text=edited_text),
)
elif choice == ApplyType.PREVIOUS: elif choice == ApplyType.PREVIOUS:
if i == 0: if idx == 0:
print(bold(f"There is no previous diff for {old_filepath.name}!")) print(bold(f"There is no previous diff for {old_filepath.name}!"))
input("Press enter...") input("Press enter...")
continue continue
i -= 1 idx -= 1
continue continue
i += 1 idx += 1
log.info(f"Number of matching nodes = {matching_nodes_count}") log.info(f"Number of matching nodes = {matching_nodes_count}")
return self.patches return self.patches
@ -558,10 +747,16 @@ class Differ:
old_filepath = old_file[k]["filepath"] old_filepath = old_file[k]["filepath"]
new_filepath = new_file[k]["filepath"] new_filepath = new_file[k]["filepath"]
diffs_to_process = max(len(new_file[k]["nodes"]), len(old_file[k]["nodes"])) diffs_to_process = max(len(new_file[k]["nodes"]), len(old_file[k]["nodes"]))
print_prominent_info(self.get_diff_intro_msg(old_filepath, new_filepath, k + 1, i, diffs_to_process)) print_prominent_info(
self.get_diff_intro_msg(
old_filepath, new_filepath, k + 1, i, diffs_to_process
)
)
if diffs_to_process == 0: if diffs_to_process == 0:
continue continue
patches[new_filepath] = self.diff_nodes(old_filepath, new_file[k]["nodes"], old_file[k]["nodes"]) patches[new_filepath] = self.diff_nodes(
old_filepath, new_file[k]["nodes"], old_file[k]["nodes"]
)
self.patch_files(patches) self.patch_files(patches)
log.info("Done") log.info("Done")
@ -590,6 +785,38 @@ class Differ:
run_clang_format(list(file_patches.keys())) run_clang_format(list(file_patches.keys()))
return return
def edit_patch(self, diff_lines: list[str]) -> bytes | None:
tmp_file = tempfile.NamedTemporaryFile(suffix="c", delete=False)
tmp_file_name = tmp_file.name
tmp_file.writelines([line.encode() + b"\n" for line in diff_lines])
tmp_file.write(self.get_edit_explanation())
tmp_file.close()
editor = self.conf_general["patch_editor"]
try:
subprocess.run([editor, tmp_file_name])
except FileNotFoundError:
log.error(f"Could not find editor '{editor}'")
return None
edited_text = b""
with open(tmp_file_name, "rb") as tmp_file:
for line in tmp_file.readlines():
if self.get_separator_line() in line:
break
edited_text += line
tmp_file.close()
return edited_text
@staticmethod
def get_separator_line() -> bytes:
return f"// {'=' * 50}".encode()
def get_edit_explanation(self) -> bytes:
return (
f"{self.get_separator_line().decode('utf8')}\n"
"// Everything below this line will be deleted\n"
"// Edit the file to your liking. The result will be written 'as is' to the source file.\n"
).encode()
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@ -603,7 +830,11 @@ def parse_args() -> argparse.Namespace:
action="store_true", action="store_true",
) )
parser.add_argument( parser.add_argument(
"-a", dest="arch", help="Name of target architecture.", choices=["ARM", "PPC, AArch64", "Alpha"], required=True "-a",
dest="arch",
help="Name of target architecture (ignored with -t option)",
choices=["ARM", "PPC, AArch64", "Alpha"],
required=True,
) )
parser.add_argument( parser.add_argument(
"-v", "-v",
@ -613,7 +844,7 @@ def parse_args() -> argparse.Namespace:
default="info", default="info",
) )
parser.add_argument( parser.add_argument(
"-c", dest="config_path", help="Config file for architectures.", default="arch_config.json", type=Path "-t", dest="testing", help="Run with test configuration.", action="store_true"
) )
arguments = parser.parse_args() arguments = parser.parse_args()
return arguments return arguments
@ -629,9 +860,12 @@ if __name__ == "__main__":
stream=sys.stdout, stream=sys.stdout,
format="%(levelname)-5s - %(message)s", format="%(levelname)-5s - %(message)s",
) )
cfg = Configurator(args.arch, args.config_path) if args.testing:
cfg = Configurator("ARCH", get_path("{DIFFER_TEST_CONFIG_FILE}"))
else:
cfg = Configurator(args.arch, get_path("{CPP_TRANSLATOR_CONFIG}"))
differ = Differ(cfg, args.no_auto_apply) differ = Differ(cfg, args.no_auto_apply, testing=args.testing)
try: try:
differ.diff() differ.diff()
except Exception as e: except Exception as e:

View File

@ -1,3 +1,8 @@
<!--
Copyright © 2022 Rot127 <unisono@quyllur.org>
SPDX-License-Identifier: BSD-3
-->
# C++ Translator # C++ Translator
Capstone uses source files from LLVM to disassemble opcodes. Capstone uses source files from LLVM to disassemble opcodes.
@ -13,16 +18,14 @@ The configuration for each architecture is set in `arch_config.json`.
The config values have the following meaning: The config values have the following meaning:
- `General`: Settings valid for all architectures. - `General`: Settings valid for all architectures.
- `patch_persistent_file`: Path to the file which saves the selections from the `Differ`.
- `translation_out_dir`: Path to the directory where the `CppTranslator` stores its files.
- `diff_out_dir`: Path to the directory where the `Differ` stores its files.
- `diff_color_new`: Color in the `Differ` for translated content. - `diff_color_new`: Color in the `Differ` for translated content.
- `diff_color_old`: Color in the `Differ` for old/current Capstone content. - `diff_color_old`: Color in the `Differ` for old/current Capstone content.
- `diff_color_saved`: Color in the `Differ` for saved content. - `diff_color_saved`: Color in the `Differ` for saved content.
- `diff_color_edited`: Color in the `Differ` for edited content. - `diff_color_edited`: Color in the `Differ` for edited content.
- `patch_editor`: Editor to open for patch editing.
- `nodes_to_diff`: List of parse tree nodes which get diffed - *Mind the note below*. - `nodes_to_diff`: List of parse tree nodes which get diffed - *Mind the note below*.
- `node_type`: The `type` of the node to be diffed. - `node_type`: The `type` of the node to be diffed.
- `identifier_node_type`: Types of child nodes which identify the node during diffing (the identifier must be the same in the translated and the old file!). - `identifier_node_type`: Types of child nodes which identify the node during diffing (the identifier must be the same in the translated and the old file!). Types can be of the form `<parent-type>/<child type>`.
- `<ARCH>`: Settings valid for a specific architecture - `<ARCH>`: Settings valid for a specific architecture
- `files_to_translate`: A list of file paths to translate. - `files_to_translate`: A list of file paths to translate.
- `in`: *Path* to a specific source file. - `in`: *Path* to a specific source file.

View File

@ -1,11 +1,13 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re import re
from pathlib import Path from pathlib import Path
from tree_sitter import Language, Parser from tree_sitter import Language, Node, Parser, Query
import logging as log
from tree_sitter.binding import Query, Node from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.HelperMethods import get_text
class TemplateRefInstance: class TemplateRefInstance:
@ -27,7 +29,9 @@ class TemplateRefInstance:
# (parameters are set by the template parameters of the calling function). # (parameters are set by the template parameters of the calling function).
caller_param_indices: [{str: int}] = list() caller_param_indices: [{str: int}] = list()
def __init__(self, name: bytes, args: bytes, start_point, start_byte, end_point, end_byte): def __init__(
self, name: bytes, args: bytes, start_point, start_byte, end_point, end_byte
):
self.name = name self.name = name
self.args = args self.args = args
self.start_point = start_point self.start_point = start_point
@ -41,7 +45,14 @@ class TemplateRefInstance:
return ( return (
self.name == other.name self.name == other.name
and self.args == other.args and self.args == other.args
and any([a == b for a, b in zip(self.caller_param_indices, other.caller_param_indices)]) and any(
[
a == b
for a, b in zip(
self.caller_param_indices, other.caller_param_indices
)
]
)
and self.start_byte == other.start_byte and self.start_byte == other.start_byte
and self.start_point == other.start_point and self.start_point == other.start_point
and self.end_byte == other.end_byte and self.end_byte == other.end_byte
@ -75,7 +86,13 @@ class TemplateCollector:
incomplete_template_refs: {bytes: [TemplateRefInstance]} = dict() incomplete_template_refs: {bytes: [TemplateRefInstance]} = dict()
sources: [{str: bytes}] = list() sources: [{str: bytes}] = list()
def __init__(self, ts_parser: Parser, ts_cpp: Language, searchable_files: [Path], temp_arg_deduction: [bytes]): def __init__(
self,
ts_parser: Parser,
ts_cpp: Language,
searchable_files: [Path],
temp_arg_deduction: [bytes],
):
self.parser = ts_parser self.parser = ts_parser
self.lang_cpp = ts_cpp self.lang_cpp = ts_cpp
self.searchable_files = searchable_files self.searchable_files = searchable_files
@ -100,10 +117,17 @@ class TemplateCollector:
args = get_text(src, templ_args.start_byte, templ_args.end_byte) args = get_text(src, templ_args.start_byte, templ_args.end_byte)
ti = TemplateRefInstance( ti = TemplateRefInstance(
name, args, cb[0][0].start_point, cb[0][0].start_byte, cb[0][0].end_point, cb[0][0].end_byte name,
args,
cb[0][0].start_point,
cb[0][0].start_byte,
cb[0][0].end_point,
cb[0][0].end_byte,
) )
log.debug(f"Found new template ref: {name.decode('utf8')}{args.decode('utf8')}") log.debug(
f"Found new template ref: {name.decode('utf8')}{args.decode('utf8')}"
)
if not self.contains_template_dependent_param(src, ti, cb[0]): if not self.contains_template_dependent_param(src, ti, cb[0]):
if name not in self.template_refs: if name not in self.template_refs:
@ -118,7 +142,10 @@ class TemplateCollector:
def resolve_dependencies(self): def resolve_dependencies(self):
# Resolve dependencies of templates until nothing new was resolved. # Resolve dependencies of templates until nothing new was resolved.
prev_len = 0 prev_len = 0
while len(self.incomplete_template_refs) > 0 and len(self.incomplete_template_refs) != prev_len: while (
len(self.incomplete_template_refs) > 0
and len(self.incomplete_template_refs) != prev_len
):
# Dict with new template calls which were previously incomplete # Dict with new template calls which were previously incomplete
# because one or more parameters were unknown. # because one or more parameters were unknown.
new_completed_tcs: {str: list} = dict() new_completed_tcs: {str: list} = dict()
@ -134,7 +161,9 @@ class TemplateCollector:
for caller_template in tc_instance_list: for caller_template in tc_instance_list:
incomplete_tc: TemplateRefInstance incomplete_tc: TemplateRefInstance
for incomplete_tc in self.incomplete_template_refs[caller_name]: for incomplete_tc in self.incomplete_template_refs[caller_name]:
new_tc: TemplateRefInstance = self.get_completed_tc(caller_template, incomplete_tc) new_tc: TemplateRefInstance = self.get_completed_tc(
caller_template, incomplete_tc
)
callee_name = new_tc.name callee_name = new_tc.name
if callee_name not in new_completed_tcs: if callee_name not in new_completed_tcs:
new_completed_tcs[callee_name] = list() new_completed_tcs[callee_name] = list()
@ -149,11 +178,22 @@ class TemplateCollector:
self.template_refs[templ_name] = tc_list self.template_refs[templ_name] = tc_list
prev_len = len(self.incomplete_template_refs) prev_len = len(self.incomplete_template_refs)
if prev_len > 0: if prev_len > 0:
log.info(f"Unresolved template calls: {self.incomplete_template_refs.keys()}. Patch them by hand!") log.info(
f"Unresolved template calls: {self.incomplete_template_refs.keys()}. Patch them by hand!"
)
@staticmethod @staticmethod
def get_completed_tc(tc: TemplateRefInstance, itc: TemplateRefInstance) -> TemplateRefInstance: def get_completed_tc(
new_tc = TemplateRefInstance(itc.name, itc.args, itc.start_byte, itc.start_byte, itc.end_point, itc.end_byte) tc: TemplateRefInstance, itc: TemplateRefInstance
) -> TemplateRefInstance:
new_tc = TemplateRefInstance(
itc.name,
itc.args,
itc.start_byte,
itc.start_byte,
itc.end_point,
itc.end_byte,
)
for indices in itc.caller_param_indices: for indices in itc.caller_param_indices:
if tc.name not in indices: if tc.name not in indices:
# Index of other caller function. Skip. # Index of other caller function. Skip.
@ -165,7 +205,9 @@ class TemplateCollector:
new_tc.templ_name = new_tc.name + new_tc.args new_tc.templ_name = new_tc.name + new_tc.args
return new_tc return new_tc
def contains_template_dependent_param(self, src, ti: TemplateRefInstance, parse_tree: (Node, str)) -> bool: def contains_template_dependent_param(
self, src, ti: TemplateRefInstance, parse_tree: (Node, str)
) -> bool:
"""Here we check if one of the template parameters of the given template call, """Here we check if one of the template parameters of the given template call,
is a parameter of the callers template definition. is a parameter of the callers template definition.
@ -201,14 +243,20 @@ class TemplateCollector:
return False return False
caller_fcn_id = node.named_children[2].named_children[0] caller_fcn_id = node.named_children[2].named_children[0]
caller_fcn_name = get_text(src, caller_fcn_id.start_byte, caller_fcn_id.end_byte) caller_fcn_name = get_text(
caller_templ_params = get_text(src, node.prev_sibling.start_byte, node.prev_sibling.end_byte) src, caller_fcn_id.start_byte, caller_fcn_id.end_byte
)
caller_templ_params = get_text(
src, node.prev_sibling.start_byte, node.prev_sibling.end_byte
)
pl = TemplateCollector.templ_params_to_list(caller_templ_params) pl = TemplateCollector.templ_params_to_list(caller_templ_params)
has_parameter_dependency = False has_parameter_dependency = False
for i, param in enumerate(pl): for i, param in enumerate(pl):
if param in ti.args_list: if param in ti.args_list:
has_parameter_dependency = True has_parameter_dependency = True
ti.caller_param_indices.append({caller_fcn_name: i, "self_i": ti.args_list.index(param)}) ti.caller_param_indices.append(
{caller_fcn_name: i, "self_i": ti.args_list.index(param)}
)
if not has_parameter_dependency: if not has_parameter_dependency:
return False return False

View File

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
#include "some_header_I.h"
#include <some_system_header.h>
#define MACRO_A 0
#define MACRO_B 0
#define FCN_MACRO_A(x) function_a(x)
#define FCN_MACRO_B(x) \
function_b(x + 1)
int main() {
int x = 10000 * 71;
return x;
}
void function_a(int x) {
return;
}
void function_b(unsigned x) {
return;
}
void only_in_new() {}

View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
#include "some_header_I.h"
#include <some_system_header.h>
#define MACRO_A 0
#define MACRO_B 0
#define FCN_MACRO_A(x) function_a(x)
#define FCN_MACRO_B(x) \
function_b(x)
int main() {
int x = 71;
return x;
}
void function_a(int x) {
return;
}
void function_b(int x) {
return;
}
void only_in_old_I() {}
void only_in_old_II() {}

View File

@ -0,0 +1,35 @@
{
"General": {
"diff_color_new": "green",
"diff_color_old": "light_blue",
"diff_color_saved": "yellow",
"diff_color_edited": "light_magenta",
"patch_editor": "vim",
"nodes_to_diff": [
{
"node_type": "function_definition",
"identifier_node_type": ["function_declarator/identifier"]
},{
"node_type": "preproc_function_def",
"identifier_node_type": ["identifier"]
},{
"node_type": "preproc_include",
"identifier_node_type": ["string_literal", "system_lib_string"]
},{
"node_type": "preproc_define",
"identifier_node_type": ["identifier"]
}
]
},
"ARCH": {
"files_to_translate": [
{
"in": "{DIFFER_TEST_OLD_SRC_DIR}/diff_test_file.c",
"out": "diff_test_file.c"
}
],
"files_for_template_search": [],
"templates_with_arg_deduction": [],
"manually_edited_files": []
}
}

View File

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
int main() {
tfunction<int, int>();
tfunction<int, char>();
}

View File

@ -0,0 +1,16 @@
{
"General": {
"diff_color_new": "green",
"diff_color_old": "light_blue",
"diff_color_saved": "yellow",
"diff_color_edited": "light_magenta",
"patch_editor": "vim",
"nodes_to_diff": []
},
"ARCH": {
"files_to_translate": [],
"files_for_template_search": ["{PATCHES_TEST_DIR}/template_src.c"],
"templates_with_arg_deduction": [],
"manually_edited_files": []
}
}

View File

@ -0,0 +1,106 @@
# SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import unittest
from tree_sitter import Node
from autosync.cpptranslator.Configurator import Configurator
from autosync.cpptranslator.Differ import ApplyType, Differ, Patch, PatchCoord
from autosync.cpptranslator.TemplateCollector import TemplateCollector
from autosync.Helper import get_path
class TestHeaderPatcher(unittest.TestCase):
@classmethod
def setUpClass(cls):
configurator = Configurator("ARCH", get_path("{DIFFER_TEST_CONFIG_FILE}"))
cls.ts_cpp_lang = configurator.get_cpp_lang()
cls.parser = configurator.get_parser()
cls.template_collector = TemplateCollector(
configurator.get_parser(), configurator.get_cpp_lang(), [], []
)
cls.differ = Differ(configurator, testing=True, no_auto_apply=True)
def check_persistence(self, nid, expected, apply_type, edited_text):
new_node: Node = self.new_nodes[nid] if nid in self.new_nodes else None
old_node: Node = self.old_nodes[nid] if nid in self.old_nodes else None
if not new_node:
before_old_node = old_node.start_byte - 1
coord = PatchCoord(
before_old_node,
before_old_node,
(before_old_node, before_old_node),
(before_old_node, before_old_node),
)
else:
coord = PatchCoord(
new_node.start_byte,
new_node.end_byte,
new_node.start_point,
new_node.end_point,
)
patch = Patch(
node_id=nid,
old=old_node.text if old_node else b"",
new=new_node.text if new_node else b"",
coord=coord,
apply=apply_type,
edit=edited_text,
)
self.assertEqual(patch.get_persist_info(), expected)
def parse_files(self, filename: str):
self.old_nodes = self.differ.parse_file(
get_path("{DIFFER_TEST_OLD_SRC_DIR}").joinpath(filename)
)
self.new_nodes = self.differ.parse_file(
get_path("{DIFFER_TEST_NEW_SRC_DIR}").joinpath(filename)
)
def test_patch_persistence(self):
self.parse_files("diff_test_file.c")
nid = "function_b"
expected = {
f"{nid}": {
"apply_type": "OLD",
"edit": "aaaaaaa",
"new_hash": "e5b3e0e5c6fb1f5f39e5725e464e6dfa3c6a7f1a8a5d104801e1fc10b6f1cc2b",
"old_hash": "8fc2b2123209c37534bb60c8e38564ed773430b9fc5bca37a0ae73a64b2883ab",
}
}
edited_text: bytes = b"aaaaaaa"
self.check_persistence(nid, expected, ApplyType.OLD, edited_text)
nid = "only_in_old_I"
expected = {
f"{nid}": {
"apply_type": "NEW",
"edit": "",
"new_hash": "",
"old_hash": "37431b6fe6707794a8e07902bef6510fc1d10b833db9b1dccc70b1530997b2b1",
}
}
self.check_persistence(nid, expected, ApplyType.NEW, b"")
self.assertRaises(
NotImplementedError,
self.check_persistence,
nid=nid,
expected=expected,
apply_type=ApplyType.SAVED,
edited_text=b"",
)
nid = "function_b"
expected = {
f"{nid}": {
"apply_type": "EDIT",
"edit": "aaaaaaa\n\n\n\n\n91928",
"new_hash": "e5b3e0e5c6fb1f5f39e5725e464e6dfa3c6a7f1a8a5d104801e1fc10b6f1cc2b",
"old_hash": "8fc2b2123209c37534bb60c8e38564ed773430b9fc5bca37a0ae73a64b2883ab",
}
}
edited_text: bytes = b"aaaaaaa\n\n\n\n\n91928"
self.check_persistence(nid, expected, ApplyType.EDIT, edited_text)

View File

@ -0,0 +1,563 @@
#!/usr/bin/env python3
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import unittest
from tree_sitter import Node, Query
from autosync.cpptranslator import CppTranslator
from autosync.cpptranslator.Configurator import Configurator
from autosync.cpptranslator.patches.AddCSDetail import AddCSDetail
from autosync.cpptranslator.patches.AddOperand import AddOperand
from autosync.cpptranslator.patches.Assert import Assert
from autosync.cpptranslator.patches.BitCastStdArray import BitCastStdArray
from autosync.cpptranslator.patches.CheckDecoderStatus import CheckDecoderStatus
from autosync.cpptranslator.patches.ClassesDef import ClassesDef
from autosync.cpptranslator.patches.ConstMCInstParameter import ConstMCInstParameter
from autosync.cpptranslator.patches.ConstMCOperand import ConstMCOperand
from autosync.cpptranslator.patches.CppInitCast import CppInitCast
from autosync.cpptranslator.patches.CreateOperand0 import CreateOperand0
from autosync.cpptranslator.patches.CreateOperand1 import CreateOperand1
from autosync.cpptranslator.patches.DeclarationInConditionClause import (
DeclarationInConditionalClause,
)
from autosync.cpptranslator.patches.DecodeInstruction import DecodeInstruction
from autosync.cpptranslator.patches.DecoderCast import DecoderCast
from autosync.cpptranslator.patches.DecoderParameter import DecoderParameter
from autosync.cpptranslator.patches.FallThrough import FallThrough
from autosync.cpptranslator.patches.FeatureBits import FeatureBits
from autosync.cpptranslator.patches.FeatureBitsDecl import FeatureBitsDecl
from autosync.cpptranslator.patches.FieldFromInstr import FieldFromInstr
from autosync.cpptranslator.patches.GetNumOperands import GetNumOperands
from autosync.cpptranslator.patches.GetOpcode import GetOpcode
from autosync.cpptranslator.patches.GetOperand import GetOperand
from autosync.cpptranslator.patches.GetOperandRegImm import GetOperandRegImm
from autosync.cpptranslator.patches.GetRegClass import GetRegClass
from autosync.cpptranslator.patches.GetRegFromClass import GetRegFromClass
from autosync.cpptranslator.patches.GetSubReg import GetSubReg
from autosync.cpptranslator.patches.Includes import Includes
from autosync.cpptranslator.patches.InlineToStaticInline import InlineToStaticInline
from autosync.cpptranslator.patches.IsOptionalDef import IsOptionalDef
from autosync.cpptranslator.patches.IsPredicate import IsPredicate
from autosync.cpptranslator.patches.IsRegImm import IsOperandRegImm
from autosync.cpptranslator.patches.LLVMFallThrough import LLVMFallThrough
from autosync.cpptranslator.patches.LLVMunreachable import LLVMUnreachable
from autosync.cpptranslator.patches.MethodToFunctions import MethodToFunction
from autosync.cpptranslator.patches.MethodTypeQualifier import MethodTypeQualifier
from autosync.cpptranslator.patches.NamespaceAnon import NamespaceAnon
from autosync.cpptranslator.patches.NamespaceArch import NamespaceArch
from autosync.cpptranslator.patches.NamespaceLLVM import NamespaceLLVM
from autosync.cpptranslator.patches.OutStreamParam import OutStreamParam
from autosync.cpptranslator.patches.PredicateBlockFunctions import (
PredicateBlockFunctions,
)
from autosync.cpptranslator.patches.PrintAnnotation import PrintAnnotation
from autosync.cpptranslator.patches.PrintRegImmShift import PrintRegImmShift
from autosync.cpptranslator.patches.QualifiedIdentifier import QualifiedIdentifier
from autosync.cpptranslator.patches.ReferencesDecl import ReferencesDecl
from autosync.cpptranslator.patches.RegClassContains import RegClassContains
from autosync.cpptranslator.patches.SetOpcode import SetOpcode
from autosync.cpptranslator.patches.SignExtend import SignExtend
from autosync.cpptranslator.patches.SizeAssignments import SizeAssignment
from autosync.cpptranslator.patches.STIArgument import STIArgument
from autosync.cpptranslator.patches.STIFeatureBits import STIFeatureBits
from autosync.cpptranslator.patches.STParameter import SubtargetInfoParam
from autosync.cpptranslator.patches.StreamOperation import StreamOperations
from autosync.cpptranslator.patches.TemplateDeclaration import TemplateDeclaration
from autosync.cpptranslator.patches.TemplateDefinition import TemplateDefinition
from autosync.cpptranslator.patches.TemplateParamDecl import TemplateParamDecl
from autosync.cpptranslator.patches.TemplateRefs import TemplateRefs
from autosync.cpptranslator.patches.UseMarkup import UseMarkup
from autosync.cpptranslator.patches.UsingDeclaration import UsingDeclaration
from autosync.cpptranslator.TemplateCollector import TemplateCollector
from autosync.Helper import get_path
class TestPatches(unittest.TestCase):
@classmethod
def setUpClass(cls):
configurator = Configurator("ARCH", get_path("{PATCHES_TEST_CONFIG}"))
cls.translator = CppTranslator.Translator(configurator, False)
cls.ts_cpp_lang = configurator.get_cpp_lang()
cls.parser = configurator.get_parser()
cls.template_collector = TemplateCollector(
configurator.get_parser(), configurator.get_cpp_lang(), [], []
)
def check_patching_result(self, patch, syntax, expected, filename=""):
if filename:
kwargs = {"filename": filename}
else:
kwargs = self.translator.get_patch_kwargs(patch)
query: Query = self.ts_cpp_lang.query(patch.get_search_pattern())
captures_bundle: [[(Node, str)]] = list()
for q in query.captures(self.parser.parse(syntax, keep_text=True).root_node):
if q[1] == patch.get_main_capture_name():
captures_bundle.append([q])
else:
captures_bundle[-1].append(q)
self.assertGreater(len(captures_bundle), 0)
for cb in captures_bundle:
self.assertEqual(patch.get_patch(cb, syntax, **kwargs), expected)
def test_addcsdetail(self):
patch = AddCSDetail(0, "ARCH")
syntax = b"int i = x; void printThumbLdrLabelOperand(MCInst *MI, unsigned OpNo, SStream *O) { int i = OpNo; }"
self.check_patching_result(
patch,
syntax,
b"void printThumbLdrLabelOperand(MCInst *MI, unsigned OpNo, SStream *O){ "
b"add_cs_detail(MI, ARCH_OP_GROUP_ThumbLdrLabelOperand, OpNo); "
b"int i = OpNo; "
b"}",
)
def test_addoperand(self):
patch = AddOperand(0)
syntax = b"MI.addOperand(OPERAND)"
self.check_patching_result(
patch,
syntax,
b"MCInst_addOperand2(MI, (OPERAND))",
)
def test_assert(self):
patch = Assert(0)
syntax = b"assert(0 == 0)"
self.check_patching_result(patch, syntax, b"")
def test_bitcaststdarray(self):
patch = BitCastStdArray(0)
syntax = b"auto S = bit_cast<std::array<int32_t, 2>>(Imm);"
self.check_patching_result(
patch,
syntax,
b"union {\n"
b" typeof(Imm) In;\n"
b" int32_t Out[ 2];\n"
b"} U_S;\n"
b"U_S.In = Imm"
b";\n"
b"int32_t *S = U_S.Out;",
)
def test_checkdecoderstatus(self):
patch = CheckDecoderStatus(0)
syntax = b"Check(S, functions())"
self.check_patching_result(patch, syntax, b"Check(&S, functions())")
def test_classesdef(self):
patch = ClassesDef(0)
syntax = b"""class AArch64Disassembler : public MCDisassembler {
std::unique_ptr<const MCInstrInfo> const MCII;
public:
AArch64Disassembler(const MCSubtargetInfo &STI, MCContext &Ctx,
MCInstrInfo const *MCII)
: MCDisassembler(STI, Ctx), MCII(MCII) {}
~AArch64Disassembler() override = default;
MCDisassembler::DecodeStatus
getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef<uint8_t> Bytes,
uint64_t Address, raw_ostream &CStream) const override;
uint64_t suggestBytesToSkip(ArrayRef<uint8_t> Bytes,
uint64_t Address) const override;
};
"""
self.check_patching_result(
patch,
syntax,
b"MCDisassembler::DecodeStatus\n"
b" getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef<uint8_t> Bytes,\n"
b" uint64_t Address, raw_ostream &CStream) const override;\n"
b"uint64_t suggestBytesToSkip(ArrayRef<uint8_t> Bytes,\n"
b" uint64_t Address) const override;\n",
)
def test_constmcinstparameter(self):
patch = ConstMCInstParameter(0)
syntax = b"void function(const MCInst *MI);"
expected = b"MCInst *MI"
self.check_patching_result(patch, syntax, expected)
def test_constmcoperand(self):
patch = ConstMCOperand(0)
syntax = b"const MCOperand op = { 0 };"
self.check_patching_result(patch, syntax, b"MCOperand op = { 0 };")
def test_cppinitcast(self):
patch = CppInitCast(0)
syntax = b"int(0x0000)"
self.check_patching_result(patch, syntax, b"((int)(0x0000))")
def test_createoperand0(self):
patch = CreateOperand0(0)
syntax = b"Inst.addOperand(MCOperand::createReg(REGISTER));"
self.check_patching_result(
patch,
syntax,
b"MCOperand_CreateReg0(Inst, (REGISTER))",
)
def test_createoperand1(self):
patch = CreateOperand1(0)
syntax = b"MI.insert(I, MCOperand::createReg(REGISTER));"
self.check_patching_result(
patch,
syntax,
b"MCInst_insert0(MI, I, MCOperand_CreateReg1(MI, (REGISTER)))",
)
def test_declarationinconditionclause(self):
patch = DeclarationInConditionalClause(0)
syntax = b"if (int i = 0) {}"
self.check_patching_result(patch, syntax, b"int i = 0;\nif (i)\n{}")
def test_decodeinstruction(self):
patch = DecodeInstruction(0)
syntax = (
b"decodeInstruction(DecoderTableThumb16, MI, Insn16, Address, this, STI);"
)
self.check_patching_result(
patch,
syntax,
b"decodeInstruction_2(DecoderTableThumb16, MI, Insn16, Address)",
)
syntax = b"decodeInstruction(Table[i], MI, Insn16, Address, this, STI);"
self.check_patching_result(
patch,
syntax,
b"decodeInstruction_2(Table[i], MI, Insn16, Address)",
)
def test_decodercast(self):
patch = DecoderCast(0)
syntax = (
b"const MCDisassembler *Dis = static_cast<const MCDisassembler*>(Decoder);"
)
self.check_patching_result(patch, syntax, b"")
def test_decoderparameter(self):
patch = DecoderParameter(0)
syntax = b"void function(const MCDisassembler *Decoder);"
self.check_patching_result(patch, syntax, b"const void *Decoder")
def test_fallthrough(self):
patch = FallThrough(0)
syntax = b"[[fallthrough]]"
self.check_patching_result(patch, syntax, b"// fall through")
def test_featurebitsdecl(self):
patch = FeatureBitsDecl(0)
syntax = b"const FeatureBitset &FeatureBits = ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits();"
self.check_patching_result(patch, syntax, b"")
def test_featurebits(self):
patch = FeatureBits(0, b"ARCH")
syntax = b"bool hasD32 = featureBits[ARCH::HasV8Ops];"
self.check_patching_result(
patch,
syntax,
b"ARCH_getFeatureBits(Inst->csh->mode, ARCH::HasV8Ops)",
)
def test_fieldfrominstr(self):
patch = FieldFromInstr(0)
syntax = b"unsigned Rm = fieldFromInstruction(Inst16, 0, 4);"
self.check_patching_result(
patch,
syntax,
b"fieldFromInstruction_2(Inst16, 0, 4)",
)
syntax = b"void function(MCInst *MI, unsigned Val) { unsigned Rm = fieldFromInstruction(Val, 0, 4); }"
self.check_patching_result(
patch,
syntax,
b"fieldFromInstruction_4(Val, 0, 4)",
)
def test_getnumoperands(self):
patch = GetNumOperands(0)
syntax = b"MI.getNumOperands();"
self.check_patching_result(patch, syntax, b"MCInst_getNumOperands(MI)")
def test_getopcode(self):
patch = GetOpcode(0)
syntax = b"Inst.getOpcode();"
self.check_patching_result(patch, syntax, b"MCInst_getOpcode(Inst)")
def test_getoperand(self):
patch = GetOperand(0)
syntax = b"MI.getOperand(0);"
self.check_patching_result(patch, syntax, b"MCInst_getOperand(MI, (0))")
def test_getoperandregimm(self):
patch = GetOperandRegImm(0)
syntax = b"OPERAND.getReg()"
self.check_patching_result(patch, syntax, b"MCOperand_getReg(OPERAND)")
def test_getregclass(self):
patch = GetRegClass(0)
syntax = b"MRI.getRegClass(RegClass);"
expected = b"MCRegisterInfo_getRegClass(Inst->MRI, RegClass)"
self.check_patching_result(patch, syntax, expected)
def test_getregfromclass(self):
patch = GetRegFromClass(0)
syntax = b"ARCHMCRegisterClasses[ARCH::FPR128RegClassID].getRegister(RegNo);"
self.check_patching_result(
patch,
syntax,
b"ARCHMCRegisterClasses[ARCH::FPR128RegClassID].RegsBegin[RegNo]",
)
def test_getsubreg(self):
patch = GetSubReg(0)
syntax = b"MRI.getSubReg(REGISTER);"
self.check_patching_result(
patch,
syntax,
b"MCRegisterInfo_getSubReg(Inst->MRI, REGISTER)",
)
def test_includes(self):
patch = Includes(0, "TEST_ARCH")
syntax = b'#include "some_llvm_header.h"'
self.check_patching_result(
patch,
syntax,
b"#include <stdio.h>\n"
b"#include <string.h>\n"
b"#include <stdlib.h>\n"
b"#include <capstone/platform.h>\n\n"
b"test_output",
"filename",
)
def test_inlinetostaticinline(self):
patch = InlineToStaticInline(0)
syntax = b"inline void FUNCTION() {}"
self.check_patching_result(
patch,
syntax,
b"static inline void FUNCTION() {}",
)
def test_isoptionaldef(self):
patch = IsOptionalDef(0)
syntax = b"OpInfo[i].isOptionalDef()"
self.check_patching_result(
patch,
syntax,
b"MCOperandInfo_isOptionalDef(&OpInfo[i])",
)
def test_ispredicate(self):
patch = IsPredicate(0)
syntax = b"OpInfo[i].isPredicate()"
self.check_patching_result(
patch,
syntax,
b"MCOperandInfo_isPredicate(&OpInfo[i])",
)
def test_isregimm(self):
patch = IsOperandRegImm(0)
syntax = b"OPERAND.isReg()"
self.check_patching_result(patch, syntax, b"MCOperand_isReg(OPERAND)")
def test_llvmfallthrough(self):
patch = LLVMFallThrough(0)
syntax = b"LLVM_FALLTHROUGH;"
self.check_patching_result(patch, syntax, b"")
def test_llvmunreachable(self):
patch = LLVMUnreachable(0)
syntax = b'llvm_unreachable("Error msg")'
self.check_patching_result(patch, syntax, b'assert(0 && "Error msg")')
def test_methodtofunctions(self):
patch = MethodToFunction(0)
syntax = b"void CLASS::METHOD_NAME(int a) {}"
self.check_patching_result(patch, syntax, b"METHOD_NAME(int a)")
def test_methodtypequalifier(self):
patch = MethodTypeQualifier(0)
syntax = b"void a_const_method() const {}"
self.check_patching_result(patch, syntax, b"a_const_method()")
def test_namespaceanon(self):
patch = NamespaceAnon(0)
syntax = b"namespace { int a = 0; }"
self.check_patching_result(patch, syntax, b" int a = 0; ")
def test_namespacearch(self):
patch = NamespaceArch(0)
syntax = b"namespace ArchSpecificNamespace { int a = 0; }"
self.check_patching_result(
patch,
syntax,
b"// CS namespace begin: ArchSpecificNamespace\n\n"
b"int a = 0;\n\n"
b"// CS namespace end: ArchSpecificNamespace\n\n",
)
def test_namespacellvm(self):
patch = NamespaceLLVM(0)
syntax = b"namespace llvm {int a = 0}"
self.check_patching_result(patch, syntax, b"int a = 0")
def test_outstreamparam(self):
patch = OutStreamParam(0)
syntax = b"void function(int a, raw_ostream &OS);"
self.check_patching_result(patch, syntax, b"(int a, SStream *OS)")
def test_predicateblockfunctions(self):
patch = PredicateBlockFunctions(0)
syntax = b"void function(MCInst *MI) { VPTBlock.instrInVPTBlock(); }"
self.check_patching_result(
patch,
syntax,
b"VPTBlock_instrInVPTBlock(&(MI->csh->VPTBlock))",
)
def test_predicateblockfunctions(self):
patch = PrintAnnotation(0)
syntax = b"printAnnotation();"
self.check_patching_result(patch, syntax, b"")
def test_printregimmshift(self):
patch = PrintRegImmShift(0)
syntax = b"printRegImmShift(0)"
self.check_patching_result(patch, syntax, b"printRegImmShift(Inst, 0)")
def test_qualifiedidentifier(self):
patch = QualifiedIdentifier(0)
syntax = b"NAMESPACE::ID"
self.check_patching_result(patch, syntax, b"NAMESPACE_ID")
def test_referencesdecl(self):
patch = ReferencesDecl(0)
syntax = b"int &Param = 0;"
self.check_patching_result(patch, syntax, b"*Param")
def test_regclasscontains(self):
patch = RegClassContains(0)
syntax = b"if (MRI.getRegClass(AArch64::GPR32RegClassID).contains(Reg)) {}"
self.check_patching_result(
patch,
syntax,
b"MCRegisterClass_contains(MRI.getRegClass(AArch64::GPR32RegClassID), Reg)",
)
def test_setopcode(self):
patch = SetOpcode(0)
syntax = b"Inst.setOpcode(0)"
self.check_patching_result(patch, syntax, b"MCInst_setOpcode(Inst, (0))")
def test_signextend(self):
patch = SignExtend(0)
syntax = b"SignExtend32<A>(0)"
self.check_patching_result(patch, syntax, b"SignExtend32((0), A)")
def test_sizeassignments(self):
patch = SizeAssignment(0)
syntax = b"void function(int &Size) { Size = 0; }"
self.check_patching_result(patch, syntax, b"*Size = 0")
def test_stiargument(self):
patch = STIArgument(0)
syntax = b"printSomeOperand(MI, NUM, STI, NUM)"
self.check_patching_result(patch, syntax, b"(MI, NUM, NUM)")
def test_stifeaturebits(self):
patch = STIFeatureBits(0, b"ARCH")
syntax = b"STI.getFeatureBits()[ARCH::FLAG];"
self.check_patching_result(
patch,
syntax,
b"ARCH_getFeatureBits(Inst->csh->mode, ARCH::FLAG)",
)
def test_stifeaturebits(self):
patch = SubtargetInfoParam(0)
syntax = b"void function(MCSubtargetInfo &STI);"
self.check_patching_result(patch, syntax, b"()")
def test_streamoperation(self):
patch = StreamOperations(0)
syntax = b"{ OS << 'a'; }"
self.check_patching_result(patch, syntax, b'SStream_concat0(OS, "a");\n')
syntax = b'{ OS << "aaaa" << "bbbb" << "cccc"; }'
self.check_patching_result(
patch,
syntax,
b'SStream_concat(OS, "%s%s", "aaaa", "bbbb");\nSStream_concat0(OS, "cccc");',
)
syntax = b'{ OS << "aaaa" << \'a\' << "cccc"; }'
self.check_patching_result(
patch,
syntax,
b'SStream_concat(OS, "%s", "aaaa");\n'
b"SStream_concat1(OS, 'a');\n"
b'SStream_concat0(OS, "cccc");',
)
def test_templatedeclaration(self):
patch = TemplateDeclaration(0, self.template_collector)
syntax = b"template<A, B> void tfunction();"
self.check_patching_result(
patch,
syntax,
b"#define DECLARE_tfunction(A, B) \\\n"
b" void CONCAT(tfunction, CONCAT(A, B))();\n"
b"DECLARE_tfunction(int, int);\n"
b"DECLARE_tfunction(int, char);\n",
)
def test_templatedefinition(self):
patch = TemplateDefinition(0, self.template_collector)
syntax = b"template<A, B> void tfunction() {}"
self.check_patching_result(
patch,
syntax,
b"#define DEFINE_tfunction(A, B) \\\n"
b" void CONCAT(tfunction, CONCAT(A, B))(){}\n"
b"DEFINE_tfunction(int, int);\n"
b"DEFINE_tfunction(int, char);\n",
)
def test_templateparamdecl(self):
patch = TemplateParamDecl(0)
syntax = b"void function(ArrayRef<uint8_t> x);"
self.check_patching_result(patch, syntax, b"const uint8_t *x")
def test_templaterefs(self):
patch = TemplateRefs(0)
syntax = b"TemplateFunction<A, B>();"
self.check_patching_result(
patch,
syntax,
b"CONCAT(TemplateFunction, CONCAT(A, B))",
)
def test_usemarkup(self):
patch = UseMarkup(0)
syntax = b"UseMarkup()"
self.check_patching_result(patch, syntax, b"getUseMarkup()")
def test_usingdecl(self):
patch = UsingDeclaration(0)
syntax = b"using namespace llvm;"
self.check_patching_result(patch, syntax, b"")

View File

@ -1,12 +1,10 @@
{ {
"General": { "General": {
"patch_persistence_file": "{CPP_TRANSLATOR_DIR}/saved_patches.json",
"translation_out_dir": "{BUILD_DIR}/translate_out/",
"diff_out_dir": "{BUILD_DIR}/diff_out/",
"diff_color_new": "green", "diff_color_new": "green",
"diff_color_old": "light_blue", "diff_color_old": "light_blue",
"diff_color_saved": "yellow", "diff_color_saved": "yellow",
"diff_color_edited": "light_magenta", "diff_color_edited": "light_magenta",
"patch_editor": "vim",
"nodes_to_diff": [ "nodes_to_diff": [
{ {
"node_type": "function_definition", "node_type": "function_definition",
@ -116,7 +114,8 @@
"printImmSVE", "printImmSVE",
"printAMIndexedWB", "printAMIndexedWB",
"isSVECpyImm", "isSVECpyImm",
"isSVEAddSubImm" "isSVEAddSubImm",
"printVectorIndex"
], ],
"manually_edited_files": [] "manually_edited_files": []
}, },

View File

@ -1,10 +1,17 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_MCInst_var_name, template_param_list_to_dict from autosync.cpptranslator.patches.Helper import (
from CppTranslator.Patches.Patch import Patch get_MCInst_var_name,
get_text,
template_param_list_to_dict,
)
from autosync.cpptranslator.patches.Patch import Patch
class AddCSDetail(Patch): class AddCSDetail(Patch):
@ -33,7 +40,11 @@ class AddCSDetail(Patch):
super().__init__(priority) super().__init__(priority)
self.arch = arch self.arch = arch
self.apply_only_to = { self.apply_only_to = {
"files": ["ARMInstPrinter.cpp", "PPCInstPrinter.cpp", "AArch64InstPrinter.cpp"], "files": [
"ARMInstPrinter.cpp",
"PPCInstPrinter.cpp",
"AArch64InstPrinter.cpp",
],
"archs": list(), "archs": list(),
} }
@ -68,15 +79,31 @@ class AddCSDetail(Patch):
comp = get_text(src, comp.start_byte, comp.end_byte) comp = get_text(src, comp.start_byte, comp.end_byte)
return b"void " + fcn_id + params + b"{ " + add_cs_detail + comp.strip(b"{") return b"void " + fcn_id + params + b"{ " + add_cs_detail + comp.strip(b"{")
def get_add_cs_detail(self, src: bytes, fcn_def: Node, fcn_id: bytes, params: bytes) -> bytes: def get_add_cs_detail(
op_group_enum = self.arch.encode("utf8") + b"_OP_GROUP_" + fcn_id[5:] # Remove "print" from function id self, src: bytes, fcn_def: Node, fcn_id: bytes, params: bytes
) -> bytes:
op_group_enum = (
self.arch.encode("utf8") + b"_OP_GROUP_" + fcn_id[5:]
) # Remove "print" from function id
is_template = fcn_def.prev_sibling.type == "template_parameter_list" is_template = fcn_def.prev_sibling.type == "template_parameter_list"
op_num_var_name = b"OpNum" if b"OpNum" in params else (b"OpNo" if b"OpNo" in params else b"-.-") op_num_var_name = (
b"OpNum"
if b"OpNum" in params
else (b"OpNo" if b"OpNo" in params else b"-.-")
)
if not is_template and op_num_var_name in params: if not is_template and op_num_var_name in params:
# Standard printOperand() parameters # Standard printOperand() parameters
mcinst_var = get_MCInst_var_name(src, fcn_def) mcinst_var = get_MCInst_var_name(src, fcn_def)
return b"add_cs_detail(" + mcinst_var + b", " + op_group_enum + b", " + op_num_var_name + b");" return (
b"add_cs_detail("
+ mcinst_var
+ b", "
+ op_group_enum
+ b", "
+ op_num_var_name
+ b");"
)
elif op_group_enum == b"ARM_OP_GROUP_RegImmShift": elif op_group_enum == b"ARM_OP_GROUP_RegImmShift":
return b"add_cs_detail(MI, " + op_group_enum + b", ShOpc, ShImm);" return b"add_cs_detail(MI, " + op_group_enum + b", ShOpc, ShImm);"
elif is_template and op_num_var_name in params: elif is_template and op_num_var_name in params:
@ -84,7 +111,9 @@ class AddCSDetail(Patch):
templ_p = template_param_list_to_dict(fcn_def.prev_sibling) templ_p = template_param_list_to_dict(fcn_def.prev_sibling)
cs_args = b"" cs_args = b""
for tp in templ_p: for tp in templ_p:
op_group_enum = b"CONCAT(" + op_group_enum + b", " + tp["identifier"] + b")" op_group_enum = (
b"CONCAT(" + op_group_enum + b", " + tp["identifier"] + b")"
)
cs_args += b", " + tp["identifier"] cs_args += b", " + tp["identifier"]
return ( return (
b"add_cs_detail(" b"add_cs_detail("

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class AddOperand(Patch): class AddOperand(Patch):

View File

@ -1,5 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch
from autosync.cpptranslator.patches.Patch import Patch
class Assert(Patch): class Assert(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class BitCastStdArray(Patch): class BitCastStdArray(Patch):
@ -46,14 +49,36 @@ class BitCastStdArray(Patch):
arr_name: bytes = captures[1][0].text arr_name: bytes = captures[1][0].text
array_type: Node = captures[3][0] array_type: Node = captures[3][0]
cast_target: bytes = captures[4][0].text.strip(b"()") cast_target: bytes = captures[4][0].text.strip(b"()")
array_templ_args: bytes = array_type.named_children[0].named_children[1].named_children[1].text.strip(b"<>") array_templ_args: bytes = (
array_type.named_children[0]
.named_children[1]
.named_children[1]
.text.strip(b"<>")
)
arr_type = array_templ_args.split(b",")[0] arr_type = array_templ_args.split(b",")[0]
arr_len = array_templ_args.split(b",")[1] arr_len = array_templ_args.split(b",")[1]
return ( return (
b"union {\n" b"union {\n"
+ b" typeof(" + cast_target + b") In;\n" + b" typeof("
+ b" " + arr_type + b" Out[" + arr_len + b"];\n" + cast_target
+ b"} U_" + arr_name + b";\n" + b") In;\n"
+ b"U_" + arr_name + b".In = " + cast_target + b";\n" + b" "
+ arr_type + b" *" + arr_name + b" = U_" + arr_name + b".Out;" + arr_type
+ b" Out["
+ arr_len
+ b"];\n"
+ b"} U_"
+ arr_name
+ b";\n"
+ b"U_"
+ arr_name
+ b".In = "
+ cast_target
+ b";\n"
+ arr_type
+ b" *"
+ arr_name
+ b" = U_"
+ arr_name
+ b".Out;"
) )

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class CheckDecoderStatus(Patch): class CheckDecoderStatus(Patch):

View File

@ -1,5 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch
from autosync.cpptranslator.patches.Patch import Patch
class ClassConstructorDef(Patch): class ClassConstructorDef(Patch):

View File

@ -1,10 +1,13 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class ClassesDef(Patch): class ClassesDef(Patch):
@ -31,7 +34,9 @@ class ClassesDef(Patch):
for field_decl in field_decl_list.named_children: for field_decl in field_decl_list.named_children:
if ( if (
field_decl.type in "field_declaration" field_decl.type in "field_declaration"
and ("function_declarator" in [t.type for t in field_decl.named_children]) and (
"function_declarator" in [t.type for t in field_decl.named_children]
)
) or field_decl.type == "template_declaration": ) or field_decl.type == "template_declaration":
# Keep comments # Keep comments
sibling = field_decl.prev_named_sibling sibling = field_decl.prev_named_sibling

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class ConstMCInstParameter(Patch): class ConstMCInstParameter(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class ConstMCOperand(Patch): class ConstMCOperand(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class CppInitCast(Patch): class CppInitCast(Patch):
@ -14,7 +17,12 @@ class CppInitCast(Patch):
super().__init__(priority) super().__init__(priority)
def get_search_pattern(self) -> str: def get_search_pattern(self) -> str:
return "(call_expression" " (primitive_type) @cast_type" " (argument_list) @cast_target" ") @cast" return (
"(call_expression"
" (primitive_type) @cast_type"
" (argument_list) @cast_target"
") @cast"
)
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "cast" return "cast"

View File

@ -1,9 +1,12 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class CreateOperand0(Patch): class CreateOperand0(Patch):
@ -44,7 +47,11 @@ class CreateOperand0(Patch):
op_create_args: Node = captures[4][0] op_create_args: Node = captures[4][0]
# Capstone spells the function with capital letter 'C' for whatever reason. # Capstone spells the function with capital letter 'C' for whatever reason.
fcn = re.sub(b"create", b"Create", get_text(src, op_create_fcn.start_byte, op_create_fcn.end_byte)) fcn = re.sub(
b"create",
b"Create",
get_text(src, op_create_fcn.start_byte, op_create_fcn.end_byte),
)
inst = get_text(src, inst_var.start_byte, inst_var.end_byte) inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
args = get_text(src, op_create_args.start_byte, op_create_args.end_byte) args = get_text(src, op_create_args.start_byte, op_create_args.end_byte)
if args[0] == b"(" and args[-1] == b")": if args[0] == b"(" and args[-1] == b")":

View File

@ -1,9 +1,12 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_MCInst_var_name from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class CreateOperand1(Patch): class CreateOperand1(Patch):
@ -50,7 +53,11 @@ class CreateOperand1(Patch):
insert_arg_t = get_text(src, insert_arg.start_byte, insert_arg.end_byte) insert_arg_t = get_text(src, insert_arg.start_byte, insert_arg.end_byte)
# Capstone spells the function with capital letter 'C' for whatever reason. # Capstone spells the function with capital letter 'C' for whatever reason.
fcn = re.sub(b"create", b"Create", get_text(src, op_create_fcn.start_byte, op_create_fcn.end_byte)) fcn = re.sub(
b"create",
b"Create",
get_text(src, op_create_fcn.start_byte, op_create_fcn.end_byte),
)
inst = get_text(src, inst_var.start_byte, inst_var.end_byte) inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
args = get_text(src, op_create_args.start_byte, op_create_args.end_byte) args = get_text(src, op_create_args.start_byte, op_create_args.end_byte)
return ( return (
@ -62,7 +69,7 @@ class CreateOperand1(Patch):
+ b"MCOperand_" + b"MCOperand_"
+ fcn + fcn
+ b"1(" + b"1("
+ get_MCInst_var_name(src, inst_var) + inst
+ b", " + b", "
+ args + args
+ b"))" + b"))"

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_capture_node from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class DeclarationInConditionalClause(Patch): class DeclarationInConditionalClause(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class DecodeInstruction(Patch): class DecodeInstruction(Patch):
@ -31,8 +34,12 @@ class DecodeInstruction(Patch):
args_text = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()") args_text = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()")
table, mi_inst, opcode_var, address, this, sti = args_text.split(b",") table, mi_inst, opcode_var, address, this, sti = args_text.split(b",")
is_32bit = table[-2:].decode("utf8") == "32" or opcode_var[-2:].decode("utf8") == "32" is_32bit = (
is_16bit = table[-2:].decode("utf8") == "16" or opcode_var[-2:].decode("utf8") == "16" table[-2:].decode("utf8") == "32" or opcode_var[-2:].decode("utf8") == "32"
)
is_16bit = (
table[-2:].decode("utf8") == "16" or opcode_var[-2:].decode("utf8") == "16"
)
args = table + b", " + mi_inst + b", " + opcode_var + b", " + address args = table + b", " + mi_inst + b", " + opcode_var + b", " + address
if is_16bit and not is_32bit: if is_16bit and not is_32bit:

View File

@ -1,6 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class DecoderCast(Patch): class DecoderCast(Patch):

View File

@ -1,6 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class DecoderParameter(Patch): class DecoderParameter(Patch):

View File

@ -1,5 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch
from autosync.cpptranslator.patches.Patch import Patch
class FallThrough(Patch): class FallThrough(Patch):

View File

@ -1,12 +1,16 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_MCInst_var_name
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from autosync.cpptranslator.patches.Patch import Patch
class FeatureBits(Patch): class FeatureBits(Patch):
""" """
Patch featureBits[FLAG] Patch featureBits[FLAG]
to ARCH_getFeatureBits(Inst->csh->mode, ...) to ARCH_getFeatureBits(Inst->csh->mode, FLAG)
""" """
def __init__(self, priority: int, arch: bytes): def __init__(self, priority: int, arch: bytes):
@ -30,4 +34,11 @@ class FeatureBits(Patch):
qualified_id: Node = captures[2][0] qualified_id: Node = captures[2][0]
flag = get_text(src, qualified_id.start_byte, qualified_id.end_byte) flag = get_text(src, qualified_id.start_byte, qualified_id.end_byte)
mcinst_var_name = get_MCInst_var_name(src, qualified_id) mcinst_var_name = get_MCInst_var_name(src, qualified_id)
return self.arch + b"_getFeatureBits(" + mcinst_var_name + b"->csh->mode, " + flag + b")" return (
self.arch
+ b"_getFeatureBits("
+ mcinst_var_name
+ b"->csh->mode, "
+ flag
+ b")"
)

View File

@ -1,6 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class FeatureBitsDecl(Patch): class FeatureBitsDecl(Patch):

View File

@ -1,16 +1,19 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_function_params_of_node from autosync.cpptranslator.patches.Helper import get_function_params_of_node, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class FieldFromInstr(Patch): class FieldFromInstr(Patch):
""" """
Patch fieldFromInstr(...) Patch fieldFromInstruction(...)
to fieldFromInstr_<instr_width>(...) to fieldFromInstruction_<instr_width>(...)
""" """
def __init__(self, priority: int): def __init__(self, priority: int):
@ -32,7 +35,9 @@ class FieldFromInstr(Patch):
ffi_call: Node = captures[0][0] ffi_call: Node = captures[0][0]
ffi_first_arg: Node = captures[2][0] ffi_first_arg: Node = captures[2][0]
param_list_caller = get_function_params_of_node(ffi_call) param_list_caller = get_function_params_of_node(ffi_call)
ffi_first_arg_text = get_text(src, ffi_first_arg.start_byte, ffi_first_arg.end_byte).decode("utf8") ffi_first_arg_text = get_text(
src, ffi_first_arg.start_byte, ffi_first_arg.end_byte
).decode("utf8")
# Determine width of instruction by the variable name. # Determine width of instruction by the variable name.
if ffi_first_arg_text[-2:] == "32": if ffi_first_arg_text[-2:] == "32":

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class GetNumOperands(Patch): class GetNumOperands(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class GetOpcode(Patch): class GetOpcode(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class GetOperand(Patch): class GetOperand(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_capture_node from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class GetOperandRegImm(Patch): class GetOperandRegImm(Patch):

View File

@ -1,13 +1,20 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_capture_node, get_MCInst_var_name from autosync.cpptranslator.patches.Helper import (
from CppTranslator.Patches.Patch import Patch get_capture_node,
get_MCInst_var_name,
get_text,
)
from autosync.cpptranslator.patches.Patch import Patch
class GetRegClass(Patch): class GetRegClass(Patch):
""" """
Patch MRI.getRegClass(...) Patch MRI.getRegClass(...)
to MCRegisterClass_getRegClass(MI->MRI, ...) to MCRegisterInfo_getRegClass(Inst->MRI, ...)
""" """
def __init__(self, priority: int): def __init__(self, priority: int):
@ -31,6 +38,8 @@ class GetRegClass(Patch):
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes: def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
arg_list: Node = get_capture_node(captures, "arg_list") arg_list: Node = get_capture_node(captures, "arg_list")
args = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()") args = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()")
mcinst_var = get_MCInst_var_name(src, get_capture_node(captures, "get_reg_class")) mcinst_var = get_MCInst_var_name(
src, get_capture_node(captures, "get_reg_class")
)
res = b"MCRegisterInfo_getRegClass(" + mcinst_var + b"->MRI, " + args + b")" res = b"MCRegisterInfo_getRegClass(" + mcinst_var + b"->MRI, " + args + b")"
return res return res

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_capture_node from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class GetRegFromClass(Patch): class GetRegFromClass(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_MCInst_var_name from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class GetSubReg(Patch): class GetSubReg(Patch):
@ -31,6 +34,8 @@ class GetSubReg(Patch):
# Get arg list # Get arg list
op_create_args: Node = captures[2][0] op_create_args: Node = captures[2][0]
args = get_text(src, op_create_args.start_byte, op_create_args.end_byte).strip(b"()") args = get_text(src, op_create_args.start_byte, op_create_args.end_byte).strip(
b"()"
)
mcinst_var_name = get_MCInst_var_name(src, op_create_args) mcinst_var_name = get_MCInst_var_name(src, op_create_args)
return b"MCRegisterInfo_getSubReg(" + mcinst_var_name + b"->MRI, " + args + b")" return b"MCRegisterInfo_getSubReg(" + mcinst_var_name + b"->MRI, " + args + b")"

View File

@ -1,9 +1,12 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re import re
from tree_sitter import Node from tree_sitter import Node
import logging as log
from Helper import fail_exit from autosync.Helper import fail_exit
def get_function_params_of_node(n: Node) -> Node: def get_function_params_of_node(n: Node) -> Node:
@ -33,9 +36,12 @@ def get_MCInst_var_name(src: bytes, n: Node) -> bytes:
"""Searches for the name of the parameter of type MCInst and returns it.""" """Searches for the name of the parameter of type MCInst and returns it."""
params = get_function_params_of_node(n) params = get_function_params_of_node(n)
mcinst_var_name = b"" mcinst_var_name = b""
for p in params.named_children:
p_text = get_text(src, p.start_byte, p.end_byte) if params:
if b"MCInst" in p_text: for p in params.named_children:
p_text = get_text(src, p.start_byte, p.end_byte)
if b"MCInst" not in p_text:
continue
mcinst_var_name = p_text.split((b"&" if b"&" in p_text else b"*"))[1] mcinst_var_name = p_text.split((b"&" if b"&" in p_text else b"*"))[1]
break break
if mcinst_var_name == b"": if mcinst_var_name == b"":
@ -46,7 +52,9 @@ def get_MCInst_var_name(src: bytes, n: Node) -> bytes:
def template_param_list_to_dict(param_list: Node) -> [dict]: def template_param_list_to_dict(param_list: Node) -> [dict]:
if param_list.type != "template_parameter_list": if param_list.type != "template_parameter_list":
log.fatal(f"Wrong node type '{param_list.type}'. Not 'template_parameter_list'.") log.fatal(
f"Wrong node type '{param_list.type}'. Not 'template_parameter_list'."
)
exit(1) exit(1)
pl = list() pl = list()
for c in param_list.named_children: for c in param_list.named_children:
@ -64,7 +72,9 @@ def template_param_list_to_dict(param_list: Node) -> [dict]:
def parameter_declaration_to_dict(param_decl: Node) -> dict: def parameter_declaration_to_dict(param_decl: Node) -> dict:
if param_decl.type != "parameter_declaration": if param_decl.type != "parameter_declaration":
log.fatal(f"Wrong node type '{param_decl.type}'. Should be 'parameter_declaration'.") log.fatal(
f"Wrong node type '{param_decl.type}'. Should be 'parameter_declaration'."
)
exit(1) exit(1)
return { return {
"prim_type": param_decl.children[0].type == "primitive_type", "prim_type": param_decl.children[0].type == "primitive_type",
@ -95,18 +105,21 @@ def namespace_enum(src: bytes, ns_id: bytes, enum: Node) -> bytes:
type_id = c type_id = c
primary_tid_set = True primary_tid_set = True
if not (enumerator_list and type_id): if not enumerator_list and not type_id:
log.fatal("Could not find enumerator_list or enum type_identifier.") log.fatal("Could not find enumerator_list or enum type_identifier.")
exit(1) exit(1)
tid = get_text(src, type_id.start_byte, type_id.end_byte) tid = get_text(src, type_id.start_byte, type_id.end_byte) if type_id else None
elist = get_text(src, enumerator_list.start_byte, enumerator_list.end_byte) elist = get_text(src, enumerator_list.start_byte, enumerator_list.end_byte)
for e in enumerator_list.named_children: for e in enumerator_list.named_children:
if e.type == "enumerator": if e.type == "enumerator":
enum_entry_text = get_text(src, e.start_byte, e.end_byte) enum_entry_text = get_text(src, e.start_byte, e.end_byte)
elist = elist.replace(enum_entry_text, ns_id + b"_" + enum_entry_text) elist = elist.replace(enum_entry_text, ns_id + b"_" + enum_entry_text)
new_enum = b"typedef enum " + tid + b" " + elist + b"\n " + ns_id + b"_" + tid if tid:
new_enum = b"typedef enum " + tid + b" " + elist + b"\n " + ns_id + b"_" + tid
else:
new_enum = b"enum " + b" " + elist + b"\n"
return new_enum return new_enum
@ -152,7 +165,9 @@ def namespace_struct(src: bytes, ns_id: bytes, struct: Node) -> bytes:
tid = get_text(src, type_id.start_byte, type_id.end_byte) tid = get_text(src, type_id.start_byte, type_id.end_byte)
fields = get_text(src, field_list.start_byte, field_list.end_byte) fields = get_text(src, field_list.start_byte, field_list.end_byte)
typed_struct = b"typedef struct " + tid + b" " + fields + b"\n " + ns_id + b"_" + tid typed_struct = (
b"typedef struct " + tid + b" " + fields + b"\n " + ns_id + b"_" + tid
)
return typed_struct return typed_struct
@ -193,9 +208,16 @@ def parse_function_capture(
case _: case _:
raise NotImplementedError(f"Node type {node.type} not handled.") raise NotImplementedError(f"Node type {node.type} not handled.")
from CppTranslator.TemplateCollector import TemplateCollector from autosync.cpptranslator.TemplateCollector import TemplateCollector
return TemplateCollector.templ_params_to_list(temp_args), st_class_ids, ret_type, func_name, func_params, comp_stmt return (
TemplateCollector.templ_params_to_list(temp_args),
st_class_ids,
ret_type,
func_name,
func_params,
comp_stmt,
)
def get_capture_node(captures: [(Node, str)], name: str) -> Node: def get_capture_node(captures: [(Node, str)], name: str) -> Node:

View File

@ -1,9 +1,12 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class Includes(Patch): class Includes(Patch):
@ -34,11 +37,17 @@ class Includes(Patch):
include_text = get_text(src, captures[0][0].start_byte, captures[0][0].end_byte) include_text = get_text(src, captures[0][0].start_byte, captures[0][0].end_byte)
# Special cases, which appear somewhere in the code. # Special cases, which appear somewhere in the code.
if b"GenDisassemblerTables.inc" in include_text: if b"GenDisassemblerTables.inc" in include_text:
return b'#include "' + bytes(self.arch, "utf8") + b'GenDisassemblerTables.inc"\n\n' return (
b'#include "'
+ bytes(self.arch, "utf8")
+ b'GenDisassemblerTables.inc"\n\n'
)
elif b"GenAsmWriter.inc" in include_text: elif b"GenAsmWriter.inc" in include_text:
return b'#include "' + bytes(self.arch, "utf8") + b'GenAsmWriter.inc"\n\n' return b'#include "' + bytes(self.arch, "utf8") + b'GenAsmWriter.inc"\n\n'
elif b"GenSystemOperands.inc" in include_text: elif b"GenSystemOperands.inc" in include_text:
return b'#include "' + bytes(self.arch, "utf8") + b'GenSystemOperands.inc"\n\n' return (
b'#include "' + bytes(self.arch, "utf8") + b'GenSystemOperands.inc"\n\n'
)
if self.include_count[filename] > 1: if self.include_count[filename] > 1:
# Only the first include is replaced with all CS includes. # Only the first include is replaced with all CS includes.
@ -53,6 +62,8 @@ class Includes(Patch):
return res + get_PPC_includes(filename) + get_general_macros() return res + get_PPC_includes(filename) + get_general_macros()
case "AArch64": case "AArch64":
return res + get_AArch64_includes(filename) + get_general_macros() return res + get_AArch64_includes(filename) + get_general_macros()
case "TEST_ARCH":
return res + b"test_output"
case _: case _:
log.fatal(f"Includes of {self.arch} not handled.") log.fatal(f"Includes of {self.arch} not handled.")
exit(1) exit(1)
@ -245,4 +256,6 @@ def get_AArch64_includes(filename: str) -> bytes:
def get_general_macros(): def get_general_macros():
return b"#define CONCAT(a, b) CONCAT_(a, b)\n" b"#define CONCAT_(a, b) a ## _ ## b\n" return (
b"#define CONCAT(a, b) CONCAT_(a, b)\n" b"#define CONCAT_(a, b) a ## _ ## b\n"
)

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class InlineToStaticInline(Patch): class InlineToStaticInline(Patch):
@ -20,7 +23,10 @@ class InlineToStaticInline(Patch):
def get_search_pattern(self) -> str: def get_search_pattern(self) -> str:
return ( return (
"(function_definition" ' ((storage_class_specifier) @scs (#eq? @scs "inline"))' " (_)+" ") @inline_def" "(function_definition"
' ((storage_class_specifier) @scs (#eq? @scs "inline"))'
" (_)+"
") @inline_def"
) )
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class IsOptionalDef(Patch): class IsOptionalDef(Patch):
@ -34,4 +37,4 @@ class IsOptionalDef(Patch):
index = captures[2][0] index = captures[2][0]
op_info_var = get_text(src, op_info_var.start_byte, op_info_var.end_byte) op_info_var = get_text(src, op_info_var.start_byte, op_info_var.end_byte)
index = get_text(src, index.start_byte, index.end_byte) index = get_text(src, index.start_byte, index.end_byte)
return b"MCOperandInfo_isOptionalDef(&" + op_info_var + b"[" + index + b"])" return b"MCOperandInfo_isOptionalDef(&" + op_info_var + index + b")"

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class IsPredicate(Patch): class IsPredicate(Patch):
@ -34,4 +37,4 @@ class IsPredicate(Patch):
index = captures[2][0] index = captures[2][0]
op_info_var = get_text(src, op_info_var.start_byte, op_info_var.end_byte) op_info_var = get_text(src, op_info_var.start_byte, op_info_var.end_byte)
index = get_text(src, index.start_byte, index.end_byte) index = get_text(src, index.start_byte, index.end_byte)
return b"MCOperandInfo_isPredicate(&" + op_info_var + b"[" + index + b"])" return b"MCOperandInfo_isPredicate(&" + op_info_var + index + b")"

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class IsOperandRegImm(Patch): class IsOperandRegImm(Patch):

View File

@ -1,6 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class LLVMFallThrough(Patch): class LLVMFallThrough(Patch):
@ -12,7 +15,11 @@ class LLVMFallThrough(Patch):
super().__init__(priority) super().__init__(priority)
def get_search_pattern(self) -> str: def get_search_pattern(self) -> str:
return "(expression_statement" ' ((identifier) @id (#eq? @id "LLVM_FALLTHROUGH"))' ") @llvm_fall_through" return (
"(expression_statement"
' ((identifier) @id (#eq? @id "LLVM_FALLTHROUGH"))'
") @llvm_fall_through"
)
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "llvm_fall_through" return "llvm_fall_through"

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class LLVMUnreachable(Patch): class LLVMUnreachable(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class MethodToFunction(Patch): class MethodToFunction(Patch):
@ -35,6 +38,8 @@ class MethodToFunction(Patch):
name = captures[1][0] name = captures[1][0]
parameter_list = captures[2][0] parameter_list = captures[2][0]
name = get_text(src, name.start_byte, name.end_byte) name = get_text(src, name.start_byte, name.end_byte)
parameter_list = get_text(src, parameter_list.start_byte, parameter_list.end_byte) parameter_list = get_text(
src, parameter_list.start_byte, parameter_list.end_byte
)
res = name + parameter_list res = name + parameter_list
return res return res

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class MethodTypeQualifier(Patch): class MethodTypeQualifier(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class NamespaceAnon(Patch): class NamespaceAnon(Patch):
@ -16,7 +19,11 @@ class NamespaceAnon(Patch):
super().__init__(priority) super().__init__(priority)
def get_search_pattern(self) -> str: def get_search_pattern(self) -> str:
return "(namespace_definition" " (declaration_list) @decl_list" ") @namespace_def" return (
"(namespace_definition"
" (declaration_list) @decl_list"
") @namespace_def"
)
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "namespace_def" return "namespace_def"

View File

@ -1,7 +1,15 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, namespace_enum, namespace_fcn_def, namespace_struct from autosync.cpptranslator.patches.Helper import (
from CppTranslator.Patches.Patch import Patch get_text,
namespace_enum,
namespace_fcn_def,
namespace_struct,
)
from autosync.cpptranslator.patches.Patch import Patch
class NamespaceArch(Patch): class NamespaceArch(Patch):
@ -16,7 +24,12 @@ class NamespaceArch(Patch):
super().__init__(priority) super().__init__(priority)
def get_search_pattern(self) -> str: def get_search_pattern(self) -> str:
return "(namespace_definition" " (namespace_identifier)" " (declaration_list) @decl_list" ") @namespace_def" return (
"(namespace_definition"
" (namespace_identifier)"
" (declaration_list) @decl_list"
") @namespace_def"
)
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "namespace_def" return "namespace_def"
@ -24,7 +37,11 @@ class NamespaceArch(Patch):
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes: def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
namespace = captures[0][0] namespace = captures[0][0]
decl_list = captures[1][0] decl_list = captures[1][0]
namespace_id = get_text(src, namespace.named_children[0].start_byte, namespace.named_children[0].end_byte) namespace_id = get_text(
src,
namespace.named_children[0].start_byte,
namespace.named_children[0].end_byte,
)
# We need to prepend the namespace id to all enum members, function declarators and struct types. # We need to prepend the namespace id to all enum members, function declarators and struct types.
# Because in the generated files they are accessed via NAMESPACE::X which becomes NAMESPACE_X. # Because in the generated files they are accessed via NAMESPACE::X which becomes NAMESPACE_X.

View File

@ -1,12 +1,15 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class NamespaceLLVM(Patch): class NamespaceLLVM(Patch):
""" """
Patch namespace {CONTENT} Patch namespace llvm {CONTENT}
to CONTENT to CONTENT
Only for anonymous or llvm namespaces Only for anonymous or llvm namespaces

View File

@ -1,13 +1,18 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class OutStreamParam(Patch): class OutStreamParam(Patch):
""" """
Patch raw_ostream &OS Patches the parameter list only:
to SStream *OS
Patch void function(int a, raw_ostream &OS)
to void function(int a, SStream *OS)
""" """
def __init__(self, priority: int): def __init__(self, priority: int):

View File

@ -1,6 +1,10 @@
from tree_sitter import Node # Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
from tree_sitter import Node
class Patch: class Patch:
priority: int = None priority: int = None

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_MCInst_var_name from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class PredicateBlockFunctions(Patch): class PredicateBlockFunctions(Patch):

View File

@ -1,5 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch
from autosync.cpptranslator.patches.Patch import Patch
class PrintAnnotation(Patch): class PrintAnnotation(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_MCInst_var_name from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class PrintRegImmShift(Patch): class PrintRegImmShift(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class QualifiedIdentifier(Patch): class QualifiedIdentifier(Patch):

View File

@ -1,9 +1,12 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class ReferencesDecl(Patch): class ReferencesDecl(Patch):
@ -18,7 +21,12 @@ class ReferencesDecl(Patch):
super().__init__(priority) super().__init__(priority)
def get_search_pattern(self) -> str: def get_search_pattern(self) -> str:
return "[" "(reference_declarator)" "(type_identifier) (abstract_reference_declarator)" "] @reference_decl" return (
"["
"(reference_declarator)"
"(type_identifier) (abstract_reference_declarator)"
"] @reference_decl"
)
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "reference_decl" return "reference_decl"

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_capture_node from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class RegClassContains(Patch): class RegClassContains(Patch):
@ -32,6 +35,8 @@ class RegClassContains(Patch):
reg_class_getter: Node = get_capture_node(captures, "reg_class") reg_class_getter: Node = get_capture_node(captures, "reg_class")
arg_list: Node = get_capture_node(captures, "arg_list") arg_list: Node = get_capture_node(captures, "arg_list")
args = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()") args = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()")
reg_class = get_text(src, reg_class_getter.start_byte, reg_class_getter.end_byte) reg_class = get_text(
src, reg_class_getter.start_byte, reg_class_getter.end_byte
)
res = b"MCRegisterClass_contains(" + reg_class + b", " + args + b")" res = b"MCRegisterClass_contains(" + reg_class + b", " + args + b")"
return res return res

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class STIArgument(Patch): class STIArgument(Patch):

View File

@ -1,13 +1,16 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class STIFeatureBits(Patch): class STIFeatureBits(Patch):
""" """
Patch STI.getFeatureBits()[FLAG] Patch STI.getFeatureBits()[ARCH::FLAG]
to ARCH_getFeatureBits(Inst->csh->mode, ...) to ARCH_getFeatureBits(Inst->csh->mode, ARCH::FLAG)
""" """
def __init__(self, priority: int, arch: bytes): def __init__(self, priority: int, arch: bytes):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class SubtargetInfoParam(Patch): class SubtargetInfoParam(Patch):

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class SetOpcode(Patch): class SetOpcode(Patch):

View File

@ -1,7 +1,11 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.TemplateCollector import TemplateCollector from autosync.cpptranslator.patches.Patch import Patch
from autosync.cpptranslator.TemplateCollector import TemplateCollector
class SignExtend(Patch): class SignExtend(Patch):

View File

@ -1,9 +1,12 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text, get_function_params_of_node from autosync.cpptranslator.patches.Helper import get_function_params_of_node, get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class SizeAssignment(Patch): class SizeAssignment(Patch):
@ -18,7 +21,11 @@ class SizeAssignment(Patch):
super().__init__(priority) super().__init__(priority)
def get_search_pattern(self) -> str: def get_search_pattern(self) -> str:
return "(assignment_expression" ' ((identifier) @id (#eq? @id "Size"))' ") @assign" return (
"(assignment_expression"
' ((identifier) @id (#eq? @id "Size"))'
") @assign"
)
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "assign" return "assign"

View File

@ -1,7 +1,10 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class StreamOperations(Patch): class StreamOperations(Patch):
@ -58,11 +61,22 @@ class StreamOperations(Patch):
+ b', "' + b', "'
+ b"%s" * len(string_ops) + b"%s" * len(string_ops)
+ b'", ' + b'", '
+ b", ".join([get_text(src, o.start_byte, o.end_byte) for o in string_ops]) + b", ".join(
[
get_text(src, o.start_byte, o.end_byte)
for o in string_ops
]
)
+ b");\n" + b");\n"
) )
string_ops.clear() string_ops.clear()
res += b"SStream_concat1(" + s_name + b", " + get_text(src, op.start_byte, op.end_byte) + b");\n" res += (
b"SStream_concat1("
+ s_name
+ b", "
+ get_text(src, op.start_byte, op.end_byte)
+ b");\n"
)
else: else:
string_ops.append(op) string_ops.append(op)
i += 1 i += 1
@ -75,14 +89,22 @@ class StreamOperations(Patch):
+ b', "' + b', "'
+ b"%s" * len(string_ops) + b"%s" * len(string_ops)
+ b'", ' + b'", '
+ b", ".join([get_text(src, o.start_byte, o.end_byte) for o in string_ops]) + b", ".join(
[get_text(src, o.start_byte, o.end_byte) for o in string_ops]
)
+ b");\n" + b");\n"
) )
string_ops.clear() string_ops.clear()
last_op_text = get_text(src, last_op.start_byte, last_op.end_byte) last_op_text = get_text(src, last_op.start_byte, last_op.end_byte)
if last_op.type == "char_literal": if last_op.type == "char_literal":
res += b"SStream_concat0(" + s_name + b", " + last_op_text.replace(b"'", b'"') + b");\n" res += (
b"SStream_concat0("
+ s_name
+ b", "
+ last_op_text.replace(b"'", b'"')
+ b");\n"
)
else: else:
res += b"SStream_concat0(" + s_name + b", " + last_op_text + b");" res += b"SStream_concat0(" + s_name + b", " + last_op_text + b");"
stream = captures[0][0] stream = captures[0][0]

View File

@ -1,9 +1,16 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import parse_function_capture from autosync.cpptranslator.patches.Helper import parse_function_capture
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
from CppTranslator.TemplateCollector import TemplateCollector, TemplateRefInstance from autosync.cpptranslator.TemplateCollector import (
TemplateCollector,
TemplateRefInstance,
)
class TemplateDeclaration(Patch): class TemplateDeclaration(Patch):
@ -37,13 +44,24 @@ class TemplateDeclaration(Patch):
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "template_decl" return "template_decl"
def get_patch(self, captures: list[tuple[Node, str]], src: bytes, **kwargs) -> bytes: def get_patch(
self, captures: list[tuple[Node, str]], src: bytes, **kwargs
) -> bytes:
t_params, sc, tid, f_name, f_params, _ = parse_function_capture(captures, src) t_params, sc, tid, f_name, f_params, _ = parse_function_capture(captures, src)
if f_name in self.collector.templates_with_arg_deduction: if f_name in self.collector.templates_with_arg_deduction:
return sc + tid + b" " + f_name + f_params + b";" return sc + tid + b" " + f_name + f_params + b";"
declaration = b"#define DECLARE_" + f_name + b"(" + b", ".join(t_params) + b")\n" declaration = (
declaration += sc + b" " + tid + b" " + TemplateCollector.get_macro_c_call(f_name, t_params, f_params) + b";" b"#define DECLARE_" + f_name + b"(" + b", ".join(t_params) + b")\n"
)
declaration += (
sc
+ b" "
+ tid
+ b" "
+ TemplateCollector.get_macro_c_call(f_name, t_params, f_params)
+ b";"
)
declaration = declaration.replace(b"\n", b" \\\n") + b"\n" declaration = declaration.replace(b"\n", b" \\\n") + b"\n"
template_instance: TemplateRefInstance template_instance: TemplateRefInstance
@ -52,7 +70,13 @@ class TemplateDeclaration(Patch):
self.collector.log_missing_ref_and_exit(f_name) self.collector.log_missing_ref_and_exit(f_name)
for template_instance in self.collector.template_refs[f_name]: for template_instance in self.collector.template_refs[f_name]:
d = b"DECLARE_" + f_name + b"(" + b", ".join(template_instance.get_args_for_decl()) + b");\n" d = (
b"DECLARE_"
+ f_name
+ b"("
+ b", ".join(template_instance.get_args_for_decl())
+ b");\n"
)
if d in declared_implementations: if d in declared_implementations:
continue continue
declared_implementations.append(d) declared_implementations.append(d)

View File

@ -1,11 +1,17 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
import re import re
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import parse_function_capture from autosync.cpptranslator.patches.Helper import parse_function_capture
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
from CppTranslator.TemplateCollector import TemplateCollector, TemplateRefInstance from autosync.cpptranslator.TemplateCollector import (
TemplateCollector,
TemplateRefInstance,
)
class TemplateDefinition(Patch): class TemplateDefinition(Patch):
@ -40,14 +46,23 @@ class TemplateDefinition(Patch):
def get_main_capture_name(self) -> str: def get_main_capture_name(self) -> str:
return "template_def" return "template_def"
def get_patch(self, captures: list[tuple[Node, str]], src: bytes, **kwargs) -> bytes: def get_patch(
t_params, sc, tid, f_name, f_params, f_compound = parse_function_capture(captures, src) self, captures: list[tuple[Node, str]], src: bytes, **kwargs
) -> bytes:
t_params, sc, tid, f_name, f_params, f_compound = parse_function_capture(
captures, src
)
if f_name in self.collector.templates_with_arg_deduction: if f_name in self.collector.templates_with_arg_deduction:
return sc + tid + b" " + f_name + f_params + f_compound return sc + tid + b" " + f_name + f_params + f_compound
definition = b"#define DEFINE_" + f_name + b"(" + b", ".join(t_params) + b")\n" definition = b"#define DEFINE_" + f_name + b"(" + b", ".join(t_params) + b")\n"
definition += ( definition += (
sc + b" " + tid + b" " + TemplateCollector.get_macro_c_call(f_name, t_params, f_params) + f_compound sc
+ b" "
+ tid
+ b" "
+ TemplateCollector.get_macro_c_call(f_name, t_params, f_params)
+ f_compound
) )
# Remove // comments # Remove // comments
definition = re.sub(b" *//.*", b"", definition) definition = re.sub(b" *//.*", b"", definition)
@ -59,7 +74,13 @@ class TemplateDefinition(Patch):
self.collector.log_missing_ref_and_exit(f_name) self.collector.log_missing_ref_and_exit(f_name)
for template_instance in self.collector.template_refs[f_name]: for template_instance in self.collector.template_refs[f_name]:
d = b"DEFINE_" + f_name + b"(" + b", ".join(template_instance.get_args_for_decl()) + b");\n" d = (
b"DEFINE_"
+ f_name
+ b"("
+ b", ".join(template_instance.get_args_for_decl())
+ b");\n"
)
if d in declared_implementations: if d in declared_implementations:
continue continue
declared_implementations.append(d) declared_implementations.append(d)

View File

@ -1,9 +1,12 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log import logging as log
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class TemplateParamDecl(Patch): class TemplateParamDecl(Patch):

View File

@ -1,8 +1,11 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.HelperMethods import get_text from autosync.cpptranslator.patches.Helper import get_text
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
from CppTranslator.TemplateCollector import TemplateCollector from autosync.cpptranslator.TemplateCollector import TemplateCollector
class TemplateRefs(Patch): class TemplateRefs(Patch):

View File

@ -1,5 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch
from autosync.cpptranslator.patches.Patch import Patch
class UseMarkup(Patch): class UseMarkup(Patch):

View File

@ -1,6 +1,9 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node from tree_sitter import Node
from CppTranslator.Patches.Patch import Patch from autosync.cpptranslator.patches.Patch import Patch
class UsingDeclaration(Patch): class UsingDeclaration(Patch):

Some files were not shown because too many files have changed in this diff Show More