tp/tools/libdol2asm/data/function/base.py

191 lines
7.4 KiB
Python

import libdemangle
from dataclasses import dataclass, field
from typing import List
from ...builder import AsyncBuilder
from ... import util
from ...types import *
from ..symbol import *
special_func_no_return = set([
"ct",
"dt"
])
@dataclass(eq=False)
class Function(Symbol):
return_type: Type = None
argument_types: List[Type] = field(default_factory=list)
func_name: libdemangle.QualifiedName = None
special_func_name: str = None
func_is_const: bool = False
template_index: int = -1
asm: bool = False
@property
def label(self):
return self.identifier.label
def function_name(self,
original: bool = False,
full_qualified_name: bool = True,
without_template: bool = False,
specialize_templates: bool = False):
if not self.func_name or original:
return self.identifier.label
name = self.func_name
if self.special_func_name and self.has_class and specialize_templates:
# fix up the constructor and destructor if the function is template specialized
special_name = None
if self.special_func_name == "ct":
special_name = name.second_last.to_str(specialize_templates=specialize_templates,
without_template=without_template)
if self.special_func_name == "dt":
special_name = "~" + name.second_last.to_str(specialize_templates=specialize_templates,
without_template=without_template)
if special_name:
name = NamedType(
self.func_name.names[:-1] + [ClassName(special_name, [])])
if full_qualified_name:
return name.to_str(specialize_templates=specialize_templates,
without_template=without_template)
else:
return name.last.to_str(specialize_templates=specialize_templates,
without_template=without_template)
def is_demangled(self):
return self.func_name != None
def valid_reference(self, addr):
return addr % 4 == 0
def cpp_reference(self, accessor, addr):
if addr == self.addr:
return self.label
else:
offset = addr - self.addr
return f"(((char*){self.label})+0x{offset:X})"
def relocation_symbols(self, context, symbol_table, section):
symbols = section.relocations_in_range(symbol_table, self.start, self.end)
return symbols
def types(self):
return set()
@property
def has_class(self):
return self.func_name and self.func_name.has_class
@property
def has_template(self):
return self.func_name and self.func_name.has_template
async def export_function_header(self, exporter,
builder: AsyncBuilder,
forward: bool,
original: bool = False,
full_qualified_name: bool = True,
specialize_templates: bool = False,
without_template: bool = False,
comment_arguments: bool = False,
template_args: List[str] = None):
# prints internal references for the function
if False:
if not forward:
refs = self.internal_references(exporter.context, exporter.gst)
await builder.write(f"/* internal references (count {len(refs)})")
for ref in refs:
await builder.write(f"// {ref.addr:08X} {ref.label}")
declspec = "extern \"C\" "
if not original and self.is_demangled():
declspec = ""
if self._section == ".init":
declspec = "SECTION_INIT "
await builder.write_nonewline(f"{declspec}")
if self.asm and not forward:
await builder.write_nonewline(f"asm ")
if full_qualified_name and not self.has_class:
# this symbol is only referenced by other symbol in the same translation unit
if self.is_static:
await builder.write_nonewline(f"static ")
if not self.special_func_name in special_func_no_return or original:
return_type = self.return_type
if not self.return_type:
return_type = VOID
await builder.write_nonewline(f"{return_type.type(specialize_templates,without_template)} ")
await builder.write_nonewline(f"{self.function_name(original, full_qualified_name, without_template, specialize_templates)}")
arg_type = ""
if not original and self.is_demangled():
if forward:
arg_type = ", ".join([x.type(specialize_templates,without_template) for x in self.argument_types])
else:
arg_type = ", ".join([x.decl(f"param_{i}",specialize_templates,without_template) for i, x in zip(
range(len(self.argument_types)), self.argument_types)])
if template_args:
args = ", ".join(template_args)
await builder.write_nonewline(f"<{args}>")
if comment_arguments:
arg_type = f"/* {arg_type} */"
await builder.write_nonewline(f"({arg_type})")
if not original and self.has_class and self.func_is_const:
await builder.write_nonewline(f" const")
async def export_forward_references(self, exporter,
builder: AsyncBuilder,
c_export: bool = False,
full_qualified_name: bool = True,
template_args: List[str] = None):
if not template_args:
template_args = []
if c_export:
# export unmangled name
await self.export_function_header(exporter, builder, forward=True, original=True)
await builder.write(f";")
return
if full_qualified_name:
if self.is_demangled():
# forward references are not written for class functions
if not self.has_class:
await self.export_function_header(exporter, builder, forward=True, full_qualified_name=True, specialize_templates=True)
await builder.write(f";")
else:
# export the function as a class method
await self.export_function_header(exporter, builder, forward=True, full_qualified_name=False, template_args=template_args)
await builder.write(f";")
async def export_function_body(self, exporter, builder: AsyncBuilder):
assert False
async def export_declaration(self, exporter, builder: AsyncBuilder):
assert self.padding == 0
if self.alignment:
await builder.write("#pragma push")
await builder.write(f"#pragma function_align {self.alignment}")
await self.export_function_header(exporter, builder, forward=False, specialize_templates=self.has_template)
await self.export_function_body(exporter, builder)
if self.alignment:
await builder.write("#pragma pop")
await builder.write("")