Fancy C++ Methods!
This commit is contained in:
parent
dcc35de95c
commit
6ee26aad80
@ -112,3 +112,12 @@ export function prependArg(args: string, arg: string) {
|
|||||||
}
|
}
|
||||||
return '(' + arg + args.substring(1);
|
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 += '#include <map>\n';
|
||||||
result += '\n// Warnings\n';
|
result += '\n// Warnings\n';
|
||||||
result += '#pragma GCC diagnostic push\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 += makeHeaderPart();
|
||||||
result += '\n// Cleanup Warnings\n';
|
result += '\n// Cleanup Warnings\n';
|
||||||
result += '#pragma GCC diagnostic pop\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);
|
fs.writeFileSync(output, result);
|
||||||
}
|
}
|
||||||
makeMainHeader(headerOutput);
|
makeMainHeader(headerOutput);
|
||||||
@ -212,16 +211,6 @@ function makeCompiledCode(output: string) {
|
|||||||
result += init;
|
result += init;
|
||||||
result += '}\n\n';
|
result += '}\n\n';
|
||||||
result += declarations;
|
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);
|
fs.writeFileSync(output, result);
|
||||||
}
|
}
|
||||||
makeCompiledCode(sourceOutput);
|
makeCompiledCode(sourceOutput);
|
||||||
|
@ -47,7 +47,7 @@ function parseMethod(args: string, self: string, insertSelfArg: boolean, isInher
|
|||||||
methodArgs = prependArg(methodArgs, selfArg);
|
methodArgs = prependArg(methodArgs, selfArg);
|
||||||
}
|
}
|
||||||
const address = safeParseInt(end[1]!);
|
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
|
// Load Structure
|
||||||
@ -144,7 +144,7 @@ export function load(target: Struct, name: string, isExtended: boolean) {
|
|||||||
}
|
}
|
||||||
case 'method': {
|
case 'method': {
|
||||||
// Add Method
|
// Add Method
|
||||||
const method = parseMethod(args, target.getName(), true, isExtended);
|
const method = parseMethod(args, name, true, isExtended);
|
||||||
target.addMethod(method, false);
|
target.addMethod(method, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,19 @@ export class Method {
|
|||||||
readonly args: string;
|
readonly args: string;
|
||||||
readonly address: number;
|
readonly address: number;
|
||||||
readonly isInherited: boolean;
|
readonly isInherited: boolean;
|
||||||
|
readonly hasVarargs: boolean;
|
||||||
|
readonly isStatic: boolean;
|
||||||
|
|
||||||
// Constructor
|
// 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.self = self;
|
||||||
this.shortName = name;
|
this.shortName = name;
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.isInherited = isInherited;
|
this.isInherited = isInherited;
|
||||||
|
this.hasVarargs = this.args.includes('...');
|
||||||
|
this.isStatic = isStatic;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Type
|
// Get Type
|
||||||
@ -38,9 +42,9 @@ export class Method {
|
|||||||
out += `typedef ${returnType}(*${this.getType()})${this.args};\n`;
|
out += `typedef ${returnType}(*${this.getType()})${this.args};\n`;
|
||||||
|
|
||||||
// Fancy Overwrite Does Not Support Varargs
|
// Fancy Overwrite Does Not Support Varargs
|
||||||
if (!this.args.includes('...')) {
|
if (!this.hasVarargs) {
|
||||||
// Overwrite Helper
|
// 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}[]${this.args} { \\\n`;
|
||||||
out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}method(${['original'].concat(getArgNames(this.args)).join(', ')}); \\\n`;
|
out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}method(${['original'].concat(getArgNames(this.args)).join(', ')}); \\\n`;
|
||||||
out += `${INDENT}}\n`;
|
out += `${INDENT}}\n`;
|
||||||
@ -57,7 +61,7 @@ export class Method {
|
|||||||
|
|
||||||
// Generate "New Method" Test
|
// Generate "New Method" Test
|
||||||
generateNewMethodTest(parent: string | null, prefix: string, suffix: string) {
|
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) {
|
if (!this.isInherited) {
|
||||||
out += 'true';
|
out += 'true';
|
||||||
} else {
|
} 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 { Method } from './method';
|
||||||
import { Property, StaticProperty } from './property';
|
import { Property, StaticProperty } from './property';
|
||||||
import { VTable } from './vtable';
|
import { VTable } from './vtable';
|
||||||
@ -58,13 +58,19 @@ export class Struct {
|
|||||||
|
|
||||||
// Add Method
|
// Add Method
|
||||||
addMethod(method: Method, isVirtual: boolean) {
|
addMethod(method: Method, isVirtual: boolean) {
|
||||||
|
if (method.returnType !== this.#name && method.returnType in STRUCTURE_FILES) {
|
||||||
|
this.#addDependency(method.returnType);
|
||||||
|
}
|
||||||
|
if (isVirtual) {
|
||||||
if (method.self !== this.#name) {
|
if (method.self !== this.#name) {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
if (isVirtual) {
|
|
||||||
this.ensureVTable();
|
this.ensureVTable();
|
||||||
this.#vtable!.add(method);
|
this.#vtable!.add(method);
|
||||||
} else {
|
} else {
|
||||||
|
if (method.isInherited) {
|
||||||
|
this.#addDependency(method.self);
|
||||||
|
}
|
||||||
this.#methods.push(method);
|
this.#methods.push(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,6 +137,55 @@ export class Struct {
|
|||||||
return out;
|
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 Header
|
||||||
generate() {
|
generate() {
|
||||||
let out = '';
|
let out = '';
|
||||||
@ -144,10 +199,12 @@ export class Struct {
|
|||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
for (const method of this.#methods) {
|
for (const method of this.#methods) {
|
||||||
|
if (!method.isInherited) {
|
||||||
out += method.generateTypedef();
|
out += method.generateTypedef();
|
||||||
out += `extern ${method.generateDefinition()}`;
|
out += `extern ${method.generateDefinition()}`;
|
||||||
out += method.generateNewMethodTest(this.#directParent, '', '');
|
out += method.generateNewMethodTest(this.#directParent, '', '');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VTable
|
// VTable
|
||||||
if (this.#vtable !== null) {
|
if (this.#vtable !== null) {
|
||||||
@ -162,6 +219,7 @@ export class Struct {
|
|||||||
// Structure
|
// Structure
|
||||||
out += `struct ${this.#name} {\n`;
|
out += `struct ${this.#name} {\n`;
|
||||||
out += this.#generateProperties();
|
out += this.#generateProperties();
|
||||||
|
out += this.#generateMethods();
|
||||||
out += `};\n`;
|
out += `};\n`;
|
||||||
|
|
||||||
// Sanity Check Offsets
|
// Sanity Check Offsets
|
||||||
@ -199,9 +257,11 @@ export class Struct {
|
|||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
for (const method of this.#methods) {
|
for (const method of this.#methods) {
|
||||||
|
if (!method.isInherited) {
|
||||||
init += `${INDENT}${method.getName()} = (${method.getType()}) ${toHex(method.address)};\n`;
|
init += `${INDENT}${method.getName()} = (${method.getType()}) ${toHex(method.address)};\n`;
|
||||||
declarations += method.generateDefinition();
|
declarations += method.generateDefinition();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VTable
|
// VTable
|
||||||
if (this.#vtable !== null) {
|
if (this.#vtable !== null) {
|
||||||
@ -221,15 +281,6 @@ export class Struct {
|
|||||||
return {functions: declarations, init};
|
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)
|
// Set Direct Parent (Used For "New Method" Testing)
|
||||||
setDirectParent(directParent: string) {
|
setDirectParent(directParent: string) {
|
||||||
this.#directParent = directParent;
|
this.#directParent = directParent;
|
||||||
|
@ -71,15 +71,15 @@ export class VTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Full Methods Table
|
// Get Full Methods Table
|
||||||
#getMethods() {
|
getMethods() {
|
||||||
// Copy Array
|
// Copy Array
|
||||||
const out = [];
|
const out = [];
|
||||||
out.push(...this.#methods);
|
out.push(...this.#methods);
|
||||||
// Add Destructors (https://stackoverflow.com/a/17960941)
|
// Add Destructors (https://stackoverflow.com/a/17960941)
|
||||||
const destructor_return = `${this.#self} *`;
|
const destructor_return = `${this.#self} *`;
|
||||||
const destructor_args = `(${getSelfArg(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_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));
|
this.#add(out, new Method(this.#self, 'destructor_deleting', destructor_return, destructor_args, 0x4 + this.#destructorOffset, false, false));
|
||||||
// Return
|
// Return
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ export class VTable {
|
|||||||
this.#check();
|
this.#check();
|
||||||
|
|
||||||
// Method Prototypes
|
// Method Prototypes
|
||||||
const methods = this.#getMethods();
|
const methods = this.getMethods();
|
||||||
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) {
|
||||||
@ -158,7 +158,7 @@ export class VTable {
|
|||||||
init += `${INDENT}${this.#getName()}_base = (${this.#getName()} *) ${toHex(this.#address)};\n`;
|
init += `${INDENT}${this.#getName()}_base = (${this.#getName()} *) ${toHex(this.#address)};\n`;
|
||||||
declarations += `${this.#getName()} *${this.#getName()}_base;\n`;
|
declarations += `${this.#getName()} *${this.#getName()}_base;\n`;
|
||||||
// Methods
|
// Methods
|
||||||
const methods = this.#getMethods();
|
const methods = this.getMethods();
|
||||||
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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user