diff --git a/README.md b/README.md
index 6f7f20f..4da238e 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,46 @@
# What is DOL C-Kit?
-DOL C-Kit is a toolkit for compiling C code (or assembly) using DevkitPPC to inject into a GameCube/Wii \*.dol executable. It has been written in such a way that it can be adapted to many different games. You will need [Python 3](https://www.python.org/downloads/) and [DevKitPPC](https://devkitpro.org/wiki/Getting_Started) installed to use it. As well, DOL C-Kit is dependent on [pyelftools](https://github.com/eliben/pyelftools), JoshuaMK's fork of [dolreader](https://github.com/JoshuaMKW/dolreader), and [geckocode-libs](https://github.com/JoshuaMKW/geckocode-libs).
+DOL C-Kit is a toolkit for compiling C/C++ code (or assembly) using DevkitPPC to inject into a GameCube/Wii \*.dol executable. It has been written in such a way that it can be adapted to many different games. You will need [Python 3](https://www.python.org/downloads/) and [DevKitPPC](https://devkitpro.org/wiki/Getting_Started) installed to use it. As well, DOL C-Kit is dependent on [pyelftools](https://github.com/eliben/pyelftools), JoshuaMK's fork of [dolreader](https://github.com/JoshuaMKW/dolreader), and [geckocode-libs](https://github.com/JoshuaMKW/geckocode-libs).
Credit to Yoshi2 for creating the original GC C-Kit. DOL C-Kit couldn't exist without it.
-# How to use it / The Project Class
+## How to use it
DOL C-Kit is a Python module. To install on Windows, run INSTALL.bat as administrator. To install on Linux, run INSTALL.sh as superuser.
-The Project class automates the tedious parts of compiling, linking, and injecting custom code into a \*.dol executable. The Project class constructor has two optional parameters to set the "base_addr" and "verbose" member variables. By default, they are set to None and False, respectively.
+## The Project Class
+`from dol_c_kit import Project`
-The Project class has many member variables that may be directly modified:
-* src_dir: Path to C code (or assembly) source files. Default is "".
-* obj_dir: Path to output \*.o files and other files generated by DOL C-Kit to. Default is "".
-* devkitppc_path: Change this if DevKitPPC is not installed at its default location.
-* project_name: Name used for certain files generated by DOL C-Kit. Default is "project".
-* gcc_flags: Non-crucial flags passed to powerpc-eabi-gcc. Defaults include "-w", "-std=c99", "-O1", and "-fno-asynchronous-unwind-tables".
-* as_flags: Non-crucial flags passed to powerpc-eabi-as. Defaults include "-w".
-* ld_flags: Non-crucial flags passed to powerpc-eabi-ld. Defaults include nothing.
-* base_addr: The location new data will be put at. This is set by the constructor, but may be modified directly as well.
-* sda_base: The value used for the \_SDA\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well.
-* sda2_base: The value used for the \_SDA2\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well.
-* verbose: Flag for additional information printing. This is set by the constructor, but may be modified directly as well.
+The Project class automates the tedious parts of compiling, linking, and injecting custom code into a \*.dol executable.
By shifting forward the stack, db_stack, and OSArenaLo, space for new data can be allocated. To do this, a patching function modifying a given game's "\_\_init_registers", "OSInit", and "\_\_OSThreadInit" functions must be written. The project's save_dol function passes two parameters to this patching function: a DolFile class, and the base_addr of your project.
-## Step 1: Populate the project
-* `add_c_file(filepath, gcc_flags=(), use_global_flags=True)`
-Add a C source file to the project. Two optional arguments may be given: gcc_flags is a tuple of strings passed to powerpc-eabi-gcc as flags, and use_global_flags determines if the gcc_flags member of the Project class are used for this source file.
+### Class constructor
+* `Project(self, base_addr=None, verbose=False)`
+The Project class constructor has two optional parameters. base_addr and verbose set their respective class members upon class construction.
-* `add_asm_file(filepath, as_flags=(), use_global_flags=True)`
-Add an assembly source file to the project. Two optional arguments may be given: as_flags is a tuple of strings passed to powerpc-eabi-as as flags, and use_global_flags determines if the as_flags member of the Project class are used for this source file.
+### Class members
+The Project class has many member variables that may be directly modified:
+* `src_dir` Path to C code (or assembly) source files. Default is "".
+* `obj_dir` Path to output \*.o files and other files generated by DOL C-Kit to. Default is "".
+* `devkitppc_path` Change this if DevKitPPC is not installed at its default location.
+* `project_name` Name used for certain files generated by DOL C-Kit. Default is "project".
+* `c_flags` Non-crucial flags passed to powerpc-eabi-gcc. Defaults include "-w", "-std=c99", "-O1", and "-fno-asynchronous-unwind-tables".
+* `cpp_flags` Non-crucial flags passed to powerpc-eabi-g++. Defaults include "-w", "-std=c++98", "-O1", "-fno-asynchronous-unwind-tables", and "-fno-rtti".
+* `asm_flags` Non-crucial flags passed to powerpc-eabi-as. Defaults include "-w".
+* `linker_flags` Non-crucial flags passed to powerpc-eabi-ld. Defaults include nothing.
+* `base_addr` The location new data will be put at. This is set by the constructor, but may be modified directly as well.
+* `sda_base` The value used for the \_SDA\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well.
+* `sda2_base` The value used for the \_SDA2\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well.
+* `verbose` Flag for additional information printing. This is set by the constructor, but may be modified directly as well.
+
+### Step 1: Methods to populate the project
+* `add_c_file(filepath, flags=(), use_global_flags=True)`
+Add a C source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to powerpc-eabi-gcc as flags, and use_global_flags determines if the c_flags member of the Project class are used for this source file.
+
+* `add_cpp_file(filepath, flags=(), use_global_flags=True)`
+Add a C++ source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to powerpc-eabi-g++ as flags, and use_global_flags determines if the cpp_flags member of the Project class are used for this source file.
+
+* `add_asm_file(filepath, flags=(), use_global_flags=True)`
+Add an assembly source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to powerpc-eabi-as as flags, and use_global_flags determines if the asm_flags member of the Project class are used for this source file.
* `add_obj_file(filepath, do_cleanup=False)`
Add an unlinked object file to the project. This object file must be in the obj_dir, not the src_dir. The cleanup method WILL DELETE FILES added by the add_obj_file method if the optional do_cleanup argument is True.
@@ -69,7 +81,7 @@ Give your project a game-specific patching function to use to allocate space for
* `set_sda_bases(sda_base, sda2_base)`
Set the \_SDA\_BASE\_ and \_SDA2\_BASE\_ symbols. These values get passed to the linker. They are also important for the @sda and @sda2 modifiers for Immediate16Hooks.
-## Step 2: Build the project
+### Step 2: Methods to build the project
* `build_dol(in_dol_path, out_dol_path)`
Compile, assemble, and link all source files, hooks, and supported Gecko Codes into a \*.dol executable. If no base_addr is specified, the ROM end will automatically be detected and used. A new text section will be allocated to contain the new data. If no text sections are available, a data section will be allocated instead.
Note: Automatic ROM end detection does not work for DOLs that allocate space for .sbss2.
@@ -82,3 +94,62 @@ Generate a CodeWarrior-like symbol map from the project. Run this after buildin
* `cleanup()`
Delete unimportant files created by DOL C-Kit. This includes unlinked \*.o files, and .o, .bin, and .map.
+
+# How to work with mangled symbols (C++)
+In C++, there is the concept of mangled symbol names. For example, the function signature `int foo::bar(MyClass arg1)` becomes the symbol `_ZN3foo3barE7MyClass`. DOL C-Kit provides faculties to make working with mangled symbols easy.
+
+## The LDPlusPlus class
+`from dol_c_kit import LDPlusPlus`
+
+Just like a usual linker script to give dol-side symbols a value for C and ASM, one is needed for C++ as well. LDPlusPlus makes this easier by mangling function signatures in bulk for you.
+
+### Class constructor
+* `LDPlusPlus(abi)`
+The LDPlusPlus class constructor has one parameter. abi is the desired ABI (an enum value). At the moment, only ABI.Itanium is available.
+
+### Methods
+* `assign(prototype, value)`
+[Assign](https://sourceware.org/binutils/docs/ld/Simple-Assignments.html) a value to a symbol in the linker script.
+
+* `provide(prototype, value)`
+[Provide](https://sourceware.org/binutils/docs/ld/PROVIDE.html) a value for a symbol in the linker script.
+
+* `save(filepath)`
+Save the linker script to a given filepath.
+
+## mangle (and itanium_mangle)
+`from dol_c_kit import mangle, itanium_mangle`
+
+To mangle an individual signature, use the mangle function (or itanium_mangle function). This is useful for hooks which take a symbol name as an argument.
+
+* `mangle(prototype, abi)`
+Returns a mangled symbol from a given signature prototype in a given ABI (an enum value). At the moment, only ABI.Itanium is available.
+
+* `itanium_mangle(prototype)`
+Returns a mangled symbol from a given signature prototype in the Itanium ABI.
+
+## Limitations and workarounds of the mangler
+Writing a C++ mangler that has no concept of user defined types, or really any context at all, was a struggle that came with a few compromises.
+* Function pointer types are not supported.
+* Certain kinds of template instancing are not supported.
+* Typedefs are not supported (yet).
+* To mangle certain edge cases, special tokens starting with "$$" are used:
+ * `$$vtable` is used for vtable signatures. e.g. `ClassA::$$vtable` will mangle to `_ZTV6ClassA`.
+ * `$$rtti` is used for typeinfo signatures. e.g. `ClassA::$$rtti` will mangle to `_ZTI6ClassA`.
+ * `$$vtt_structure` is used for vtable structure signatures. e.g. `ClassA::$$vtt_structure` will mangle to `_ZTT6ClassA`.
+ * `$$rtti_name` is used for typeinfo names. e.g. `ClassA::$$rtti_name` will mangle to `_ZTS6ClassA`.
+ * `$$ctor` is used for (the assumed default) object constructors. e.g. `ClassA::$$ctor` will mangle to `_ZN6ClassAC1Ev`.
+ * `$$ctor1` is used for complete object constructors. e.g. `ClassA::$$ctor1` will mangle to `_ZN6ClassAC1Ev`.
+ * `$$ctor2` is used for base object constructors. e.g. `ClassA::$$ctor2` will mangle to `_ZN6ClassAC2Ev`.
+ * `$$ctor3` is used for complete object allocating. e.g. `ClassA::$$ctor3` will mangle to `_ZN6ClassAC3Ev`.
+ * `$$dtor` is used for (the assumed default) object destructors. e.g. `ClassA::$$dtor` will mangle to `_ZN6ClassAD1Ev`.
+ * `$$dtor0` is used for deleting destructors. e.g. `ClassA::$$dtor0` will mangle to `_ZN6ClassAD0Ev`.
+ * `$$dtor1` is used for complete object destructors. e.g. `ClassA::$$dtor1` will mangle to `_ZN6ClassAD1Ev`.
+ * `$$dtor2` is used for base object destructors. e.g. `ClassA::$$dtor2` will mangle to `_ZN6ClassAD2Ev`.
+ * `$$unary` is used for operator overloads that are ambigious without context.
+ * `operator+ $$unary` will produce the "positive" operator override rather than the "add" operator override.
+ * `operator- $$unary` will produce the "negative" operator override rather than the "subtract" operator override.
+ * `operator& $$unary` will produce the "reference" operator override rather than the "bitwise AND" operator override.
+ * `operator* $$unary` will produce the "dereference" operator override rather than the "multiply" operator override.
+
+There are likely other cases that the mangler won't cover, but for 99% of signatures, it should work.
\ No newline at end of file
diff --git a/dol_c_kit/__init__.py b/dol_c_kit/__init__.py
index abb08df..e9618be 100644
--- a/dol_c_kit/__init__.py
+++ b/dol_c_kit/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "2.4.1"
+__version__ = "3.0.0"
__author__ = "Minty Meeo"
__credits__ = "Yoshi2 (RenolY2)"
@@ -24,5 +24,9 @@
from dol_c_kit.doltools import write_li
from dol_c_kit.doltools import write_lis
from dol_c_kit.doltools import write_nop
+from dol_c_kit.mangle import ABI
+from dol_c_kit.mangle import LDPlusPlus
+from dol_c_kit.mangle import mangle
+from dol_c_kit.mangle import itanium_mangle
from dol_c_kit.devkit_tools import Project
diff --git a/dol_c_kit/devkit_tools.py b/dol_c_kit/devkit_tools.py
index fc6fd95..3ce3803 100644
--- a/dol_c_kit/devkit_tools.py
+++ b/dol_c_kit/devkit_tools.py
@@ -287,12 +287,14 @@ def __init__(self, base_addr=None, verbose=False):
self.obj_dir = ""
self.project_name = "project"
self.c_files = []
+ self.cpp_files = []
self.asm_files = []
self.obj_files = []
self.linker_script_files = []
- self.gcc_flags = ["-w", "-std=c99", "-O1", "-fno-asynchronous-unwind-tables",]
- self.as_flags = ["-w",]
- self.ld_flags = []
+ self.c_flags = ["-w", "-std=c99", "-O1", "-fno-asynchronous-unwind-tables",]
+ self.cpp_flags = ["-w", "-std=c++98", "-O1", "-fno-asynchronous-unwind-tables", "-fno-rtti",]
+ self.asm_flags = ["-w",]
+ self.linker_flags = []
self.symbols = {}
self.verbose = verbose
@@ -307,11 +309,14 @@ def __init__(self, base_addr=None, verbose=False):
# Add stuff
- def add_c_file(self, filepath, gcc_flags=(), use_global_flags=True):
- self.c_files.append((filepath, gcc_flags, use_global_flags))
+ def add_c_file(self, filepath, flags=(), use_global_flags=True):
+ self.c_files.append((filepath, flags, use_global_flags))
- def add_asm_file(self, filepath, as_flags=(), use_global_flags=True):
- self.asm_files.append((filepath, as_flags, use_global_flags))
+ def add_cpp_file(self, filepath, flags=(), use_global_flags=True):
+ self.cpp_files.append((filepath, flags, use_global_flags))
+
+ def add_asm_file(self, filepath, flags=(), use_global_flags=True):
+ self.asm_files.append((filepath, flags, use_global_flags))
def add_obj_file(self, filepath, do_cleanup=False):
self.obj_files.append((filepath, do_cleanup))
@@ -549,12 +554,12 @@ def cleanup(self):
# Private stuff
- def __compile(self, infile, gcc_flags, use_global_flags):
+ def __compile(self, infile, flags, use_global_flags):
args = [self.devkitppc_path+"powerpc-eabi-gcc", "-c", self.src_dir+infile, "-o", self.obj_dir+infile+".o", "-I", self.src_dir]
if use_global_flags:
- for flag in self.gcc_flags:
+ for flag in self.c_flags:
args.append(flag)
- for flag in gcc_flags:
+ for flag in flags:
args.append(flag)
if self.verbose:
print(args)
@@ -562,12 +567,25 @@ def __compile(self, infile, gcc_flags, use_global_flags):
self.obj_files.append((infile+".o", True))
return True
- def __assemble(self, infile, as_flags, use_global_flags):
+ def __compileplusplus(self, infile, flags, use_global_flags):
+ args = [self.devkitppc_path+"powerpc-eabi-g++", "-c", self.src_dir+infile, "-o", self.obj_dir+infile+".o", "-I", self.src_dir]
+ if use_global_flags:
+ for flag in self.cpp_flags:
+ args.append(flag)
+ for flag in flags:
+ args.append(flag)
+ if self.verbose:
+ print(args)
+ subprocess.call(args)
+ self.obj_files.append((infile+".o", True))
+ return True
+
+ def __assemble(self, infile, flags, use_global_flags):
args = [self.devkitppc_path+"powerpc-eabi-as", self.src_dir+infile, "-o", self.obj_dir+infile+".o", "-I", self.src_dir]
if use_global_flags:
- for flag in self.as_flags:
+ for flag in self.asm_flags:
args.append(flag)
- for flag in as_flags:
+ for flag in flags:
args.append(flag)
if self.verbose:
print(args)
@@ -594,7 +612,7 @@ def __link_project(self):
for filename, do_cleanup in self.obj_files:
args.append(self.obj_dir+filename)
args.extend(("-Map", self.obj_dir+self.project_name+".map"))
- for flag in self.ld_flags:
+ for flag in self.linker_flags:
args.append(flag)
if self.verbose:
print(args)
@@ -631,11 +649,14 @@ def __build_project(self):
is_linked = False
is_processed = False
- for filepath, gcc_flags, use_global_flags in self.c_files:
- is_built |= self.__compile(filepath, gcc_flags, use_global_flags)
+ for filepath, flags, use_global_flags in self.c_files:
+ is_built |= self.__compile(filepath, flags, use_global_flags)
+
+ for filepath, flags, use_global_flags in self.cpp_files:
+ is_built |= self.__compileplusplus(filepath, flags, use_global_flags)
- for filepath, as_flags, use_global_flags in self.asm_files:
- is_built |= self.__assemble(filepath, as_flags, use_global_flags)
+ for filepath, flags, use_global_flags in self.asm_files:
+ is_built |= self.__assemble(filepath, flags, use_global_flags)
if is_built == True:
is_linked |= self.__link_project()
diff --git a/dol_c_kit/mangle.py b/dol_c_kit/mangle.py
new file mode 100644
index 0000000..0522c90
--- /dev/null
+++ b/dol_c_kit/mangle.py
@@ -0,0 +1,1331 @@
+from enum import Enum
+
+# Massive thank you to these two documents:
+# https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
+# https://github.com/gchatelet/gcc_cpp_mangling_documentation
+
+# Mutable String class
+
+class MutableString(list):
+ def __init__(self, string):
+ for char in string:
+ self.append(char)
+
+ def __str__(self):
+ return "".join(char for char in self)
+
+ def pop_front(self, num):
+ ret = ""
+ for i in range(num):
+ ret += self.pop(0)
+ return ret
+
+ def pop_back(self, num):
+ ret = ""
+ for i in range(num):
+ ret += self.pop()
+ return ret
+
+ def startswith(self, string):
+ return str(self).startswith(string)
+
+# Simple Token class
+
+class SyntaxToken(str):
+ def __init__(self, string):
+ self = string
+
+ def itanium_mangle(self, compressibles):
+ return "{}{}".format(len(self), self)
+
+# Special Tokens
+
+class SpecialTokenCtor(object):
+ def __init__(self, num = 1):
+ self.num = num
+ def __str__(self):
+ return "$$ctor{}".format(self.num)
+ def __repr__(self):
+ return "\'$$ctor{}\'".format(self.num)
+ def itanium_mangle(self, compressibles):
+ return "C{}".format(self.num)
+
+class SpecialTokenDtor(object):
+ def __init__(self, num = 1):
+ self.num = num
+ def __str__(self):
+ return "$$dtor{}".format(self.num)
+ def __repr__(self):
+ return "\'$$dtor{}\'".format(self.num)
+ def itanium_mangle(self, compressibles):
+ return "D{}".format(self.num)
+
+class SpecialTokenVTable(object):
+ def __str__(self):
+ return "$$vtable"
+ def __repr__(self):
+ return "\'$$vtable\'"
+ def itanium_mangle(self, compressibles):
+ return "TV"
+
+class SpecialTokenRTTI(object):
+ def __str__(self):
+ return "$$rtti"
+ def __repr__(self):
+ return "\'$$rtti\'"
+ def itanium_mangle(self, compressibles):
+ return "TI"
+
+class SpecialTokenVTTStructure(object): # Itanium exclusive?
+ def __str__(self):
+ return "$$vtt_structure"
+ def __repr__(self):
+ return "\'$$vtt_structure\'"
+ def itanium_mangle(self, compressibles):
+ return "TT"
+
+class SpecialTokenRTTIName(object): # Itanium exclusive?
+ def __str__(self):
+ return "$$rtti_name"
+ def __repr__(self):
+ return "\'$$rtti_name\'"
+ def itanium_mangle(self, compressibles):
+ return "TS"
+
+class SpecialTokenUnary(object): # This one is extra special. It should never be mangled, only provide context and be removed.
+ def __str__(self):
+ return "$$unary"
+ def __repr__(self):
+ return "\'$$unary\'"
+
+# Special Tokens (operator overrides)
+
+class SpecialOperatorNew(object):
+ def __str__(self):
+ return "operator new"
+ def __repr__(self):
+ return "\'operator new\'"
+ def itanium_mangle(self, compressibles):
+ return "nw"
+
+class SpecialOperatorNewArray(object):
+ def __str__(self):
+ return "operator new[]"
+ def __repr__(self):
+ return "\'operator new[]\'"
+ def itanium_mangle(self, compressibles):
+ return "na"
+
+class SpecialOperatorDelete(object):
+ def __str__(self):
+ return "operator delete"
+ def __repr__(self):
+ return "\'operator delete\'"
+ def itanium_mangle(self, compressibles):
+ return "dl"
+
+class SpecialOperatorDeleteArray(object):
+ def __str__(self):
+ return "operator delete[]"
+ def __repr__(self):
+ return "\'operator delete[]\'"
+ def itanium_mangle(self, compressibles):
+ return "da"
+
+class SpecialOperatorCoAwait(object):
+ def __str__(self):
+ return "operator co_await"
+ def __repr__(self):
+ return "\'operator co_await\'"
+ def itanium_mangle(self, compressibles):
+ return "aw"
+
+class SpecialOperatorPositive(object): # unary
+ def __str__(self):
+ return "operator + (unary)"
+ def __repr__(self):
+ return "\'operator + (unary)\'"
+ def itanium_mangle(self, compressibles):
+ return "ps"
+
+class SpecialOperatorNegative(object): # unary
+ def __str__(self):
+ return "operator - (unary)"
+ def __repr__(self):
+ return "\'operator - (unary)\'"
+ def itanium_mangle(self, compressibles):
+ return "ng"
+
+class SpecialOperatorReference(object): # unary
+ def __str__(self):
+ return "operator & (unary)"
+ def __repr__(self):
+ return "\'operator & (unary)\'"
+ def itanium_mangle(self, compressibles):
+ return "ad" # address of
+
+class SpecialOperatorDereference(object): # unary
+ def __str__(self):
+ return "operator * (unary)"
+ def __repr__(self):
+ return "\'operator * (unary)\'"
+ def itanium_mangle(self, compressibles):
+ return "de"
+
+class SpecialOperatorBitwiseNOT(object):
+ def __str__(self):
+ return "operator ~"
+ def __repr__(self):
+ return "\'operator ~\'"
+ def itanium_mangle(self, compressibles):
+ return "co"
+
+class SpecialOperatorAdd(object):
+ def __str__(self):
+ return "operator +"
+ def __repr__(self):
+ return "\'operator +\'"
+ def itanium_mangle(self, compressibles):
+ return "pl"
+
+class SpecialOperatorSubtract(object):
+ def __str__(self):
+ return "operator -"
+ def __repr__(self):
+ return "\'operator -\'"
+ def itanium_mangle(self, compressibles):
+ return "mi"
+
+class SpecialOperatorMultiply(object):
+ def __str__(self):
+ return "operator *"
+ def __repr__(self):
+ return "\'operator *\'"
+ def itanium_mangle(self, compressibles):
+ return "ml"
+
+class SpecialOperatorDivide(object):
+ def __str__(self):
+ return "operator /"
+ def __repr__(self):
+ return "\'operator /\'"
+ def itanium_mangle(self, compressibles):
+ return "dv"
+
+class SpecialOperatorModulo(object):
+ def __str__(self):
+ return "operator %"
+ def __repr__(self):
+ return "\'operator %\'"
+ def itanium_mangle(self, compressibles):
+ return "rm" # remainder
+
+class SpecialOperatorBitwiseAND(object):
+ def __str__(self):
+ return "operator &"
+ def __repr__(self):
+ return "\'operator &\'"
+ def itanium_mangle(self, compressibles):
+ return "an"
+
+class SpecialOperatorBitwiseOR(object):
+ def __str__(self):
+ return "operator |"
+ def __repr__(self):
+ return "\'operator |\'"
+ def itanium_mangle(self, compressibles):
+ return "or"
+
+class SpecialOperatorBitwiseXOR(object):
+ def __str__(self):
+ return "operator ^"
+ def __repr__(self):
+ return "\'operator ^\'"
+ def itanium_mangle(self, compressibles):
+ return "eo" # exclusive or
+
+class SpecialOperatorAssign(object):
+ def __str__(self):
+ return "operator ="
+ def __repr__(self):
+ return "\'operator =\'"
+ def itanium_mangle(self, compressibles):
+ return "aS"
+
+class SpecialOperatorAssignAdd(object):
+ def __str__(self):
+ return "operator +="
+ def __repr__(self):
+ return "\'operator +=\'"
+ def itanium_mangle(self, compressibles):
+ return "pL"
+
+class SpecialOperatorAssignSubtract(object):
+ def __str__(self):
+ return "operator -="
+ def __repr__(self):
+ return "\'operator -=\'"
+ def itanium_mangle(self, compressibles):
+ return "mI"
+
+class SpecialOperatorAssignMultiply(object):
+ def __str__(self):
+ return "operator *="
+ def __repr__(self):
+ return "\'operator *=\'"
+ def itanium_mangle(self, compressibles):
+ return "mL"
+
+class SpecialOperatorAssignDivide(object):
+ def __str__(self):
+ return "operator /="
+ def __repr__(self):
+ return "\'operator /=\'"
+ def itanium_mangle(self, compressibles):
+ return "dV"
+
+class SpecialOperatorAssignModulo(object):
+ def __str__(self):
+ return "operator &="
+ def __repr__(self):
+ return "\'operator &=\'"
+ def itanium_mangle(self, compressibles):
+ return "rM" # remainder
+
+class SpecialOperatorAssignBitwiseAND(object):
+ def __str__(self):
+ return "operator &="
+ def __repr__(self):
+ return "\'operator &=\'"
+ def itanium_mangle(self, compressibles):
+ return "aN"
+
+class SpecialOperatorAssignBitwiseOR(object):
+ def __str__(self):
+ return "operator |="
+ def __repr__(self):
+ return "\'operator |=\'"
+ def itanium_mangle(self, compressibles):
+ return "oR"
+
+class SpecialOperatorAssignBitwiseXOR(object):
+ def __str__(self):
+ return "operator ^="
+ def __repr__(self):
+ return "\'operator ^=\'"
+ def itanium_mangle(self, compressibles):
+ return "eO" # exclusive or
+
+class SpecialOperatorLeftShift(object):
+ def __str__(self):
+ return "operator <<"
+ def __repr__(self):
+ return "\'operator <<\'"
+ def itanium_mangle(self, compressibles):
+ return "ls"
+
+class SpecialOperatorRightShift(object):
+ def __str__(self):
+ return "operator >>"
+ def __repr__(self):
+ return "\'operator >>\'"
+ def itanium_mangle(self, compressibles):
+ return "rs"
+
+class SpecialOperatorAssignLeftShift(object):
+ def __str__(self):
+ return "operator <<="
+ def __repr__(self):
+ return "\'operator <<=\'"
+ def itanium_mangle(self, compressibles):
+ return "lS"
+
+class SpecialOperatorAssignRightShift(object):
+ def __str__(self):
+ return "operator >>="
+ def __repr__(self):
+ return "\'operator >>=\'"
+ def itanium_mangle(self, compressibles):
+ return "rS"
+
+class SpecialOperatorEqual(object):
+ def __str__(self):
+ return "operator =="
+ def __repr__(self):
+ return "\'operator ==\'"
+ def itanium_mangle(self, compressibles):
+ return "eq"
+
+class SpecialOperatorNotEqual(object):
+ def __str__(self):
+ return "operator !="
+ def __repr__(self):
+ return "\'operator !=\'"
+ def itanium_mangle(self, compressibles):
+ return "ne"
+
+class SpecialOperatorLesserThan(object):
+ def __str__(self):
+ return "operator <"
+ def __repr__(self):
+ return "\'operator <\'"
+ def itanium_mangle(self, compressibles):
+ return "lt"
+
+class SpecialOperatorGreaterThan(object):
+ def __str__(self):
+ return "operator >"
+ def __repr__(self):
+ return "\'operator >\'"
+ def itanium_mangle(self, compressibles):
+ return "gt"
+
+class SpecialOperatorLesserThanOrEqual(object):
+ def __str__(self):
+ return "operator <="
+ def __repr__(self):
+ return "\'operator <=\'"
+ def itanium_mangle(self, compressibles):
+ return "le"
+
+class SpecialOperatorGreaterThanOrEqual(object):
+ def __str__(self):
+ return "operator >="
+ def __repr__(self):
+ return "\'operator >=\'"
+ def itanium_mangle(self, compressibles):
+ return "ge"
+
+class SpecialOperatorSpaceShip(object):
+ def __str__(self):
+ return "operator <=>"
+ def __repr__(self):
+ return "\'operator <=>\'"
+ def itanium_mangle(self, compressibles):
+ return "ss"
+
+class SpecialOperatorLogicalNOT(object):
+ def __str__(self):
+ return "operator !"
+ def __repr__(self):
+ return "\'operator !\'"
+ def itanium_mangle(self, compressibles):
+ return "nt"
+
+class SpecialOperatorLogicalAND(object):
+ def __str__(self):
+ return "operator &&"
+ def __repr__(self):
+ return "\'operator &&\'"
+ def itanium_mangle(self, compressibles):
+ return "aa"
+
+class SpecialOperatorLogicalOR(object):
+ def __str__(self):
+ return "operator ||"
+ def __repr__(self):
+ return "\'operator ||\'"
+ def itanium_mangle(self, compressibles):
+ return "oo"
+
+class SpecialOperatorIncrement(object):
+ def __str__(self):
+ return "operator ++"
+ def __repr__(self):
+ return "\'operator ++\'"
+ def itanium_mangle(self, compressibles):
+ return "pp"
+
+class SpecialOperatorDecrement(object):
+ def __str__(self):
+ return "operator --"
+ def __repr__(self):
+ return "\'operator --\'"
+ def itanium_mangle(self, compressibles):
+ return "mm"
+
+class SpecialOperatorArraySubscript(object):
+ def __str__(self):
+ return "operator []"
+ def __repr__(self):
+ return "\'operator []\'"
+ def itanium_mangle(self, compressibles):
+ return "ix"
+
+# There are a few more operator overrides but I genuinely don't understand them. I'm sorry.
+
+# Built-in types
+
+class BuiltinVoid(object):
+ def __str__(self):
+ return "void"
+ def __repr__(self):
+ return "\'void\'"
+ def itanium_mangle(self, compressibles):
+ return "v"
+
+class BuiltinBool(object):
+ def __str__(self):
+ return "bool"
+ def __repr__(self):
+ return "\'bool\'"
+ def itanium_mangle(self, compressibles):
+ return "b"
+
+class BuiltinChar(object):
+ def __str__(self):
+ return "char"
+ def __repr__(self):
+ return "\'char\'"
+ def itanium_mangle(self, compressibles):
+ return "c"
+
+class BuiltinShort(object):
+ def __str__(self):
+ return "short"
+ def __repr__(self):
+ return "\'short\'"
+ def itanium_mangle(self, compressibles):
+ return "s"
+
+class BuiltinInt(object):
+ def __str__(self):
+ return "int"
+ def __repr__(self):
+ return "\'int\'"
+ def itanium_mangle(self, compressibles):
+ return "i"
+
+class Builtin__int64(object):
+ def __str__(self):
+ return "__int64"
+ def __repr__(self):
+ return "\'__int64\'"
+ def itanium_mangle(self, compressibles):
+ return "x"
+
+class Builtin__int128(object):
+ def __str__(self):
+ return "__int128"
+ def __repr__(self):
+ return "\'__int128\'"
+ def itanium_mangle(self, compressibles):
+ return "n"
+
+class BuiltinFloat(object):
+ def __str__(self):
+ return "float"
+ def __repr__(self):
+ return "\'float\'"
+ def itanium_mangle(self, compressibles):
+ return "f"
+
+class BuiltinDouble(object):
+ def __str__(self):
+ return "double"
+ def __repr__(self):
+ return "\'double\'"
+ def itanium_mangle(self, compressibles):
+ return "d"
+
+class Builtin__float80(object):
+ def __str__(self):
+ return "__float80"
+ def __repr__(self):
+ return "\'__float80\'"
+ def itanium_mangle(self, compressibles):
+ return "e"
+
+class Builtin__float128(object):
+ def __str__(self):
+ return "__float128"
+ def __repr__(self):
+ return "\'__float128\'"
+ def itanium_mangle(self, compressibles):
+ return "g"
+
+class BuiltinEllipses(object):
+ def __str__(self):
+ return "..."
+ def __repr__(self):
+ return "\'...\'"
+ def itanium_mangle(self, compressibles):
+ return "z"
+
+# Decorator Classes
+
+class OperatorUnsigned(object):
+ def __init__(self):
+ self.rhand = None
+
+ def __str__(self):
+ return "unsigned {}".format(self.rhand)
+ def __repr__(self):
+ return "\'unsigned\' {}".format(repr(self.rhand))
+
+ def itanium_mangle(self, compressibles):
+ rhand_symbol = self.rhand.itanium_mangle(compressibles)
+ if rhand_symbol == "c": # char
+ return "h"
+ if rhand_symbol == "s": # short
+ return "t"
+ if rhand_symbol == "i": # int
+ return "j"
+ if rhand_symbol == "l": # long int
+ return "m"
+ if rhand_symbol == "x": # long long int
+ return "y"
+ if rhand_symbol == "n": # __int128
+ return "o"
+ raise Exception("Type {} is incompatible with unsigned decorator!".format(rhand_symbol))
+
+class OperatorSigned(object):
+ def __init__(self):
+ self.rhand = None
+
+ def __str__(self):
+ return "signed {}".format(self.rhand)
+ def __repr__(self):
+ return "\'signed\' {}".format(repr(self.rhand))
+
+ def itanium_mangle(self, compressibles):
+ rhand_symbol = self.rhand.itanium_mangle(compressibles)
+ if rhand_symbol == "c": # char
+ return "a"
+ if rhand_symbol == "s": # short
+ return "s"
+ if rhand_symbol == "i": # int
+ return "i"
+ if rhand_symbol == "l": # long int
+ return "l"
+ if rhand_symbol == "x": # long long int
+ return "x"
+ if rhand_symbol == "n": # __int128
+ return "n"
+ raise Exception("Type {} is incompatible with signed decorator!".format(rhand_symbol))
+
+class OperatorLong(object):
+ def __init__(self):
+ self.rhand = None
+
+ def __str__(self):
+ return "long {}".format(self.rhand)
+ def __repr__(self):
+ return "\'long\' {}".format(repr(self.rhand))
+
+ def itanium_mangle(self, compressibles):
+ rhand_symbol = self.rhand.itanium_mangle(compressibles)
+ if rhand_symbol == "i": # int
+ return "l"
+ if rhand_symbol == "l": # long int
+ return "x"
+ if rhand_symbol == "d": # double
+ return "e"
+ raise Exception("Type {} is incompatible with long decorator!".format(rhand_symbol))
+
+class OperatorConst(object):
+ def __init__(self):
+ self.lhand = None
+
+ def __str__(self):
+ return "{} const".format(self.lhand)
+ def __repr__(self):
+ return "{} \'const\'".format(repr(self.lhand))
+
+ def itanium_mangle(self, compressibles):
+ key = str(self.lhand)
+ if key in compressibles:
+ lhand_symbol = compressibles[key]
+ else:
+ lhand_symbol = self.lhand.itanium_mangle(compressibles)
+ if type(self.lhand) not in builtin_types:
+ compressibles.register(key)
+
+ # Constness does not matter until indirection happens
+ return lhand_symbol
+
+class OperatorPointer(object):
+ def __init__(self):
+ self.lhand = None
+
+ def __str__(self):
+ return "{} *".format(self.lhand)
+ def __repr__(self):
+ return "{} \'*\'".format(repr(self.lhand))
+
+ def itanium_mangle(self, compressibles):
+ key = str(self.lhand)
+ if key in compressibles:
+ lhand_symbol = compressibles[key]
+ else:
+ lhand_symbol = self.lhand.itanium_mangle(compressibles)
+ if type(self.lhand) not in builtin_types:
+ compressibles.register(key)
+
+ if type(self.lhand) == OperatorConst:
+ return "PK{}".format(lhand_symbol)
+ else:
+ return "P{}".format(lhand_symbol)
+
+class OperatorReference(object):
+ def __init__(self):
+ self.lhand = None
+
+ def __str__(self):
+ return "{} &".format(self.lhand)
+ def __repr__(self):
+ return "{} \'&\'".format(repr(self.lhand))
+
+ def itanium_mangle(self, compressibles):
+ key = str(self.lhand)
+ if key in compressibles:
+ lhand_symbol = compressibles[key]
+ else:
+ lhand_symbol = self.lhand.itanium_mangle(compressibles)
+ if type(self.lhand) not in builtin_types:
+ compressibles.register(key)
+
+ if type(self.lhand) == OperatorConst:
+ return "RK{}".format(lhand_symbol)
+ else:
+ return "R{}".format(lhand_symbol)
+
+class OperatorRHandReference(object):
+ def __init__(self):
+ self.lhand = None
+
+ def __str__(self):
+ return "{} &&".format(self.lhand)
+ def __repr__(self):
+ return "{} \'&&\'".format(repr(self.lhand))
+
+ def itanium_mangle(self, compressibles):
+ key = str(self.lhand)
+ if key in compressibles:
+ lhand_symbol = compressibles[key]
+ else:
+ lhand_symbol = self.lhand.itanium_mangle(compressibles)
+ if type(self.lhand) not in builtin_types:
+ compressibles.register(key)
+
+ if type(self.lhand) == OperatorConst:
+ return "OK{}".format(lhand_symbol)
+ else:
+ return "O{}".format(lhand_symbol)
+
+# Complex Classes
+
+class OperatorNamespace(object):
+ def __init__(self):
+ self.lhand = None
+ self.rhand = None
+
+ def __str__(self):
+ return "{} :: {}".format(self.lhand, self.rhand)
+ def __repr__(self):
+ return "{} \'::\' {}".format(repr(self.lhand), repr(self.rhand))
+
+ def itanium_mangle(self, compressibles):
+ if self.lhand == "std":
+ return "St" + self.rhand.itanium_mangle(compressibles)
+ if type(self.rhand) == SpecialTokenVTable \
+ or type(self.rhand) == SpecialTokenRTTI \
+ or type(self.rhand) == SpecialTokenVTTStructure \
+ or type(self.rhand) == SpecialTokenRTTIName:
+ return self.rhand.itanium_mangle(compressibles) + self.lhand.itanium_mangle(compressibles) # I hate this dirty hack
+ return "N" + self.itanium_mangle_ns(compressibles) + "E"
+
+ def itanium_mangle_ns(self, compressibles):
+ key = str(self.lhand)
+ if key in compressibles:
+ return compressibles[key] + self.rhand.itanium_mangle(compressibles)
+ else:
+ if type(self.lhand) == OperatorNamespace:
+ lhand_symbol = self.lhand.itanium_mangle_ns(compressibles)
+ if type(self.lhand) not in builtin_types:
+ compressibles.register(key)
+ rhand_symbol = self.rhand.itanium_mangle(compressibles)
+ else:
+ lhand_symbol = self.lhand.itanium_mangle(compressibles)
+ if type(self.lhand) not in builtin_types:
+ compressibles.register(key)
+ rhand_symbol = self.rhand.itanium_mangle(compressibles)
+ return lhand_symbol + rhand_symbol
+
+class OperatorTemplateArgs(list):
+ def __init__(self, mutstring):
+ self.lhand = None
+
+ mutstring.pop(0)
+ while mutstring:
+ char = mutstring[0]
+ if char in whitespace:
+ mutstring.pop(0)
+ continue
+ if char == ',':
+ mutstring.pop(0)
+ continue
+ if char == ')':
+ raise Exception("\')\' was unexpected at this time.")
+ if char == '>':
+ mutstring.pop(0)
+ if not self:
+ raise Exception("TODO: what is the behavior for empty template arguments?")
+ return
+ self.append(Expression(mutstring)[0])
+ raise Exception("No closing brace found!")
+
+ def __str__(self):
+ return "{} < {} >".format(self.lhand, ", ".join(str(iter) for iter in self))
+ def __hash__(self):
+ return hash((tuple(self), self.lhand))
+
+ def itanium_mangle(self, compressibles):
+ # Lefthand mangles first
+ key = str(self.lhand)
+ if key in compressibles:
+ lhand_symbol = compressibles[key]
+ else:
+ lhand_symbol = self.lhand.itanium_mangle(compressibles)
+ if type(self.lhand) not in builtin_types:
+ compressibles.register(key)
+
+ # Arguments mangle second
+ args = ""
+ for iter in self:
+ key = str(iter)
+ if key in compressibles:
+ args += compressibles[key]
+ else:
+ args += iter.itanium_mangle(compressibles)
+ if type(iter) not in builtin_types:
+ compressibles.register(key)
+
+ # Finally, the entire thing mangles third
+ key = str(self)
+ if key in compressibles:
+ return compressibles[key]
+ compressibles.register(key)
+
+ return "{}I{}E".format(lhand_symbol, args)
+
+class OperatorFunctionArgs(list):
+ def __init__(self, mutstring):
+ mutstring.pop(0)
+ while mutstring:
+ char = mutstring[0]
+ if char in whitespace:
+ mutstring.pop(0)
+ continue
+ if char == ',':
+ mutstring.pop(0)
+ continue
+ if char == ')':
+ mutstring.pop(0)
+ if not self:
+ self.append(BuiltinVoid())
+ return
+ if char == '>':
+ raise Exception("\'>\' was unexpected at this time.")
+ self.append(Expression(mutstring)[0])
+ raise Exception("No closing brace found!")
+
+ def __str__(self):
+ return "( {} )".format(", ".join(str(iter) for iter in self))
+
+ def itanium_mangle(self, compressibles):
+ args = ""
+ for iter in self:
+ key = str(iter)
+ if key in compressibles:
+ args += compressibles[key]
+ else:
+ args += iter.itanium_mangle(compressibles)
+ if type(iter) not in builtin_types:
+ compressibles.register(key)
+ return args
+
+class Expression(list):
+ def __init__(self, mutstring = None):
+ while mutstring:
+ if mutstring[0] in whitespace:
+ mutstring.pop(0)
+ continue
+
+ # Operators (and ellipses because idk how else to parse them)
+ if mutstring.startswith("*"):
+ mutstring.pop_front(1)
+ self.append(OperatorPointer())
+ continue
+ if mutstring.startswith("&&"):
+ mutstring.pop_front(2)
+ self.append(OperatorRHandReference())
+ continue
+ if mutstring.startswith("&"):
+ mutstring.pop_front(1)
+ self.append(OperatorReference())
+ continue
+ if mutstring.startswith("::"):
+ mutstring.pop_front(2)
+ self.append(OperatorNamespace())
+ continue
+ if mutstring.startswith("..."):
+ mutstring.pop_front(3)
+ self.append(BuiltinEllipses())
+ continue
+
+ # Argument Lists
+ char = mutstring[0]
+ if char == '(':
+ self.append(OperatorFunctionArgs(mutstring))
+ continue
+ if char == '<':
+ self.append(OperatorTemplateArgs(mutstring))
+ continue
+ if char == ")" \
+ or char == ">" \
+ or char == ",":
+ break
+
+ # Get a string
+ curr_string = ""
+ while mutstring:
+ if mutstring[0] not in alphanumeric and mutstring[0] != '$': # '$' is for special tokens
+ break
+ curr_string += mutstring.pop(0)
+
+ # const, unsigned, signed, long
+ # plus, built-in types and a redundant ellipses check
+ if \
+ ( self.append(OperatorConst()) ,) if curr_string == "const" else \
+ ( self.append(OperatorUnsigned()) ,) if curr_string == "unsigned" else \
+ ( self.append(OperatorSigned()) ,) if curr_string == "signed" else \
+ ( self.append(OperatorLong()) ,) if curr_string == "long" else \
+ ( self.append(BuiltinVoid()) ,) if curr_string == "void" else \
+ ( self.append(BuiltinBool()) ,) if curr_string == "bool" else \
+ ( self.append(BuiltinChar()) ,) if curr_string == "char" else \
+ ( self.append(BuiltinShort()) ,) if curr_string == "short" else \
+ ( self.append(BuiltinInt()) ,) if curr_string == "int" else \
+ ( self.append(Builtin__int64()) ,) if curr_string == "__int64" else \
+ ( self.append(Builtin__int128()) ,) if curr_string == "__int128" else \
+ ( self.append(BuiltinFloat()) ,) if curr_string == "float" else \
+ ( self.append(BuiltinDouble()) ,) if curr_string == "double" else \
+ ( self.append(Builtin__float80()) ,) if curr_string == "__float80" else \
+ ( self.append(Builtin__float128()),) if curr_string == "__float128" else \
+ ( self.append(BuiltinEllipses()) ,) if curr_string == "..." else ():
+ continue
+
+ # SpecialTokens
+ if \
+ ( self.append(SpecialTokenUnary()) ,) if curr_string == "$$unary" else \
+ ( self.append(SpecialTokenCtor()) ,) if curr_string == "$$ctor" else \
+ ( self.append(SpecialTokenCtor(1)) ,) if curr_string == "$$ctor1" else \
+ ( self.append(SpecialTokenCtor(2)) ,) if curr_string == "$$ctor2" else \
+ ( self.append(SpecialTokenCtor(3)) ,) if curr_string == "$$ctor3" else \
+ ( self.append(SpecialTokenDtor()) ,) if curr_string == "$$dtor" else \
+ ( self.append(SpecialTokenDtor(0)) ,) if curr_string == "$$dtor0" else \
+ ( self.append(SpecialTokenDtor(1)) ,) if curr_string == "$$dtor1" else \
+ ( self.append(SpecialTokenDtor(2)) ,) if curr_string == "$$dtor2" else \
+ ( self.append(SpecialTokenVTable()) ,) if curr_string == "$$vtable" else \
+ ( self.append(SpecialTokenRTTI()) ,) if curr_string == "$$rtti" else \
+ ( self.append(SpecialTokenVTTStructure()),) if curr_string == "$$vtt_structure" else \
+ ( self.append(SpecialTokenRTTIName()) ,) if curr_string == "$$rtti_name" else ():
+ continue
+
+ # Operator overrides (are complicated)
+ if curr_string == "operator":
+ while mutstring[0] in whitespace:
+ mutstring.pop(0)
+
+ # Non-alphanumeric operators. Also, this is a grotesque use of the ternary operator, tuples, and line continuations.
+ if \
+ ( mutstring.pop_front(3), self.append(SpecialOperatorAssignLeftShift()) ) if mutstring.startswith("<<=") else \
+ ( mutstring.pop_front(3), self.append(SpecialOperatorAssignRightShift()) ) if mutstring.startswith(">>=") else \
+ ( mutstring.pop_front(3), self.append(SpecialOperatorSpaceShip()) ) if mutstring.startswith("<=>") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignAdd()) ) if mutstring.startswith("+=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignSubtract()) ) if mutstring.startswith("-=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignMultiply()) ) if mutstring.startswith("*=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignDivide()) ) if mutstring.startswith("/=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignModulo()) ) if mutstring.startswith("%=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignBitwiseAND()) ) if mutstring.startswith("&=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignBitwiseOR()) ) if mutstring.startswith("|=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorAssignBitwiseXOR()) ) if mutstring.startswith("^=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorLeftShift()) ) if mutstring.startswith("<<") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorRightShift()) ) if mutstring.startswith(">>") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorEqual()) ) if mutstring.startswith("==") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorNotEqual()) ) if mutstring.startswith("!=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorLesserThanOrEqual()) ) if mutstring.startswith("<=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorGreaterThanOrEqual()) ) if mutstring.startswith(">=") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorLogicalAND()) ) if mutstring.startswith("&&") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorLogicalOR()) ) if mutstring.startswith("||") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorIncrement()) ) if mutstring.startswith("++") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorDecrement()) ) if mutstring.startswith("--") else \
+ ( mutstring.pop_front(2), self.append(SpecialOperatorArraySubscript()) ) if mutstring.startswith("[]") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseNOT()) ) if mutstring.startswith("~") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorAdd()) ) if mutstring.startswith("+") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorSubtract()) ) if mutstring.startswith("-") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorMultiply()) ) if mutstring.startswith("*") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorDivide()) ) if mutstring.startswith("/") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorModulo()) ) if mutstring.startswith("%") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseAND()) ) if mutstring.startswith("&") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseOR()) ) if mutstring.startswith("|") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseXOR()) ) if mutstring.startswith("^") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorAssign()) ) if mutstring.startswith("=") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorLesserThan()) ) if mutstring.startswith("<") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorGreaterThan()) ) if mutstring.startswith(">") else \
+ ( mutstring.pop_front(1), self.append(SpecialOperatorLogicalNOT()) ) if mutstring.startswith("!") else ():
+ continue
+
+ # new, delete, new[], delete[], and co_await
+ curr_string = ""
+ while mutstring:
+ if mutstring[0] not in alphanumeric and mutstring[0] != '$': # '$' is for special tokens
+ break
+ curr_string += mutstring.pop(0)
+ if curr_string == "new":
+ while mutstring[0] in whitespace:
+ mutstring.pop(0)
+ ( mutstring.pop_front(2), self.append(SpecialOperatorNewArray()) ) if mutstring.startswith("[]") else self.append(SpecialOperatorNew())
+ continue
+ if curr_string == "delete":
+ while mutstring[0] in whitespace:
+ mutstring.pop(0)
+ ( mutstring.pop_front(2), self.append(SpecialOperatorDeleteArray()) ) if mutstring.startswith("[]") else self.append(SpecialOperatorDelete())
+ continue
+ if curr_string == "co_await":
+ self.append(SpecialOperatorCoAwait())
+ continue
+ # uh oh
+ raise Exception("Special operator parsing failed!")
+
+ if curr_string:
+ self.append(SyntaxToken(curr_string))
+ continue
+ else:
+ raise Exception("Expression parsing failed!")
+# print(self) # uncomment this to see the expression before operator resolving
+ # Order of Operations
+ i = 0
+ while i < len(self):
+ iter = self[i]
+ if type(iter) == OperatorTemplateArgs:
+ iter.lhand = self.pop(i-1)
+ continue
+ if type(iter) == SpecialOperatorArraySubscript:
+ iter.lhand = self.pop(i-1)
+ continue
+ i += 1
+ continue
+ i = 0
+ while i < len(self):
+ iter = self[i]
+ if type(iter) == OperatorNamespace:
+ iter.rhand = self.pop(i+1)
+ iter.lhand = self.pop(i-1)
+ continue
+ if type(iter) == SpecialTokenUnary:
+ if type(self[i-1]) == SpecialOperatorAdd:
+ self[i-1] = SpecialOperatorPositive()
+ self.pop(i)
+ continue
+ if type(self[i-1]) == SpecialOperatorSubtract:
+ self[i-1] = SpecialOperatorNegative()
+ self.pop(i)
+ continue
+ if type(self[i-1]) == SpecialOperatorBitwiseAND:
+ self[i-1] = SpecialOperatorReference()
+ self.pop(i)
+ continue
+ if type(self[i-1]) == SpecialOperatorMultiply:
+ self[i-1] = SpecialOperatorDereference()
+ self.pop(i)
+ continue
+ raise Exception("Special Token \"$$unary\" can't be used for this!")
+ i += 1
+ continue
+ i = len(self) - 1
+ while i >= 0:
+ iter = self[i]
+ if type(iter) == OperatorLong:
+ iter.rhand = self.pop(i+1)
+ if type(iter) == OperatorUnsigned:
+ iter.rhand = self.pop(i+1)
+ if type(iter) == OperatorSigned:
+ iter.rhand = self.pop(i+1)
+ i -= 1
+ i = 0
+ while i < len(self):
+ iter = self[i]
+ if type(iter) == OperatorPointer:
+ iter.lhand = self.pop(i-1)
+ continue
+ if type(iter) == OperatorReference:
+ iter.lhand = self.pop(i-1)
+ continue
+ if type(iter) == OperatorRHandReference:
+ iter.lhand = self.pop(i-1)
+ continue
+ if type(iter) == OperatorConst:
+ if type(self[i-1]) == OperatorReference:
+ raise Exception("\'const\' qualifiers cannot be applied to references")
+ if type(self[i-1]) == OperatorConst:
+ raise Exception("Duplicate \'const\' qualifiers")
+ iter.lhand = self.pop(i-1)
+ continue
+ i += 1
+ continue
+
+ def __str__(self):
+ return " ".join(str(iter) for iter in self)
+
+class Signature(Expression):
+ def itanium_mangle(self, compressibles = None):
+ if compressibles == None: # This is for debugging only
+ compressibles = ItaniumSymbolDictionary()
+
+ if len(self) == 0:
+ return ""
+ if len(self) == 1:
+ # Special signature (vtable, rtti, etc.)
+ return "_Z" + self[0].itanium_mangle(compressibles)
+ if len(self) == 2:
+ # Special function signature (ctors and dtors)
+ if type(self[1]) == OperatorFunctionArgs \
+ or type(self[1]) == OperatorConst and type(self[1].lhand) == OperatorFunctionArgs:
+ name = self[0].itanium_mangle(compressibles)
+ args = self[1].itanium_mangle(compressibles)
+ return "_Z" + name + args
+ # Global variable signature
+ else:
+ name = self[1].itanium_mangle(compressibles)
+ if type(self[0]) == OperatorConst and type(self[0].lhand) in indirection_decorators:
+ name = "L" + name
+ return "_Z" + name
+ if len(self) == 3:
+ # Function signature
+ if type(self[2]) == OperatorFunctionArgs:
+ name = self[1].itanium_mangle(compressibles)
+ args = self[2].itanium_mangle(compressibles)
+ return "_Z" + name + args
+ # Const class methods
+ if type(self[2]) == OperatorConst and type(self[2].lhand) == OperatorFunctionArgs:
+ name = "NK" + self[1].itanium_mangle_ns(compressibles) + "E" # This will always be a namespace, unless it is invalid C++.
+ args = self[2].itanium_mangle(compressibles)
+ return "_Z" + name + args
+ raise Exception("Too much stuff!")
+
+
+# Some stuff
+
+alphanumeric = ('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9','0','_','~',)
+whitespace = (' ', ' ',)
+builtin_types = (
+ BuiltinVoid,
+ BuiltinBool,
+ BuiltinChar,
+ BuiltinShort,
+ BuiltinInt,
+ Builtin__int64,
+ Builtin__int128,
+ BuiltinFloat,
+ BuiltinDouble,
+ Builtin__float80,
+ Builtin__float128,
+ BuiltinEllipses,
+ OperatorUnsigned,
+ OperatorSigned,
+ OperatorLong,
+)
+indirection_decorators = (
+ OperatorPointer,
+ OperatorReference,
+ OperatorRHandReference,
+)
+
+class ItaniumSymbolDictionary(dict):
+ def __init__(self):
+ self["std"] = "St"
+ self["std :: nullptr_t"] = "Dn"
+ self["std :: allocator"] = "Sa"
+ self["std :: basic_string"] = "Sb"
+ self["std :: basic_string < char, std :: char_traits < char >, std :: allocator < char > >"] = "Ss"
+ self["std :: basic_istream < char, std :: char_traits < char > >"] = "Si"
+ self["std :: basic_ostream < char, std :: char_traits < char > >"] = "So"
+ self["std :: basic_iostream < char, std :: char_traits < char > >"] = "Sd"
+ self.n = -1
+
+ def __str__(self):
+ for iter in self:
+ return "\n".join("{} : {}".format(iter, self[iter]) for iter in self)
+
+ base_36 = {
+ 0 : "0",
+ 1 : "1",
+ 2 : "2",
+ 3 : "3",
+ 4 : "4",
+ 5 : "5",
+ 6 : "6",
+ 7 : "7",
+ 8 : "8",
+ 9 : "9",
+ 10 : "A",
+ 11 : "B",
+ 12 : "C",
+ 13 : "D",
+ 14 : "E",
+ 15 : "F",
+ 16 : "G",
+ 17 : "H",
+ 18 : "I",
+ 19 : "J",
+ 20 : "K",
+ 21 : "L",
+ 22 : "M",
+ 23 : "N",
+ 24 : "O",
+ 25 : "P",
+ 26 : "Q",
+ 27 : "R",
+ 28 : "S",
+ 29 : "T",
+ 30 : "U",
+ 31 : "V",
+ 32 : "W",
+ 33 : "X",
+ 34 : "Y",
+ 35 : "Z"
+ }
+
+ def register(self, key):
+ if key not in self:
+ if self.n == -1:
+ val = "S_"
+ self.n = 0
+ elif self.n == 0:
+ val = "S0_"
+ self.n = 1
+ else:
+ idx = self.n
+ self.n += 1
+ val = ""
+ while idx > 0:
+ val = ItaniumSymbolDictionary.base_36[idx % 36] + val
+ idx //= 36
+ val = "S" + val + "_"
+ self[key] = val
+
+# Accessible for external use
+
+class ABI(Enum):
+ Itanium = 0
+
+class LDPlusPlus(object):
+ def __init__(self, abi):
+ self.buffer = ""
+ self.abi = abi
+
+ # https://sourceware.org/binutils/docs/ld/Simple-Assignments.html
+ def assign(self, prototype, value):
+ if self.abi == ABI.Itanium:
+ self.buffer += "{} = {};\n".format(itanium_mangle(prototype), hex(value))
+ else:
+ raise Exception("Unsupported ABI!")
+
+ # https://sourceware.org/binutils/docs/ld/PROVIDE.html
+ def provide(self, prototype, value):
+ if self.abi == ABI.Itanium:
+ self.buffer += "PROVIDE({} = {});\n".format(itanium_mangle(prototype), hex(value))
+ else:
+ raise Exception("Unsupported ABI!")
+
+ def save(self, filepath):
+ try:
+ with open(filepath, "w") as f:
+ f.write(self.buffer)
+ except OSError:
+ print("Warning: \"{:s}\" could not be opened!".format(repr(filepath)[+1:-1]))
+
+def mangle(prototype, abi):
+ if abi == ABI.Itanium:
+ return itanium_mangle(prototype, compressibles)
+ else:
+ raise Exception("Unsupported ABI!")
+
+def itanium_mangle(prototype):
+ return Signature(MutableString(prototype)).itanium_mangle()
+
+# Some diagnostics
+def diagnose(prototype, correct_mangled, verbose = False):
+ compressibles = ItaniumSymbolDictionary()
+ signature = Signature(MutableString(prototype))
+ mangled = signature.itanium_mangle(compressibles)
+ if verbose == True:
+ print()
+ print(compressibles)
+ print("{:50s} {:30s} {} {:30s}".format(prototype, mangled, "==" if mangled == correct_mangled else "!=",correct_mangled))
+# print("{:50s} {:30s} {} {:30s}".format(str(signature), mangled, "==" if mangled == correct_mangled else "!=",correct_mangled))
+
+if __name__ == "__main__":
+ print("\nConst or nested variable declaration")
+ diagnose("int* const bar", "_ZL3bar")
+ diagnose("int a::bar", "_ZN1a3barE")
+ diagnose("int std::bar", "_ZSt3bar")
+ print("\nFunction declaration")
+ diagnose("void foo()", "_Z3foov")
+ print("\nDeclaration and user defined type encoding")
+ diagnose("void a::S::foo()", "_ZN1a1S3fooEv")
+ diagnose("void a::S::const_foo() const", "_ZNK1a1S9const_fooEv") # Const OperatorFunctionArgs are stupid
+ print("\nSubstitutions")
+ diagnose("void foo(void *, void *)", "_Z3fooPvS_")
+ print("\nMore on substitutions in function parameters")
+ diagnose("void foo(int)", "_Z3fooi")
+ diagnose("void foo()", "_Z3foov")
+ diagnose("void foo(void)", "_Z3foov")
+ diagnose("void foo(char, int, short)", "_Z3foocis")
+ print("\nIndirection decorators")
+ diagnose("void foo(int)", "_Z3fooi")
+ diagnose("void foo(int const)", "_Z3fooi")
+ diagnose("void foo(int const *)", "_Z3fooPKi")
+ diagnose("void foo(int const &)", "_Z3fooRKi")
+ diagnose("void foo(int const * const *)", "_Z3fooPKPKi")
+ diagnose("void foo(int * &)", "_Z3fooRPi")
+ print("\nFunction pointers")
+ print("--Massive TODO--")
+ print("\nMore on substitutions in scopes")
+ diagnose("void a::foo(a::A)", "_ZN1a3fooENS_1AE")
+ diagnose("void std::foo(std::A)", "_ZSt3fooSt1A")
+ diagnose("void A::foo(A::B)", "_ZN1A3fooENS_1BE") # Reduntant since classes are fancy namespaces
+ print("\nMore on substitutions in templates")
+ diagnose("void A::foo(A)", "_ZN1AIiE3fooES0_")
+ diagnose("void A::foo(int, int)", "_ZN1A3fooIiEEvT_S1_") # I don't understand this
+ diagnose("void A::foo(int, int)", "_ZN1AIiE3fooEii")
+ diagnose("void A::foo(B, B)", "_ZN1AI1BE3fooES0_S0_")
+ diagnose("int foo(char, int, char)", "_Z3fooIicET_T0_S0_S1_") # weird shit with function templates that would break the mangler as-is
+ diagnose("int foo(int, int, int)", "_Z3fooIiiET_T0_S0_S1_") # ditto
+ print("\nctors and dtors")
+ diagnose("ClassA::$$ctor()", "_ZN6ClassAC1Ev")
+ diagnose("ClassA::$$ctor1()", "_ZN6ClassAC1Ev")
+ diagnose("ClassA::$$ctor2()", "_ZN6ClassAC2Ev")
+ diagnose("ClassA::$$ctor3()", "_ZN6ClassAC3Ev")
+ diagnose("ClassA::$$dtor()", "_ZN6ClassAD1Ev")
+ diagnose("ClassA::$$dtor0()", "_ZN6ClassAD0Ev")
+ diagnose("ClassA::$$dtor1()", "_ZN6ClassAD1Ev")
+ diagnose("ClassA::$$dtor2()", "_ZN6ClassAD2Ev")
+ print("\nvtables and rtti")
+ diagnose("ClassA::$$vtable", "_ZTV6ClassA")
+ diagnose("NamespaceA::ClassA::$$vtable", "_ZTVN10NamespaceA6ClassAE")
+ diagnose("ClassA::$$vtt_structure", "?")
+ diagnose("ClassA::$$rtti", "?")
+ diagnose("ClassA::$$rtti_name", "?")
+ print("\noperator overloads")
+ diagnose("void* operator new(unsigned int)", "_Znwj")
+ diagnose("void* operator new[](unsigned int)", "_Znaj")
+ diagnose("void operator +(unsigned int)", "?")
+ diagnose("void operator + $$unary(unsigned int)", "?")
+ diagnose("void operator <=>(unsigned int)", "?")
+ diagnose("void operator >>=(unsigned int)", "?")
+ diagnose("void myclass::operator >>=(unsigned int)", "?")
+ print("\njust for fun")
+ diagnose("int foo::bar(MyClass arg1)", "?")
+