From be25749aa497c29acdb94717c17dc9ad7da50c3c Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 14 Jul 2024 05:03:18 -0400 Subject: [PATCH] Some Changes --- data/out.cpp | 15 ++ data/out.h | 189 ++++++++++++++++++ src/common.ts | 48 ++--- src/index.ts | 69 ++----- src/method.ts | 114 +++-------- src/struct.ts | 98 ++++----- src/vtable.ts | 91 ++------- .../ksyntaxhighlighting/symbol-processor.xml | 1 + .../nano/symbol-processor.nanorc | 4 +- .../Syntaxes/SymbolProcessor.tmLanguage | 2 +- 10 files changed, 341 insertions(+), 290 deletions(-) create mode 100644 data/out.cpp create mode 100644 data/out.h diff --git a/data/out.cpp b/data/out.cpp new file mode 100644 index 0000000..212356b --- /dev/null +++ b/data/out.cpp @@ -0,0 +1,15 @@ +#include "{{ headerPath }}" + +// Thunks +template +struct __Thunk { + template <__Function *func> + static Ret call(Args... args) { + return func->get_thunk_target()(std::forward(args)...); + } +}; + +// Thunk Enabler +thunk_enabler_t thunk_enabler; + +{{ main }} \ No newline at end of file diff --git a/data/out.h b/data/out.h new file mode 100644 index 0000000..b5c8c52 --- /dev/null +++ b/data/out.h @@ -0,0 +1,189 @@ +#pragma once + +// Check Architecture +#ifndef __arm__ +#error "Symbols Are ARM-Only" +#endif + +// Headers +#include +#include +#include +#include +#include +#include +#include + +// Virtual Function Information +template +class __Function; +template +class __VirtualFunctionInfo { + __VirtualFunctionInfo(T *const addr_, void *const parent_): + addr(addr_), + parent(parent_) {} + bool can_overwrite() const { + return ((void *) *addr) != parent; + } + T *const addr; + void *const parent; + template + friend class __Function; +}; + +// Thunks +template +struct __Thunk; +typedef void *(*thunk_enabler_t)(void *target, void *thunk); +extern thunk_enabler_t thunk_enabler; + +// Function Information +template +class __Function { +public: + // Types + using ptr_type = Ret (*)(Args...); + using type = std::function; + using overwrite_type = std::function; + + // Normal Function + __Function(const char *const name_, const ptr_type func_, const ptr_type thunk_): + enabled(true), + name(name_), + is_virtual(false), + func(func_), + backup(func_), + thunk(thunk_) {} + // Virtual Function + __Function(const char *const name_, const __VirtualFunctionInfo virtual_info_, const ptr_type thunk_): + enabled(virtual_info_.can_overwrite()), + name(name_), + is_virtual(true), + func(virtual_info_), + backup(*get_vtable_addr()), + thunk(thunk_) {} + __Function(const char *const name_, ptr_type *const func_, void *const parent, const ptr_type thunk_): + __Function(name_, __VirtualFunctionInfo(func_, parent), thunk_) {} + + // Overwrite Function + bool overwrite(overwrite_type target) { + // Check If Enabled + if (!enabled) { + return false; + } + // Enable Thunk + enable_thunk(); + // Overwrite + type original = get_thunk_target(); + thunk_target = [original, target](Args... args) { + return target(original, args...); + }; + return true; + } + + // Getters + ptr_type get_backup() const { + return backup; + } + ptr_type get() { + if (!enabled) { + return nullptr; + } else { + enable_thunk(); + if (is_virtual) { + return *get_vtable_addr(); + } else { + return func.normal_addr; + } + } + } + ptr_type *get_vtable_addr() const { + if (is_virtual) { + return func.virtual_info.addr; + } else { + return nullptr; + } + } + const char *get_name() const { + return name; + } + +private: + // State + const bool enabled; + const char *const name; + + // Current Function + const bool is_virtual; + union __FunctionInfo { + explicit __FunctionInfo(const ptr_type normal_addr_): normal_addr(normal_addr_) {} + explicit __FunctionInfo(const __VirtualFunctionInfo virtual_info_): virtual_info(virtual_info_) {} + ptr_type normal_addr; + const __VirtualFunctionInfo virtual_info; + } func; + + // Backup Of Original Function Pointer + const ptr_type backup; + + // Thunk + const ptr_type thunk; + bool thunk_enabled = false; + type thunk_target; + void enable_thunk() { + if (!thunk_enabled) { + ptr_type real_thunk = (ptr_type) thunk_enabler((void *) backup, (void *) thunk); + if (!is_virtual) { + func.normal_addr = real_thunk; + } + thunk_enabled = true; + } + } + type get_thunk_target() const { + if (thunk_target) { + return thunk_target; + } else { + return backup; + } + } + friend struct __Thunk; +}; + +// Shortcuts +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; + +// Duplicate VTable +#define RTTI_SIZE 4 +template +T *dup_vtable(T *vtable) { + // Check + static_assert(std::is_constructible::value, "Unable To Construct VTable"); + // Get Size + uchar *real_vtable = (uchar *) vtable; + real_vtable -= RTTI_SIZE; + size_t real_vtable_size = sizeof(T) + RTTI_SIZE; + // Allocate + uchar *new_vtable = (uchar *) ::operator new(real_vtable_size); + // Copy + memcpy((void *) new_vtable, (void *) real_vtable, real_vtable_size); + // Return + new_vtable += RTTI_SIZE; + return (T *) new_vtable; +} + +// Forward Declarations +{{ forwardDeclarations }} + +// Extra Headers +{{ extraHeaders }} + +// Warnings +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#pragma GCC diagnostic ignored "-Wshadow" + +{{ main }} + +// Cleanup Warnings +#pragma GCC diagnostic pop \ No newline at end of file diff --git a/src/common.ts b/src/common.ts index 9d77211..19a5311 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1,4 +1,5 @@ import * as fs from 'node:fs'; +import * as path from 'node:path'; export const INDENT = ' '; export const POINTER_SIZE = 4; @@ -56,7 +57,7 @@ export function toUpperSnakeCase(str: string) { return out; } export function formatType(type: string) { - if (!type.endsWith('*')) { + if (!type.endsWith('*') && !type.endsWith('&')) { type += ' '; } return type; @@ -78,42 +79,25 @@ export function safeParseInt(str: string) { export function getSelfArg(type: string) { return `${type} *self`; } -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); } -export function removeFirstArg(args: string) { - let index = args.indexOf(','); - if (index === -1) { - index = args.indexOf(')'); - } else { - index++; +export function formatFile(file: string, options: {[key: string]: string}) { + file = path.join(__dirname, '..', 'data', file); + let data = fs.readFileSync(file, {encoding: 'utf8'}); + for (const key in options) { + data = data.replace(`{{ ${key} }}`, options[key]!); } - return '(' + args.substring(index).trim(); + return data.trim() + '\n'; } -export const ORIGINAL_SUFFIX = '_backup_do_not_use'; -export const VTABLE_ADDR_SUFFIX = `_vtable_addr`; -export const OVERWRITE_TEST_SUFFIX = '__is_overwritable_'; \ No newline at end of file +export const INTERNAL = '__'; +export function preventConstruction(self: string) { + let out = ''; + out += `${INDENT}${self}() = delete;\n`; + out += `${INDENT}${self}(const ${self} &) = delete;\n`; + out += `${INDENT}${self} &operator=(const ${self} &) = delete;\n`; + return out; +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 35cf364..6f16042 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import * as fs from 'node:fs'; -import { STRUCTURE_FILES, EXTENSION } from './common'; +import { STRUCTURE_FILES, EXTENSION, formatFile } from './common'; import { getStructure } from './map'; import { Struct } from './struct'; @@ -21,7 +21,7 @@ const headerOutput = process.argv.shift()!; if (!headerOutput.endsWith('.h')) { invalidFileType(headerOutput); } -const extraHeaders: string[] = []; +const extraHeaderFiles: string[] = []; while (process.argv.length > 0) { const file = process.argv.shift()!; if (file.endsWith(EXTENSION)) { @@ -36,7 +36,7 @@ while (process.argv.length > 0) { STRUCTURE_FILES[name] = file; } else if (file.endsWith('.h')) { // Extra Headers - extraHeaders.push(file); + extraHeaderFiles.push(file); } else { // Invalid File Type invalidFileType(file); @@ -112,14 +112,8 @@ function makeHeaderPart() { // Generate Code let structures = ''; - let isFirst = true; for (const structure of structureObjects) { const name = structure.getName(); - if (isFirst) { - isFirst = false; - } else { - structures += '\n'; - } structures += `// ${name}\n`; try { structures += structure.generate(); @@ -127,6 +121,7 @@ function makeHeaderPart() { console.log(`Error Generating Header: ${name}: ${e instanceof Error ? e.stack : e}`); process.exit(1); } + structures += '\n'; } // Return @@ -135,38 +130,22 @@ function makeHeaderPart() { // Create Main Header function makeMainHeader(output: string) { - let result = ''; - result += '#pragma once\n'; - result += '\n// Check Architecture\n'; - result += '#ifndef __arm__\n'; - result += '#error "Symbols Are ARM-Only"\n'; - result += '#endif\n'; - result += '\n// Forward Declarations\n'; + // Forward Declarations + let forwardDeclarations = ''; for (const name in STRUCTURE_FILES) { - result += `typedef struct ${name} ${name};\n`; + forwardDeclarations += `typedef struct ${name} ${name};\n`; } - result += '\n// Extra Headers\n'; - for (const file of extraHeaders) { - result += fs.readFileSync(file, {encoding: 'utf8'}); + forwardDeclarations = forwardDeclarations.trim(); + // Extra Headers + let extraHeaders = ''; + for (const file of extraHeaderFiles) { + extraHeaders += fs.readFileSync(file, {encoding: 'utf8'}); } - result += '\n// Headers\n'; - result += '#include \n'; - result += '#include \n'; - result += '#include \n'; - result += '#include \n'; - result += `#include \n`; - result += `#include \n`; - result += '\n// Shortcuts\n'; - result += 'typedef unsigned char uchar;\n'; - result += 'typedef unsigned short ushort;\n'; - result += 'typedef unsigned int uint;\n'; - result += '\n// Warnings\n'; - result += '#pragma GCC diagnostic push\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'; + extraHeaders = extraHeaders.trim(); + // Main + const main = makeHeaderPart().trim(); + // Write + const result = formatFile('out.h', {forwardDeclarations, extraHeaders, main}); fs.writeFileSync(output, result); } makeMainHeader(headerOutput); @@ -178,14 +157,8 @@ function makeCompiledCode(output: string) { // Generate let declarations = ''; - let isFirst = true; for (const structure of structureObjects) { const name = structure.getName(); - if (isFirst) { - isFirst = false; - } else { - declarations += '\n'; - } declarations += `// ${name}\n`; try { const code = structure.generateCode(); @@ -194,13 +167,13 @@ function makeCompiledCode(output: string) { console.log(`Error Generating Code: ${name}: ${e instanceof Error ? e.stack : e}`); process.exit(1); } + declarations += '\n'; } // Write - let result = ''; - result += `#include "${fs.realpathSync(headerOutput)}"\n`; - result += '\n#include \n\n'; - result += declarations; + const headerPath = fs.realpathSync(headerOutput); + const main = declarations.trim(); + const result = formatFile('out.cpp', {headerPath, main}); fs.writeFileSync(output, result); } makeCompiledCode(sourceOutput); diff --git a/src/method.ts b/src/method.ts index 8f03cf0..2657daf 100644 --- a/src/method.ts +++ b/src/method.ts @@ -1,4 +1,4 @@ -import { INDENT, OVERWRITE_TEST_SUFFIX, VTABLE_ADDR_SUFFIX, formatType, getArgNames, prependArg } from './common'; +import { INDENT, INTERNAL, toHex } from './common'; export class Method { readonly self: string; @@ -7,7 +7,6 @@ export class Method { readonly args: string; readonly address: number; readonly isInherited: boolean; - readonly hasVarargs: boolean; readonly isStatic: boolean; // Constructor @@ -18,102 +17,49 @@ export class Method { this.args = args; this.address = address; this.isInherited = isInherited; - this.hasVarargs = this.args.includes('...'); this.isStatic = isStatic; } - // Get Type - getNameWithCustomSelf(self: string) { - return `${self}_${this.shortName}`; - } - getName() { - return this.getNameWithCustomSelf(this.self); - } - #getBaseType() { - return `${this.getName()}_t`; + // Getters + getName(separator: string = '_') { + return this.self + separator + this.shortName; } getType() { - return `__raw_${this.#getBaseType()}`; + return this.getName() + '_t'; } - - // Generate Type Definition - generateTypedef() { - let out = ''; - - // Normal Definition - out += `typedef ${formatType(this.returnType)}(*${this.getType()})${this.args};\n`; - - // Return - return out; + getProperty() { + return `${INDENT}${this.getWrapperType()}::ptr_type ${this.shortName};\n`; + } + getWrapperType() { + return `decltype(${this.getName()})`; } // Overwrite Helper - #hasOverwriteHelper() { - // Fancy Overwrite Does Not Support Varargs - return !this.hasVarargs; + #getVirtualCall(self: string = this.self) { + return `${self}_vtable_base->${this.shortName}`; } - generateOverwriteHelper(code: boolean, isVirtual: boolean) { + generate(code: boolean, isVirtual: boolean, parentSelf?: string) { let out = ''; - if (this.#hasOverwriteHelper()) { - // Add Additional Typedefs - const baseType = this.#getBaseType(); - const overwriteType = `__overwrite_${baseType}`; - const functionType = baseType; - const rawType = this.getType(); - if (!code) { - out += `typedef std::function::type> ${functionType};\n`; - out += `typedef std::function<${this.returnType}${prependArg(this.args, functionType + ' original')}> ${overwriteType};\n`; - } - // Thunk - const name = this.getName(); - const currentOverwriteName = '__current_overwrite_for_' + name; - const thunkName = '__overwrite_thunk_for_' + name; - if (code) { - out += `static ${functionType} &${currentOverwriteName} = *new ${functionType};\n`; - out += `static ${formatType(this.returnType)}${thunkName}${this.args} {\n`; - out += `${INDENT}${this.returnType.trim() === 'void' ? '' : 'return '}${currentOverwriteName}(${getArgNames(this.args).join(', ')});\n`; - out += `}\n`; - } - // Overwrite Helper - out += `std::string __set_overwrite_for_${name}(const ${overwriteType} &method, const std::function &overwriter)`; - if (code) { - out += ' {\n'; - if (isVirtual) { - // Check If Method Can Be Overwritten - out += `${INDENT}if (!${OVERWRITE_TEST_SUFFIX}${name}()) {\n`; - out += `${INDENT}${INDENT}return "Unable To Overwrite Virtual Method";\n`; - out += `${INDENT}}\n`; - } - // Redirect Method Calls To Thunk - out += `${INDENT}static bool enabled = false;\n`; - out += `${INDENT}if (!enabled) {\n`; - if (isVirtual) { - // Get Address Of Virtual Function - out += `${INDENT}${INDENT}${rawType} ${name} = *${name}${VTABLE_ADDR_SUFFIX};\n`; - } - out += `${INDENT}${INDENT}${rawType} &original = ${name};\n`; - out += `${INDENT}${INDENT}${currentOverwriteName} = original;\n`; - out += `${INDENT}${INDENT}original = (${rawType}) overwriter((void *) original, (void *) ${thunkName});\n`; - out += `${INDENT}${INDENT}enabled = true;\n`; - out += `${INDENT}}\n`; - // Bind Original Parameter - out += `${INDENT}${functionType} original = ${currentOverwriteName};\n`; - out += `${INDENT}${currentOverwriteName} = [method, original]${this.args} {\n`; - out += `${INDENT}${INDENT}${this.returnType.trim() === 'void' ? '' : 'return '}method(${['original'].concat(getArgNames(this.args)).join(', ')});\n`; - out += `${INDENT}};\n`; - // Return Success - out += `${INDENT}return "";\n`; - out += `}`; + if (!code) { + out += 'extern '; + } + const signature = this.returnType.trim() + this.args.trim(); + const type = `${INTERNAL}Function<${signature}>`; + out += `${type} ${this.getName()}`; + if (code) { + out += `(${JSON.stringify(this.getName('::'))}, `; + if (isVirtual) { + const parentMethod = parentSelf ? `(void *) ${this.#getVirtualCall(parentSelf)}` : 'nullptr'; + out += `&${this.#getVirtualCall()}, ${parentMethod}`; } else { - out += ';'; + out += `${this.getWrapperType()}::ptr_type(${toHex(this.address)})`; } - out += '\n'; + out += `, ${INTERNAL}Thunk<${signature}>::call<&${this.getName()}>)`; + } + out += ';\n'; + if (!code) { + out += `typedef ${this.getWrapperType()}::type ${this.getType()};\n`; } return out; } - - // Generate Variable Definition - generateDefinition(nameSuffix?: string) { - return `${this.getType()} ${this.getName()}${nameSuffix !== undefined ? nameSuffix : ''}`; - } } \ No newline at end of file diff --git a/src/struct.ts b/src/struct.ts index 691984c..e8edca8 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -1,4 +1,4 @@ -import { INDENT, STRUCTURE_FILES, toHex, assertSize, formatType, getArgNames, removeFirstArg, ORIGINAL_SUFFIX } from './common'; +import { INDENT, STRUCTURE_FILES, toHex, assertSize, INTERNAL, preventConstruction } from './common'; import { Method } from './method'; import { Property, StaticProperty } from './property'; import { VTable } from './vtable'; @@ -127,7 +127,7 @@ export class Struct { if (lastProperty) { paddingSize += ` - sizeof(${lastProperty.type()})`; } - out += `${INDENT}uchar __padding${i}[${paddingSize}];\n`; + out += `${INDENT}uchar ${INTERNAL}padding${i}[${paddingSize}];\n`; // The Actual Property if (property !== sizeProperty) { @@ -138,47 +138,42 @@ export class Struct { } // Generate C++ Method Shortcuts + #generateMethod(method: Method, isVirtual: boolean) { + // Generate Method Call + let call = ''; + if (isVirtual) { + call += `this->vtable->${method.shortName}`; + } else { + call += `${method.getName()}.get()`; + } + call += '('; + if (!method.isStatic) { + call += `(${method.self} *) this, `; + } + call += 'std::forward(args)...)'; + // Generate Shortcut + let out = ''; + out += `${INDENT}template \n`; + if (method.isStatic) { + out += 'static '; + } + out += `${INDENT}auto ${method.shortName}(Args&&... args) -> decltype(${call}) {\n`; + out += `${INDENT}${INDENT}return ${call};\n`; + out += `${INDENT}}\n`; + return out; + } #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}${method.isStatic ? 'static ' : ''}inline ${formatType(returnType)}${shortName}${args} { \\\n`; - out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}${fullName}(${getArgsInner(method)});\n`; - out += `${INDENT}}\n`; - } + out += this.#generateMethod(method, false); } // 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`; + if (method) { + out += this.#generateMethod(method, true); } } } @@ -190,31 +185,28 @@ export class Struct { generate() { let out = ''; - // Static Properties - for (const property of this.#staticProperties) { - out += property.typedef(); - } - - // Methods - for (const method of this.#methods) { - if (!method.isInherited) { - out += method.generateTypedef(); - out += `extern ${method.generateDefinition()};\n`; - out += `extern ${method.generateDefinition(ORIGINAL_SUFFIX)};\n`; - out += method.generateOverwriteHelper(false, false); - } - } - // VTable if (this.#vtable !== null) { out += this.#vtable.generate(); } + // Static Properties + for (const property of this.#staticProperties) { + out += property.typedef(); + } + // Property Typedefs for (const property of this.#properties) { out += property.typedef(); } + // Method Wrappers + for (const method of this.#methods) { + if (!method.isInherited) { + out += method.generate(false, false); + } + } + // Structure out += `struct ${this.#name} {\n`; out += this.#generateProperties(); @@ -225,9 +217,7 @@ export class Struct { } if (this.#size === null) { // Prevent Manually Copying/Allocating Structure With Undefined - out += `${INDENT}${this.#name}() = delete;\n`; - out += `${INDENT}${this.#name}(const ${this.#name} &) = delete;\n`; - out += `${INDENT}${this.#name} &operator=(const ${this.#name} &) = delete;\n`; + out += preventConstruction(this.#name); } out += `};\n`; @@ -260,9 +250,7 @@ export class Struct { // Methods for (const method of this.#methods) { if (!method.isInherited) { - out += `${method.generateDefinition()} = (${method.getType()}) ${toHex(method.address)};\n`; - out += `${method.generateDefinition(ORIGINAL_SUFFIX)} = ${method.getName()};\n`; - out += method.generateOverwriteHelper(true, false); + out += method.generate(true, false); } } diff --git a/src/vtable.ts b/src/vtable.ts index 2693414..db6b977 100644 --- a/src/vtable.ts +++ b/src/vtable.ts @@ -1,4 +1,4 @@ -import { INDENT, ORIGINAL_SUFFIX, OVERWRITE_TEST_SUFFIX, POINTER_SIZE, RTTI_SIZE, VTABLE_ADDR_SUFFIX, assertSize, getSelfArg, toHex } from './common'; +import { INDENT, POINTER_SIZE, assertSize, getSelfArg, preventConstruction, toHex } from './common'; import { Method } from './method'; import { Property } from './property'; @@ -86,29 +86,22 @@ export class VTable { return out; } - // Overwritable Test (Only For Virtual Methods) - generateOverwritableTest(method: Method, parent: string | null) { - // Test If Overwriting Method Would Also Modify Parent - let out = ''; - out += `static inline bool ${OVERWRITE_TEST_SUFFIX}${method.getName()}() {\n`; - let ret: string; + // Get Parent Method + #getParentSelf(method: Method, parent: string | null) { if (method.isInherited) { + // Parent Exists + let out: string; if (structuresWithVTableAddress.includes(parent!)) { - // Check If Method Differs From Parent - out += `${INDENT}void *parent = (void *) *${method.getNameWithCustomSelf(parent!)}${VTABLE_ADDR_SUFFIX};\n`; - out += `${INDENT}void *child = (void *) *${method.getName()}${VTABLE_ADDR_SUFFIX};\n`; - ret = 'parent != child'; + out = parent!; } else { // Unable To Determine - ret = 'false'; + out = this.#self; } + return out; } else { - // No Parent - ret = 'true'; + // Method Has No Parent + return undefined; } - out += `${INDENT}return ${ret};\n`; - out += '}\n'; - return out; } // Generate Header Code @@ -118,12 +111,11 @@ export class VTable { // Check this.#check(); - // Method Prototypes + // Wrappers const methods = this.getMethods(); - for (let i = 0; i < methods.length; i++) { - const info = methods[i]; + for (const info of methods) { if (info) { - out += info.generateTypedef(); + out += info.generate(false, true); } } @@ -131,14 +123,16 @@ export class VTable { out += `typedef struct ${this.#getName()} ${this.#getName()};\n`; out += `struct ${this.#getName()} {\n`; for (let i = 0; i < methods.length; i++) { - let name = `unknown${i}`; - let type = 'void *'; const info = methods[i]; if (info) { - name = info.shortName; - type = info.getType() + ' '; + out += info.getProperty(); + } else { + out += `${INDENT}void *unknown${i};\n`; } - out += `${INDENT}${type}${name};\n`; + } + if (this.#size === null) { + // Prevent Construction + out += preventConstruction(this.#getName()); } out += `};\n`; @@ -151,23 +145,6 @@ export class VTable { if (this.#address !== null) { // Base out += `extern ${this.#getName()} *${this.#getName()}_base;\n`; - // Methods - for (let i = 0; i < methods.length; i++) { - const info = methods[i]; - if (info) { - const name = info.getName(); - const type = info.getType(); - const pointerType = `${type} *`; - out += `extern ${pointerType}${name}${VTABLE_ADDR_SUFFIX};\n`; - out += `extern ${info.generateDefinition(ORIGINAL_SUFFIX)};\n`; - out += info.generateOverwriteHelper(false, true); - } - } - } - - // Duplication Method - if (this.#size !== null) { - out += `${this.#getName()} *dup_${this.#getName()}(${this.#getName()} *vtable);\n`; } // Return @@ -187,37 +164,13 @@ export class VTable { out += `${this.#getName()} *${this.#getName()}_base = (${this.#getName()} *) ${toHex(this.#address)};\n`; // Methods const methods = this.getMethods(); - for (let i = 0; i < methods.length; i++) { - const info = methods[i]; + for (const info of methods) { if (info) { - const vtableAddress = this.#address + (i * POINTER_SIZE); - const name = info.getName(); - const type = info.getType(); - const pointerType = `${type} *`; - out += `${pointerType}${name}${VTABLE_ADDR_SUFFIX} = (${pointerType}) ${toHex(vtableAddress)};\n`; - out += `${info.generateDefinition(ORIGINAL_SUFFIX)} = *${name}${VTABLE_ADDR_SUFFIX};\n`; - out += this.generateOverwritableTest(info, directParent); - out += info.generateOverwriteHelper(true, true); + out += info.generate(true, true, this.#getParentSelf(info, directParent)); } } } - // Duplication Method - if (this.#size !== null) { - out += `${this.#getName()} *dup_${this.#getName()}(${this.#getName()} *vtable) {\n`; - out += `${INDENT}uchar *real_vtable = (uchar *) vtable;\n`; - out += `${INDENT}real_vtable -= ${RTTI_SIZE};\n`; - out += `${INDENT}size_t real_vtable_size = sizeof(${this.#getName()}) + ${RTTI_SIZE};\n`; - out += `${INDENT}uchar *new_vtable = (uchar *) ::operator new(real_vtable_size);\n`; - out += `${INDENT}if (new_vtable == NULL) {\n`; - out += `${INDENT}${INDENT}return NULL;\n`; - out += `${INDENT}}\n`; - out += `${INDENT}memcpy((void *) new_vtable, (void *) real_vtable, real_vtable_size);\n`; - out += `${INDENT}new_vtable += ${RTTI_SIZE};\n`; - out += `${INDENT}return (${this.#getName()} *) new_vtable;\n`; - out += '}\n'; - } - // Return return out; } diff --git a/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml b/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml index 0d5e74c..f1ce253 100644 --- a/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml +++ b/syntax-highlighting/ksyntaxhighlighting/symbol-processor.xml @@ -15,6 +15,7 @@ vtable-destructor-offset + const char uchar short diff --git a/syntax-highlighting/nano/symbol-processor.nanorc b/syntax-highlighting/nano/symbol-processor.nanorc index ec81497..a190883 100644 --- a/syntax-highlighting/nano/symbol-processor.nanorc +++ b/syntax-highlighting/nano/symbol-processor.nanorc @@ -5,10 +5,12 @@ comment "//" color magenta "\<(extends|size|vtable(-size|-destructor-offset)?|property|static-property|((static|virtual)-)?method|constructor)\>" # Types -color green "\<(char|uchar|short|ushort|int|uint|float|bool|void|std::(string|vector|map))\>" +color green "\<((u?(char|short|int))|float|bool|void|std::(string|vector|map))\>" # Numbers color yellow "0x[a-f0-9]+" +# Non-hex numbers +color red " [0-9][a-f0-9]+;" # Comments color brightblue "//.*" diff --git a/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage b/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage index 643cca0..d488584 100644 --- a/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage +++ b/syntax-highlighting/textmate/SymbolProcessor.tmbundle/Syntaxes/SymbolProcessor.tmLanguage @@ -40,7 +40,7 @@ name storage.type.symbol-processor match - \b(char|uchar|short|ushort|int|uint|float|bool|void|std::string|std::vector|std::map)\b + \b(const|char|uchar|short|ushort|int|uint|float|bool|void|std::string|std::vector|std::map)\b name