Prevent Double Construction Issues

This commit is contained in:
TheBrokenRail 2024-09-20 21:30:38 -04:00
parent 2a63f1ff52
commit 5d2b146b08
9 changed files with 106 additions and 53 deletions

View File

@ -157,6 +157,38 @@ T *dup_vtable(T *vtable) {
} }
#undef RTTI_SIZE #undef RTTI_SIZE
// Internal Macros
#define __PREVENT_DESTRUCTION(self) \
~self() = delete
#define __PREVENT_CONSTRUCTION(self) \
self() = delete; \
__PREVENT_DESTRUCTION(self)
#define __PREVENT_COPY(self) \
self(const self &) = delete; \
self &operator=(const self &) = delete
// Easily Extend Structures
template<typename T, typename = void>
struct __HasAllocate : std::false_type {};
template<typename T>
struct __HasAllocate<T, std::enable_if_t<std::is_same_v<T *, decltype(T::allocate())>>> : std::true_type {};
template <typename Super, typename Self>
class __ExtendedStruct final {
static_assert(__HasAllocate<Super>::value, "Super Type Does Not Have A Defined Size");
__PREVENT_DESTRUCTION(__ExtendedStruct);
__PREVENT_COPY(__ExtendedStruct);
alignas(Super) uchar _super[sizeof(Super)];
public:
__ExtendedStruct() {}
Super *super() {
return (Super *) &_super[0];
}
Self data;
};
#define EXTEND_STRUCT(name, base, ...) \
typedef __VA_ARGS__ __##name##_data; \
typedef __ExtendedStruct<base, __##name##_data> name
// Forward Declarations // Forward Declarations
{{ forwardDeclarations }} {{ forwardDeclarations }}

View File

