Auto-Generate Custom Wrappers
This commit is contained in:
parent
fd720d2321
commit
4018ff0fb9
94
data/custom.h
Normal file
94
data/custom.h
Normal file
@ -0,0 +1,94 @@
|
||||
// Get Custom Data
|
||||
template <typename Data>
|
||||
Data *custom_get(typename Data::__Self *self) {
|
||||
return (Data *) (self + 1);
|
||||
}
|
||||
|
||||
// Duplicate VTable
|
||||
template <typename T>
|
||||
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 <typename A, typename B>
|
||||
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;
|
||||
}
|
||||
};
|
@ -65,5 +65,6 @@ public:
|
||||
|
||||
// Disable Warnings
|
||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
|
||||
{{ main }}
|
11
data/out.h
11
data/out.h
@ -21,6 +21,13 @@
|
||||
#include <map>
|
||||
#include <cstring>
|
||||
|
||||
// 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
|
||||
|
@ -10,6 +10,8 @@ export const STRUCTURE_FILES: Record<string, string> = {};
|
||||
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) {
|
||||
|
128
src/custom.ts
Normal file
128
src/custom.ts
Normal file
@ -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;
|
||||
}
|
||||
};
|
@ -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}`);
|
||||
}
|
||||
|
@ -53,6 +53,9 @@ export class Method {
|
||||
}
|
||||
return args;
|
||||
}
|
||||
getDirectCall() {
|
||||
return this.getName() + '->get(false)';
|
||||
}
|
||||
|
||||
// Typedefs
|
||||
generateTypedefs() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,9 +89,11 @@ export class VTable {
|
||||
new Method(this.#self, 'destructor_deleting', destructor_return, destructor_args, 0x4 + this.#destructorOffset, false, false)
|
||||
);
|
||||
}
|
||||
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
|
||||
|
@ -14,6 +14,7 @@
|
||||
<item>constructor</item>
|
||||
<item>vtable-destructor-offset</item>
|
||||
<item>mark-as-simple</item>
|
||||
<item>generate-custom-wrapper</item>
|
||||
</list>
|
||||
<list name="types">
|
||||
<item>const</item>
|
||||
|
@ -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))\>"
|
||||
|
@ -34,7 +34,7 @@
|
||||
<key>name</key>
|
||||
<string>keyword.control.symbol-processor</string>
|
||||
<key>match</key>
|
||||
<string>\b(extends|size|vtable-size|vtable-destructor-offset|vtable|property|static-property|method|virtual-method|static-method|constructor|mark-as-simple)\b</string>
|
||||
<string>\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</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
|
Loading…
x
Reference in New Issue
Block a user