diff --git a/data/custom.h b/data/custom.h new file mode 100644 index 0000000..5368580 --- /dev/null +++ b/data/custom.h @@ -0,0 +1,94 @@ +// Get Custom Data +template +Data *custom_get(typename Data::__Self *self) { + return (Data *) (self + 1); +} + +// Duplicate VTable +template +T *__dup_vtable(const T *vtable) { + // Get Size + constexpr size_t rtti_size = sizeof(void *); + const unsigned char *const real_vtable = ((const unsigned char *) vtable) - rtti_size; + constexpr size_t real_vtable_size = sizeof(T) + rtti_size; + // Allocate + unsigned char *const new_vtable = (unsigned char *) ::operator new(real_vtable_size); + T *ret = (T *) (new_vtable + rtti_size); + new (ret) T; + // Copy + memcpy(new_vtable, real_vtable, real_vtable_size); + // Return + return ret; +} + +// Base +template +struct __CustomBase { + // Properties + typedef A __Self; + typedef B __VTable; + __Self *const self; + + // Prevent Copying + __PREVENT_COPY(__CustomBase); + +#define get_self(ptr) (((__Self *) ptr) - 1) +#ifdef {{ BUILDING_SYMBOLS_GUARD }} + // Constructor + __CustomBase(): + self(get_self(this)) {} +#else + // Prevent Construction + __PREVENT_JUST_CONSTRUCTION(__CustomBase); +#endif + + // Destructor + virtual ~__CustomBase() = default; + + // Allocation + void *operator new(const size_t count) { + const size_t size = sizeof(__Self) + count; + __Self *out = (__Self *) ::operator new(size); + return (void *) custom_get<__CustomBase>(out); + } + void *operator new[](size_t) = delete; + + // Deallocation + void operator delete(__CustomBase *ptr, std::destroying_delete_t) { + if (ptr) { + __Self *self = ptr->self; + // This Calls The Actual Destructor + self->vtable->destructor_deleting(self); + } + } + void operator delete(void *ptr) { + if (ptr) { + // Issue During Construction, Just Free Memory + ::operator delete(get_self(ptr)); + } + } +#undef get_self + void operator delete[](void *) = delete; + +protected: + // VTable + virtual void __patch_vtable(__VTable *vtable) { + // Patch Destructors +#define patch(type) \ + vtable->type = [](__Self *self) { \ + custom_get<__CustomBase>(self)->~__CustomBase(); \ + return __VTable::base->type(self); \ + } + patch(destructor_complete); + patch(destructor_deleting); +#undef patch + } + void __set_vtable() { + static __VTable *vtable = nullptr; + if (!vtable) { + vtable = __dup_vtable(__VTable::base); + __patch_vtable(vtable); + } + this->self->vtable = vtable; + } +}; \ No newline at end of file diff --git a/data/out.cpp b/data/out.cpp index 40cbc3b..105ad51 100644 --- a/data/out.cpp +++ b/data/out.cpp @@ -65,5 +65,6 @@ public: // Disable Warnings #pragma GCC diagnostic ignored "-Winvalid-offsetof" +#pragma GCC diagnostic ignored "-Wshadow" {{ main }} \ No newline at end of file diff --git a/data/out.h b/data/out.h index 07243c1..b093568 100644 --- a/data/out.h +++ b/data/out.h @@ -21,6 +21,13 @@ #include #include +// Warnings +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" + +// Custom Wrappers +{{ include custom.h }} + // Shortcuts typedef unsigned char uchar; typedef unsigned short ushort; @@ -32,10 +39,6 @@ typedef unsigned int uint; // Extra Headers {{ extraHeaders }} -// Warnings -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" - {{ main }} // Cleanup Warnings diff --git a/src/common.ts b/src/common.ts index f6bcd31..f6cbfe0 100644 --- a/src/common.ts +++ b/src/common.ts @@ -10,6 +10,8 @@ export const STRUCTURE_FILES: Record = {}; export const COMMENT = '//'; export const INTERNAL = '__'; export const BUILDING_SYMBOLS_GUARD = 'BUILDING_SYMBOLS_LIB'; +export const CONSTRUCTOR_FUNCTION_NAME = 'constructor'; +export const SELF_ARG = 'self'; // Read Definition File export function readDefinition(name: string) { if (!STRUCTURE_FILES[name]) { @@ -115,7 +117,7 @@ export function safeParseInt(str: string) { } // Generate "Self" Argument For Functions export function getSelfArg(type: string) { - return `${type} *self`; + return type + ' *' + SELF_ARG; } // Prepend Argument To Function export function prependArg(args: string, arg: string) { diff --git a/src/custom.ts b/src/custom.ts new file mode 100644 index 0000000..5c9a172 --- /dev/null +++ b/src/custom.ts @@ -0,0 +1,128 @@ +import { CONSTRUCTOR_FUNCTION_NAME, formatType, forwardArguments, INDENT, INTERNAL, SELF_ARG } from './common'; +import { Struct } from './struct'; +import { structuresWithVTableAddress } from './vtable'; + +// Allow Making Custom Structures +export class CustomWrapperGenerator { + readonly #struct: Struct; + + // Constructor + constructor(struct: Struct) { + this.#struct = struct; + } + + // Getter + #getBase() { + return `${INTERNAL}CustomBase<${this.#struct.name}, ${this.#struct.getVTable().getName()}>`; + } + #getMethods() { + const out = []; + out.push(this.#struct.getMainConstructor()); + const methods = this.#struct.getVTable().getMethods(false); + for (const method of methods) { + if (method) { + out.push(method); + } + } + return out; + } + #getName() { + return 'Custom' + this.#struct.name; + } + + // Generate Header + generate() { + let out = ''; + + // Check + if (!structuresWithVTableAddress.includes(this.#struct.name)) { + throw new Error('Generating A Custom Wrapper Requires The VTable Address To Be Specified'); + } + + // Structure + const base = this.#getBase(); + out += `struct ${this.#getName()} : ${base} {\n`; + + // Methods + const methods = this.#getMethods(); + for (const method of methods) { + // Generate Method + const name = method.shortName; + out += INDENT; + if (name === CONSTRUCTOR_FUNCTION_NAME) { + out += 'explicit ' + this.#getName(); + } else { + out += 'virtual ' + formatType(method.returnType) + name; + } + out += method.getArgs(false) + ';\n'; + } + + // VTable + out += 'private:\n'; + out += INDENT + `virtual void ${INTERNAL}patch_vtable(${base}::${INTERNAL}VTable *) override;\n`; + out += '};\n'; + + // Return + return out; + } + + // Generate Code + generateCode() { + let out = ''; + + // Methods + const methods = this.#getMethods(); + for (const method of methods) { + const name = method.shortName; + const isConstructor = name === CONSTRUCTOR_FUNCTION_NAME; + // Generate Method + if (!isConstructor) { + out += formatType(method.returnType); + } + out += this.#getName() + '::'; + out += isConstructor ? this.#getName() : name; + const args = method.getArgs(false); + out += args + ' {\n'; + out += INDENT; + const forwardedArgs = forwardArguments(args, ['this->self']); + if (!isConstructor) { + // Virtual Method + if (method.doesReturnValue()) { + out += 'return '; + } + out += `${INTERNAL}VTable::base->${name}${forwardedArgs}`; + } else { + // Constructor + out += method.getDirectCall() + forwardedArgs; + } + out += ';\n'; + if (isConstructor) { + out += `${INDENT}${INTERNAL}set_vtable();\n`; + } + out += '}\n'; + } + + // VTable + const base = this.#getBase(); + out += `void ${this.#getName()}::${INTERNAL}patch_vtable(${base}::${INTERNAL}VTable *vtable) {\n`; + out += `${INDENT}${base}::${INTERNAL}patch_vtable(vtable);\n`; + for (const method of methods) { + const name = method.shortName; + if (name === CONSTRUCTOR_FUNCTION_NAME) { + continue; + } + // Patch Entry + out += `${INDENT}vtable->${name} = []${method.getArgs()} {\n`; + out += INDENT + INDENT; + if (method.doesReturnValue()) { + out += 'return '; + } + out += `custom_get<${this.#getName()}>(${SELF_ARG})->${name}${forwardArguments(method.getArgs(false), [])};\n`; + out += INDENT + '};\n'; + } + out += '}\n'; + + // Return + return out; + } +}; \ No newline at end of file diff --git a/src/loader.ts b/src/loader.ts index b6e7f9d..509bf76 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -1,4 +1,4 @@ -import { COMMENT, extendErrorMessage, EXTENSION, parseTypeAndName, readDefinition, safeParseInt, syntaxError } from './common'; +import { COMMENT, CONSTRUCTOR_FUNCTION_NAME, extendErrorMessage, EXTENSION, parseTypeAndName, readDefinition, safeParseInt, syntaxError } from './common'; import { Method } from './method'; import { Property, StaticProperty } from './property'; import { Struct } from './struct'; @@ -166,7 +166,7 @@ export function load(target: Struct, name: string, isExtended: boolean) { case 'constructor': { // Constructor if (!isExtended) { - let data = `${target.name} *constructor`; + let data = target.name + ' *' + CONSTRUCTOR_FUNCTION_NAME; if (args.startsWith('(')) { // No Custom Name } else { @@ -192,6 +192,13 @@ export function load(target: Struct, name: string, isExtended: boolean) { target.markAsSimple(); break; } + case 'generate-custom-wrapper': { + // Custom Wrapper + if (!isExtended) { + target.enableCustomWrapper(); + } + break; + } default: { throw new Error(`Invalid Command: ${command}`); } diff --git a/src/method.ts b/src/method.ts index 84a7f2c..525b0f4 100644 --- a/src/method.ts +++ b/src/method.ts @@ -53,6 +53,9 @@ export class Method { } return args; } + getDirectCall() { + return this.getName() + '->get(false)'; + } // Typedefs generateTypedefs() { diff --git a/src/struct.ts b/src/struct.ts index d863dd1..b24ab26 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -1,4 +1,5 @@ -import { INDENT, STRUCTURE_FILES, toHex, assertSize, INTERNAL, preventConstruction, BUILDING_SYMBOLS_GUARD, formatType, forwardArguments } from './common'; +import { INDENT, STRUCTURE_FILES, toHex, assertSize, INTERNAL, preventConstruction, BUILDING_SYMBOLS_GUARD, formatType, forwardArguments, CONSTRUCTOR_FUNCTION_NAME } from './common'; +import { CustomWrapperGenerator } from './custom'; import { Method } from './method'; import { Property, StaticProperty } from './property'; import { VTable } from './vtable'; @@ -14,6 +15,7 @@ export class Struct { readonly #staticProperties: StaticProperty[]; #directParent: string | null; #isSimple: boolean; + #custom: CustomWrapperGenerator | null; // Constructor constructor(name: string) { @@ -26,6 +28,7 @@ export class Struct { this.#staticProperties = []; this.#directParent = null; this.#isSimple = false; + this.#custom = null; } // Dependencies @@ -98,6 +101,20 @@ export class Struct { } } + // "Custom" Wrapper + enableCustomWrapper() { + this.getVTable(); + this.#custom = new CustomWrapperGenerator(this); + } + getMainConstructor() { + for (const method of this.#methods) { + if (method.shortName === CONSTRUCTOR_FUNCTION_NAME) { + return method; + } + } + throw new Error('Unable To Find Main Constructor'); + } + // Generate Properties #generateProperties() { // Sort Properties @@ -160,7 +177,7 @@ export class Struct { if (isVirtual) { out += `this->vtable->${method.shortName}`; } else { - out += `${method.getName()}->get(false)`; + out += method.getDirectCall(); } const extra = []; if (!method.isStatic) { @@ -240,6 +257,11 @@ export class Struct { } out += '};\n'; + // Generate Custom Wrapper + if (this.#custom !== null) { + out += this.#custom.generate(); + } + // Return return out; } @@ -278,6 +300,11 @@ export class Struct { out += vtable; } + // Generate Custom Wrapper + if (this.#custom !== null) { + out += this.#custom.generateCode(); + } + // Return return out; } diff --git a/src/vtable.ts b/src/vtable.ts index 94d784e..dcbb4fa 100644 --- a/src/vtable.ts +++ b/src/vtable.ts @@ -3,7 +3,7 @@ import { Method } from './method'; import { Property } from './property'; // A VTable -const structuresWithVTableAddress: string[] = []; +export const structuresWithVTableAddress: string[] = []; export class VTable { readonly #self: string; #address: number | null; @@ -22,7 +22,7 @@ export class VTable { this.#destructorOffset = 0; this.#destructors = []; // Create Property - this.property = new Property(0, this.#getName() + ' *', 'vtable', this.#self); + this.property = new Property(0, this.getName() + ' *', 'vtable', this.#self); } // Setters @@ -56,7 +56,7 @@ export class VTable { } // Get Structure Name - #getName() { + getName() { return this.#self + '_vtable'; } @@ -76,7 +76,7 @@ export class VTable { } // Get Full Methods Table - getMethods() { + getMethods(includeDestructors = true) { // Copy Array const out = []; out.push(...this.#methods); @@ -89,8 +89,10 @@ export class VTable { new Method(this.#self, 'destructor_deleting', destructor_return, destructor_args, 0x4 + this.#destructorOffset, false, false) ); } - for (const destructor of this.#destructors) { - this.#add(out, destructor); + if (includeDestructors) { + for (const destructor of this.#destructors) { + this.#add(out, destructor); + } } // Return return out; @@ -142,8 +144,8 @@ export class VTable { out += '#endif\n'; // Structure - out += `typedef struct ${this.#getName()} ${this.#getName()};\n`; - out += `struct ${this.#getName()} final {\n`; + out += `typedef struct ${this.getName()} ${this.getName()};\n`; + out += `struct ${this.getName()} final {\n`; for (let i = 0; i < methods.length; i++) { const info = methods[i]; if (info) { @@ -154,11 +156,11 @@ export class VTable { } if (this.#size === null) { // Prevent Construction - out += preventConstruction(this.#getName()); + out += preventConstruction(this.getName()); } if (this.#address !== null) { // Base - out += `${INDENT}static ${this.#getName()} *base;\n`; + out += `${INDENT}static ${this.getName()} *base;\n`; } out += '};\n'; @@ -176,7 +178,7 @@ export class VTable { // Pointers if (this.#address !== null) { // Base - out += `${this.#getName()} *${this.#getName()}::base = (${this.#getName()} *) ${toHex(this.#address)};\n`; + out += `${this.getName()} *${this.getName()}::base = (${this.getName()} *) ${toHex(this.#address)};\n`; } // Method Wrappers @@ -191,7 +193,7 @@ export class VTable { // Sanity Check Size if (this.#size !== null) { - out += assertSize(this.#getName(), this.#size); + out += assertSize(this.getName(), this.#size); } // Return diff --git a/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml b/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml index 2323239..7653aaa 100644 --- a/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml +++ b/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml @@ -14,6 +14,7 @@ constructor vtable-destructor-offset mark-as-simple + generate-custom-wrapper const diff --git a/syntax-highlighting/nano/symbol-processor.nanorc b/syntax-highlighting/nano/symbol-processor.nanorc index cca5e49..e971949 100644 --- a/syntax-highlighting/nano/symbol-processor.nanorc +++ b/syntax-highlighting/nano/symbol-processor.nanorc @@ -2,7 +2,7 @@ syntax def "\.def$" comment "//" # Commands -color magenta "\<(extends|size|vtable(-size|-destructor-offset)?|property|static-property|((static|virtual)-)?method|constructor|mark-as-simple)\>" +color magenta "\<(extends|size|vtable(-size|-destructor-offset)?|property|static-property|((static|virtual)-)?method|constructor|mark-as-simple|generate-custom-wrapper)\>" # Types color green "\<((u?(char|short|int))|float|bool|void|std::(string|vector|map))\>" diff --git a/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage b/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage index 0310d3a..9330432 100644 --- a/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage +++ b/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage @@ -34,7 +34,7 @@ name keyword.control.symbol-processor match - \b(extends|size|vtable-size|vtable-destructor-offset|vtable|property|static-property|method|virtual-method|static-method|constructor|mark-as-simple)\b + \b(extends|size|vtable-size|vtable-destructor-offset|vtable|property|static-property|method|virtual-method|static-method|constructor|mark-as-simple|generate-custom-wrapper)\b name