176 lines
5.3 KiB
TypeScript
176 lines
5.3 KiB
TypeScript
|
import { INDENT, POINTER_SIZE } from './common';
|
||
|
import { Method } from './method';
|
||
|
import { Property } from './property';
|
||
|
|
||
|
export class VTable implements Property {
|
||
|
readonly #name: string;
|
||
|
#address: number | null;
|
||
|
#size: number | null;
|
||
|
readonly #methods: Method[];
|
||
|
|
||
|
// Constructor
|
||
|
constructor(name: string) {
|
||
|
this.#name = name;
|
||
|
this.#address = null;
|
||
|
this.#size = null;
|
||
|
this.#methods = [];
|
||
|
}
|
||
|
|
||
|
// Property Information
|
||
|
propertyOffset() {
|
||
|
return 0;
|
||
|
}
|
||
|
propertySize() {
|
||
|
return POINTER_SIZE;
|
||
|
}
|
||
|
propertyType() {
|
||
|
return this.#getName() + ' *';
|
||
|
}
|
||
|
propertyName() {
|
||
|
return 'vtable';
|
||
|
}
|
||
|
propertyAlignment() {
|
||
|
return 4;
|
||
|
}
|
||
|
|
||
|
// Setters
|
||
|
setAddress(address: number) {
|
||
|
this.#address = address;
|
||
|
}
|
||
|
setSize(size: number) {
|
||
|
this.#size = size;
|
||
|
if ((this.#size % POINTER_SIZE) !== 0) {
|
||
|
throw new Error(`Invalid VTable Size: ${this.#size}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add To VTable
|
||
|
add(method: Method) {
|
||
|
// Check Offset
|
||
|
const offset = method.address;
|
||
|
if ((offset % POINTER_SIZE) !== 0) {
|
||
|
throw new Error(`Invalid VTable Offset: ${offset}`);
|
||
|
}
|
||
|
// Check Size
|
||
|
if (this.#size !== null && (offset + POINTER_SIZE) > this.#size) {
|
||
|
throw new Error(`VTable Offset Too Large: ${offset}`);
|
||
|
}
|
||
|
// Add
|
||
|
const index = offset / POINTER_SIZE;
|
||
|
this.#methods[index] = method;
|
||
|
}
|
||
|
|
||
|
// Get Structure Name
|
||
|
#getName() {
|
||
|
return this.#name + '_vtable';
|
||
|
}
|
||
|
|
||
|
// Check
|
||
|
#check() {
|
||
|
// Check Size
|
||
|
if (this.#size !== null) {
|
||
|
const maxMethodCount = this.#size / POINTER_SIZE;
|
||
|
if (maxMethodCount < this.#methods.length) {
|
||
|
throw new Error(`VTable Size Too Small: ${this.#size}`);
|
||
|
}
|
||
|
this.#methods.length = maxMethodCount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Generate Code
|
||
|
generate() {
|
||
|
let out = '';
|
||
|
|
||
|
// Check
|
||
|
this.#check();
|
||
|
|
||
|
// Method Prototypes
|
||
|
for (let i = 0; i < this.#methods.length; i++) {
|
||
|
const info = this.#methods[i];
|
||
|
if (info) {
|
||
|
out += info.generateTypedef();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Structure
|
||
|
out += `typedef struct ${this.#getName()} ${this.#getName()};\n`;
|
||
|
out += `struct ${this.#getName()} {\n`;
|
||
|
for (let i = 0; i < this.#methods.length; i++) {
|
||
|
let name = `unknown${i}`;
|
||
|
let type = 'void *';
|
||
|
const info = this.#methods[i];
|
||
|
if (info) {
|
||
|
name = info.shortName;
|
||
|
type = info.getType() + ' ';
|
||
|
}
|
||
|
out += `${INDENT}${type}${name};\n`;
|
||
|
}
|
||
|
out += `};\n`;
|
||
|
|
||
|
// Pointers
|
||
|
if (this.#address !== null) {
|
||
|
// Base
|
||
|
out += `extern ${this.#getName()} *${this.#getName()}_base;\n`;
|
||
|
// Methods
|
||
|
for (let i = 0; i < this.#methods.length; i++) {
|
||
|
const info = this.#methods[i];
|
||
|
if (info) {
|
||
|
const type = `${info.getType()} *`;
|
||
|
out += `extern ${type}${info.getName()}_vtable_addr;\n`;
|
||
|
out += `extern ${info.generateDefinition()}`;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Duplication Method
|
||
|
if (this.#size !== null) {
|
||
|
out += `${this.#getName()} *dup_${this.#getName()}(${this.#getName()} *vtable);\n`;
|
||
|
}
|
||
|
|
||
|
// Return
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
// Generate Compiled Code
|
||
|
generateCode() {
|
||
|
let declarations = '';
|
||
|
let init = '';
|
||
|
|
||
|
// Check
|
||
|
this.#check();
|
||
|
|
||
|
// Pointers
|
||
|
if (this.#address !== null) {
|
||
|
// Base
|
||
|
init += `${INDENT}${this.#getName()}_base = (${this.#getName()} *) ${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];
|
||
|
if (info) {
|
||
|
const vtableAddress = this.#address + (i * POINTER_SIZE);
|
||
|
const type = `${info.getType()} *`;
|
||
|
init += `${INDENT}${info.getName()}_vtable_addr = (${type}) ${vtableAddress};\n`;
|
||
|
declarations += `${type}${info.getName()}_vtable_addr;\n`;
|
||
|
init += `${INDENT}${info.getName()} = *${info.getName()}_vtable_addr;\n`;
|
||
|
declarations += info.generateDefinition();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Duplication Method
|
||
|
if (this.#size !== null) {
|
||
|
declarations += `${this.#getName()} *dup_${this.#getName()}(${this.#getName()} *vtable) {\n`;
|
||
|
declarations += `${INDENT}${this.#getName()} *obj = (${this.#getName()} *) malloc(${this.#size});\n`;
|
||
|
declarations += `${INDENT}if (obj == NULL) {\n`;
|
||
|
declarations += `${INDENT}${INDENT}return NULL;\n`;
|
||
|
declarations += `${INDENT}}\n`;
|
||
|
declarations += `${INDENT}memcpy((void *) obj, (void *) vtable, ${this.#size});\n`;
|
||
|
declarations += `${INDENT}return obj;\n`;
|
||
|
declarations += '}\n';
|
||
|
}
|
||
|
|
||
|
// Return
|
||
|
return {declarations, init};
|
||
|
}
|
||
|
}
|