from dataclasses import dataclass, field from ..builder import AsyncBuilder from .. import util from .symbol import * @dataclass(eq=False) class ArbitraryData(Symbol): data: bytes = field(default=None, repr=False) padding_data: bytes = field(default=None, repr=False) zero_length: bool = False always_extern: bool = False @property def has_body(self): return not self.zero_length @property def is_static(self): if self.always_extern: return False s = self.reference_count.static e = self.reference_count.extern r = self.reference_count.rel static_by_references = (s >= 0 and e == 0 and r == 0) static_by_literal = self.identifier.label.startswith("lit_") return static_by_references or static_by_literal @property def requires_force_active(self): return self.is_static and self.reference_count.total == 0 @property def export_as_static(self): if self.always_extern: return False return True @property def is_class_symbol(self): # @!game # don't generate static class variables for 'cNullVec__6Z2Calc', because it will not compile. # Z2Calc::cNullVec seems to be static data that is initialized in the class definition, thus, # every translation unit which uses Z2Calc will have a copy of the Z2Calc::cNullVec in the data # section. Could not find a way to make this compile without easily. if self.identifier.name == "cNullVec__6Z2Calc": return False return self.demangled_name and self.has_class and not self.has_template @property def element_size(self): return 1 @property def total_element_count(self): total = (self.size // self.element_size) + \ (self.padding // self.element_size) return total def element_type(self): type = self.data_type if self._section == ".rodata": type = ConstType(type) if self._section == ".extabindex": type = ConstType(type) if self._section == ".extab": type = ConstType(type) if self._section.startswith(".ctors"): type = ConstType(type) if self._section.startswith(".dtors"): type = ConstType(type) if self._section.startswith(".init"): type = ConstType(type) return type def array_type(self): if self.zero_length: return ZeroArrayType.create(self.element_type()) return PaddingArrayType.create( self.element_type(), self.size // self.element_size, self.padding // self.element_size) def return_type(self): array_type = self.array_type() if isinstance(array_type, ArrayType) or isinstance(array_type, PaddingArrayType) or isinstance(array_type, ZeroArrayType): return PointerType(array_type.base) return array_type def cpp_reference(self, accessor, addr): name = self.declaration_name(forward=False, c_export=False,full_qualified_name=True) if addr == self.addr: return f"&{name}" else: offset = addr - self.addr return f"(((char*)&{name})+0x{offset:X})" def cpp_load(self, accessor, addr): name = self.declaration_name(forward=False, c_export=False,full_qualified_name=True) if addr == self.addr: return f"{name}" else: offset = addr - self.addr return f"*(((char*)&{name})+0x{offset:X})" def declaration_name(self, forward: bool, c_export: bool, full_qualified_name: bool): if not self.is_class_symbol or c_export: return self.identifier.label if full_qualified_name: return self.demangled_name.to_str() else: return self.demangled_name.last.to_str() async def export_declaration_header(self, exporter, builder: AsyncBuilder, forward: bool, c_export: bool, full_qualified_name: bool): name = self.declaration_name(c_export=c_export, forward=forward, full_qualified_name=full_qualified_name) decl_type = self.array_type() await builder.write_nonewline(decl_type.decl(name)) async def export_forward_references(self, exporter, builder: AsyncBuilder, c_export: bool = False): if not c_export: return if not self.is_class_symbol: if self.is_static and self.export_static: if not self.require_forward_reference: return await self.export_section_header(builder) if not self.is_class_symbol: if not (self.is_static and self.export_static): await self.export_extern(builder) await self.export_declaration_header(exporter, builder, forward=True, c_export=c_export, full_qualified_name=False) await builder.write(";") async def export_declaration_head(self, exporter, builder: AsyncBuilder): name = self.declaration_name(c_export=False, forward=False, full_qualified_name=True) decl_type = self.array_type() if not self.is_class_symbol: # for empty symbols that should be exported, we need to double declare it. # otherwise, the compiler thinks that we're not actual declaring it. is_extern = not (self.is_static and self.export_as_static) if not self.data and is_extern: await self.export_section(builder) if self.force_section: await self.export_section_header(builder) await self.export_extern(builder) await builder.write_nonewline(decl_type.decl(name)) await builder.write(";") await self.export_section(builder) if self.force_section: await self.export_section_header(builder) if not self.is_class_symbol: if not is_extern: await self.export_static(builder) elif self.data and is_extern: await self.export_extern(builder) await builder.write_nonewline(decl_type.decl(name)) async def export_declaration_body(self, exporter, builder: AsyncBuilder): if self.data: assert self.size == len(self.data) if self.alignment > 0: await builder.write_nonewline(f" ALIGN_DECL({self.alignment})") await builder.write(f" = {{") await self.export_u8_data(builder, self.data) if self.padding_data: assert self.padding == len(self.padding_data) await builder.write("\t/* padding */") await self.export_u8_data(builder, self.padding_data) await builder.write("};") else: if self.alignment > 0: await builder.write_nonewline(f" ALIGN_DECL({self.alignment})") await builder.write(";") async def export_declaration(self, exporter, builder: AsyncBuilder): if self.requires_force_active: await builder.write(f"#pragma push") await builder.write(f"#pragma force_active on") await self.export_declaration_head(exporter, builder) await self.export_declaration_body(exporter, builder) if self._section == ".rodata": await builder.write(f"COMPILER_STRIP_GATE(0x{self.addr:08X}, {self.cpp_reference(None, self.addr)});") if self.requires_force_active: await builder.write(f"#pragma pop") @staticmethod def create_with_data(identifier, addr, data, padding_data): size = len(data) padding_size = len(padding_data) return ArbitraryData(identifier=identifier, addr=addr, size=size, data=data, data_type=U8, padding=padding_size, padding_data=padding_data) @staticmethod def create_without_data(identifier, addr, size, padding_size): return ArbitraryData(identifier=identifier, addr=addr, size=size, data=[], data_type=U8, padding=padding_size, padding_data=[])