Squashed 'external/capstone/' content from commit e46f64fa

git-subtree-dir: external/capstone
git-subtree-split: e46f64fadb351e9ecd05264fab26f2772feb0994
This commit is contained in:
2026-05-11 11:55:07 +02:00
commit 802798ce3c
3968 changed files with 2967598 additions and 0 deletions
@@ -0,0 +1,82 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import json
import logging as log
from pathlib import Path
import tree_sitter_cpp as ts_cpp
from tree_sitter import Language, Parser
from autosync.Helper import fail_exit
class Configurator:
"""
Holds common setup procedures for the configuration.
It reads the configuration file, compiles languages and initializes the Parser.
"""
arch: str
config_path: Path
config: dict = None
ts_cpp_lang: Language = None
parser: Parser = None
def __init__(self, arch: str, config_path: Path) -> None:
self.arch = arch
self.config_path = config_path
self.load_config()
self.ts_set_cpp_language()
self.init_parser()
def get_arch(self) -> str:
return self.arch
def get_cpp_lang(self) -> Language:
if self.ts_cpp_lang:
return self.ts_cpp_lang
self.ts_set_cpp_language()
return self.ts_cpp_lang
def get_parser(self) -> Parser:
if self.parser:
return self.parser
self.init_parser()
return self.parser
def get_arch_config(self) -> dict:
if self.config:
return self.config[self.arch]
self.load_config()
return self.config[self.arch]
def get_general_config(self) -> dict:
if self.config:
return self.config["General"]
self.load_config()
return self.config["General"]
def get_patch_config(self) -> dict:
if self.config:
return self.config["General"]["patching"]
self.load_config()
return self.config["General"]["patching"]
def load_config(self) -> None:
if not Path.exists(self.config_path):
fail_exit(f"Could not load arch config file at '{self.config_path}'")
with open(self.config_path) as f:
conf = json.loads(f.read())
if self.arch not in conf:
fail_exit(
f"{self.arch} has no configuration. Please add them in {self.config_path}!"
)
self.config = conf
def ts_set_cpp_language(self) -> None:
self.ts_cpp_lang = Language(ts_cpp.language())
def init_parser(self) -> None:
log.debug("Init parser")
self.parser = Parser(self.ts_cpp_lang)
+551
View File
@@ -0,0 +1,551 @@
#!/usr/bin/env python3
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import argparse
import logging as log
import sys
import re
from pathlib import Path
import termcolor
from tree_sitter import Language, Node, Parser, Query, Tree
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.ClassConstructorDef import ClassConstructorDef
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.Data import Data
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.BadConditionCode import BadConditionCode
from autosync.cpptranslator.patches.LLVM_DEBUG import LLVM_DEBUG
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.Override import Override
from autosync.cpptranslator.patches.Patch import Patch
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.Size import Size
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 (
convert_loglevel,
get_header,
get_path,
print_prominent_warning,
run_clang_format,
)
from autosync.cpptranslator.patches.isUInt import IsUInt
from autosync.cpptranslator.tree_sitter_compatibility import query_captures_22_3
class Translator:
ts_cpp_lang: Language = None
parser: Parser = None
template_collector: TemplateCollector = None
src_paths: [Path]
out_paths: [Path]
conf: dict
src = b""
current_src_path_in: Path = None
current_src_path_out: Path = None
tree: Tree = None
# Patch priorities: The bigger the number the later the patch will be applied.
# Patches which create templates must always be executed last. Since syntax
# in macros is no longer parsed as such (but is only recognized as macro body).
#
# If a patch must be executed before another patch (because the matching rules depend on it)
# mark this dependency as you see below.
patches: [Patch] = list()
patch_priorities: {str: int} = {
RegClassContains.__name__: 0,
GetRegClass.__name__: 0,
GetRegFromClass.__name__: 0,
CppInitCast.__name__: 0,
BitCastStdArray.__name__: 0,
PrintRegImmShift.__name__: 0,
InlineToStaticInline.__name__: 0,
GetSubReg.__name__: 0,
UseMarkup.__name__: 0,
ConstMCOperand.__name__: 0,
ClassConstructorDef.__name__: 0,
ConstMCInstParameter.__name__: 0,
PrintAnnotation.__name__: 0,
GetNumOperands.__name__: 0,
STIArgument.__name__: 0,
DecodeInstruction.__name__: 0,
FallThrough.__name__: 0,
SizeAssignment.__name__: 0,
FieldFromInstr.__name__: 0,
FeatureBitsDecl.__name__: 0,
FeatureBits.__name__: 0,
STIFeatureBits.__name__: 0,
Includes.__name__: 0,
CreateOperand0.__name__: 0, # ◁───┐ `CreateOperand0` removes most calls to MI.addOperand().
AddOperand.__name__: 1, # ────────┘ The ones left are fixed with the `AddOperand` patch.
CreateOperand1.__name__: 0,
IsUInt.__name__: 0,
GetOpcode.__name__: 0,
SetOpcode.__name__: 0,
GetOperand.__name__: 0,
GetOperandRegImm.__name__: 0,
IsOperandRegImm.__name__: 0,
SignExtend.__name__: 0,
DecoderParameter.__name__: 0,
UsingDeclaration.__name__: 0,
DecoderCast.__name__: 0,
IsPredicate.__name__: 0,
IsOptionalDef.__name__: 0,
Assert.__name__: 0, # ◁─────────┐ The llvm_unreachable calls are replaced with asserts.
LLVMUnreachable.__name__: 1, # ─┘ Those assert should stay.
LLVMFallThrough.__name__: 0,
BadConditionCode.__name__: 0,
LLVM_DEBUG.__name__: 0,
DeclarationInConditionalClause.__name__: 0,
StreamOperations.__name__: 0,
OutStreamParam.__name__: 0, # ◁──────┐ add_cs_detail() is added to printOperand functions with a certain
SubtargetInfoParam.__name__: 0, # ◁──┤ signature. This signature depends on those patches.
MethodToFunction.__name__: 0, # ◁────┤
AddCSDetail.__name__: 1, # ──────────┘
NamespaceAnon.__name__: 0, # ◁─────┐ "llvm" and anonymous namespaces must be removed first,
NamespaceLLVM.__name__: 0, # ◁─────┤ so they don't match in NamespaceArch.
NamespaceArch.__name__: 1, # ──────┘
PredicateBlockFunctions.__name__: 0,
Override.__name__: 0,
Size.__name__: 0,
Data.__name__: 0,
ClassesDef.__name__: 0, # ◁────────┐ Declarations must be extracted first from the classes.
MethodTypeQualifier.__name__: 1, # ┘
# All previous patches can contain qualified identifiers (Ids with the "::" operator) in their search patterns.
# After this patch they are removed.
QualifiedIdentifier.__name__: 2,
ReferencesDecl.__name__: 3, # ◁────┐
CheckDecoderStatus.__name__: 4, # ─┘ Reference declarations must be removed first.
TemplateParamDecl.__name__: 5,
TemplateRefs.__name__: 5,
# Template declarations are replaced with macros.
# Those declarations are parsed as macro afterwards
TemplateDeclaration.__name__: 5,
# Template definitions are replaced with macros.
# Those template functions are parsed as macro afterwards.
TemplateDefinition.__name__: 6,
}
def __init__(self, configure: Configurator, wait_for_user: bool = False):
self.configurator = configure
self.wait_for_user = wait_for_user
self.arch = self.configurator.get_arch()
self.conf = self.configurator.get_arch_config()
self.conf_general = self.configurator.get_general_config()
self.ts_cpp_lang = self.configurator.get_cpp_lang()
self.parser = self.configurator.get_parser()
self.src_paths: [Path] = [
get_path(sp["in"]) 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.init_patches()
def read_src_file(self, src_path: Path) -> None:
"""Reads the file at src_path into self.src"""
log.debug(f"Read {src_path}")
if not Path.exists(src_path):
log.fatal(f"Could not open the source file '{src_path}'")
exit(1)
with open(src_path) as f:
self.src = bytes(f.read(), "utf8")
def init_patches(self):
log.debug("Init patches")
priorities = dict(
sorted(self.patch_priorities.items(), key=lambda item: item[1])
)
for ptype, p in priorities.items():
match ptype:
case RegClassContains.__name__:
patch = RegClassContains(p)
case GetRegClass.__name__:
patch = GetRegClass(p)
case GetRegFromClass.__name__:
patch = GetRegFromClass(p)
case CppInitCast.__name__:
patch = CppInitCast(p)
case BitCastStdArray.__name__:
patch = BitCastStdArray(p)
case CheckDecoderStatus.__name__:
patch = CheckDecoderStatus(p)
case ReferencesDecl.__name__:
patch = ReferencesDecl(p)
case FieldFromInstr.__name__:
patch = FieldFromInstr(p)
case FeatureBitsDecl.__name__:
patch = FeatureBitsDecl(p)
case FeatureBits.__name__:
patch = FeatureBits(p, bytes(self.arch, "utf8"))
case STIFeatureBits.__name__:
patch = STIFeatureBits(p, bytes(self.arch, "utf8"))
case QualifiedIdentifier.__name__:
patch = QualifiedIdentifier(p)
case Includes.__name__:
patch = Includes(p, self.arch)
case ClassesDef.__name__:
patch = ClassesDef(p)
case CreateOperand0.__name__:
patch = CreateOperand0(p)
case CreateOperand1.__name__:
patch = CreateOperand1(p)
case IsUInt.__name__:
patch = IsUInt(p)
case GetOpcode.__name__:
patch = GetOpcode(p)
case SetOpcode.__name__:
patch = SetOpcode(p)
case GetOperand.__name__:
patch = GetOperand(p)
case SignExtend.__name__:
patch = SignExtend(p)
case TemplateDeclaration.__name__:
patch = TemplateDeclaration(p, self.template_collector)
case TemplateDefinition.__name__:
patch = TemplateDefinition(p, self.template_collector)
case DecoderParameter.__name__:
patch = DecoderParameter(p)
case TemplateRefs.__name__:
patch = TemplateRefs(p)
case TemplateParamDecl.__name__:
patch = TemplateParamDecl(p)
case MethodTypeQualifier.__name__:
patch = MethodTypeQualifier(p)
case UsingDeclaration.__name__:
patch = UsingDeclaration(p)
case NamespaceLLVM.__name__:
patch = NamespaceLLVM(p)
case DecoderCast.__name__:
patch = DecoderCast(p)
case IsPredicate.__name__:
patch = IsPredicate(p)
case IsOptionalDef.__name__:
patch = IsOptionalDef(p)
case Assert.__name__:
patch = Assert(p)
case LLVMFallThrough.__name__:
patch = LLVMFallThrough(p)
case BadConditionCode.__name__:
patch = BadConditionCode(p)
case DeclarationInConditionalClause.__name__:
patch = DeclarationInConditionalClause(p)
case OutStreamParam.__name__:
patch = OutStreamParam(p)
case MethodToFunction.__name__:
patch = MethodToFunction(p)
case GetOperandRegImm.__name__:
patch = GetOperandRegImm(p)
case StreamOperations.__name__:
patch = StreamOperations(p)
case SubtargetInfoParam.__name__:
patch = SubtargetInfoParam(p)
case SizeAssignment.__name__:
patch = SizeAssignment(p)
case NamespaceArch.__name__:
patch = NamespaceArch(p)
case NamespaceAnon.__name__:
patch = NamespaceAnon(p)
case PredicateBlockFunctions.__name__:
patch = PredicateBlockFunctions(p)
case FallThrough.__name__:
patch = FallThrough(p)
case DecodeInstruction.__name__:
patch = DecodeInstruction(p)
case STIArgument.__name__:
patch = STIArgument(p)
case GetNumOperands.__name__:
patch = GetNumOperands(p)
case AddOperand.__name__:
patch = AddOperand(p)
case PrintAnnotation.__name__:
patch = PrintAnnotation(p)
case ConstMCInstParameter.__name__:
patch = ConstMCInstParameter(p)
case LLVMUnreachable.__name__:
patch = LLVMUnreachable(p)
case LLVM_DEBUG.__name__:
patch = LLVM_DEBUG(p)
case ClassConstructorDef.__name__:
patch = ClassConstructorDef(p)
case ConstMCOperand.__name__:
patch = ConstMCOperand(p)
case UseMarkup.__name__:
patch = UseMarkup(p)
case GetSubReg.__name__:
patch = GetSubReg(p)
case InlineToStaticInline.__name__:
patch = InlineToStaticInline(p)
case AddCSDetail.__name__:
patch = AddCSDetail(p, self.arch)
case PrintRegImmShift.__name__:
patch = PrintRegImmShift(p)
case IsOperandRegImm.__name__:
patch = IsOperandRegImm(p)
case Override.__name__:
patch = Override(p)
case Size.__name__:
patch = Size(p)
case Data.__name__:
patch = Data(p)
case _:
log.fatal(f"Patch type {ptype} not in Patch init routine.")
exit(1)
self.patches.append(patch)
def parse(self, src_path: Path) -> None:
self.read_src_file(src_path)
log.debug("Parse source code")
self.tree = self.parser.parse(self.src)
def patch_src(self, p_list: [(bytes, Node)]) -> None:
if len(p_list) == 0:
return
# Sort list of patches descending so the patches which are last in the file
# get patched first. This way the indices of the code snippets before
# don't change.
patches = sorted(p_list, key=lambda x: x[1].start_byte, reverse=True)
new_src = b""
patch: bytes
node: Node
for patch, node in patches:
start_byte: int = node.start_byte
old_end_byte: int = node.end_byte
start_point: (int, int) = node.start_point
old_end_point: (int, int) = node.end_point
new_src = self.src[:start_byte] + patch + self.src[old_end_byte:]
self.src = new_src
d = len(patch) - (old_end_byte - start_byte)
self.tree.edit(
start_byte=start_byte,
old_end_byte=old_end_byte,
new_end_byte=old_end_byte + d,
start_point=start_point,
old_end_point=old_end_point,
new_end_point=(old_end_point[0], old_end_point[1] + d),
)
self.tree = self.parser.parse(new_src, self.tree)
def apply_patch(self, patch: Patch) -> bool:
"""Tests if the given patch should be applied for the current architecture or file."""
apply_only_to = self.configurator.get_patch_config()["apply_patch_only_to"]
patch_name = patch.__class__.__name__
if patch_name not in apply_only_to:
# No constraints
return True
file_constraints = apply_only_to[patch_name]
if self.current_src_path_in.name in file_constraints["files"]:
return True
elif (
re.search("InstPrinter.cpp", self.current_src_path_in.name)
and patch_name == AddCSDetail.__name__
):
print_prominent_warning(
(
f"The AddCSDetail patch is not applied to {self.current_src_path_in.name}. "
"Have you forgotten to add it to arch_config.json?"
),
False,
)
return False
def translate(self) -> None:
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}'")
self.parse(self.current_src_path_in)
patch: Patch
for patch in self.patches:
if not self.apply_patch(patch):
log.debug(f"Skip patch {patch.__class__.__name__}")
continue
pattern: str = patch.get_search_pattern()
# Each patch has a capture which includes the whole subtree searched for.
# Additionally, it can include captures within this subtree.
# Here we bundle these captures together.
query: Query = self.ts_cpp_lang.query(pattern)
captures_bundle: [[(Node, str)]] = list()
for q in query_captures_22_3(query, self.tree.root_node):
if q[1] == patch.get_main_capture_name():
# The main capture the patch is looking for.
captures_bundle.append([q])
else:
# A capture which is part of the main capture.
# Add it to the bundle.
if len(captures_bundle) > 0:
captures_bundle[-1].append(q)
log.debug(
f"Patch {patch.__class__.__name__} (to patch: {len(captures_bundle)})."
)
p_list: (bytes, Node) = list()
cb: [(Node, str)]
for cb in captures_bundle:
patch_kwargs = self.get_patch_kwargs(patch)
bytes_patch: bytes = patch.get_patch(cb, self.src, **patch_kwargs)
p_list.append((bytes_patch, cb[0][0]))
self.patch_src(p_list)
if self.tree.root_node.type == "ERROR":
log.fatal(
f"Patch {patch.__class__.__name__} corrupts the tree for {self.current_src_path_in.name}!"
)
exit(1)
log.info(f"Patched file at '{self.current_src_path_out}'")
with open(self.current_src_path_out, "w") as f:
f.write(get_header())
f.write(self.src.decode("utf8"))
run_clang_format(self.out_paths)
def collect_template_instances(self):
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"]
]
self.template_collector = TemplateCollector(
self.parser, self.ts_cpp_lang, search_paths, temp_arg_deduction
)
self.template_collector.collect()
def get_patch_kwargs(self, patch):
default_kwargs = dict()
default_kwargs["tree"] = self.tree
default_kwargs["ts_cpp_lang"] = self.ts_cpp_lang
if isinstance(patch, Includes) and self.current_src_path_in:
default_kwargs["filename"] = self.current_src_path_in.name
return default_kwargs
def remark_manual_files(self) -> None:
manual_edited = self.conf["manually_edited_files"]
msg = ""
if len(manual_edited) > 0:
msg += (
termcolor.colored(
"The following files are too complex to translate! Please check them by hand.",
attrs=["bold"],
)
+ "\n"
)
else:
return
for f in manual_edited:
msg += get_path(f).name + "\n"
print_prominent_warning(msg, self.wait_for_user)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="CppTranslator",
description="Capstones C++ to C translator for LLVM source files",
)
parser.add_argument(
"-a",
dest="arch",
help="Name of target architecture.",
choices=["ARM", "PPC", "AArch64", "Alpha"],
required=True,
)
parser.add_argument(
"-v",
dest="verbosity",
help="Verbosity of the log messages.",
choices=["debug", "info", "warning", "fatal"],
default="info",
)
parser.add_argument(
"-c",
dest="config_path",
help="Config file for architectures.",
default="arch_config.json",
type=Path,
)
arguments = parser.parse_args()
return arguments
if __name__ == "__main__":
if not sys.hexversion >= 0x030B00F0:
log.fatal("Python >= v3.11 required.")
exit(1)
args = parse_args()
log.basicConfig(
level=convert_loglevel(args.verbosity),
stream=sys.stdout,
format="%(levelname)-5s - %(message)s",
)
configurator = Configurator(args.arch, args.config_path)
translator = Translator(configurator)
translator.translate()
translator.remark_manual_files()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,166 @@
<!--
Copyright © 2022 Rot127 <unisono@quyllur.org>
SPDX-License-Identifier: BSD-3
-->
# C++ Translator
Capstone uses source files from LLVM to disassemble opcodes.
Because LLVM is written in C++ we must translate those files to C.
The task of the `CppTranslator` is to do just that.
The translation will not result in a completely correct C file! But it takes away most of the manual work.
## The configuration file
The configuration for each architecture is set in `arch_config.json`.
The config values have the following meaning:
- `General`: Settings valid for all architectures.
- `diff_color_new`: Color in the `Differ` for translated 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_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*.
- `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!). Types can be of the form `<parent-type>/<child type>`.
- `<ARCH>`: Settings valid for a specific architecture
- `files_to_translate`: A list of file paths to translate.
- `in`: *Path* to a specific source file.
- `out`: The *filename* of the translated file.
- `files_for_template_search`: List of file paths to search for calls to template functions.
- `manually_edite_files`: List of files which are too complicated to translate. The user will be warned about them.
- `templates_with_arg_deduction`: Template functions which uses [argument deduction](https://en.cppreference.com/w/cpp/language/template_argument_deduction). Those templates are translated to normal functions, not macro definition.
_Note_:
- To understand the `nodes_to_diff` setting, check out `Differ.py`.
- Paths can contain `{AUTO_SYNC_ROOT}`, `{CS_ROOT}` and `{CPP_TRANSLATOR_ROOT}`.
They are replaced with the absolute paths to those directories.
## Translation process
The translation process simply searches for certain syntax and patches it.
To allow searches for complicated patterns we parse the C++ file with Tree-sitter.
Afterward we can use [pattern queries](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries)
to find our syntax we would like to patch.
Here is an overview of the procedure:
- First the source file is parsed with Tree-Sitter.
- Afterward the translator iterates of a number of patches.
For each patch we do the following.
```
Translator Patch
+---+
| | +----+
| | Request pattern to search for | |
| | ----------------------------------> | |
| | | |
| | Return pattern | |
| | <--------------------------------- | |
| | | |
| | ---+ | |
| | | Find | |
| | | captures | |
| | | in src | |
| | <--+ | |
| | | |
| | Return captures found | |
| | ----------------------------------> | |
| | | |
| | +-- | |
| | Use capture | | |
| | info to | | |
| | build new | | |
| | syntax str | | |
| | +-> | |
| | | |
| | Return new syntax string to patch | |
| | <---------------------------------- | |
| | | |
| | ---+ | |
| | | Replace old | |
| | | with new syntax | |
| | | at all occurrences | |
| | | in the file. | |
| | <--+ | |
| | | |
+---+ +----+
```
## C++ Template translation
Most of the C++ syntax is simple to translate. But unfortunately the one exception are C++ templates.
Translating template functions and calls from C++ to C is tricky.
Since each template has a number of actual implementations we do the following.
- A template function definition is translated into a C macro.
- The template parameters get translated to the macro parameters.
- To differentiate the C implementations, the functions follow the naming pattern `fcn_[template_param_0]_[template_param_1]()`
<hr>
**Example**
This C++ template function
```cpp
template<unsigned X>
void fcn() {
unsigned a = X * 8;
}
```
becomes
```
#define DEFINE_FCN(X) \
void fcn ## _ ## X() { \
unsigned a = X * 8; \
}
```
To define an implementation where `X = 0` we do
```
DEFINE_FCN(0)
```
To call this implementation we call `fcn_0()`.
_(There is a special case when a template parameter is passed on to a template call. But this is explained in the code.)_
<hr>
### Enumerate template instances
In our C++ code a template function can be called with different template parameters.
For each of those calls we need to define a template implementation in C.
To do that we first scan source files for calls to template functions (`TemplateCollector.py` does this).
For each unique call we check the parameter list.
Knowing the parameter list we can now define a C function which uses exactly those parameters.
For the definition we use a macro as above.
<hr>
**Example**
Within this C++ code we see two template function calls:
```cpp
void main() {
fcn<0>();
fcn<4>();
}
```
With the knowledge that once parameter `1` and once parameter `4` was passed to the template,
we can define the implementations with the help of our `DEFINE_FCN` macro.
```c
DEFINE_FCN(0)
DEFINE_FCN(4)
```
Within the C code we can now call those with `fcn_0()` and `fcn_4()`.
<hr>
@@ -0,0 +1,346 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re
from pathlib import Path
from tree_sitter import Language, Node, Parser, Query
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.tree_sitter_compatibility import query_captures_22_3
class TemplateRefInstance:
"""
Represents a concrete instance of a template function reference.
E.g. DecodeT2Imm7<shift, 2>
"""
name: bytes
args: bytes
args_list: list
dependent_calls = list()
# Holds the indices of the caller template parameters which set the templ. parameters
# of this TemplateCallInstance.
# Structure: {caller_name: i, "self_i": k}
#
# Only used if this is an incomplete TemplateInstance
# (parameters are set by the template parameters of the calling function).
caller_param_indices: [{str: int}] = list()
def __init__(
self, name: bytes, args: bytes, start_point, start_byte, end_point, end_byte
):
self.name = name
self.args = args
self.start_point = start_point
self.start_byte = start_byte
self.end_point = end_point
self.end_byte = end_byte
self.args_list = TemplateCollector.templ_params_to_list(args)
self.templ_name = name + args
def __eq__(self, other):
return (
self.name == other.name
and self.args == other.args
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_point == other.start_point
and self.end_byte == other.end_byte
and self.end_point == other.end_point
)
def set_dep_calls(self, deps: list):
self.dependent_calls = deps
def get_c_name(self):
return b"_".join([self.name] + self.args_list)
def get_args_for_decl(self) -> list[bytes]:
"""Returns the list of arguments, but replaces all characters which
can not be part of a C identifier with _
"""
args_list = [re.sub(b"'", b"", a) for a in self.args_list]
return args_list
class TemplateCollector:
"""
Searches through the given files for calls to template functions.
And creates a list with concrete template instances.
"""
# List of completed template instances indexed by their name.
# One function can have multiple template instances. Depending on the template arguments
template_refs: {bytes: [TemplateRefInstance]} = dict()
# List of incomplete template instances indexed by the **function name they depend on**!
incomplete_template_refs: {bytes: [TemplateRefInstance]} = dict()
sources: [{str: bytes}] = list()
def __init__(
self,
ts_parser: Parser,
ts_cpp: Language,
searchable_files: [Path],
temp_arg_deduction: [bytes],
):
self.parser = ts_parser
self.lang_cpp = ts_cpp
self.searchable_files = searchable_files
self.templates_with_arg_deduction = temp_arg_deduction
def collect(self):
self.read_files()
for x in self.sources:
path = x["path"]
src = x["content"]
log.debug(f"Search for template references in {path}")
tree = self.parser.parse(src)
query: Query = self.lang_cpp.query(self.get_template_pattern())
capture_bundles = self.get_capture_bundles(query, tree)
for cb in capture_bundles:
templ_name: Node = cb[1][0]
templ_args: Node = cb[2][0]
name = get_text(src, templ_name.start_byte, templ_name.end_byte)
args = get_text(src, templ_args.start_byte, templ_args.end_byte)
ti = TemplateRefInstance(
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')}"
)
if not self.contains_template_dependent_param(src, ti, cb[0]):
if name not in self.template_refs:
self.template_refs[name] = list()
# The template function has no parameter which is part of a previous
# template definition. So all template parameters are well-defined.
# Add it to the well-defined list.
if ti not in self.template_refs[name]:
self.template_refs[name].append(ti)
self.resolve_dependencies()
def resolve_dependencies(self):
# Resolve dependencies of templates until nothing new was resolved.
prev_len = 0
while (
len(self.incomplete_template_refs) > 0
and len(self.incomplete_template_refs) != prev_len
):
# Dict with new template calls which were previously incomplete
# because one or more parameters were unknown.
new_completed_tcs: {str: list} = dict()
tc_instance_list: [TemplateRefInstance]
for caller_name, tc_instance_list in self.template_refs.items():
# Check if this caller has a dependent template call.
# In other words: If a template parameter of this caller is given
# to another template call in the callers body.
if caller_name not in self.incomplete_template_refs:
# Not in the dependency list. Skip it.
continue
# For each configuration of template parameters we complete a template reference.
for caller_template in tc_instance_list:
incomplete_tc: TemplateRefInstance
for incomplete_tc in self.incomplete_template_refs[caller_name]:
new_tc: TemplateRefInstance = self.get_completed_tc(
caller_template, incomplete_tc
)
callee_name = new_tc.name
if callee_name not in new_completed_tcs:
new_completed_tcs[callee_name] = list()
if new_tc not in new_completed_tcs[callee_name]:
new_completed_tcs[callee_name].append(new_tc)
del self.incomplete_template_refs[caller_name]
for templ_name, tc_list in new_completed_tcs.items():
if templ_name in self.template_refs:
self.template_refs[templ_name] += tc_list
else:
self.template_refs[templ_name] = tc_list
prev_len = len(self.incomplete_template_refs)
if prev_len > 0:
log.info(
f"Unresolved template calls: {self.incomplete_template_refs.keys()}. Patch them by hand!"
)
@staticmethod
def get_completed_tc(
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:
if tc.name not in indices:
# Index of other caller function. Skip.
continue
caller_i = indices[tc.name]
self_i = indices["self_i"]
new_tc.args_list[self_i] = tc.args_list[caller_i]
new_tc.args = TemplateCollector.list_to_templ_params(new_tc.args_list)
new_tc.templ_name = new_tc.name + new_tc.args
return new_tc
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,
is a parameter of the callers template definition.
Let's assume we find the template call `func_B<X>()`.
Now look at the context `func_B<X>` is in:
template<X>
void func_A() {
func_B<X>(a)
}
Since `X` is a template parameter of `func_A` we have to wait until we see a call
to `func_A<X>` where `X` gets properly defined.
Until then we save the TemplateInstance of `func_B<X>` in a list of incomplete
template calls and note that it depends on `func_A`.
If later a call to function `func_A` is found (with a concrete value for `X`) we can add
a concrete TemplateInstance of `func_B`.
:param: src The current source code to operate on.
:param: ti The TemplateInstance for which to check dependencies.
:param: parse_tree The parse tree of the template call.
:return: True if a dependency was found. False otherwise.
"""
# Search up to the function definition this call belongs to
node: Node = parse_tree[0]
while node.type != "function_definition":
node = node.parent
if not node.prev_named_sibling.type == "template_parameter_list":
# Caller is a normal function definition.
# Nothing to do here.
return False
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_templ_params = get_text(
src, node.prev_sibling.start_byte, node.prev_sibling.end_byte
)
pl = TemplateCollector.templ_params_to_list(caller_templ_params)
has_parameter_dependency = False
for i, param in enumerate(pl):
if param in ti.args_list:
has_parameter_dependency = True
ti.caller_param_indices.append(
{caller_fcn_name: i, "self_i": ti.args_list.index(param)}
)
if not has_parameter_dependency:
return False
if caller_fcn_name not in self.incomplete_template_refs:
self.incomplete_template_refs[caller_fcn_name] = list()
if ti not in self.incomplete_template_refs[caller_fcn_name]:
self.incomplete_template_refs[caller_fcn_name].append(ti)
return True
def read_files(self):
for sf in self.searchable_files:
if not Path.exists(sf):
log.fatal(f"TemplateCollector: Could not find '{sf}' for search.")
exit(1)
log.debug(f"TemplateCollector: Read {sf}")
with open(sf) as f:
file = {"path": sf, "content": bytes(f.read(), "utf8")}
self.sources.append(file)
@staticmethod
def get_capture_bundles(query, tree):
captures_bundle: list[list[tuple[Node, str]]] = list()
for q in query_captures_22_3(query, tree.root_node):
if q[1] == "templ_ref":
captures_bundle.append([q])
else:
captures_bundle[-1].append(q)
return captures_bundle
@staticmethod
def get_template_pattern():
"""
:return: A pattern which finds either a template function calls or references.
"""
return (
"(template_function"
" ((identifier) @name)"
" ((template_argument_list) @templ_args)"
") @templ_ref"
)
@staticmethod
def templ_params_to_list(templ_params: bytes) -> list[bytes]:
if not templ_params:
return list()
params = templ_params.strip(b"<>").split(b",")
params = [p.strip() for p in params]
res = list()
for p in params:
if len(p.split(b" ")) == 2:
# Typename specified for parameter. Remove it.
# If it was more than one space, it is likely an operation like `size + 1`
p = p.split(b" ")[1]
# true and false get resolved to 1 and 0
if p == "true":
p = "1"
elif p == "false":
p = "0"
res.append(p)
return res
@staticmethod
def list_to_templ_params(temp_param_list: list) -> bytes:
return b"<" + b", ".join(temp_param_list) + b">"
@staticmethod
def get_macro_c_call(name: bytes, arg_param_list: [bytes], fcn_args: bytes = b""):
res = b""
fa = [name] + arg_param_list
for x in fa[:-1]:
res += b"CONCAT(" + x + b", "
res += fa[-1]
return res + (b")" * (len(fa) - 1)) + fcn_args
@staticmethod
def log_missing_ref_and_exit(func_ref: bytes) -> None:
log.fatal(
f"Template collector has no reference for {func_ref}.\n\n"
f"The possible reasons are:\n"
"\t\t\t- Not all C++ source files which call this function are listed in the config.\n"
"\t\t\t- You removed the C++ template syntax from the .td file for this function.\n"
"\t\t\t- The function is a template with argument deduction and has no `template<...>` preamble. "
"Add it in the config as exception in this case."
)
exit(1)
@@ -0,0 +1,30 @@
void patch_beginning_of_file() {
return;
}
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
void function_a(int x) {
return;
}
int patch_new_into_old_II() {
// Should be at the same position between patch_new_into_old_I and function_a
// in the old file.
// The order of these two new functions are switched because they are
// applied backwards
return 0xffffffff;
}
int patch_new_into_old_I() {
// Should be at the same position between function_b and patch_new_into_old_II
// in the old file.
// The order of these two new functions are switched because they are
// applied backwards
return 0xffffffff;
}
void function_b(int x) {
return;
}
@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
void function_a(int x) {
return;
}
int patch_same_into_old_smaller() {
// Should replace the same function in the old file.
// At the same position between function_a and function_b.
// Function has more line than the function in the old file.
return 0xffffffff;
}
void function_b(int x) {
return;
}
void patch_same_into_old_bigger() {
// Should replace the same function in the old file.
// At the same position after function_b.
// Function has less lines than the function in the old file.
return 1;
}
@@ -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() {}
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
void function_a(int x) {
return;
}
int patch_new_into_old_I() {
// Should be at the same position between function_b and patch_new_into_old_II
// in the old file.
// The order of these two new functions are switched because they are
// applied backwards
return 0xffffffff;
}
int patch_new_into_old_II() {
// Should be at the same position between patch_new_into_old_I and function_a
// in the old file.
// The order of these two new functions are switched because they are
// applied backwards
return 0xffffffff;
}
void function_b(int x) {
return;
}
void patch_beginning_of_file() {
return;
}
@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
void function_a(int x) {
return;
}
int patch_same_into_old_smaller() {
// Should replace the same function in the old file.
// At the same position between function_a and function_b.
// Function has more line than the function in the old file.
return 0xffffffff;
}
void function_b(int x) {
return;
}
void patch_same_into_old_bigger() {
// Should replace the same function in the old file.
// At the same position after function_b.
// Function has less lines than the function in the old file.
return 1;
}
@@ -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() {}
@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
void function_a(int x) {
return;
}
void function_b(int x) {
return;
}
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
void function_a(int x) {
return;
}
int patch_same_into_old_smaller() {
// Function has less lines than the function in the new file.
return 0;
}
void function_b(int x) {
return;
}
int patch_same_into_old_bigger() {
// Function has more lines than the function in the new file.
int o = 0;
o += 1;
o += 1;
o += 1;
o += 1;
o += 1;
return 0;
}
@@ -0,0 +1,6 @@
<!--
Copyright © 2024 Rot127 <unisono@quyllur.org>
SPDX-License-Identifier: BSD-3
-->
Output directory for patched files.
@@ -0,0 +1,43 @@
{
"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"
},
{
"in": "{DIFFER_TEST_OLD_SRC_DIR}/diff_test_file_patching_new.c",
"out": "diff_test_file_patching_new.c"
},
{
"in": "{DIFFER_TEST_OLD_SRC_DIR}/diff_test_file_patching_replace.c",
"out": "diff_test_file_patching_replace.c"
}
],
"files_for_template_search": [],
"templates_with_arg_deduction": [],
"manually_edited_files": []
}
}
@@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
int main() {
tfunction<int, int>();
tfunction<int, char>();
}
@@ -0,0 +1,39 @@
{
"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": [],
"patching": {
"apply_patch_only_to": {
"AddCSDetail": {
"files": [
"ARMInstPrinter.cpp",
"PPCInstPrinter.cpp",
"AArch64InstPrinter.cpp",
"LoongArchInstPrinter.cpp",
"MipsInstPrinter.cpp"
]
},
"InlineToStaticInline": {
"files": [
"ARMAddressingModes.h"
]
},
"PrintRegImmShift": {
"files": [
"ARMInstPrinter.cpp"
]
}
}
}
},
"ARCH": {
"files_to_translate": [],
"files_for_template_search": ["{PATCHES_TEST_DIR}/template_src.c"],
"templates_with_arg_deduction": [],
"manually_edited_files": []
}
}
@@ -0,0 +1,117 @@
# 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)
cls.maxDiff = 10000
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)
def test_patching(self):
self.differ.diff(user_choices=list("nnnnnnnnnnnnnnnnnnnnnnnnn"))
for efile in get_path("{DIFFER_TEST_EXPECTED_DIR}").iterdir():
with open(efile) as f:
expected = f.read()
with open(get_path("{DIFFER_TEST_OUTPUT_DIR}").joinpath(efile.name)) as f:
actual = f.read()
print(f"Compare {efile}")
self.assertEqual(expected, actual)
@@ -0,0 +1,633 @@
#!/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 pathlib import Path
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.Data import Data
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.LLVM_DEBUG import LLVM_DEBUG
from autosync.cpptranslator.patches.LLVMunreachable import LLVMUnreachable
from autosync.cpptranslator.patches.Override import Override
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.Size import Size
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.cpptranslator.patches.BadConditionCode import BadConditionCode
from autosync.Helper import get_path
from autosync.cpptranslator.patches.isUInt import IsUInt
from autosync.cpptranslator.tree_sitter_compatibility import query_captures_22_3
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: Path = None, tree: dict = None
):
kwargs = self.translator.get_patch_kwargs(patch)
if filename:
kwargs["filename"] = filename
query: Query = self.ts_cpp_lang.query(patch.get_search_pattern())
tree = self.parser.parse(syntax)
kwargs["tree"] = tree
captures_bundle: [[(Node, str)]] = list()
for q in query_captures_22_3(query, tree.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:
actual = patch.get_patch(cb, syntax, **kwargs)
self.assertEqual(actual, 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"static inline void printThumbLdrLabelOperand(MCInst *MI, unsigned OpNo, SStream *O){ "
b"ARCH_add_cs_detail_0(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"CS_ASSERT(0 == 0);")
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_data(self):
patch = Data(0)
syntax = b"Bytes.data()"
self.check_patching_result(patch, syntax, b"Bytes")
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, NULL)",
)
syntax = b"decodeInstruction(Table[i], MI, Insn16, Address, this, STI);"
self.check_patching_result(
patch,
syntax,
b"decodeInstruction_2(Table[i], MI, Insn16, Address, NULL)",
)
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)",
)
syntax = b"static unsigned function(unsigned Insn) { return fieldFromInstruction(Insn, 6, 6); }"
self.check_patching_result(
patch,
syntax,
b"fieldFromInstruction_4(Insn, 6, 6)",
)
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_getoperandreg(self):
patch = GetOperandRegImm(0)
syntax = b"OPERAND.getReg()"
self.check_patching_result(patch, syntax, b"MCOperand_getReg(OPERAND)")
def test_getoperandimm(self):
patch = GetOperandRegImm(0)
syntax = b"OPERAND.getImm()"
self.check_patching_result(patch, syntax, b"MCOperand_getImm(OPERAND)")
def test_getoperandexpr(self):
patch = GetOperandRegImm(0)
syntax = b"OPERAND.getExpr()"
self.check_patching_result(patch, syntax, b"MCOperand_getExpr(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=Path("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_isreg(self):
patch = IsOperandRegImm(0)
syntax = b"OPERAND.isReg()"
self.check_patching_result(patch, syntax, b"MCOperand_isReg(OPERAND)")
def test_isimm(self):
patch = IsOperandRegImm(0)
syntax = b"OPERAND.isImm()"
self.check_patching_result(patch, syntax, b"MCOperand_isImm(OPERAND)")
def test_isexpr(self):
patch = IsOperandRegImm(0)
syntax = b"OPERAND.isExpr()"
self.check_patching_result(patch, syntax, b"MCOperand_isExpr(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'CS_ASSERT(0 && "Error msg")')
def test_llvmdebug(self):
patch = LLVM_DEBUG(0)
syntax = b'LLVM_DEBUG(dbgs() << "Error msg")'
self.check_patching_result(patch, syntax, b"")
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_override(self):
patch = Override(0)
syntax = b"class a { void function(int a) override; };"
self.check_patching_result(patch, syntax, b"function(int a)")
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_size(self):
patch = Size(0)
syntax = b"Bytes.size()"
self.check_patching_result(patch, syntax, b"BytesLen")
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_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");',
)
syntax = b"{ int y = 1; int x = 1; OS << x; }"
self.check_patching_result(patch, syntax, b"printInt32(OS, x);")
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, size_t xLen")
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"")
def test_isuintn(self):
patch = IsUInt(0)
syntax = b"isUInt<RegUnitBits>(FirstRU);"
self.check_patching_result(patch, syntax, b"isUIntN(RegUnitBits, FirstRU)")
def test_badconditioncode(self):
patch = BadConditionCode(0)
syntax = b"return BadConditionCode(BRCC)"
self.check_patching_result(
patch, syntax, b'CS_ASSERT(0 && "Unknown condition code passed");'
)
@@ -0,0 +1,44 @@
# SPDX-FileCopyrightText: 2024 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import unittest
from pathlib import Path
from autosync.Helper import get_path
from autosync.cpptranslator import CppTranslator
from autosync.cpptranslator.Configurator import Configurator
from autosync.cpptranslator.patches.AddCSDetail import AddCSDetail
from autosync.cpptranslator.patches.InlineToStaticInline import InlineToStaticInline
from autosync.cpptranslator.patches.PrintRegImmShift import PrintRegImmShift
from autosync.cpptranslator.patches.Data import Data
class TestCppTranslator(unittest.TestCase):
@classmethod
def setUpClass(cls):
configurator = Configurator("ARCH", get_path("{PATCHES_TEST_CONFIG}"))
cls.translator = CppTranslator.Translator(configurator, False)
def test_patching_constraints(self):
self.translator.current_src_path_in = Path("Random_file.cpp")
patch_add_cs_detail = AddCSDetail(0, "ARCH")
patch_inline_to_static_inline = InlineToStaticInline(0)
patch_print_reg_imm_shift = PrintRegImmShift(0)
patch_data = Data(0)
self.assertFalse(self.translator.apply_patch(patch_add_cs_detail))
self.assertFalse(self.translator.apply_patch(patch_inline_to_static_inline))
self.assertFalse(self.translator.apply_patch(patch_print_reg_imm_shift))
self.assertTrue(self.translator.apply_patch(patch_data))
self.translator.current_src_path_in = Path("ARMInstPrinter.cpp")
self.assertTrue(self.translator.apply_patch(patch_add_cs_detail))
self.assertFalse(self.translator.apply_patch(patch_inline_to_static_inline))
self.assertTrue(self.translator.apply_patch(patch_print_reg_imm_shift))
self.assertTrue(self.translator.apply_patch(patch_data))
self.translator.current_src_path_in = Path("ARMAddressingModes.h")
self.assertFalse(self.translator.apply_patch(patch_add_cs_detail))
self.assertTrue(self.translator.apply_patch(patch_inline_to_static_inline))
self.assertFalse(self.translator.apply_patch(patch_print_reg_imm_shift))
self.assertTrue(self.translator.apply_patch(patch_data))
@@ -0,0 +1,347 @@
{
"General": {
"diff_color_new": "green",
"diff_color_old": "light_blue",
"diff_color_saved": "yellow",
"diff_color_edited": "light_magenta",
"patch_editor": "vim",
"patching": {
"apply_patch_only_to": {
"AddCSDetail": {
"files": [
"ARMInstPrinter.cpp",
"PPCInstPrinter.cpp",
"AArch64InstPrinter.cpp",
"LoongArchInstPrinter.cpp",
"MipsInstPrinter.cpp",
"SystemZInstPrinter.cpp",
"XtensaInstPrinter.cpp",
"SparcInstPrinter.cpp",
"RISCVInstPrinter.cpp"
]
},
"InlineToStaticInline": {
"files": [
"ARMAddressingModes.h"
]
},
"PrintRegImmShift": {
"files": [
"ARMInstPrinter.cpp"
]
}
}
},
"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"]
}
]
},
"ARM": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp",
"out": "ARMDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp",
"out": "ARMInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h",
"out": "ARMInstPrinter.h"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMAddressingModes.h",
"out": "ARMAddressingModes.h"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/Utils/ARMBaseInfo.cpp",
"out": "ARMBaseInfo.c"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/ARMGenDisassemblerTables.inc",
"{CPP_INC_OUT_DIR}/ARMGenAsmWriter.inc",
"{LLVM_ROOT}/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp",
"{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp"
],
"templates_with_arg_deduction": [],
"manually_edited_files": [
"{LLVM_ROOT}/llvm/lib/Target/ARM/Utils/ARMBaseInfo.h"
]
},
"PPC": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp",
"out": "PPCDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp",
"out": "PPCInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.h",
"out": "PPCMCTargetDesc.h"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/MCTargetDesc/PPCPredicates.h",
"out": "PPCPredicates.h"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/PPCGenDisassemblerTables.inc",
"{LLVM_ROOT}/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp"
],
"templates_with_arg_deduction": [
"decodeRegisterClass"
],
"manually_edited_files": [
"{LLVM_ROOT}/llvm/lib/Target/PowerPC/PPCInstrInfo.h",
"{LLVM_ROOT}/llvm/lib/Target/PowerPC/PPCRegisterInfo.h"
]
},
"AArch64": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp",
"out": "AArch64Disassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp",
"out": "AArch64InstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.h",
"out": "AArch64InstPrinter.h"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AddressingModes.h",
"out": "AArch64AddressingModes.h"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp",
"out": "AArch64BaseInfo.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h",
"out": "AArch64BaseInfo.h"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/AArch64GenDisassemblerTables.inc",
"{CPP_INC_OUT_DIR}/AArch64GenAsmWriter.inc",
"{LLVM_ROOT}/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp",
"{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp"
],
"templates_with_arg_deduction": [
"printImmSVE",
"printAMIndexedWB",
"isSVECpyImm",
"isSVEAddSubImm",
"printVectorIndex"
],
"manually_edited_files": []
},
"Alpha": {
"files_to_translate": [],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/AlphaGenDisassemblerTables.inc",
"{CPP_INC_OUT_DIR}/AlphaGenAsmWriter.inc"
],
"templates_with_arg_deduction": [],
"manually_edited_files": []
},
"LoongArch": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp",
"out": "LoongArchDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp",
"out": "LoongArchInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.h",
"out": "LoongArchInstPrinter.h"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/LoongArchGenDisassemblerTables.inc",
"{CPP_INC_OUT_DIR}/LoongArchGenAsmWriter.inc",
"{LLVM_ROOT}/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp",
"{LLVM_ROOT}/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp"
],
"templates_with_arg_deduction": [],
"manually_edited_files": []
},
"Mips": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/Mips/Disassembler/MipsDisassembler.cpp",
"out": "MipsDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.cpp",
"out": "MipsInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.h",
"out": "MipsInstPrinter.h"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/MipsGenDisassemblerTables.inc",
"{LLVM_ROOT}/llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.cpp",
"{CPP_INC_OUT_DIR}/MipsGenAsmWriter.inc"
],
"templates_with_arg_deduction": [
"DecodeINSVE_DF",
"DecodeDAHIDATIMMR6",
"DecodeDAHIDATI",
"DecodeAddiGroupBranch",
"DecodePOP35GroupBranchMMR6",
"DecodeDaddiGroupBranch",
"DecodePOP37GroupBranchMMR6",
"DecodePOP65GroupBranchMMR6",
"DecodePOP75GroupBranchMMR6",
"DecodeBlezlGroupBranch",
"DecodeBgtzlGroupBranch",
"DecodeBgtzGroupBranch",
"DecodeBlezGroupBranch",
"DecodeBgtzGroupBranchMMR6",
"DecodeBlezGroupBranchMMR6",
"DecodeDINS",
"DecodeDEXT",
"DecodeCRC",
"isReg"
],
"manually_edited_files": []
},
"SystemZ": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/Disassembler/SystemZDisassembler.cpp",
"out": "SystemZDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZInstPrinter.cpp",
"out": "SystemZInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZInstPrinter.h",
"out": "SystemZInstPrinter.h"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp",
"out": "SystemZMCTargetDesc.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.h",
"out": "SystemZMCTargetDesc.h"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/SystemZGenDisassemblerTables.inc",
"{CPP_INC_OUT_DIR}/SystemZGenAsmWriter.inc",
"{LLVM_ROOT}/llvm/lib/Target/SystemZ/Disassembler/SystemZDisassembler.cpp",
"{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZInstPrinter.cpp"
],
"templates_with_arg_deduction": [],
"manually_edited_files": []
},
"Xtensa": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp",
"out": "XtensaDisassembler.c"
},
{
"in": "{LLVM_ROOT}/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp",
"out": "XtensaInstPrinter.c"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/XtensaGenDisassemblerTables.inc",
"{CPP_INC_OUT_DIR}/XtensaGenAsmWriter.inc"
],
"templates_with_arg_deduction": [],
"manually_edited_files": []
},
"TriCore": {
"files_to_translate": [],
"files_for_template_search": [],
"templates_with_arg_deduction": [],
"manually_edited_files": []
},
"ARC": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/Disassembler/ARCDisassembler.cpp",
"out": "ARCDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInstPrinter.cpp",
"out": "ARCInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInstPrinter.h",
"out": "ARCInstPrinter.h"
},
{
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInfo.h",
"out": "ARCInfo.h"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/ARCGenDisassemblerTables.inc",
"{CPP_INC_OUT_DIR}/ARCGenAsmWriter.inc",
"{LLVM_ROOT}/llvm/lib/Target/ARC/Disassembler/ARCDisassembler.cpp",
"{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInstPrinter.cpp"
],
"templates_with_arg_deduction": [],
"manually_edited_files": []
},
"Sparc": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/Disassembler/SparcDisassembler.cpp",
"out": "SparcDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.cpp",
"out": "SparcInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.h",
"out": "SparcInstPrinter.h"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/MCTargetDesc/SparcMCTargetDesc.h",
"out": "SparcMCTargetDesc.h"
}
],
"files_for_template_search": [
"{CPP_INC_OUT_DIR}/SparcGenDisassemblerTables.inc",
"{LLVM_ROOT}/llvm/lib/Target/Sparc/Disassembler/SparcDisassembler.cpp"
],
"templates_with_arg_deduction": [],
"manually_edited_files": []
},
"RISCV": {
"files_to_translate": [
{
"in": "{LLVM_ROOT}/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp",
"out": "RISCVDisassembler.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp",
"out": "RISCVInstPrinter.c"
},{
"in": "{LLVM_ROOT}/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h",
"out": "RISCVInstPrinter.h"
},
{
"in": "{LLVM_ROOT}/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h",
"out": "RISCVBaseInfo.h"
},
{
"in": "{LLVM_ROOT}/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp",
"out": "RISCVBaseInfo.c"
}
],
"files_for_template_search": [
"{LLVM_ROOT}/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp",
"{CPP_INC_OUT_DIR}/RISCVGenDisassemblerTables.inc"
],
"templates_with_arg_deduction": [],
"manually_edited_files": []
}
}
@@ -0,0 +1,144 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import (
get_MCInst_var_name,
get_text,
template_param_list_to_dict,
)
from autosync.cpptranslator.patches.Patch import Patch
class AddCSDetail(Patch):
"""
Adds calls to `<ARCH>_add_cs_detail_<n>()` for printOperand functions in <ARCH>InstPrinter.c
Patch void printThumbLdrLabelOperand(MCInst *MI, unsigned OpNo, SStream *O) {...}
to void printThumbLdrLabelOperand(MCInst *MI, unsigned OpNo, SStream *O) {
<ARCH>_add_cs_detail_<n>(MI, ARM_OP_GROUP_ThumbLdrLabelOperand, ...);
...
}
"""
# TODO Simply checking for the passed types would be so much nicer.
# Parameter lists of printOperand() functions we need to add `<ARCH>_add_cs_detail_<n>()` to.
# Spaces are removed, so we only need to check the letters.
valid_param_lists = [
b"(MCInst*MI,unsignedOpNum,SStream*O)", # Default printOperand parameters.
b"(MCInst*MI,unsignedOpNo,SStream*O)", # ARM - printComplexRotationOp / PPC default
b"(MCInst*MI,intopNum,SStream *O)", # Mips - printMemOperandEA and others
b"(SStream*O,ARM_AM::ShiftOpcShOpc,unsignedShImm,boolgetUseMarkup())", # ARM - printRegImmShift
b"(MCInst*MI,unsignedOpNo,SStream*O,constchar*Modifier)", # PPC - printPredicateOperand
b"(MCInst*MI,uint64_tAddress,unsignedOpNo,SStream*O)", # PPC - printBranchOperand
b"(MCInst*MI,intOpNum,SStream*O)", # SystemZ
b"(MCInst*MI,intOpNum,SStream*O)", # Xtensa printOperand parameters.
b"(MCInst*MI,intOpNum,SStream*OS)", # Xtensa printOperand parameters.
b"(MCInst*MI,intopNum,SStream*O)", # Sparc printOperand parameters.
]
def __init__(self, priority: int, arch: str):
super().__init__(priority)
self.arch = arch
def get_search_pattern(self) -> str:
return (
"(function_definition"
" (_)+"
" (function_declarator"
' ((identifier) @fcn_id (#match? @fcn_id "print.*"))'
" ((parameter_list) @p_list)"
" )"
" (compound_statement) @comp_stmt"
") @print_op"
)
def get_main_capture_name(self) -> str:
return "print_op"
def get_patch(
self, captures: list[tuple[Node, str]], src: bytes, **kwargs
) -> bytes:
fcn_def: Node = captures[0][0]
params = captures[2][0]
params = get_text(src, params.start_byte, params.end_byte)
if re.sub(b"[\n \t]", b"", params) not in self.valid_param_lists:
return get_text(src, fcn_def.start_byte, fcn_def.end_byte)
fcn_id = captures[1][0]
fcn_id = get_text(src, fcn_id.start_byte, fcn_id.end_byte)
add_cs_detail = self.get_add_cs_detail(src, fcn_def, fcn_id, params)
comp = captures[3][0]
comp = get_text(src, comp.start_byte, comp.end_byte)
return (
b"static inline 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:
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"
if b"OpNum" in params:
op_num_var_name = b"OpNum"
elif b"OpNo" in params:
op_num_var_name = b"OpNo"
elif b"opNum" in params:
op_num_var_name = b"opNum"
else:
raise ValueError("OpNum parameter could not be identified.")
if not is_template and op_num_var_name in params:
# Standard printOperand() parameters
mcinst_var = get_MCInst_var_name(src, fcn_def)
return (
f"{self.arch}_add_cs_detail_0(".encode()
+ mcinst_var
+ b", "
+ op_group_enum
+ b", "
+ op_num_var_name
+ b");"
)
elif op_group_enum == b"ARM_OP_GROUP_RegImmShift":
return (
f"{self.arch}_add_cs_detail_2(MI, ".encode()
+ op_group_enum
+ b", ShOpc, ShImm);"
)
elif is_template and op_num_var_name in params:
mcinst_var = get_MCInst_var_name(src, fcn_def)
templ_p = template_param_list_to_dict(fcn_def.prev_sibling)
cs_args = b""
for tp in templ_p:
op_group_enum = (
b"CONCAT(" + op_group_enum + b", " + tp["identifier"] + b")"
)
cs_args += b", " + tp["identifier"]
return (
f"{self.arch}_add_cs_detail_{len(templ_p)}(".encode()
+ mcinst_var
+ b", "
+ op_group_enum
+ b", "
+ op_num_var_name
+ b" "
+ cs_args
+ b");"
)
log.fatal(f"Case {op_group_enum} not handled.")
exit(1)
@@ -0,0 +1,41 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class AddOperand(Patch):
"""
Patch MI.addOperand(...)
to MCInst_addOperand(MI, ...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression "
" (field_expression"
" ((identifier) @inst_var)"
' ((field_identifier) @field_id_op (#eq? @field_id_op "addOperand"))'
" )"
" ((argument_list) @arg_list)"
") @add_operand"
)
return q
def get_main_capture_name(self) -> str:
return "add_operand"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get instruction variable name (MI, Inst)
inst_var: Node = captures[1][0]
# Arguments of getOperand(...)
get_op_args = captures[3][0]
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
args = get_text(src, get_op_args.start_byte, get_op_args.end_byte)
return b"MCInst_addOperand2(" + inst + b", " + args + b")"
@@ -0,0 +1,34 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
from autosync.cpptranslator.patches.Helper import get_text_from_node
class Assert(Patch):
"""
Patch replace `assert`|`report_fatal_error` with `CS_ASSERT`
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(expression_statement"
" (call_expression"
' ((identifier) @id (#match? @id "assert|report_fatal_error"))'
" ((argument_list) @arg_list)"
" )"
") @assert"
)
def get_main_capture_name(self) -> str:
return "assert"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
args = get_text_from_node(src, captures[2][0])
return b"CS_ASSERT" + args + b";"
@@ -0,0 +1,32 @@
# Copyright © 2024 Dmitry Sibitsev <sibirtsevdl@gmail.com>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class BadConditionCode(Patch):
"""
Patch return BadConditionCode
to CS_ASSERT(0 && "Unknown condition code passed")
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(return_statement "
" (call_expression "
' (identifier) @fcn_name (#eq? @fcn_name "BadConditionCode")'
" (argument_list)"
" )"
") @bad_condition_code"
)
def get_main_capture_name(self) -> str:
return "bad_condition_code"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b'CS_ASSERT(0 && "Unknown condition code passed");'
@@ -0,0 +1,84 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class BitCastStdArray(Patch):
"""
Patch auto S = bit_cast<std::array<int32_t, 2>>(Imm);
to union {
typeof(Imm) In;
int32_t Out[2];
} U_S;
U_S.In = Imm;
int32_t *S = U_S.Out;
MSVC doesn't support typeof so it has to be resolved manually.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(declaration"
" (placeholder_type_specifier)"
" (init_declarator"
" (identifier) @arr_name"
" (call_expression"
" (template_function"
' ((identifier) @tfid (#eq @tfid "bit_cast"))'
" (template_argument_list"
' ((type_descriptor) @td (#match @td "std::array<.*>"))'
" )"
" )"
" (argument_list) @cast_target"
" )"
" )"
") @array_bit_cast"
)
def get_main_capture_name(self) -> str:
return "array_bit_cast"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
c1 = captures[1][0]
c4 = captures[4][0]
arr_name: bytes = get_text(src, c1.start_byte, c1.end_byte)
array_type: Node = captures[3][0]
cast_target: bytes = get_text(src, c4.start_byte, c4.end_byte).strip(b"()")
named_child = array_type.named_children[0].named_children[1].named_children[1]
array_templ_args: bytes = get_text(
src, named_child.start_byte, named_child.end_byte
).strip(b"<>")
arr_type = array_templ_args.split(b",")[0]
arr_len = array_templ_args.split(b",")[1]
return (
b"union {\n"
+ b" typeof("
+ cast_target
+ b") In;\n"
+ b" "
+ 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;"
)
@@ -0,0 +1,37 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class CheckDecoderStatus(Patch):
"""
Patch "Check(S, ..."
to "Check(&S, ..."
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
' ((identifier) @fcn_name (#eq? @fcn_name "Check"))'
" ((argument_list) @arg_list)"
") @check_call"
)
def get_main_capture_name(self) -> str:
return "check_call"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
call_expr: Node = captures[0][0]
first_arg: Node = captures[2][0].named_children[0]
call_text = get_text(src, call_expr.start_byte, call_expr.end_byte)
first_arg_text = get_text(src, first_arg.start_byte, first_arg.end_byte)
return call_text.replace(first_arg_text + b",", b"&" + first_arg_text + b",")
@@ -0,0 +1,32 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class ClassConstructorDef(Patch):
"""
Removes Class constructor definitions with a field initializer list.
Removes Class::Class(...) : ... {}
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(function_definition"
" (function_declarator)"
" (field_initializer_list)"
" (compound_statement)"
") @class_constructor"
)
return q
def get_main_capture_name(self) -> str:
return "class_constructor"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b""
@@ -0,0 +1,50 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class ClassesDef(Patch):
"""
Patch Class definitions
to Removes class but extracts method declarations.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return "(class_specifier (_)* ((field_declaration_list) @decl_list)*) @class_specifier"
def get_main_capture_name(self) -> str:
return "class_specifier"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
if len(captures) < 2:
# Forward class definition. Ignore it.
return b""
field_decl_list = captures[1][0]
functions = list()
for field_decl in field_decl_list.named_children:
if (
field_decl.type in "field_declaration"
and (
"function_declarator" in [t.type for t in field_decl.named_children]
)
) or field_decl.type == "template_declaration":
# Keep comments
sibling = field_decl.prev_named_sibling
while sibling.type == "comment":
functions.append(sibling)
sibling = sibling.prev_named_sibling
functions.append(field_decl)
fcn_decl_text = b""
for f in functions:
fcn_decl_text += get_text(src, f.start_byte, f.end_byte) + b"\n"
return fcn_decl_text
@@ -0,0 +1,36 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class ConstMCInstParameter(Patch):
"""
Patch const MCInst *MI
to MCInst *MI
Removes the const qualifier from MCInst parameters because functions like MCInst_getOperand() ignore them anyway.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(parameter_declaration"
" ((type_qualifier) @type_qualifier)"
' ((type_identifier) @type_id (#eq? @type_id "MCInst"))'
" (pointer_declarator) @ptr_decl"
") @mcinst_param"
)
def get_main_capture_name(self) -> str:
return "mcinst_param"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
inst = captures[3][0]
inst = get_text(src, inst.start_byte, inst.end_byte)
return b"MCInst " + inst
@@ -0,0 +1,36 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class ConstMCOperand(Patch):
"""
Patch const MCOperand ...
to MCOperand
Removes the const qualifier from MCOperand declarations. They are ignored by the following functions.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(declaration"
" (type_qualifier)"
' ((type_identifier) @tid (#eq? @tid "MCOperand"))'
" (init_declarator) @init_decl"
") @const_mcoperand"
)
def get_main_capture_name(self) -> str:
return "const_mcoperand"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
init_decl = captures[2][0]
init_decl = get_text(src, init_decl.start_byte, init_decl.end_byte)
return b"MCOperand " + init_decl + b";"
@@ -0,0 +1,36 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class CppInitCast(Patch):
"""
Patch int(...)
to (int)(...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (primitive_type) @cast_type"
" (argument_list) @cast_target"
") @cast"
)
def get_main_capture_name(self) -> str:
return "cast"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
cast_type: Node = captures[1][0]
cast_target: Node = captures[2][0]
ctype = get_text(src, cast_type.start_byte, cast_type.end_byte)
ctarget = get_text(src, cast_target.start_byte, cast_target.end_byte)
return b"((" + ctype + b")" + ctarget + b")"
@@ -0,0 +1,59 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class CreateOperand0(Patch):
"""
Patch Inst.addOperand(MCOperand::createReg(...));
to MCOperand_CreateReg0(...)
(and equivalent for CreateImm)
This is the `0` variant of the CS `CreateReg`/`CreateImm` functions. It is used if the
operand is added via `addOperand()`.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression "
" (field_expression ((identifier) @inst_var"
' (field_identifier) @field_id (#eq? @field_id "addOperand")))'
" (argument_list (call_expression "
" (qualified_identifier ((_) (identifier) @create_fcn))"
" (argument_list) @arg_list"
" )"
" )"
") @create_operand0"
)
def get_main_capture_name(self) -> str:
return "create_operand0"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get name of instruction variable
inst_var: Node = captures[1][0]
# Get 'create[Reg/Imm]'
op_create_fcn: Node = captures[3][0]
# Get arg list
op_create_args: Node = captures[4][0]
# 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),
)
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)
if args[0] == b"(" and args[-1] == b")":
args = args
return b"MCOperand_" + fcn + b"0(" + inst + b", " + args + b")"
@@ -0,0 +1,76 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from autosync.cpptranslator.patches.Patch import Patch
class CreateOperand1(Patch):
"""
Patch MI.insert(..., MCOperand::createReg(...));
to MCInst_insert0(..., MCOperand_createReg1(...));
(and equivalent for CreateImm)
This is the `1` variant of the CS `CreateReg`/`CreateImm` functions. It is used if the
operand is added via `insert()`.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression "
" (field_expression ((identifier) @MC_id"
' ((field_identifier) @field_id (#match? @field_id "insert")))'
" )"
" (argument_list"
" ((identifier) @inst_var"
" (call_expression"
" (qualified_identifier ((_) (identifier) @create_fcn))"
" (argument_list) @arg_list)"
" )"
" )"
") @create_operand1"
)
def get_main_capture_name(self) -> str:
return "create_operand1"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get instruction variable
inst_var: Node = captures[1][0]
# Get argument of .insert() call
insert_arg: Node = captures[3][0]
# Get 'create[Reg/Imm]'
op_create_fcn: Node = captures[4][0]
# CreateReg/Imm args
op_create_args: Node = captures[5][0]
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.
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)
args = get_text(src, op_create_args.start_byte, op_create_args.end_byte)
return (
b"MCInst_insert0("
+ inst
+ b", "
+ insert_arg_t
+ b", "
+ b"MCOperand_"
+ fcn
+ b"1("
+ inst
+ b", "
+ args
+ b"))"
)
@@ -0,0 +1,35 @@
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class Data(Patch):
"""
Patch Bytes.data()
to Bytes
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression "
" (field_expression"
" ((identifier) @data_var)"
' ((field_identifier) @field_id_op (#eq? @field_id_op "data"))'
" )"
" ((argument_list) @arg_list)"
") @data"
)
return q
def get_main_capture_name(self) -> str:
return "data"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get operand variable name (Bytes, ArrayRef)
op_var: Node = captures[1][0]
op = get_text(src, op_var.start_byte, op_var.end_byte)
return op
@@ -0,0 +1,50 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from autosync.cpptranslator.patches.Patch import Patch
class DeclarationInConditionalClause(Patch):
"""
Patch if (DECLARATION) ...
to DECLARATION
if (VAR) ...
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(if_statement"
" (condition_clause"
" (declaration"
" (_)"
" ((identifier) @id)"
" (_)"
" ) @decl"
" )"
" (_) @if_body"
") @condition_clause"
)
def get_main_capture_name(self) -> str:
return "condition_clause"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
cond = get_capture_node(captures, "condition_clause")
for nc in cond.named_children:
if nc.type == "if_statement":
# Skip if statements with else if
return get_text(src, cond.start_byte, cond.end_byte)
declaration = get_capture_node(captures, "decl")
identifier = get_capture_node(captures, "id")
if_body = get_capture_node(captures, "if_body")
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
declaration = get_text(src, declaration.start_byte, declaration.end_byte)
if_body = get_text(src, if_body.start_byte, if_body.end_byte)
res = declaration + b";\nif (" + identifier + b")\n" + if_body
return res
@@ -0,0 +1,53 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class DecodeInstruction(Patch):
"""
Patch decodeInstruction(..., this, STI)
to decodeInstruction_<instr_width>(..., NULL)
It also removes the arguments `this, STI`.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression ("
' (identifier) @fcn_name (#eq? @fcn_name "decodeInstruction")'
" ((argument_list) @arg_list)"
")) @decode_instr"
)
def get_main_capture_name(self) -> str:
return "decode_instr"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
arg_list = captures[2][0]
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",")
is_32bit = (
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 + b", NULL"
)
if is_16bit and not is_32bit:
return b"decodeInstruction_2(" + args + b")"
elif is_32bit and not is_16bit:
return b"decodeInstruction_4(" + args + b")"
else:
# Cannot determine instruction width easily. Only update the calls arguments.
return b"decodeInstruction(" + args + b")"
@@ -0,0 +1,38 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class DecoderCast(Patch):
"""
Patch Removes casts like `const MCDisassembler *Dis = static_cast<const MCDisassembler*>(Decoder);`
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(declaration"
" (type_qualifier)*"
' ((type_identifier) @tid (#eq? @tid "MCDisassembler"))'
" (init_declarator"
" (pointer_declarator)"
" (call_expression"
" (template_function)" # static_cast<const MCDisassembler>
" (argument_list"
' ((identifier) @id (#eq? @id "Decoder"))'
" )"
" )"
" )"
") @decoder_cast"
)
def get_main_capture_name(self) -> str:
return "decoder_cast"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b""
@@ -0,0 +1,31 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class DecoderParameter(Patch):
"""
Patch const MCDisassembler *Decoder
to const void *Decoder
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(parameter_declaration"
" ((type_qualifier) @type_qualifier)"
' ((type_identifier) @type_id (#eq? @type_id "MCDisassembler"))'
" (pointer_declarator) @ptr_decl"
") @decoder_param"
)
def get_main_capture_name(self) -> str:
return "decoder_param"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b"const void *Decoder"
@@ -0,0 +1,25 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class FallThrough(Patch):
"""
Patch [[fallthrough]]
to // fall through
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return '(attributed_statement) @attr (#match? @attr "fallthrough")'
def get_main_capture_name(self) -> str:
return "attr"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b"// fall through"
@@ -0,0 +1,44 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from autosync.cpptranslator.patches.Patch import Patch
class FeatureBits(Patch):
"""
Patch featureBits[FLAG]
to ARCH_getFeatureBits(Inst->csh->mode, FLAG)
"""
def __init__(self, priority: int, arch: bytes):
self.arch = arch
super().__init__(priority)
def get_search_pattern(self) -> str:
# Search for featureBits usage.
return (
"(subscript_expression "
' ((identifier) @id (#match? @id "[fF]eatureBits"))'
" (subscript_argument_list ((qualified_identifier) @qid))"
") @feature_bits"
)
def get_main_capture_name(self) -> str:
return "feature_bits"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get flag name of feature bit.
qualified_id: Node = captures[2][0]
flag = get_text(src, qualified_id.start_byte, qualified_id.end_byte)
mcinst_var_name = get_MCInst_var_name(src, qualified_id)
return (
self.arch
+ b"_getFeatureBits("
+ mcinst_var_name
+ b"->csh->mode, "
+ flag
+ b")"
)
@@ -0,0 +1,30 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class FeatureBitsDecl(Patch):
"""
Patch ... featureBits = ...
to REMOVED
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
# Search for featureBits declarations.
return (
"(declaration (init_declarator (reference_declarator "
'((identifier) @id (#match? @id "[fF]eatureBits"))))) @feature_bits_decl'
)
def get_main_capture_name(self) -> str:
return "feature_bits_decl"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Remove declaration
return b""
@@ -0,0 +1,98 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_function_params_of_node, get_text
from autosync.cpptranslator.patches.Patch import Patch
class FieldFromInstr(Patch):
"""
Patch fieldFromInstruction(...)
to fieldFromInstruction_<instr_width>(...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
# Search for fieldFromInstruction() calls.
return (
"(call_expression"
' ((identifier) @fcn_name (#eq? @fcn_name "fieldFromInstruction"))'
" (argument_list ((identifier) @first_arg) (_) (_))"
") @field_from_instr"
)
def get_main_capture_name(self) -> str:
return "field_from_instr"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
ffi_call: Node = captures[0][0]
ffi_first_arg: Node = captures[2][0]
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")
# Determine width of instruction by the variable name.
if ffi_first_arg_text[-2:] == "32":
inst_width = b"4"
elif ffi_first_arg_text[-2:] == "16":
inst_width = b"2"
else:
# Get the Val/Inst parameter.
# Its type determines the instruction width.
if len(param_list_caller.named_children) == 1:
# If function just return fieldFromInstruction(...)
inst_param: Node = param_list_caller.named_children[0]
else:
inst_param = param_list_caller.named_children[1]
inst_param_text = get_text(src, inst_param.start_byte, inst_param.end_byte)
# Search for the 'Inst' parameter and determine its type
# and with it the width of the instruction.
inst_type = inst_param_text.split(b" ")[0]
if inst_type:
if inst_type in [b"uint64_t"]:
inst_width = b"8"
elif inst_type in [b"unsigned", b"uint32_t"]:
inst_width = b"4"
elif inst_type in [b"uint16_t"]:
inst_width = b"2"
elif inst_type in [b"InsnType"]:
# Case means the decode function inherits the type from
# a template argument InsnType. The InsnType template argument
# is the type of integer holding the instruction bytes.
# This type is defined in ARCHDisassembler on calling the right macro.
# Hence, we do not know at this point of patching which type it might be.
# It needs to call fieldOfInstruction_X() which detects dynamically which
# integer type might hold the bytes (e.g. a uint32_t or uint16_t).
# You can check it manually in ARCHDisassembler.c, but the script can't.
#
# Here we just create a function with the postfix fieldFromInstruction_w (for width).
# This function must be implemented by hand, and check MCInst for the actual bit width.
# The bit width must be set in the ARCHDisassembler.c. Just add the code there by hand.
# Then call fieldFromInstruction_4, fieldFromInstruction_2 appropriately.
log.warning(
"Variable fieldFromInstruction width detected.\n"
"Please implement fieldFromInstruction_w() and call "
"fieldFromInstruction_4, fieldFromInstruction_2 appropriately.\n"
"In fieldFromInstruction_w() check MCInst for the actual bit width.\n"
"The bit width must be set in the ARCHDisassembler.c. Just add the code there by hand."
)
inst_width = b"w"
else:
raise ValueError(f"Type {inst_type} not handled.")
else:
# Needs manual fix
return get_text(src, ffi_call.start_byte, ffi_call.end_byte)
return re.sub(
rb"fieldFromInstruction",
b"fieldFromInstruction_%s" % inst_width,
get_text(src, ffi_call.start_byte, ffi_call.end_byte),
)
@@ -0,0 +1,38 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class GetNumOperands(Patch):
"""
Patch MI.getNumOperands()
to MCInst_getNumOperands(MI)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression "
" (field_expression"
" ((identifier) @inst_var)"
' ((field_identifier) @field_id_op (#eq? @field_id_op "getNumOperands"))'
" )"
" ((argument_list) @arg_list)"
") @get_num_operands"
)
return q
def get_main_capture_name(self) -> str:
return "get_num_operands"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get instruction variable name: MI, Inst etc.
inst_var: Node = captures[1][0]
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
return b"MCInst_getNumOperands(" + inst + b")"
@@ -0,0 +1,44 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class GetOpcode(Patch):
"""
Patch Inst.getOpcode()
to MCInst_getOpcode(Inst)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (field_expression ("
" ((identifier) @inst_var)"
' ((field_identifier) @field_id (#eq? @field_id "getOpcode")))'
" )"
" (argument_list) @arg_list"
") @get_opcode"
)
def get_main_capture_name(self) -> str:
return "get_opcode"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Instruction variable
inst_var: Node = captures[1][0]
arg_list: Node = captures[3][0]
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
args = get_text(src, arg_list.start_byte, arg_list.end_byte)
if args != b"()":
args = b", " + args
else:
args = b""
return b"MCInst_getOpcode(" + inst + args + b")"
@@ -0,0 +1,41 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class GetOperand(Patch):
"""
Patch MI.getOperand(...)
to MCInst_getOperand(MI, ...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression "
" (field_expression"
" ((identifier) @inst_var)"
' ((field_identifier) @field_id_op (#eq? @field_id_op "getOperand"))'
" )"
" ((argument_list) @arg_list)"
") @get_operand"
)
return q
def get_main_capture_name(self) -> str:
return "get_operand"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get instruction variable name (MI, Inst)
inst_var: Node = captures[1][0]
# Arguments of getOperand(...)
get_op_args = captures[3][0]
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
args = get_text(src, get_op_args.start_byte, get_op_args.end_byte)
return b"MCInst_getOperand(" + inst + b", " + args + b")"
@@ -0,0 +1,44 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from autosync.cpptranslator.patches.Patch import Patch
class GetOperandRegImm(Patch):
"""
Patch OPERAND.getReg()
to MCOperand_getReg(OPERAND)
Same for getImm()|getExpr
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression"
" (field_expression"
" ((_) @operand)"
' ((field_identifier) @field_id (#match? @field_id "get(Reg|Imm|Expr)"))'
" )"
' ((argument_list) @arg_list (#eq? @arg_list "()"))'
") @get_operand"
)
return q
def get_main_capture_name(self) -> str:
return "get_operand"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# The operand
operand: Node = get_capture_node(captures, "operand")
# 'getReg()/getImm()\getExpr'
get_reg_imm = get_capture_node(captures, "field_id")
fcn = get_text(src, get_reg_imm.start_byte, get_reg_imm.end_byte)
op = get_text(src, operand.start_byte, operand.end_byte)
return b"MCOperand_" + fcn + b"(" + op + b")"
@@ -0,0 +1,45 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import (
get_capture_node,
get_MCInst_var_name,
get_text,
)
from autosync.cpptranslator.patches.Patch import Patch
class GetRegClass(Patch):
"""
Patch MRI.getRegClass(...)
to MCRegisterInfo_getRegClass(Inst->MRI, ...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression"
" (field_expression"
" (_)"
' ((field_identifier) @field_id (#eq? @field_id "getRegClass"))'
" )"
" ((argument_list) @arg_list)"
") @get_reg_class"
)
return q
def get_main_capture_name(self) -> str:
return "get_reg_class"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
arg_list: Node = get_capture_node(captures, "arg_list")
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")
)
res = b"MCRegisterInfo_getRegClass(" + mcinst_var + b"->MRI, " + args + b")"
return res
@@ -0,0 +1,44 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from autosync.cpptranslator.patches.Patch import Patch
class GetRegFromClass(Patch):
"""
Patch <ARCH>MCRegisterClasses[<ARCH>::FPR128RegClassID].getRegister(RegNo);
to <ARCH>MCRegisterClasses[<ARCH>::FPR128RegClassID].RegsBegin[RegNo];
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression"
" (field_expression"
' ((_) @operand (#match? @operand ".+MCRegisterClasses.*"))'
' ((field_identifier) @field_id (#eq? @field_id "getRegister"))'
" )"
" (argument_list) @arg_list"
") @get_reg_from_class"
)
return q
def get_main_capture_name(self) -> str:
return "get_reg_from_class"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Table
table: Node = get_capture_node(captures, "operand")
# args
getter_args = get_capture_node(captures, "arg_list")
tbl = get_text(src, table.start_byte, table.end_byte)
args = get_text(src, getter_args.start_byte, getter_args.end_byte)
res = tbl + b".RegsBegin" + args.replace(b"(", b"[").replace(b")", b"]")
return res
@@ -0,0 +1,41 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from autosync.cpptranslator.patches.Patch import Patch
class GetSubReg(Patch):
"""
Patch MRI.getSubReg(...);
to MCRegisterInfo_getSubReg(MI->MRI, ...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (field_expression ("
" (identifier)"
' ((field_identifier) @field_id (#eq? @field_id "getSubReg")))'
" )"
" (argument_list) @arg_list"
") @get_sub_reg"
)
def get_main_capture_name(self) -> str:
return "get_sub_reg"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get arg list
op_create_args: Node = captures[2][0]
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)
return b"MCRegisterInfo_getSubReg(" + mcinst_var_name + b"->MRI, " + args + b")"
@@ -0,0 +1,234 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re
from tree_sitter import Node
from autosync.Helper import fail_exit
def get_function_params_of_node(n: Node) -> Node:
"""
Returns for a given node the parameters of the function this node is a children from.
Or None if the node is not part of a function definition.
"""
fcn_def: Node = n
while fcn_def.type != "function_definition":
if fcn_def.parent == None:
# root node reached
return None
fcn_def = fcn_def.parent
# Get parameter list of the function definition
param_list: Node = None
for child in fcn_def.children:
if child.type == "function_declarator":
param_list = child.children[1]
break
if not param_list:
log.warning(f"Could not find the functions parameter list for {n.text}")
return param_list
def get_MCInst_var_name(src: bytes, n: Node) -> bytes:
"""Searches for the name of the parameter of type MCInst and returns it."""
params = get_function_params_of_node(n)
mcinst_var_name = b""
if params:
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]
break
if mcinst_var_name == b"":
log.debug("Could not find `MCInst` variable name. Defaulting to `Inst`.")
mcinst_var_name = b"Inst"
return mcinst_var_name
def template_param_list_to_dict(param_list: Node) -> [dict]:
if param_list.type != "template_parameter_list":
log.fatal(
f"Wrong node type '{param_list.type}'. Not 'template_parameter_list'."
)
exit(1)
pl = list()
for c in param_list.named_children:
if c.type == "type_parameter_declaration":
type_decl = {
"prim_type": False,
"type": "",
"identifier": c.children[1].text,
}
pl.append(type_decl)
else:
pl.append(parameter_declaration_to_dict(c))
return pl
def parameter_declaration_to_dict(param_decl: Node) -> dict:
if param_decl.type != "parameter_declaration":
log.fatal(
f"Wrong node type '{param_decl.type}'. Should be 'parameter_declaration'."
)
exit(1)
return {
"prim_type": param_decl.children[0].type == "primitive_type",
"type": param_decl.children[0].text,
"identifier": param_decl.children[1].text,
}
def get_text(src: bytes, start_byte: int, end_byte: int) -> bytes:
"""Workaround for https://github.com/tree-sitter/py-tree-sitter/issues/122"""
return src[start_byte:end_byte]
def get_text_from_node(src: bytes, node: Node) -> bytes:
return src[node.start_byte : node.end_byte]
def namespace_enum(src: bytes, ns_id: bytes, enum: Node) -> bytes:
"""
Alters an enum in the way that it prepends the namespace id to every enum member.
And defines it as a type.
Example: naemspace_id = "ARM"
enum { X } -> typedef enum { ARM_X } ARM_enum
"""
enumerator_list: Node = None
type_id: Node = None
primary_tid_set = False
for c in enum.named_children:
if c.type == "enumerator_list":
enumerator_list = c
elif c.type == "type_identifier" and not primary_tid_set:
type_id = c
primary_tid_set = True
if not enumerator_list and not type_id:
log.fatal("Could not find enumerator_list or enum type_identifier.")
exit(1)
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)
for e in enumerator_list.named_children:
if e.type == "enumerator":
enum_entry_text = get_text(src, e.start_byte, e.end_byte)
elist = elist.replace(enum_entry_text, ns_id + b"_" + enum_entry_text)
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
def namespace_fcn_def(src: bytes, ns_id: bytes, fcn_def: Node) -> bytes:
fcn_id: Node = None
for c in fcn_def.named_children:
if c.type == "function_declarator":
fcn_id = c.named_children[0]
break
elif c.named_children and c.named_children[0].type == "function_declarator":
fcn_id = c.named_children[0].named_children[0]
break
if not fcn_id:
# Not a function declaration
return get_text(src, fcn_def.start_byte, fcn_def.end_byte)
fcn_id_text = get_text(src, fcn_id.start_byte, fcn_id.end_byte)
fcn_def_text = get_text(src, fcn_def.start_byte, fcn_def.end_byte)
res = re.sub(fcn_id_text, ns_id + b"_" + fcn_id_text, fcn_def_text)
return res
def namespace_struct(src: bytes, ns_id: bytes, struct: Node) -> bytes:
"""
Defines a struct as a type.
Example: naemspace_id = "ARM"
struct id { X } -> typedef struct { } ARM_id
"""
type_id: Node = None
field_list: Node = None
for c in struct.named_children:
if c.type == "type_identifier":
type_id = c
elif c.type == "base_class_clause":
# Inheritances should be fixed manually.
return get_text(src, struct.start_byte, struct.end_byte)
elif c.type == "field_declaration_list":
field_list = c
if not (type_id and field_list):
log.fatal("Could not find struct type_identifier or field declaration list.")
exit(1)
tid = get_text(src, type_id.start_byte, type_id.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
)
return typed_struct
def parse_function_capture(
capture: list[tuple[Node, str]], src: bytes
) -> tuple[list[bytes], bytes, bytes, bytes, bytes, bytes]:
"""
Parses the capture of a (template) function definition or declaration and returns the byte strings
for each node in the following order:
list[template_args], storage_class_identifiers, return_type_id, function_name, function_params, compound_stmt
If any of those is not present it returns an empty byte string for this position.
"""
temp_args = b""
st_class_ids = b""
ret_type = b""
func_name = b""
func_params = b""
comp_stmt = b""
for node, node_name in capture:
t = get_text(src, node.start_byte, node.end_byte)
match node.type:
case "template_declaration":
continue
case "template_parameter_list":
temp_args += t if not temp_args else b" " + t
case "storage_class_specifier":
st_class_ids += b" " + t
case "type_identifier" | "primitive_type":
ret_type += b" " + t
case "identifier":
func_name += t if not func_name else b" " + t
case "parameter_list":
func_params += t if not func_params else b" " + t
case "compound_statement":
comp_stmt += t if not comp_stmt else b" " + t
case _:
raise NotImplementedError(f"Node type {node.type} not handled.")
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,
)
def get_capture_node(captures: [(Node, str)], name: str) -> Node:
"""
Returns the captured node with the given name.
"""
for c in captures:
if c[1] == name:
return c[0]
fail_exit(f'Capture "{name}" is not in captures:\n{captures}')
@@ -0,0 +1,536 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class Includes(Patch):
"""
Patch LLVM includes
to Capstone includes
"""
include_count = dict()
def __init__(self, priority: int, arch: str):
self.arch = arch
super().__init__(priority)
def get_search_pattern(self) -> str:
return "(preproc_include) @preproc_include"
def get_main_capture_name(self) -> str:
return "preproc_include"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
filename = kwargs["filename"]
if filename not in self.include_count:
self.include_count[filename] = 1
else:
self.include_count[filename] += 1
include_text = get_text(src, captures[0][0].start_byte, captures[0][0].end_byte)
# Special cases, which appear somewhere in the code.
if b"GenDisassemblerTables.inc" in include_text:
return (
b'#include "'
+ bytes(self.arch, "utf8")
+ b'GenDisassemblerTables.inc"\n\n'
)
elif b"GenAsmWriter.inc" in include_text:
return b'#include "' + bytes(self.arch, "utf8") + b'GenAsmWriter.inc"\n\n'
elif b"GenSystemOperands.inc" in include_text:
return (
b'#include "' + bytes(self.arch, "utf8") + b'GenSystemOperands.inc"\n\n'
)
if self.include_count[filename] > 1:
# Only the first include is replaced with all CS includes.
return b""
# All includes which belong to the source files top.
res = get_general_inc()
match self.arch:
case "ARM":
return res + get_ARM_includes(filename) + get_general_macros()
case "PPC":
return res + get_PPC_includes(filename) + get_general_macros()
case "AArch64":
return res + get_AArch64_includes(filename) + get_general_macros()
case "LoongArch":
return res + get_LoongArch_includes(filename) + get_general_macros()
case "Mips":
return res + get_Mips_includes(filename) + get_general_macros()
case "SystemZ":
return res + get_SystemZ_includes(filename) + get_general_macros()
case "Xtensa":
return res + get_Xtensa_includes(filename) + get_general_macros()
case "ARC":
return res + get_ARC_includes(filename) + get_general_macros()
case "Sparc":
return res + get_sparc_includes(filename) + get_general_macros()
case "RISCV":
return res + get_RISCV_includes(filename) + get_general_macros()
case "TEST_ARCH":
return res + b"test_output"
case _:
log.fatal(f"Includes of {self.arch} not handled.")
exit(1)
def get_general_inc() -> bytes:
return (
b"#include <stdio.h>\n"
+ b"#include <string.h>\n"
+ b"#include <stdlib.h>\n"
+ b"#include <capstone/platform.h>\n\n"
)
def get_PPC_includes(filename: str) -> bytes:
match filename:
case "PPCDisassembler.cpp":
return (
b'#include "../../LEB128.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstrDesc.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "../../utils.h"\n'
+ b'#include "PPCLinkage.h"\n'
+ b'#include "PPCMapping.h"\n'
+ b'#include "PPCMCTargetDesc.h"\n'
+ b'#include "PPCPredicates.h"\n\n'
)
case "PPCInstPrinter.cpp":
return (
b'#include "../../LEB128.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstrDesc.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "PPCInstrInfo.h"\n'
+ b'#include "PPCInstPrinter.h"\n'
+ b'#include "PPCLinkage.h"\n'
+ b'#include "PPCMCTargetDesc.h"\n'
+ b'#include "PPCMapping.h"\n'
+ b'#include "PPCPredicates.h"\n\n'
+ b'#include "PPCRegisterInfo.h"\n\n'
)
case "PPCInstPrinter.h":
return (
b'#include "../../LEB128.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstrDesc.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "PPCMCTargetDesc.h"\n\n'
)
case "PPCMCTargetDesc.h":
return (
b'#include "../../LEB128.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstrDesc.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
)
log.fatal(f"No includes given for PPC source file: {filename}")
exit(1)
def get_ARM_includes(filename: str) -> bytes:
match filename:
case "ARMDisassembler.cpp":
return (
b'#include "../../LEB128.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstrDesc.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../cs_priv.h"\n'
+ b'#include "../../utils.h"\n'
+ b'#include "ARMAddressingModes.h"\n'
+ b'#include "ARMBaseInfo.h"\n'
+ b'#include "ARMDisassemblerExtension.h"\n'
+ b'#include "ARMInstPrinter.h"\n'
+ b'#include "ARMLinkage.h"\n'
+ b'#include "ARMMapping.h"\n\n'
+ b"#define GET_INSTRINFO_MC_DESC\n"
+ b'#include "ARMGenInstrInfo.inc"\n\n'
)
case "ARMInstPrinter.cpp":
return (
b'#include "../../Mapping.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "../../utils.h"\n'
+ b'#include "ARMAddressingModes.h"\n'
+ b'#include "ARMBaseInfo.h"\n'
+ b'#include "ARMDisassemblerExtension.h"\n'
+ b'#include "ARMInstPrinter.h"\n'
+ b'#include "ARMLinkage.h"\n'
+ b'#include "ARMMapping.h"\n\n'
+ b"#define GET_BANKEDREG_IMPL\n"
+ b'#include "ARMGenSystemRegister.inc"\n'
)
case "ARMInstPrinter.h":
return (
b'#include "ARMMapping.h"\n\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../utils.h"\n\n'
)
case "ARMBaseInfo.cpp":
return b'#include "ARMBaseInfo.h"\n\n'
case "ARMAddressingModes.h":
return b"#include <assert.h>\n" + b'#include "../../MathExtras.h"\n\n'
log.fatal(f"No includes given for ARM source file: {filename}")
exit(1)
def get_AArch64_includes(filename: str) -> bytes:
match filename:
case "AArch64Disassembler.cpp":
return (
b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstrDesc.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../LEB128.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../cs_priv.h"\n'
+ b'#include "../../utils.h"\n'
+ b'#include "AArch64AddressingModes.h"\n'
+ b'#include "AArch64BaseInfo.h"\n'
+ b'#include "AArch64DisassemblerExtension.h"\n'
+ b'#include "AArch64Linkage.h"\n'
+ b'#include "AArch64Mapping.h"\n\n'
+ b"#define GET_INSTRINFO_MC_DESC\n"
+ b'#include "AArch64GenInstrInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "AArch64GenInstrInfo.inc"\n\n'
)
case "AArch64InstPrinter.cpp":
return (
b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "../../utils.h"\n'
+ b'#include "AArch64AddressingModes.h"\n'
+ b'#include "AArch64BaseInfo.h"\n'
+ b'#include "AArch64DisassemblerExtension.h"\n'
+ b'#include "AArch64InstPrinter.h"\n'
+ b'#include "AArch64Linkage.h"\n'
+ b'#include "AArch64Mapping.h"\n\n'
+ b"#define GET_BANKEDREG_IMPL\n"
+ b'#include "AArch64GenSystemOperands.inc"\n\n'
+ b"#define CONCATs(a, b) CONCATS(a, b)\n"
+ b"#define CONCATS(a, b) a##b\n\n"
)
case "AArch64InstPrinter.h":
return (
b'#include "AArch64Mapping.h"\n\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "../../utils.h"\n\n'
)
case "AArch64BaseInfo.cpp":
return b'#include "AArch64BaseInfo.h"\n\n'
case "AArch64BaseInfo.h":
return (
b'#include "../../utils.h"\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "AArch64GenRegisterInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "AArch64GenInstrInfo.inc"\n\n'
)
case "AArch64AddressingModes.h":
return b"#include <assert.h>\n" + b'#include "../../MathExtras.h"\n\n'
log.fatal(f"No includes given for AArch64 source file: {filename}")
exit(1)
def get_LoongArch_includes(filename: str) -> bytes:
match filename:
case "LoongArchDisassembler.cpp":
return (
b'#include "../../MCInst.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "../../cs_priv.h"\n'
+ b'#include "../../utils.h"\n'
+ b'#include "LoongArchDisassemblerExtension.h"\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "LoongArchGenSubtargetInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "LoongArchGenInstrInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "LoongArchGenRegisterInfo.inc"\n\n'
)
case "LoongArchInstPrinter.cpp":
return (
b'#include "LoongArchMapping.h"\n'
+ b'#include "LoongArchInstPrinter.h"\n\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "LoongArchGenSubtargetInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "LoongArchGenInstrInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "LoongArchGenRegisterInfo.inc"\n\n'
)
case "LoongArchInstPrinter.h":
return (
b'#include "../../MCInstPrinter.h"\n' + b'#include "../../cs_priv.h"\n'
)
log.fatal(f"No includes given for LoongArch source file: {filename}")
exit(1)
def get_Mips_includes(filename: str) -> bytes:
match filename:
case "MipsDisassembler.cpp":
return (
b'#include "../../MCInst.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "../../cs_priv.h"\n'
+ b'#include "../../utils.h"\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "MipsGenSubtargetInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "MipsGenInstrInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "MipsGenRegisterInfo.inc"\n\n'
)
case "MipsInstPrinter.cpp":
return (
b'#include "MipsMapping.h"\n'
+ b'#include "MipsInstPrinter.h"\n\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "MipsGenSubtargetInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "MipsGenInstrInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "MipsGenRegisterInfo.inc"\n\n'
)
case "MipsInstPrinter.h":
return (
b'#include "../../MCInstPrinter.h"\n' + b'#include "../../cs_priv.h"\n'
)
log.fatal(f"No includes given for Mips source file: {filename}")
exit(1)
def get_SystemZ_includes(filename: str) -> bytes:
match filename:
case "SystemZDisassembler.cpp":
return (
b'#include "../../MCInst.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../cs_priv.h"\n'
+ b'#include "../../utils.h"\n\n'
+ b'#include "SystemZMCTargetDesc.h"\n'
+ b'#include "SystemZDisassemblerExtension.h"\n\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "SystemZGenSubtargetInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "SystemZGenInstrInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "SystemZGenRegisterInfo.inc"\n\n'
)
case "SystemZInstPrinter.cpp":
return (
b'#include "../../MCAsmInfo.h"\n'
+ b'#include "SystemZMapping.h"\n'
+ b'#include "SystemZInstPrinter.h"\n\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "SystemZGenSubtargetInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "SystemZGenInstrInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "SystemZGenRegisterInfo.inc"\n\n'
)
case "SystemZInstPrinter.h":
return b"\n"
case "SystemZMCTargetDesc.h":
return (
b'#include "../../MCInstPrinter.h"\n' + b'#include "../../cs_priv.h"\n'
)
case "SystemZMCTargetDesc.cpp":
return (
b'#include "../../MCInst.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n\n'
+ b'#include "SystemZMCTargetDesc.h"\n'
+ b'#include "SystemZInstPrinter.h"\n\n'
+ b"#define GET_INSTRINFO_MC_DESC\n"
+ b"#define ENABLE_INSTR_PREDICATE_VERIFIER\n"
+ b'#include "SystemZGenInstrInfo.inc"\n\n'
+ b"#define GET_SUBTARGETINFO_MC_DESC\n"
+ b'#include "SystemZGenSubtargetInfo.inc"\n\n'
+ b"#define GET_REGINFO_MC_DESC\n"
+ b'#include "SystemZGenRegisterInfo.inc"\n\n'
)
log.fatal(f"No includes given for SystemZ source file: {filename}")
exit(1)
def get_Xtensa_includes(filename: str) -> bytes:
match filename:
case "XtensaDisassembler.cpp":
return b"""
#include "../../MathExtras.h"
#include "../../MCDisassembler.h"
#include "../../MCFixedLenDisassembler.h"
#include "../../SStream.h"
#include "../../cs_priv.h"
#include "priv.h"
#define GET_INSTRINFO_MC_DESC
#include "XtensaGenInstrInfo.inc"
"""
case "XtensaInstPrinter.cpp":
return b"""
#include "../../MCInstPrinter.h"
#include "../../SStream.h"
#include "XtensaMapping.h"
#include "priv.h"
"""
case _:
return b""
def get_ARC_includes(filename: str) -> bytes:
match filename:
case "ARCDisassembler.cpp":
return (
b'#include "../../MCInst.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../utils.h"\n'
)
case "ARCInstPrinter.cpp":
return (
b'#include "../../SStream.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "ARCInfo.h"\n'
+ b'#include "ARCInstPrinter.h"\n'
+ b'#include "ARCLinkage.h"\n'
+ b'#include "ARCMapping.h"\n'
)
case "ARCInstPrinter.h":
return b'#include "../../SStream.h"\n' + b'#include "../../MCInst.h"\n'
log.fatal(f"No includes given for ARC source file: {filename}")
exit(1)
def get_sparc_includes(filename: str) -> bytes:
match filename:
case "SparcDisassembler.cpp":
return (
b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "SparcDisassemblerExtension.h"\n'
+ b'#include "SparcLinkage.h"\n'
+ b'#include "SparcMapping.h"\n'
+ b'#include "SparcMCTargetDesc.h"\n'
)
case "SparcInstPrinter.cpp":
return (
b'#include "SparcInstrInfo.h"\n'
+ b'#include "SparcInstPrinter.h"\n'
+ b'#include "SparcLinkage.h"\n'
+ b'#include "SparcMCTargetDesc.h"\n'
+ b'#include "SparcMapping.h"\n'
+ b'#include "SparcRegisterInfo.h"\n\n'
)
case "SparcInstPrinter.h":
return b'#include "SparcMCTargetDesc.h"\n\n'
case "SparcMCTargetDesc.h":
return (
b'#include "../../LEB128.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../MCInst.h"\n'
+ b'#include "../../MCInstrDesc.h"\n'
+ b'#include "../../MCRegisterInfo.h"\n'
)
log.fatal(f"No includes given for Sparc source file: {filename}")
exit(1)
def get_RISCV_includes(filename: str) -> bytes:
match filename:
case "RISCVDisassembler.cpp":
return (
b'#include "../../MCInst.h"\n'
+ b'#include "../../MathExtras.h"\n'
+ b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../MCDisassembler.h"\n'
+ b'#include "../../MCFixedLenDisassembler.h"\n'
+ b'#include "../../cs_priv.h"\n'
+ b'#include "../../utils.h"\n'
+ b'#include "RISCVDisassemblerExtension.h"\n'
+ b'#include "RISCVBaseInfo.h"\n\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "RISCVGenSubtargetInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "RISCVGenRegisterInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b"#define GET_INSTRINFO_MC_DESC\n"
+ b'#include "RISCVGenInstrInfo.inc"\n\n'
)
case "RISCVInstPrinter.cpp":
return (
b'#include "RISCVMapping.h"\n'
+ b'#include "RISCVInstPrinter.h"\n\n'
+ b"#define GET_SUBTARGETINFO_ENUM\n"
+ b'#include "RISCVGenSubtargetInfo.inc"\n\n'
+ b"#define GET_INSTRINFO_ENUM\n"
+ b'#include "RISCVGenInstrInfo.inc"\n\n'
+ b"#define GET_REGINFO_ENUM\n"
+ b'#include "RISCVGenRegisterInfo.inc"\n\n'
)
case "RISCVInstPrinter.h":
return (
b'#include "../../MCInstPrinter.h"\n'
+ b'#include "../../cs_priv.h"\n'
+ b'#include "../../SStream.h"\n'
+ b'#include "RISCVBaseInfo.h"\n'
)
case "RISCVBaseInfo.h":
return b'#include "../../utils.h"\n'
case "RISCVBaseInfo.cpp":
return b'#include "RISCVBaseInfo.h"\n\n'
log.fatal(f"No includes given for RISCV source file: {filename}")
exit(1)
def get_general_macros():
return (
b"#define CONCAT(a, b) CONCAT_(a, b)\n" b"#define CONCAT_(a, b) a ## _ ## b\n"
)
@@ -0,0 +1,37 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class InlineToStaticInline(Patch):
"""
Removes the qualified identifier of the class from method definitions.
Translating them to functions.
Patch inline void FUNCTION(...) {...}
to static inline void FUNCTION(...) {...}
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(function_definition"
' ((storage_class_specifier) @scs (#eq? @scs "inline"))'
" (_)+"
") @inline_def"
)
def get_main_capture_name(self) -> str:
return "inline_def"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
inline_def = captures[0][0]
inline_def = get_text(src, inline_def.start_byte, inline_def.end_byte)
return b"static " + inline_def
@@ -0,0 +1,40 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class IsOptionalDef(Patch):
"""
Patch OpInfo[i].isOptionalDef()
to MCOperandInfo_isOptionalDef(&OpInfo[i])
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (field_expression"
" (subscript_expression"
" ((identifier) @op_info_var)"
" ((_) @index)"
" )"
' ((field_identifier) @fid (#eq? @fid "isOptionalDef"))'
" )"
") @is_optional_def"
)
def get_main_capture_name(self) -> str:
return "is_optional_def"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
op_info_var = captures[1][0]
index = captures[2][0]
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)
return b"MCOperandInfo_isOptionalDef(&" + op_info_var + index + b")"
@@ -0,0 +1,40 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class IsPredicate(Patch):
"""
Patch OpInfo[i].isPredicate()
to MCOperandInfo_isPredicate(&OpInfo[i])
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (field_expression"
" (subscript_expression"
" ((identifier) @op_info_var)"
" ((_) @index)"
" )"
' ((field_identifier) @fid (#eq? @fid "isPredicate"))'
" )"
") @is_predicate"
)
def get_main_capture_name(self) -> str:
return "is_predicate"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
op_info_var = captures[1][0]
index = captures[2][0]
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)
return b"MCOperandInfo_isPredicate(&" + op_info_var + index + b")"
@@ -0,0 +1,44 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class IsOperandRegImm(Patch):
"""
Patch OPERAND.isReg()
to MCOperand_isReg(OPERAND)
Same for isImm() | isExpr()
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression"
" (field_expression"
" ((_) @operand)"
' ((field_identifier) @field_id (#match? @field_id "is(Reg|Imm|Expr)"))'
" )"
" (argument_list)"
") @is_operand"
)
return q
def get_main_capture_name(self) -> str:
return "is_operand"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# The operand
operand: Node = captures[1][0]
# 'isReg()/isImm()/isExpr'
get_reg_imm = captures[2][0]
fcn = get_text(src, get_reg_imm.start_byte, get_reg_imm.end_byte)
op = get_text(src, operand.start_byte, operand.end_byte)
return b"MCOperand_" + fcn + b"(" + op + b")"
@@ -0,0 +1,28 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class LLVMFallThrough(Patch):
"""
Patch Remove LLVM_FALLTHROUGH
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(expression_statement"
' ((identifier) @id (#eq? @id "LLVM_FALLTHROUGH"))'
") @llvm_fall_through"
)
def get_main_capture_name(self) -> str:
return "llvm_fall_through"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b""
@@ -0,0 +1,36 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# Copyright © 2024 Billow <billow.fun@gmail.com>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class LLVM_DEBUG(Patch):
"""
Patch LLVM_DEBUG(dbgs() << "Error msg")
to ""
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return """
(call_expression (
(identifier) @fcn_name (#eq? @fcn_name "LLVM_DEBUG")
(argument_list (
(binary_expression (
(call_expression)
(string_literal) @err_msg
))
))
)) @llvm_debug"""
def get_main_capture_name(self) -> str:
return "llvm_debug"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b""
@@ -0,0 +1,34 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class LLVMUnreachable(Patch):
"""
Patch llvm_unreachable("Error msg")
to assert(0 && "Error msg")
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression ("
' (identifier) @fcn_name (#eq? @fcn_name "llvm_unreachable")'
" (argument_list) @err_msg"
")) @llvm_unreachable"
)
def get_main_capture_name(self) -> str:
return "llvm_unreachable"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
err_msg = captures[2][0]
err_msg = get_text(src, err_msg.start_byte, err_msg.end_byte).strip(b"()")
res = b"CS_ASSERT(0 && " + err_msg + b")"
return res
@@ -0,0 +1,45 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class MethodToFunction(Patch):
"""
Removes the qualified identifier of the class from method definitions.
Translating them to functions.
Patch void CLASS::METHOD_NAME(...) {...}
to void METHOD_NAME(...) {...}
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(function_declarator"
" (qualified_identifier"
" (namespace_identifier)"
" (identifier) @method_name"
" )"
" (parameter_list) @param_list"
") @method_def"
)
def get_main_capture_name(self) -> str:
return "method_def"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
name = captures[1][0]
parameter_list = captures[2][0]
name = get_text(src, name.start_byte, name.end_byte)
parameter_list = get_text(
src, parameter_list.start_byte, parameter_list.end_byte
)
res = name + parameter_list
return res
@@ -0,0 +1,40 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class MethodTypeQualifier(Patch):
"""
Patch Removes type qualifiers like "const" etc. from methods.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(function_declarator"
" (["
" (qualified_identifier)"
" (identifier)"
" ]) @id"
" (parameter_list) @param_list"
" (type_qualifier)"
")"
"@method_type_qualifier"
)
def get_main_capture_name(self) -> str:
return "method_type_qualifier"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
identifier = captures[1][0]
parameter_list = captures[2][0]
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
p_list = get_text(src, parameter_list.start_byte, parameter_list.end_byte)
res = identifier + p_list
return res
@@ -0,0 +1,34 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class NamespaceAnon(Patch):
"""
Patch namespace {CONTENT}
to CONTENT
Only for anonymous or llvm namespaces
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(namespace_definition"
" (declaration_list) @decl_list"
") @namespace_def"
)
def get_main_capture_name(self) -> str:
return "namespace_def"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
decl_list = captures[1][0]
dl = get_text(src, decl_list.start_byte, decl_list.end_byte).strip(b"{}")
return dl
@@ -0,0 +1,67 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import (
get_text,
namespace_enum,
namespace_fcn_def,
namespace_struct,
)
from autosync.cpptranslator.patches.Patch import Patch
class NamespaceArch(Patch):
"""
Patch namespace ArchSpecificNamespace {CONTENT}
to CONTENT
Patches namespaces specific to architecture. This needs to patch enums and functions within this namespace.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(namespace_definition"
" (namespace_identifier)"
" (declaration_list) @decl_list"
") @namespace_def"
)
def get_main_capture_name(self) -> str:
return "namespace_def"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
namespace = captures[0][0]
decl_list = captures[1][0]
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.
# Because in the generated files they are accessed via NAMESPACE::X which becomes NAMESPACE_X.
res = b""
for d in decl_list.named_children:
match d.type:
case "enum_specifier":
res += namespace_enum(src, namespace_id, d) + b";\n\n"
case "declaration" | "function_definition":
res += namespace_fcn_def(src, namespace_id, d) + b"\n\n"
case "struct_specifier":
res += namespace_struct(src, namespace_id, d) + b";\n\n"
case _:
res += get_text(src, d.start_byte, d.end_byte) + b"\n"
return (
b"// CS namespace begin: "
+ namespace_id
+ b"\n\n"
+ res
+ b"// CS namespace end: "
+ namespace_id
+ b"\n\n"
)
@@ -0,0 +1,35 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class NamespaceLLVM(Patch):
"""
Patch namespace llvm {CONTENT}
to CONTENT
Only for anonymous or llvm namespaces
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(namespace_definition"
' (namespace_identifier) @id (#eq? @id "llvm")'
" (declaration_list) @decl_list"
") @namespace_def"
)
def get_main_capture_name(self) -> str:
return "namespace_def"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
decl_list = captures[2][0]
dl = get_text(src, decl_list.start_byte, decl_list.end_byte).strip(b"{}")
return dl
@@ -0,0 +1,44 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class OutStreamParam(Patch):
"""
Patches the parameter list only:
Patch void function(int a, raw_ostream &OS)
to void function(int a, SStream *OS)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(parameter_list"
" (_)*"
" (parameter_declaration"
' ((type_identifier) @tid (#eq? @tid "raw_ostream"))'
" (_)"
" )"
" (_)*"
") @ostream_param"
)
def get_main_capture_name(self) -> str:
return "ostream_param"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
param_list = list()
for param in captures[0][0].named_children:
p_text = get_text(src, param.start_byte, param.end_byte)
if b"raw_ostream" in p_text:
p_text = p_text.replace(b"raw_ostream", b"SStream").replace(b"&", b"*")
param_list.append(p_text)
res = b"(" + b", ".join(param_list) + b")"
return res
@@ -0,0 +1,36 @@
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class Override(Patch):
"""
Patch function(args) override
to function(args)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(function_declarator "
" ((field_identifier) @declarator)"
" ((parameter_list) @parameter_list)"
' ((virtual_specifier) @specifier (#eq? @specifier "override"))'
") @override"
)
return q
def get_main_capture_name(self) -> str:
return "override"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get function name
declarator: Node = captures[1][0]
# Get parameter list
parameter_list: Node = captures[2][0]
decl = get_text(src, declarator.start_byte, declarator.end_byte)
params = get_text(src, parameter_list.start_byte, parameter_list.end_byte)
return decl + params
@@ -0,0 +1,46 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
from tree_sitter import Node
class Patch:
priority: int = None
def __init__(self, priority: int = 0):
self.priority = priority
def get_search_pattern(self) -> str:
"""
Returns a search pattern for the syntax tree of the C++ file.
The search pattern must be formed according to:
https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries
Also, each pattern needs to be assigned a name in order to work.
See: https://github.com/tree-sitter/py-tree-sitter/issues/77
:return: The search pattern which matches a part in the syntax tree which will be patched.
"""
log.fatal("Method must be overloaded.")
exit(1)
def get_main_capture_name(self) -> str:
"""
:return: The name of the capture which matches the complete syntax to be patched.
"""
log.fatal("Method must be overloaded.")
exit(1)
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
"""
Patches the given subtree accordingly and returns the patch as string.
:param src: The source code currently patched.
:param captures: The subtree and its name which needs to be patched.
:param **kwargs: Additional arguments the Patch might need.
:return: The patched version of the code.
"""
log.fatal("Method must be overloaded.")
exit(1)
@@ -0,0 +1,48 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from autosync.cpptranslator.patches.Patch import Patch
class PredicateBlockFunctions(Patch):
"""
Patch VPTBlock.instrInVPTBlock()
to VPTBlock_instrInVPTBlock(&(MI->csh->VPTBlock))
And other functions of VPTBlock and ITBlock
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression "
" (field_expression"
' ((identifier) @block_var (#match? @block_var "[VI][PT]T?Block"))'
" ((field_identifier) @field_id)"
" )"
" ((argument_list) @arg_list)"
") @block_fcns"
)
def get_main_capture_name(self) -> str:
return "block_fcns"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
block_var = captures[1][0]
fcn_id = captures[2][0]
args = captures[3][0]
block_var_text = get_text(src, block_var.start_byte, block_var.end_byte)
fcn_id_text = get_text(src, fcn_id.start_byte, fcn_id.end_byte)
args_text = get_text(src, args.start_byte, args.end_byte)
mcinst_var: bytes = get_MCInst_var_name(src, block_var)
a = b"&(" + mcinst_var + b"->csh->" + block_var_text + b")"
args_text = args_text.strip(b"()")
if args_text:
a += b"," + args_text
return block_var_text + b"_" + fcn_id_text + b"(" + a + b")"
@@ -0,0 +1,29 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class PrintAnnotation(Patch):
"""
Removes printAnnotation(...) calls.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression ("
' (identifier) @fcn_name (#eq? @fcn_name "printAnnotation")'
" (argument_list)"
")) @print_annotation"
)
def get_main_capture_name(self) -> str:
return "print_annotation"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b""
@@ -0,0 +1,36 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
from autosync.cpptranslator.patches.Patch import Patch
class PrintRegImmShift(Patch):
"""
Patch printRegImmShift(...)
to printRegImmShift(MI, ...)
"""
def __init__(self, priority: int):
super().__init__(priority)
self.apply_only_to = {"files": ["ARMInstPrinter.cpp"], "archs": list()}
def get_search_pattern(self) -> str:
return (
"(call_expression ("
' (identifier) @fcn_name (#eq? @fcn_name "printRegImmShift")'
" ((argument_list) @arg_list)"
")) @print_call"
)
def get_main_capture_name(self) -> str:
return "print_call"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
call: Node = captures[0][0]
mcinst_var = get_MCInst_var_name(src, call)
params = captures[2][0]
params = get_text(src, params.start_byte, params.end_byte)
return b"printRegImmShift(" + mcinst_var + b", " + params.strip(b"(")
@@ -0,0 +1,40 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class QualifiedIdentifier(Patch):
"""
Patch NAMESPACE::ID
to NAMESPACE_ID
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return "(qualified_identifier) @qualified_id"
def get_main_capture_name(self) -> str:
return "qualified_id"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
if len(captures[0][0].named_children) > 1:
identifier = captures[0][0].named_children[1]
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
namespace = captures[0][0].named_children[0]
namespace = get_text(src, namespace.start_byte, namespace.end_byte)
else:
# The namespace can be omitted. E.g. std::transform(..., ::tolower)
namespace = b""
identifier = captures[0][0].named_children[0]
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
match (namespace, identifier):
case (b"std", b"size"):
return b"sizeof"
case _:
return namespace + b"_" + identifier
@@ -0,0 +1,39 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class ReferencesDecl(Patch):
"""
Patch TYPE &Param
to TYPE *Param
Param is optional
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"["
"(reference_declarator)"
"(type_identifier) (abstract_reference_declarator)"
"] @reference_decl"
)
def get_main_capture_name(self) -> str:
return "reference_decl"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
ref_decl: Node = captures[0][0]
ref_decl_text = get_text(src, ref_decl.start_byte, ref_decl.end_byte)
res = re.sub(rb"&", b"*", ref_decl_text)
return res
@@ -0,0 +1,42 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
from autosync.cpptranslator.patches.Patch import Patch
class RegClassContains(Patch):
"""
Patch ...getRegClass(...).contains(Reg)
to MCRegisterClass_contains(...getRegClass(...), Reg)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression"
" (field_expression"
' ((_) @reg_class (#match? @reg_class ".+getRegClass.+"))'
' ((field_identifier) @field_id (#eq? @field_id "contains"))'
" )"
" ((argument_list) @arg_list)"
") @reg_class_contains"
)
return q
def get_main_capture_name(self) -> str:
return "reg_class_contains"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
reg_class_getter: Node = get_capture_node(captures, "reg_class")
arg_list: Node = get_capture_node(captures, "arg_list")
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
)
res = b"MCRegisterClass_contains(" + reg_class + b", " + args + b")"
return res
@@ -0,0 +1,33 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class STIArgument(Patch):
"""
Patch printSomeOperand(MI, NUM, STI, NUM)
to printSomeOperand(MI, NUM, NUM)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return '(argument_list (_) (_) (_)? ((identifier) @id (#eq? @id "STI")) (_)) @sti_arg'
def get_main_capture_name(self) -> str:
return "sti_arg"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
param_list = list()
for param in captures[0][0].named_children:
p_text = get_text(src, param.start_byte, param.end_byte)
if b"STI" in p_text:
continue
param_list.append(p_text)
res = b"(" + b", ".join(param_list) + b")"
return res
@@ -0,0 +1,42 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class STIFeatureBits(Patch):
"""
Patch STI.getFeatureBits()[ARCH::FLAG]
to ARCH_getFeatureBits(Inst->csh->mode, ARCH::FLAG)
"""
def __init__(self, priority: int, arch: bytes):
self.arch = arch
super().__init__(priority)
def get_search_pattern(self) -> str:
# Search for featureBits usage.
return (
"(subscript_expression "
" (call_expression"
" (field_expression"
" (identifier)"
' ((field_identifier) @fid (#eq? @fid "getFeatureBits"))'
" )"
" (argument_list)"
" )"
" (subscript_argument_list ((qualified_identifier) @flag))"
") @sti_feature_bits"
)
def get_main_capture_name(self) -> str:
return "sti_feature_bits"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get flag name of feature bit.
qualified_id: Node = captures[2][0]
flag = get_text(src, qualified_id.start_byte, qualified_id.end_byte)
return self.arch + b"_getFeatureBits(Inst->csh->mode, " + flag + b")"
@@ -0,0 +1,41 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class SubtargetInfoParam(Patch):
"""
Patch Removes MCSubtargetInfo &STI parameter
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(parameter_list"
" (_)*"
" (parameter_declaration"
' ((type_identifier) @tid (#eq? @tid "MCSubtargetInfo"))'
" (_)"
" )"
" (_)*"
") @subtarget_info_param"
)
def get_main_capture_name(self) -> str:
return "subtarget_info_param"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
param_list = list()
for param in captures[0][0].named_children:
p_text = get_text(src, param.start_byte, param.end_byte)
if b"MCSubtargetInfo" in p_text:
continue
param_list.append(p_text)
res = b"(" + b", ".join(param_list) + b")"
return res
@@ -0,0 +1,44 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class SetOpcode(Patch):
"""
Patch Inst.setOpcode(...)
to MCInst_setOpcode(Inst, ...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (field_expression ("
" ((identifier) @inst_var)"
' ((field_identifier) @field_id (#eq? @field_id "setOpcode")))'
" )"
" (argument_list) @arg_list"
") @set_opcode"
)
def get_main_capture_name(self) -> str:
return "set_opcode"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Instruction variable
inst_var: Node = captures[1][0]
arg_list: Node = captures[3][0]
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
args = get_text(src, arg_list.start_byte, arg_list.end_byte)
if args != b"()":
args = b", " + args
else:
args = b""
return b"MCInst_setOpcode(" + inst + args + b")"
@@ -0,0 +1,45 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
from autosync.cpptranslator.TemplateCollector import TemplateCollector
class SignExtend(Patch):
"""
Patch SignExtend32<A>(...)
to SignExtend32(..., A)
Same for SignExtend64
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (template_function"
' ((identifier) @name (#match? @name "SignExtend(32|64)"))'
" ((template_argument_list) @templ_args)"
" )"
" ((argument_list) @fcn_args)"
") @sign_extend"
)
def get_main_capture_name(self) -> str:
return "sign_extend"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
sign_extend: Node = captures[1][0]
templ_args: Node = captures[2][0]
fcn_args: Node = captures[3][0]
name = get_text(src, sign_extend.start_byte, sign_extend.end_byte)
t_args = get_text(src, templ_args.start_byte, templ_args.end_byte)
t_args = b", ".join(TemplateCollector.templ_params_to_list(t_args))
f_args = get_text(src, fcn_args.start_byte, fcn_args.end_byte)
return name + b"(" + f_args + b", " + t_args + b")"
@@ -0,0 +1,35 @@
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class Size(Patch):
"""
Patch Bytes.size()
to BytesLen
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
q = (
"(call_expression "
" (field_expression"
" ((identifier) @inst_var)"
' ((field_identifier) @field_id_op (#eq? @field_id_op "size"))'
" )"
" ((argument_list) @arg_list)"
") @size"
)
return q
def get_main_capture_name(self) -> str:
return "size"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
# Get operand variable name (Bytes, ArrayRef)
op_var: Node = captures[1][0]
op = get_text(src, op_var.start_byte, op_var.end_byte)
return op + b"Len"
@@ -0,0 +1,46 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_function_params_of_node, get_text
from autosync.cpptranslator.patches.Patch import Patch
class SizeAssignment(Patch):
"""
Patch Size = <num>
to *Size = <num>
if Size is a reference.
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(assignment_expression"
' ((identifier) @id (#eq? @id "Size"))'
") @assign"
)
def get_main_capture_name(self) -> str:
return "assign"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
assign = captures[0][0]
assign_text = get_text(src, assign.start_byte, assign.end_byte)
param_list = get_function_params_of_node(assign)
if not param_list:
return assign_text
for p in param_list.named_children:
p_text = get_text(src, p.start_byte, p.end_byte)
if b"&Size" in p_text:
return re.sub(b"Size", b"*Size", assign_text)
return assign_text
@@ -0,0 +1,145 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text, get_text_from_node
from autosync.cpptranslator.patches.Patch import Patch
class StreamOperations(Patch):
"""
Patch OS << ...
to SStream_concat(OS, ...)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(expression_statement"
" (binary_expression"
" ((binary_expression)"
' "<<"'
" (_))*"
" ) @bin_expr"
") @stream"
)
def get_main_capture_name(self) -> str:
return "stream"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
bin_expr = captures[1][0]
# Extract operands passed to the stream into a list.
ops = list()
while bin_expr.type == "binary_expression":
ops.append(bin_expr.named_children[1])
bin_expr = bin_expr.named_children[0]
s_name = get_text(src, bin_expr.start_byte, bin_expr.end_byte)
# We added the operands from right to left.
# We reversing it so the left most operand comes first.
ops.reverse()
res = b""
# Capstone uses the following functions to copy the strings to a buffer:
# SStream_concat - Copies multiple strings.
# SStream_concat1 - Copies a char.
# SStream_concat0 - Copies a string and null terminates the buffer.
last_op: Node = ops[-1]
op: Node = ops[0]
string_ops = list()
i = 0
while op != last_op:
if op.type == "char_literal":
if len(string_ops) != 0:
# Make a SStream_concat call with all string literals collected before.
res += (
b"SStream_concat("
+ s_name
+ b', "'
+ b"%s" * len(string_ops)
+ b'", '
+ b", ".join(
[
get_text(src, o.start_byte, o.end_byte)
for o in string_ops
]
)
+ b");\n"
)
string_ops.clear()
res += (
b"SStream_concat1("
+ s_name
+ b", "
+ get_text(src, op.start_byte, op.end_byte)
+ b");\n"
)
else:
string_ops.append(op)
i += 1
op = ops[i]
if len(string_ops) != 0:
res += (
b"SStream_concat("
+ s_name
+ b', "'
+ b"%s" * len(string_ops)
+ b'", '
+ b", ".join(
[get_text(src, o.start_byte, o.end_byte) for o in string_ops]
)
+ b");\n"
)
string_ops.clear()
last_op_text = get_text(src, last_op.start_byte, last_op.end_byte)
if last_op.type == "char_literal":
res += (
b"SStream_concat0("
+ s_name
+ b", "
+ last_op_text.replace(b"'", b'"')
+ b");\n"
)
elif last_op.type == "identifier":
queue_str = f"""
(declaration (
(primitive_type) @typ
(init_declarator
(identifier) @ident (#eq? @ident "{last_op_text.decode("utf8")}")
)
)) @decl
"""
query = kwargs["ts_cpp_lang"].query(queue_str)
root_node = kwargs["tree"].root_node
query_result = list(
filter(
lambda x: "typ" in x[1],
query.matches(root_node),
)
)
if len(query_result) == 0:
res += b"SStream_concat0(" + s_name + b", " + last_op_text + b");"
else:
typ = get_text_from_node(src, query_result[0][1]["typ"][-1])
match typ:
case b"int":
res += b"printInt32(" + s_name + b", " + last_op_text + b");"
case b"int64_t":
res += b"printInt64(" + s_name + b", " + last_op_text + b");"
case _:
res += (
b"SStream_concat0(" + s_name + b", " + last_op_text + b");"
)
else:
res += b"SStream_concat0(" + s_name + b", " + last_op_text + b");"
stream = captures[0][0]
if len(ops) > 1 and stream.parent.type in ["if_statement"]:
# If statements without {} brackets might execute a single line `OS << ...;` statement.
# Which we then translate into multiple lines. For this case we need to add the brackets.
res = b"{ " + res + b" }"
return res
@@ -0,0 +1,84 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import parse_function_capture
from autosync.cpptranslator.patches.Patch import Patch
from autosync.cpptranslator.TemplateCollector import (
TemplateCollector,
TemplateRefInstance,
)
class TemplateDeclaration(Patch):
"""
Patch template<A, B>
RET_TYPE TemplateFunction(...);
to #define DECLARE_TemplateFunction_A_B \
RET_TYPE CONCAT(TemplateFunction, CONCAT(A, B))(...);
"""
def __init__(self, priority: int, template_collector: TemplateCollector):
self.collector: TemplateCollector = template_collector
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(template_declaration"
" ((template_parameter_list) @templ_params)"
" (declaration"
" ((storage_class_specifier)* @storage_class_id)"
" ([(type_identifier)(primitive_type)] @type_id)"
" (function_declarator"
" ((identifier) @fcn_name)"
" ((parameter_list) @fcn_params)"
" )"
" )"
") @template_decl"
)
def get_main_capture_name(self) -> str:
return "template_decl"
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)
if f_name in self.collector.templates_with_arg_deduction:
return sc + tid + b" " + f_name + f_params + b";"
declaration = (
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"
template_instance: TemplateRefInstance
declared_implementations = list()
if f_name not in self.collector.template_refs:
self.collector.log_missing_ref_and_exit(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"
)
if d in declared_implementations:
continue
declared_implementations.append(d)
declaration += d
return declaration
@@ -0,0 +1,88 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
import re
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import parse_function_capture
from autosync.cpptranslator.patches.Patch import Patch
from autosync.cpptranslator.TemplateCollector import (
TemplateCollector,
TemplateRefInstance,
)
class TemplateDefinition(Patch):
"""
Patch template<A, B>
RET_TYPE TemplateFunction(...) {...}
to #define DEFINE_TemplateFunction_A_B \
RET_TYPE CONCAT(TemplateFunction, CONCAT(A, B))(...) {...}
"""
def __init__(self, priority: int, template_collector: TemplateCollector):
self.collector: TemplateCollector = template_collector
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(template_declaration"
" ((template_parameter_list) @templ_params)"
" (function_definition"
" ((storage_class_specifier)* @storage_class_id)"
" ([(type_identifier)(primitive_type)] @type_id)"
" (function_declarator"
" ((identifier) @fcn_name)"
" ((parameter_list) @fcn_params)"
" )"
" ((compound_statement) @compound)"
" )"
") @template_def"
)
def get_main_capture_name(self) -> str:
return "template_def"
def get_patch(
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:
return sc + tid + b" " + f_name + f_params + f_compound
definition = b"#define DEFINE_" + f_name + b"(" + b", ".join(t_params) + b")\n"
definition += (
sc
+ b" "
+ tid
+ b" "
+ TemplateCollector.get_macro_c_call(f_name, t_params, f_params)
+ f_compound
)
# Remove // comments
definition = re.sub(b" *//.*", b"", definition)
definition = definition.replace(b"\n", b" \\\n") + b"\n"
template_instance: TemplateRefInstance
declared_implementations = list()
if f_name not in self.collector.template_refs:
self.collector.log_missing_ref_and_exit(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"
)
if d in declared_implementations:
continue
declared_implementations.append(d)
definition += d
return definition
@@ -0,0 +1,57 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
import logging as log
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
class TemplateParamDecl(Patch):
"""
Example:
Patch ArrayRef<uint8_t> x
to const uint8_t *x, size_t xLen
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(parameter_declaration"
" (template_type"
" (type_identifier) @templ_type"
" (template_argument_list) @arg_list"
" )"
" (identifier) @param_id"
") @template_param_decl"
)
def get_main_capture_name(self) -> str:
return "template_param_decl"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
template_type = captures[1][0]
arg_list = captures[2][0]
param_id = captures[3][0]
templ_type = get_text(src, template_type.start_byte, template_type.end_byte)
args = get_text(src, arg_list.start_byte, arg_list.end_byte)
p_id = get_text(src, param_id.start_byte, param_id.end_byte)
if templ_type == b"ArrayRef":
res = (
b"const "
+ args.strip(b"<>")
+ b" *"
+ p_id
+ b", size_t "
+ p_id
+ b"Len"
)
return res
log.fatal(f"Template type {templ_type} not handled as parameter")
exit(1)
@@ -0,0 +1,41 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
from autosync.cpptranslator.TemplateCollector import TemplateCollector
class TemplateRefs(Patch):
"""
Patch TemplateFunction<A, B>
to CONCAT(TemplateFunction, CONCAT(A, B))
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(template_function"
" ((identifier) @name)"
" ((template_argument_list) @templ_args)"
") @template_refs"
)
def get_main_capture_name(self) -> str:
return "template_refs"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
tc: Node = captures[1][0]
templ_args: Node = captures[2][0]
name = get_text(src, tc.start_byte, tc.end_byte)
t_params = get_text(src, templ_args.start_byte, templ_args.end_byte)
if name == b"static_cast" or name == b"dyn_cast":
return t_params.replace(b"<", b"(").replace(b">", b")")
t_params_list = TemplateCollector.templ_params_to_list(t_params)
res = TemplateCollector.get_macro_c_call(name, t_params_list)
return res
@@ -0,0 +1,25 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class UseMarkup(Patch):
"""
Patch UseMarkup
to getUseMarkup()
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return '((identifier) @use_markup (#eq? @use_markup "UseMarkup"))'
def get_main_capture_name(self) -> str:
return "use_markup"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b"getUseMarkup()"
@@ -0,0 +1,24 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Patch import Patch
class UsingDeclaration(Patch):
"""
Patch Removes declarations with the keyword "using"
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return "([(using_declaration) (alias_declaration)]) @using_declaration"
def get_main_capture_name(self) -> str:
return "using_declaration"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
return b""
@@ -0,0 +1,45 @@
# Copyright © 2022 Rot127 <unisono@quyllur.org>
# SPDX-License-Identifier: BSD-3
from tree_sitter import Node
from autosync.cpptranslator.patches.Helper import get_text
from autosync.cpptranslator.patches.Patch import Patch
from autosync.cpptranslator.TemplateCollector import TemplateCollector
class IsUInt(Patch):
"""
Patch isUInt|isInt<N>(...)
to isUInt|isInt(..., N)
"""
def __init__(self, priority: int):
super().__init__(priority)
def get_search_pattern(self) -> str:
return (
"(call_expression"
" (template_function"
' ((identifier) @id (#match? @id "isUInt|isInt"))'
" ((template_argument_list) @templ_args)"
" )"
" ((argument_list) @arg_list)"
") @is_u_int"
)
def get_main_capture_name(self) -> str:
return "is_u_int"
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
identifier: Node = captures[1][0]
templ_args: Node = captures[2][0]
args_list: Node = captures[3][0]
name = get_text(src, identifier.start_byte, identifier.end_byte)
targs = get_text(src, templ_args.start_byte, templ_args.end_byte).strip(b"<>")
args = get_text(src, args_list.start_byte, args_list.end_byte).strip(b"()")
res = name + b"N(" + targs + b", " + args + b")"
return res
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,20 @@
from tree_sitter import Node, Query
# Queries for the given pattern and converts the query back to the tree-siter v22.3 format.
# Which is: A list of tuples where the first element is the
# Node of the capture and the second one is the name.
def query_captures_22_3(query: Query, node: Node) -> list[tuple[Node, str]]:
result = list()
captures = query.captures(node)
# Captures are no longer sorted by start point.
captures_sorted = dict()
nodes: list[Node]
for name, nodes in captures.items():
captures_sorted[name] = sorted(nodes, key=lambda n: n.start_point)
while len(captures_sorted) != 0:
for name, nodes in captures_sorted.items():
node = nodes.pop(0)
result.append((node, name))
captures_sorted = {k: l for k, l in captures_sorted.items() if len(l) != 0}
return result