@ -100,9 +100,8 @@ export function formatFile(file: string, options: {[key: string]: string}) {
export const INTERNAL = '__'; export const INTERNAL = '__';
export function preventConstruction(self: string) { export function preventConstruction(self: string) {
let out = ''; let out = '';
out += `${INDENT}${self}() = delete;\n`; out += `${INDENT}${INTERNAL}PREVENT_CONSTRUCTION(${self});\n`;
out += `${INDENT}${self}(const ${self} &) = delete;\n`; out += `${INDENT}${INTERNAL}PREVENT_COPY(${self});\n`;
out += `${INDENT}${self} &operator=(const ${self} &) = delete;\n`;
return out; return out;
} }
export const LEAN_HEADER_GUARD = '#ifndef LEAN_SYMBOLS_HEADER\n'; export const LEAN_HEADER_GUARD = '#ifndef LEAN_SYMBOLS_HEADER\n';

View File

@ -52,9 +52,9 @@ function loadSymbols() {
} }
// Sort // Sort
structureObjects.sort((a, b) => { structureObjects.sort((a, b) => {
if (a.getName() > b.getName()) { if (a.name > b.name) {
return 1; return 1;
} else if (a.getName() < b.getName()) { } else if (a.name < b.name) {
return -1; return -1;
} else { } else {
return 0; return 0;
@ -113,7 +113,7 @@ function makeHeaderPart() {
// Generate Code // Generate Code
let structures = ''; let structures = '';
for (const structure of structureObjects) { for (const structure of structureObjects) {
const name = structure.getName(); const name = structure.name;
structures += `// ${name}\n`; structures += `// ${name}\n`;
try { try {
structures += structure.generate(); structures += structure.generate();
@ -167,7 +167,7 @@ function makeCompiledCode(outputDir: string) {
} }
// Structure // Structure
const name = structure.getName(); const name = structure.name;
declarations += `// ${name}\n`; declarations += `// ${name}\n`;
try { try {
declarations += structure.generateCode().trim(); declarations += structure.generateCode().trim();

View File

@ -116,29 +116,29 @@ export function load(target: Struct, name: string, isExtended: boolean) {
case 'vtable-size': { case 'vtable-size': {
// Set VTable Size // Set VTable Size
if (!isExtended) { if (!isExtended) {
target.setVTableSize(safeParseInt(args)); target.getVTable().setSize(safeParseInt(args));
} }
break; break;
} }
case 'vtable': { case 'vtable': {
// Set VTable Address // Set VTable Address
target.ensureVTable(); const vtable = target.getVTable();
if (!isExtended && args.length > 0) { if (!isExtended && args.length > 0) {
target.setVTableAddress(safeParseInt(args)); vtable.setAddress(safeParseInt(args));
} }
break; break;
} }
case 'property': { case 'property': {
// Add Property // Add Property
const info = parseProperty(args); const info = parseProperty(args);
target.addProperty(new Property(info.offset, info.type, info.name, target.getName())); target.addProperty(new Property(info.offset, info.type, info.name, target.name));
break; break;
} }
case 'static-property': { case 'static-property': {
// Add Static Property // Add Static Property
if (!isExtended) { if (!isExtended) {
const info = parseProperty(args); const info = parseProperty(args);
target.addStaticProperty(new StaticProperty(info.offset, info.type, info.name, target.getName())); target.addStaticProperty(new StaticProperty(info.offset, info.type, info.name, target.name));
} }
break; break;
} }
@ -150,14 +150,14 @@ export function load(target: Struct, name: string, isExtended: boolean) {
} }
case 'virtual-method': { case 'virtual-method': {
// Add Virtual Method // Add Virtual Method
const method = parseMethod(args, target.getName(), true, isExtended); const method = parseMethod(args, target.name, true, isExtended);
target.addMethod(method, true); target.addMethod(method, true);
break; break;
} }
case 'static-method': { case 'static-method': {
// Add Static Method // Add Static Method
if (!isExtended) { if (!isExtended) {
const method = parseMethod(args, target.getName(), false, false); const method = parseMethod(args, target.name, false, false);
target.addMethod(method, false); target.addMethod(method, false);
} }
break; break;
@ -165,7 +165,7 @@ export function load(target: Struct, name: string, isExtended: boolean) {
case 'constructor': { case 'constructor': {
// Constructor // Constructor
if (!isExtended) { if (!isExtended) {
let data = `${target.getName()} *constructor`; let data = `${target.name} *constructor`;
if (args.startsWith('(')) { if (args.startsWith('(')) {
// No Custom Name // No Custom Name
data += ' '; data += ' ';
@ -174,14 +174,22 @@ export function load(target: Struct, name: string, isExtended: boolean) {
data += '_'; data += '_';
} }
data += args; data += args;
const method = parseMethod(data, target.getName(), true, false); const method = parseMethod(data, target.name, true, false);
target.addMethod(method, false); target.addMethod(method, false);
} }
break; break;
} }
case 'vtable-destructor-offset': { case 'vtable-destructor-offset': {
// Set VTable Destructor Offset // Set VTable Destructor Offset
target.setVTableDestructorOffset(safeParseInt(args)); target.getVTable().setDestructorOffset(safeParseInt(args));
break;
}
case 'mark-as-simple': {
// Mark As Simple
if (isExtended) {
throw new Error('Cannot Extend Simple Structure');
}
target.markAsSimple();
break; break;
} }
default: { default: {

View File

@ -4,7 +4,7 @@ import { Property, StaticProperty } from './property';
import { VTable } from './vtable'; import { VTable } from './vtable';
export class Struct { export class Struct {
readonly #name: string; readonly name: string;
#vtable: VTable | null; #vtable: VTable | null;
readonly #methods: Method[]; readonly #methods: Method[];
readonly #properties: Property[]; readonly #properties: Property[];
@ -12,10 +12,11 @@ export class Struct {
readonly #dependencies: string[]; readonly #dependencies: string[];
readonly #staticProperties: StaticProperty[]; readonly #staticProperties: StaticProperty[];
#directParent: string | null; #directParent: string | null;
#isSimple: boolean;
// Constructor // Constructor
constructor(name: string) { constructor(name: string) {
this.#name = name; this.name = name;
this.#methods = []; this.#methods = [];
this.#properties = []; this.#properties = [];
this.#vtable = null; this.#vtable = null;
@ -23,6 +24,7 @@ export class Struct {
this.#dependencies = []; this.#dependencies = [];
this.#staticProperties = []; this.#staticProperties = [];
this.#directParent = null; this.#directParent = null;
this.#isSimple = false;
} }
// Dependencies // Dependencies
@ -34,39 +36,29 @@ export class Struct {
} }
// Ensure VTable Exists // Ensure VTable Exists
ensureVTable() { getVTable() {
if (this.#vtable === null) { if (this.#vtable === null) {
this.#vtable = new VTable(this.#name); this.#vtable = new VTable(this.name);
this.addProperty(this.#vtable.property); this.addProperty(this.#vtable.property);
} }
} return this.#vtable;
// Set VTable Destructor Offset
setVTableDestructorOffset(offset: number) {
this.ensureVTable();
this.#vtable!.setDestructorOffset(offset);
} }
// Setters // Setters
setSize(size: number) { setSize(size: number) {
this.#size = size; this.#size = size;
} }
// Getters
getName() {
return this.#name;
}
// Add Method // Add Method
addMethod(method: Method, isVirtual: boolean) { addMethod(method: Method, isVirtual: boolean) {
if (method.returnType !== this.#name && method.returnType in STRUCTURE_FILES) { if (method.returnType !== this.name && method.returnType in STRUCTURE_FILES) {
this.#addDependency(method.returnType); this.#addDependency(method.returnType);
} }
if (isVirtual) { if (isVirtual) {
if (method.self !== this.#name) { if (method.self !== this.name) {
throw new Error(); throw new Error();
} }
this.ensureVTable(); this.getVTable().add(method);
this.#vtable!.add(method);
} else { } else {
if (method.isInherited) { if (method.isInherited) {
this.#addDependency(method.self); this.#addDependency(method.self);
@ -88,14 +80,21 @@ export class Struct {
this.#staticProperties.push(property); this.#staticProperties.push(property);
} }
// Configure VTable // "Simple" Structures
setVTableSize(size: number) { markAsSimple() {
this.ensureVTable(); this.#isSimple = true;
this.#vtable!.setSize(size);
} }
setVTableAddress(address: number) { #checkSimple() {
this.ensureVTable(); const checks = [
this.#vtable!.setAddress(address); ['Cannot Inherit', this.#directParent !== null],
['Must Have A Defined Size', this.#size === null],
['Cannot Have A VTable', this.#vtable !== null]
];
for (const check of checks) {
if (check[1]) {
throw new Error('Simple Structures ' + check[0]);
}
}
} }
// Generate Properties // Generate Properties
@ -127,7 +126,9 @@ export class Struct {
if (lastProperty) { if (lastProperty) {
paddingSize += ` - sizeof(${lastProperty.type()})`; paddingSize += ` - sizeof(${lastProperty.type()})`;
} }
out += `${INDENT}uchar ${INTERNAL}padding${i}[${paddingSize}];\n`; if (!this.#isSimple) {
out += `${INDENT}uchar ${INTERNAL}padding${i}[${paddingSize}];\n`;
}
// The Actual Property // The Actual Property
if (property !== sizeProperty) { if (property !== sizeProperty) {
@ -175,6 +176,13 @@ export class Struct {
} }
} }
} }
// Allocation Method
if (this.#size !== null) {
// THIS DOES NOT CONSTRUCT THE OBJECT
out += `${INDENT}static ${this.name} *allocate() {\n`;
out += `${INDENT}${INDENT}return (${this.name} *) ::operator new(sizeof(${this.name}));\n`;
out += `${INDENT}}\n`;
}
// Return // Return
out += '#endif\n'; out += '#endif\n';
return out; return out;
@ -184,6 +192,11 @@ export class Struct {
generate() { generate() {
let out = ''; let out = '';
// Check "Simple" Status
if (this.#isSimple) {
this.#checkSimple();
}
// VTable // VTable
if (this.#vtable !== null) { if (this.#vtable !== null) {
out += this.#vtable.generate(); out += this.#vtable.generate();
@ -214,16 +227,16 @@ export class Struct {
out += '#endif\n'; out += '#endif\n';
// Structure // Structure
out += `struct ${this.#name} {\n`; out += `struct ${this.name} final {\n`;
out += this.#generateProperties(); out += this.#generateProperties();
out += this.#generateMethods(); out += this.#generateMethods();
for (const property of this.#staticProperties) { for (const property of this.#staticProperties) {
// Static Property References // Static Property References
out += `${INDENT}static ${property.referenceDefinition(false)};\n`; out += `${INDENT}static ${property.referenceDefinition(false)};\n`;
} }
if (this.#size === null) { if (!this.#isSimple) {
// Prevent Manually Copying/Allocating Structure With Undefined // Disable Construction Of Complex Structures
out += preventConstruction(this.#name); out += preventConstruction(this.name);
} }
out += `};\n`; out += `};\n`;
@ -232,12 +245,12 @@ export class Struct {
const property = this.#properties[i]!; const property = this.#properties[i]!;
const name = property.name(); const name = property.name();
const offset = property.offset; const offset = property.offset;
out += `static_assert(offsetof(${this.#name}, ${name}) == ${toHex(offset)}, "Invalid Offset");\n`; out += `static_assert(offsetof(${this.name}, ${name}) == ${toHex(offset)}, "Invalid Offset");\n`;
} }
// Sanity Check Size // Sanity Check Size
if (this.#size !== null) { if (this.#size !== null) {
out += assertSize(this.#name, this.#size); out += assertSize(this.name, this.#size);
} }
// Return // Return

View File

@ -133,7 +133,7 @@ export class VTable {
// Structure // Structure
out += `typedef struct ${this.#getName()} ${this.#getName()};\n`; out += `typedef struct ${this.#getName()} ${this.#getName()};\n`;
out += `struct ${this.#getName()} {\n`; out += `struct ${this.#getName()} final {\n`;
for (let i = 0; i < methods.length; i++) { for (let i = 0; i < methods.length; i++) {
const info = methods[i]; const info = methods[i];
if (info) { if (info) {

View File

@ -13,6 +13,7 @@
<item>static-method</item> <item>static-method</item>
<item>constructor</item> <item>constructor</item>
<item>vtable-destructor-offset</item> <item>vtable-destructor-offset</item>
<item>mark-as-simple</item>
</list> </list>
<list name="types"> <list name="types">
<item>const</item> <item>const</item>

View File

@ -2,7 +2,7 @@ syntax def "\.def$"
comment "//" comment "//"
# Commands # Commands
color magenta "\<(extends|size|vtable(-size|-destructor-offset)?|property|static-property|((static|virtual)-)?method|constructor)\>" color magenta "\<(extends|size|vtable(-size|-destructor-offset)?|property|static-property|((static|virtual)-)?method|constructor|mark-as-simple)\>"
# Types # Types
color green "\<((u?(char|short|int))|float|bool|void|std::(string|vector|map))\>" color green "\<((u?(char|short|int))|float|bool|void|std::(string|vector|map))\>"

View File

@ -34,13 +34,13 @@
<key>name</key> <key>name</key>
<string>keyword.control.symbol-processor</string> <string>keyword.control.symbol-processor</string>
<key>match</key> <key>match</key>
<string>\b(extends|size|vtable-size|vtable-destructor-offset|vtable|property|static-property|method|virtual-method|static-method|constructor)\b</string> <string>\b(extends|size|vtable-size|vtable-destructor-offset|vtable|property|static-property|method|virtual-method|static-method|constructor|mark-as-simple)\b</string>
</dict> </dict>
<dict> <dict>
<key>name</key> <key>name</key>
<string>storage.type.symbol-processor</string> <string>storage.type.symbol-processor</string>
<key>match</key> <key>match</key>
<string>\b(const|char|uchar|short|ushort|int|uint|float|bool|void|std::string|std::vector|std::map)\b</string> <string>\b(const|char|uchar|short|ushort|int|uint|float|bool|void|std::string|std::vector|std::map|unsigned|long)\b</string>
</dict> </dict>
<dict> <dict>
<key>name</key> <key>name</key>