Fancy C++ Methods!
This commit is contained in:
parent
dcc35de95c
commit
6ee26aad80
@ -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();
|
||||
}
|
15
src/index.ts
15
src/index.ts
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user