diff --git a/src/struct.ts b/src/struct.ts index d7c00f3..10f2d1a 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -12,7 +12,6 @@ export class Struct { readonly #dependencies: string[]; readonly #staticProperties: StaticProperty[]; #directParent: string | null; - #vtableDestructorOffset: number; // Constructor constructor(name: string) { @@ -24,7 +23,6 @@ export class Struct { this.#dependencies = []; this.#staticProperties = []; this.#directParent = null; - this.#vtableDestructorOffset = 0; } // Dependencies @@ -38,17 +36,15 @@ export class Struct { // Ensure VTable Exists ensureVTable() { if (this.#vtable === null) { - this.#vtable = new VTable(this.#name, this.#vtableDestructorOffset); + this.#vtable = new VTable(this.#name); this.addProperty(this.#vtable.property); } } // Set VTable Destructor Offset setVTableDestructorOffset(offset: number) { - if (this.#vtable) { - throw new Error('VTable Already Created'); - } - this.#vtableDestructorOffset = offset; + this.ensureVTable(); + this.#vtable!.setDestructorOffset(offset); } // Setters diff --git a/src/vtable.ts b/src/vtable.ts index 87caaf8..9e3147a 100644 --- a/src/vtable.ts +++ b/src/vtable.ts @@ -3,25 +3,22 @@ import { Method } from './method'; import { Property } from './property'; export class VTable { - readonly #name: string; + readonly #self: string; #address: number | null; #size: number | null; readonly #methods: Method[]; readonly property: Property; + #destructorOffset: number; // Constructor - constructor(name: string, destructorOffset: number) { - this.#name = name; + constructor(self: string) { + this.#self = self; this.#address = null; this.#size = null; this.#methods = []; - // 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 + destructorOffset, false)); - this.add(new Method(name, 'destructor_deleting', destructor_return, destructor_args, 0x4 + destructorOffset, false)); + this.#destructorOffset = 0; // Create Property - this.property = new Property(0, this.#getName() + ' *', 'vtable', this.#name); + this.property = new Property(0, this.#getName() + ' *', 'vtable', this.#self); } // Setters @@ -31,9 +28,12 @@ export class VTable { setSize(size: number) { this.#size = size; } + setDestructorOffset(destructorOffset: number) { + this.#destructorOffset = destructorOffset; + } // Add To VTable - add(method: Method) { + #add(target: Method[], method: Method) { // Check Offset const offset = method.address; if ((offset % POINTER_SIZE) !== 0) { @@ -41,15 +41,18 @@ export class VTable { } // Add const index = offset / POINTER_SIZE; - if (this.#methods[index]) { + if (target[index]) { throw new Error(`Duplicate Virtual Method At Offset: ${toHex(offset)}`); } - this.#methods[index] = method; + target[index] = method; + } + add(method: Method) { + this.#add(this.#methods, method); } // Get Structure Name #getName() { - return this.#name + '_vtable'; + return this.#self + '_vtable'; } // Check @@ -67,6 +70,20 @@ export class VTable { } } + // Get Full Methods Table + #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)); + // Return + return out; + } + // Generate Header Code generate(directParent: string | null) { let out = ''; @@ -75,8 +92,9 @@ export class VTable { this.#check(); // Method Prototypes - for (let i = 0; i < this.#methods.length; i++) { - const info = this.#methods[i]; + const methods = this.#getMethods(); + for (let i = 0; i < methods.length; i++) { + const info = methods[i]; if (info) { out += info.generateTypedef(); } @@ -85,10 +103,10 @@ export class VTable { // Structure out += `typedef struct ${this.#getName()} ${this.#getName()};\n`; out += `struct ${this.#getName()} {\n`; - for (let i = 0; i < this.#methods.length; i++) { + for (let i = 0; i < methods.length; i++) { let name = `unknown${i}`; let type = 'void *'; - const info = this.#methods[i]; + const info = methods[i]; if (info) { name = info.shortName; type = info.getType() + ' '; @@ -107,8 +125,8 @@ export class VTable { // Base out += `extern ${this.#getName()} *${this.#getName()}_base;\n`; // Methods - for (let i = 0; i < this.#methods.length; i++) { - const info = this.#methods[i]; + for (let i = 0; i < methods.length; i++) { + const info = methods[i]; if (info) { const type = `${info.getType()} *`; out += `extern ${type}${info.getName()}_vtable_addr;\n`; @@ -140,8 +158,9 @@ export class VTable { init += `${INDENT}${this.#getName()}_base = (${this.#getName()} *) ${toHex(this.#address)};\n`; declarations += `${this.#getName()} *${this.#getName()}_base;\n`; // Methods - for (let i = 0; i < this.#methods.length; i++) { - const info = this.#methods[i]; + const methods = this.#getMethods(); + for (let i = 0; i < methods.length; i++) { + const info = methods[i]; if (info) { const vtableAddress = this.#address + (i * POINTER_SIZE); const type = `${info.getType()} *`;