diff --git a/src/common.ts b/src/common.ts index eb34b30..f513f82 100644 --- a/src/common.ts +++ b/src/common.ts @@ -163,4 +163,31 @@ export class Size { isExact() { return this.#isExact; } +} +export function getArgNames(args: string) { + // Remove Parentheses + args = args.substring(1, args.length - 1); + if (args.length === 0) { + return []; + } + // Split + const argsList = args.split(','); + // Parse + const out = []; + for (let arg of argsList) { + arg = arg.trim(); + // Remove Type + const nameStart = Math.max(arg.lastIndexOf(' '), arg.lastIndexOf('*')) + 1; + arg = arg.substring(nameStart); + // Collect + out.push(arg); + } + // Return + return out; +} +export function prependArg(args: string, arg: string) { + if (args !== '()') { + arg += ', '; + } + return '(' + arg + args.substring(1); } \ No newline at end of file diff --git a/src/cpp.ts b/src/cpp.ts deleted file mode 100644 index 2c8c683..0000000 --- a/src/cpp.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { INDENT } from './common'; -import { SimpleProperty } from './property'; - -// List Of Supported C++ Types -const CPP_TYPES = [ - 'std::string', - 'std::vector<', - 'std::map<' -]; -const CPP_HEADERS = [ - 'string', - 'vector', - 'map' -]; - -// Get Fake Type Name -function isGeneric(type: string) { - return type.endsWith('<'); -} -function removeGeneric(type: string) { - if (isGeneric(type)) { - // Remove Generic Parameters - type = type.substring(0, type.length - 1); - } - return type; -} -function getFakeName(type: string) { - return removeGeneric(type).replaceAll('std::', '_std_'); -} - -// Generate C Type Definition -function generateStruct(type: string) { - // Get Information - const property = new SimpleProperty(0, type, 'fake'); - const size = property.propertySize(); - const alignment = property.propertyAlignment(); - // Generate - let struct = ''; - struct += `typedef struct __attribute__((aligned(${alignment}))) __attribute__((packed)) {\n`; - struct += `${INDENT}uchar data[${size}];\n`; - struct += `} ${getFakeName(type)};\n`; - return struct; -} - -// Turn A C++ Header Into A C/C++ Headeer -export function fixupCpp(data: string) { - // Replace Types - for (const type of CPP_TYPES) { - // Check If Type Is Generic - if (isGeneric(type)) { - // Generic - while (data.includes(type)) { - // Find Type - let index = data.indexOf(type); - // Replace Type - data = data.substring(0, index) + getFakeName(type) + '(' + data.substring(index + type.length); - // Replace > With ( - index = data.indexOf('>', index); - if (index !== -1) { - data = data.substring(0, index) + ')' + data.substring(index + 1); - } - } - } else { - // Normal - data = data.replaceAll(type, getFakeName(type)); - } - } - - // Setup Macros - let header = ''; - header += '// C++ Types\n'; - - // C++ Support - header += '#ifdef __cplusplus\n'; - for (const headerFile of CPP_HEADERS) { - header += `#include <${headerFile}>\n`; - } - for (const type of CPP_TYPES) { - const fakeName = getFakeName(type); - if (isGeneric(type)) { - header += `#define ${fakeName}(...) ${type}__VA_ARGS__>\n`; - } else { - header += `#define ${fakeName} ${type}\n`; - } - } - - // C Support - header += '#else\n'; - for (let type of CPP_TYPES) { - const fakeName = getFakeName(type); - header += generateStruct(type); - // Strip Generic Information - if (isGeneric(type)) { - type = removeGeneric(type); - header += `#define ${fakeName}(...) ${fakeName}\n`; - } - } - header += '#endif\n'; - - // Cleanup Macros - let footer = ''; - footer += '// Cleanup C++ Types\n'; - for (const type of CPP_TYPES) { - const fakeName = getFakeName(type); - footer += `#undef ${fakeName}\n`; - } - - // Return - return header + '\n' + data + '\n' + footer; -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ba7e56c..ba98e5f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,6 @@ import * as fs from 'node:fs'; import { STRUCTURE_FILES, EXTENSION, INDENT } from './common'; import { getStructure } from './map'; import { Struct } from './struct'; -import { fixupCpp } from './cpp'; // Arguments if (process.argv.length < 5) { @@ -158,25 +157,19 @@ function makeMainHeader(output: string) { for (const file of extraHeaders) { result += fs.readFileSync(file, {encoding: 'utf8'}); } - let core = ''; - core += '// C/C++ Support\n'; - core += '#ifndef __cplusplus\n'; - core += '#include \n'; - core += '#include \n'; - core += 'typedef uchar bool;\n'; - core += '#else\n'; - core += '#include \n'; - core += 'extern "C" {\n'; - core += '#pragma GCC diagnostic push\n'; - core += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\n'; - core += '#endif\n\n'; - core += makeHeaderPart(); - core += '\n// Cleanup C++ Support\n'; - core += '#ifdef __cplusplus\n'; - core += '#pragma GCC diagnostic pop\n'; - core += '}\n'; - core += '#endif\n'; - result += '\n' + fixupCpp(core); + result += '\n// Headers\n'; + result += '#include \n'; + result += '#include \n'; + result += '#include \n'; + result += '#include \n'; + result += '\n// Warnings\n'; + result += '#pragma GCC diagnostic push\n'; + result += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\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); @@ -219,6 +212,16 @@ 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); diff --git a/src/loader.ts b/src/loader.ts index de0697f..710ef96 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -1,4 +1,4 @@ -import { COMMENT, EXTENSION, getSelfArg, parseTypeAndName, readDefinition, safeParseInt, syntaxError } from './common'; +import { COMMENT, EXTENSION, getSelfArg, parseTypeAndName, prependArg, readDefinition, safeParseInt, syntaxError } from './common'; import { Method } from './method'; import { SimpleProperty, StaticProperty } from './property'; import { Struct } from './struct'; @@ -27,7 +27,7 @@ function parseProperty(args: string) { } // Parse Method -function parseMethod(args: string, self: string, insertSelfArg: boolean) { +function parseMethod(args: string, self: string, insertSelfArg: boolean, isInherited: boolean) { const argsStart = args.indexOf('('); if (argsStart === -1) { syntaxError('Cannot Find Arguments'); @@ -43,14 +43,11 @@ function parseMethod(args: string, self: string, insertSelfArg: boolean) { syntaxError('Invalid Method Arguments'); } if (insertSelfArg) { - let selfArg = `(${getSelfArg(self)}`; - if (methodArgs !== '()') { - selfArg += ', '; - } - methodArgs = selfArg + methodArgs.substring(1); + const selfArg = getSelfArg(self); + methodArgs = prependArg(methodArgs, selfArg); } const address = safeParseInt(end[1]!); - return new Method(self, name, type, methodArgs, address); + return new Method(self, name, type, methodArgs, address, isInherited); } // Load Structure @@ -103,6 +100,9 @@ export function load(target: Struct, name: string, isExtended: boolean) { switch (command) { case 'extends': { // Load Parent + if (!isExtended) { + target.setDirectParent(args); + } load(target, args, true); break; } @@ -148,20 +148,20 @@ export function load(target: Struct, name: string, isExtended: boolean) { } case 'method': { // Add Method - const method = parseMethod(args, target.getName(), true); + const method = parseMethod(args, target.getName(), true, isExtended); target.addMethod(method, false); break; } case 'virtual-method': { // Add Virtual Method - const method = parseMethod(args, target.getName(), true); + const method = parseMethod(args, target.getName(), true, isExtended); target.addMethod(method, true); break; } case 'static-method': { // Add Static Method if (!isExtended) { - const method = parseMethod(args, target.getName(), false); + const method = parseMethod(args, target.getName(), false, false); target.addMethod(method, false); } break; @@ -178,11 +178,16 @@ export function load(target: Struct, name: string, isExtended: boolean) { data += '_'; } data += args; - const method = parseMethod(data, target.getName(), true); + const method = parseMethod(data, target.getName(), true, false); target.addMethod(method, false); } break; } + case 'vtable-destructor-offset': { + // Set VTable Destructor Offset + target.setVTableDestructorOffset(safeParseInt(args)); + break; + } default: { throw new Error(`Invalid Command: ${command}`); } diff --git a/src/method.ts b/src/method.ts index a668df2..d8ec734 100644 --- a/src/method.ts +++ b/src/method.ts @@ -1,4 +1,4 @@ -import { formatType } from './common'; +import { INDENT, formatType, getArgNames } from './common'; export class Method { readonly self: string; @@ -6,19 +6,24 @@ export class Method { readonly returnType: string; readonly args: string; readonly address: number; + readonly isInherited: boolean; // Constructor - constructor(self: string, name: string, returnType: string, args: string, address: number) { + constructor(self: string, name: string, returnType: string, args: string, address: number, isInherited: boolean) { this.self = self; this.shortName = name; this.returnType = returnType; this.args = args; this.address = address; + this.isInherited = isInherited; } // Get Type + getNameWithCustomSelf(self: string) { + return `${self}_${this.shortName}`; + } getName() { - return `${this.self}_${this.shortName}`; + return this.getNameWithCustomSelf(this.self); } getType() { return `${this.getName()}_t`; @@ -26,12 +31,39 @@ export class Method { // Generate Type Definition generateTypedef() { + let out = ''; + + // Normal Definition const returnType = formatType(this.returnType); - return `typedef ${returnType}(*${this.getType()})${this.args};\n`; + out += `typedef ${returnType}(*${this.getType()})${this.args};\n`; + + // Fancy Overwrite Does Not Support Varargs + if (!this.args.includes('...')) { + // Overwrite Helper + 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`; + } + + // Return + return out; } // Generate Variable Definition generateDefinition(nameSuffix?: string) { return `${this.getType()} ${this.getName()}${nameSuffix !== undefined ? nameSuffix : ''};\n`; } + + // Generate "New Method" Test + generateNewMethodTest(parent: string | null, prefix: string, suffix: string) { + let out = `#define _is_new_method_${this.getName()}() (`; + if (!this.isInherited) { + out += 'true'; + } else { + out += `((void *) ${prefix}${this.getName()}${suffix}) != ((void *) ${prefix}${this.getNameWithCustomSelf(parent!)}${suffix})`; + } + out += ')\n'; + return out; + } } \ No newline at end of file diff --git a/src/struct.ts b/src/struct.ts index 12d8b12..6589297 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -11,6 +11,8 @@ export class Struct { readonly #size: Size; readonly #dependencies: string[]; readonly #staticProperties: StaticProperty[]; + #directParent: string | null; + #vtableDestructorOffset: number; // Constructor constructor(name: string) { @@ -21,6 +23,8 @@ export class Struct { this.#size = new Size(false); this.#dependencies = []; this.#staticProperties = []; + this.#directParent = null; + this.#vtableDestructorOffset = 0; } // Dependencies @@ -34,11 +38,19 @@ export class Struct { // Ensure VTable Exists ensureVTable() { if (this.#vtable === null) { - this.#vtable = new VTable(this.#name); + this.#vtable = new VTable(this.#name, this.#vtableDestructorOffset); this.addProperty(this.#vtable); } } + // Set VTable Destructor Offset + setVTableDestructorOffset(offset: number) { + if (this.#vtable) { + throw new Error('VTable Already Created'); + } + this.#vtableDestructorOffset = offset; + } + // Setters setSize(size: number, isExact: boolean) { this.#size.set(size, isExact); @@ -200,11 +212,12 @@ export class Struct { for (const method of this.#methods) { out += method.generateTypedef(); out += `extern ${method.generateDefinition()}`; + out += method.generateNewMethodTest(this.#directParent, '', ''); } // VTable if (this.#vtable !== null) { - out += this.#vtable.generate(); + out += this.#vtable.generate(this.#directParent); } // Structure @@ -294,4 +307,18 @@ export class Struct { // Return 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; + } } \ No newline at end of file diff --git a/src/vtable.ts b/src/vtable.ts index 39f0f99..84c44a2 100644 --- a/src/vtable.ts +++ b/src/vtable.ts @@ -9,7 +9,7 @@ export class VTable implements Property { readonly #methods: Method[]; // Constructor - constructor(name: string) { + constructor(name: string, destructorOffset: number) { this.#name = name; this.#address = null; this.#size = new Size(true); @@ -17,8 +17,8 @@ export class VTable implements Property { // Add Destructors (https://stackoverflow.com/a/17960941) const destructor_return = `${name} *`; const destructor_args = `(${getSelfArg(name)})`; - this.add(new Method(name, 'destructor_complete', destructor_return, destructor_args, 0x0)); - this.add(new Method(name, 'destructor_deleting', destructor_return, destructor_args, 0x4)); + this.add(new Method(name, 'destructor_complete', destructor_return, destructor_args, 0x0 + destructorOffset, false)); + this.add(new Method(name, 'destructor_deleting', destructor_return, destructor_args, 0x4 + destructorOffset, false)); } // Property Information @@ -82,7 +82,7 @@ export class VTable implements Property { } // Generate Code - generate() { + generate(directParent: string | null) { let out = ''; // Check @@ -126,7 +126,7 @@ export class VTable implements Property { if (info) { const type = `${info.getType()} *`; out += `extern ${type}${info.getName()}_vtable_addr;\n`; - out += `extern ${info.generateDefinition('_non_virtual')}`; + out += info.generateNewMethodTest(directParent, '*', '_vtable_addr'); } } } @@ -161,8 +161,6 @@ export class VTable implements Property { const type = `${info.getType()} *`; init += `${INDENT}${info.getName()}_vtable_addr = (${type}) ${toHex(vtableAddress)};\n`; declarations += `${type}${info.getName()}_vtable_addr;\n`; - init += `${INDENT}${info.getName()}_non_virtual = *${info.getName()}_vtable_addr;\n`; - declarations += info.generateDefinition('_non_virtual'); } } } diff --git a/syntax-highlighting/def.nanorc b/syntax-highlighting/def.nanorc index 15eb237..199975f 100644 --- a/syntax-highlighting/def.nanorc +++ b/syntax-highlighting/def.nanorc @@ -2,7 +2,7 @@ syntax def "\.def$" comment "//" # Commands -color magenta "\<(extends|size|vtable(-size)?|property|static-property(-array)?|((static|virtual)-)?method|constructor)\>" +color magenta "\<(extends|size|vtable(-size|-destructor-offset)?|property|static-property(-array)?|((static|virtual)-)?method|constructor)\>" # Types color green "\<(char|uchar|short|ushort|int|uint|float|bool|void|std::(string|vector|map))\>" diff --git a/syntax-highlighting/ksyntaxhighlighting.xml b/syntax-highlighting/ksyntaxhighlighting.xml index 7a9d8ed..7a90c54 100644 --- a/syntax-highlighting/ksyntaxhighlighting.xml +++ b/syntax-highlighting/ksyntaxhighlighting.xml @@ -13,6 +13,7 @@ virtual-method static-method constructor + vtable-destructor-offset char