Fancy C++ Methods!

This commit is contained in:
TheBrokenRail 2024-05-03 22:20:03 -04:00
parent dcc35de95c
commit 6ee26aad80
6 changed files with 94 additions and 41 deletions

View File

@ -111,4 +111,13 @@ export function prependArg(args: string, arg: string) {
arg += ', ';
}
return '(' + arg + args.substring(1);
}
export function removeFirstArg(args: string) {
let index = args.indexOf(',');
if (index === -1) {
index = args.indexOf(')');
} else {
index++;
}
return '(' + args.substring(index).trim();
}

View File

@ -164,12 +164,11 @@ function makeMainHeader(output: string) {
result += '#include <map>\n';
result += '\n// Warnings\n';
result += '#pragma GCC diagnostic push\n';
result += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\n\n';
result += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\n';
result += '#pragma GCC diagnostic ignored "-Wshadow"\n\n';
result += makeHeaderPart();
result += '\n// Cleanup Warnings\n';
result += '#pragma GCC diagnostic pop\n';
result += '\n// Array Of All Method Symbols\n';
result += 'extern void *_all_method_symbols[];\n';
fs.writeFileSync(output, result);
}
makeMainHeader(headerOutput);
@ -212,16 +211,6 @@ function makeCompiledCode(output: string) {
result += init;
result += '}\n\n';
result += declarations;
result += '\n// Setup Methods Array\n';
result += 'void *_all_method_symbols[] = {\n';
for (const structure of structureObjects) {
const methods = structure.getMethodSymbols();
for (const method of methods) {
result += `${INDENT}&${method},\n`;
}
}
result += `${INDENT}nullptr\n`;
result += '};\n';
fs.writeFileSync(output, result);
}
makeCompiledCode(sourceOutput);

View File

@ -47,7 +47,7 @@ function parseMethod(args: string, self: string, insertSelfArg: boolean, isInher
methodArgs = prependArg(methodArgs, selfArg);
}
const address = safeParseInt(end[1]!);
return new Method(self, name, type, methodArgs, address, isInherited);
return new Method(self, name, type, methodArgs, address, isInherited, !insertSelfArg);
}
// Load Structure
@ -144,7 +144,7 @@ export function load(target: Struct, name: string, isExtended: boolean) {
}
case 'method': {
// Add Method
const method = parseMethod(args, target.getName(), true, isExtended);
const method = parseMethod(args, name, true, isExtended);
target.addMethod(method, false);
break;
}

View File

@ -7,15 +7,19 @@ export class Method {
readonly args: string;
readonly address: number;
readonly isInherited: boolean;
readonly hasVarargs: boolean;
readonly isStatic: boolean;
// Constructor
constructor(self: string, name: string, returnType: string, args: string, address: number, isInherited: boolean) {
constructor(self: string, name: string, returnType: string, args: string, address: number, isInherited: boolean, isStatic: boolean) {
this.self = self;
this.shortName = name;
this.returnType = returnType;
this.args = args;
this.address = address;
this.isInherited = isInherited;
this.hasVarargs = this.args.includes('...');
this.isStatic = isStatic;
}
// Get Type
@ -38,9 +42,9 @@ export class Method {
out += `typedef ${returnType}(*${this.getType()})${this.args};\n`;
// Fancy Overwrite Does Not Support Varargs
if (!this.args.includes('...')) {
if (!this.hasVarargs) {
// Overwrite Helper
out += `#define _overwrite_helper_for_${this.getName()}(method, original) \\\n`;
out += `#define __overwrite_helper_for_${this.getName()}(method, original) \\\n`;
out += `${INDENT}[]${this.args} { \\\n`;
out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}method(${['original'].concat(getArgNames(this.args)).join(', ')}); \\\n`;
out += `${INDENT}}\n`;
@ -57,7 +61,7 @@ export class Method {
// Generate "New Method" Test
generateNewMethodTest(parent: string | null, prefix: string, suffix: string) {
let out = `#define _is_new_method_${this.getName()}() (`;
let out = `#define __is_new_method_${this.getName()}() (`;
if (!this.isInherited) {
out += 'true';
} else {

View File

@ -1,4 +1,4 @@
import { INDENT, STRUCTURE_FILES, toHex, assertSize } from './common';
import { INDENT, STRUCTURE_FILES, toHex, assertSize, formatType, getArgNames, removeFirstArg } from './common';
import { Method } from './method';
import { Property, StaticProperty } from './property';
import { VTable } from './vtable';
@ -58,13 +58,19 @@ export class Struct {
// Add Method
addMethod(method: Method, isVirtual: boolean) {
if (method.self !== this.#name) {
throw new Error();
if (method.returnType !== this.#name && method.returnType in STRUCTURE_FILES) {
this.#addDependency(method.returnType);
}
if (isVirtual) {
if (method.self !== this.#name) {
throw new Error();
}
this.ensureVTable();
this.#vtable!.add(method);
} else {
if (method.isInherited) {
this.#addDependency(method.self);
}
this.#methods.push(method);
}
}
@ -131,6 +137,55 @@ export class Struct {
return out;
}
// Generate C++ Method Shortcuts
#generateMethods() {
let out = '';
// Normal Methods
const getArgsOuter = (method: Method) => {
let out = method.args;
if (!method.isStatic) {
// Remove "self"
out = removeFirstArg(out);
}
return out;
};
const getArgsInner = (method: Method) => {
const list = getArgNames(method.args);
if (!method.isStatic) {
// Replace "self" With "this"
list[0] = `(${method.self} *) this`;
}
return list.join(', ');
};
for (const method of this.#methods) {
if (!method.hasVarargs) {
const returnType = method.returnType;
const shortName = method.shortName;
const fullName = method.getName();
const args = getArgsOuter(method);
out += `${INDENT}inline ${formatType(returnType)}${shortName}${args} { \\\n`;
out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}${fullName}(${getArgsInner(method)});\n`;
out += `${INDENT}}\n`;
}
}
// Virtual Methods
if (this.#vtable !== null) {
const virtualMethods = this.#vtable.getMethods();
for (const method of virtualMethods) {
if (method && !method.hasVarargs) {
const returnType = method.returnType;
const shortName = method.shortName;
const args = getArgsOuter(method);
out += `${INDENT}inline ${formatType(returnType)}${shortName}${args} { \\\n`;
out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}this->vtable->${shortName}(${getArgsInner(method)});\n`;
out += `${INDENT}}\n`;
}
}
}
// Return
return out;
}
// Generate Header
generate() {
let out = '';
@ -144,9 +199,11 @@ export class Struct {
// Methods
for (const method of this.#methods) {
out += method.generateTypedef();
out += `extern ${method.generateDefinition()}`;
out += method.generateNewMethodTest(this.#directParent, '', '');
if (!method.isInherited) {
out += method.generateTypedef();
out += `extern ${method.generateDefinition()}`;
out += method.generateNewMethodTest(this.#directParent, '', '');
}
}
// VTable
@ -162,6 +219,7 @@ export class Struct {
// Structure
out += `struct ${this.#name} {\n`;
out += this.#generateProperties();
out += this.#generateMethods();
out += `};\n`;
// Sanity Check Offsets
@ -199,8 +257,10 @@ export class Struct {
// Methods
for (const method of this.#methods) {
init += `${INDENT}${method.getName()} = (${method.getType()}) ${toHex(method.address)};\n`;
declarations += method.generateDefinition();
if (!method.isInherited) {
init += `${INDENT}${method.getName()} = (${method.getType()}) ${toHex(method.address)};\n`;
declarations += method.generateDefinition();
}
}
// VTable
@ -221,15 +281,6 @@ export class Struct {
return {functions: declarations, init};
}
// Get Method Symbols
getMethodSymbols() {
const ret = [];
for (const method of this.#methods) {
ret.push(method.getName());
}
return ret;
}
// Set Direct Parent (Used For "New Method" Testing)
setDirectParent(directParent: string) {
this.#directParent = directParent;

View File

@ -71,15 +71,15 @@ export class VTable {
}
// Get Full Methods Table
#getMethods() {
getMethods() {
// Copy Array
const out = [];
out.push(...this.#methods);
// Add Destructors (https://stackoverflow.com/a/17960941)
const destructor_return = `${this.#self} *`;
const destructor_args = `(${getSelfArg(this.#self)})`;
this.#add(out, new Method(this.#self, 'destructor_complete', destructor_return, destructor_args, 0x0 + this.#destructorOffset, false));
this.#add(out, new Method(this.#self, 'destructor_deleting', destructor_return, destructor_args, 0x4 + this.#destructorOffset, false));
this.#add(out, new Method(this.#self, 'destructor_complete', destructor_return, destructor_args, 0x0 + this.#destructorOffset, false, false));
this.#add(out, new Method(this.#self, 'destructor_deleting', destructor_return, destructor_args, 0x4 + this.#destructorOffset, false, false));
// Return
return out;
}
@ -92,7 +92,7 @@ export class VTable {
this.#check();
// Method Prototypes
const methods = this.#getMethods();
const methods = this.getMethods();
for (let i = 0; i < methods.length; i++) {
const info = methods[i];
if (info) {
@ -158,7 +158,7 @@ export class VTable {
init += `${INDENT}${this.#getName()}_base = (${this.#getName()} *) ${toHex(this.#address)};\n`;
declarations += `${this.#getName()} *${this.#getName()}_base;\n`;
// Methods
const methods = this.#getMethods();
const methods = this.getMethods();
for (let i = 0; i < methods.length; i++) {
const info = methods[i];
if (info) {