More Checks
This commit is contained in:
parent
059e572256
commit
5730267135
@ -120,3 +120,41 @@ export function getSizeMultiplierFromArrayData(propertyName: string) {
|
|||||||
// Return
|
// Return
|
||||||
return multiplier;
|
return multiplier;
|
||||||
}
|
}
|
||||||
|
export function getSelfArg(type: string) {
|
||||||
|
return `${type} *self`;
|
||||||
|
}
|
||||||
|
export class Size {
|
||||||
|
#size: number | null;
|
||||||
|
#isExact: boolean;
|
||||||
|
readonly #pointerAligned: boolean;
|
||||||
|
|
||||||
|
constructor(pointerAligned: boolean) {
|
||||||
|
this.#size = null;
|
||||||
|
this.#isExact = false;
|
||||||
|
this.#pointerAligned = pointerAligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.#size;
|
||||||
|
}
|
||||||
|
set(size: number, isExact: boolean) {
|
||||||
|
if (size < MIN_SIZE) {
|
||||||
|
throw new Error(`Size Too Small: ${toHex(size)}`);
|
||||||
|
}
|
||||||
|
if (this.#isExact) {
|
||||||
|
throw new Error('Cannot Change Size After Being Set By Base Structure');
|
||||||
|
}
|
||||||
|
if (this.#size !== null && size < this.#size) {
|
||||||
|
throw new Error('Cannot Decrease VTable Size');
|
||||||
|
}
|
||||||
|
this.#size = size;
|
||||||
|
this.#isExact = isExact;
|
||||||
|
if (this.#pointerAligned && (this.#size % POINTER_SIZE) !== 0) {
|
||||||
|
throw new Error(`Invalid Size: ${toHex(this.#size)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Whether This Is An Exact Limit Or Just A Lower Bound
|
||||||
|
isExact() {
|
||||||
|
return this.#isExact;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { COMMENT, EXTENSION, parseTypeAndName, readDefinition, safeParseInt, syntaxError } from './common';
|
import { COMMENT, EXTENSION, getSelfArg, parseTypeAndName, readDefinition, safeParseInt, syntaxError } from './common';
|
||||||
import { isCppAllowed } from './map';
|
import { isCppAllowed } from './map';
|
||||||
import { Method } from './method';
|
import { Method } from './method';
|
||||||
import { SimpleProperty, StaticProperty } from './property';
|
import { SimpleProperty, StaticProperty } from './property';
|
||||||
@ -44,7 +44,7 @@ function parseMethod(args: string, self: string, insertSelfArg: boolean) {
|
|||||||
syntaxError('Invalid Method Arguments');
|
syntaxError('Invalid Method Arguments');
|
||||||
}
|
}
|
||||||
if (insertSelfArg) {
|
if (insertSelfArg) {
|
||||||
let selfArg = `(${self} *self`;
|
let selfArg = `(${getSelfArg(self)}`;
|
||||||
if (methodArgs !== '()') {
|
if (methodArgs !== '()') {
|
||||||
selfArg += ', ';
|
selfArg += ', ';
|
||||||
}
|
}
|
||||||
@ -113,16 +113,12 @@ export function load(target: Struct, name: string, isExtended: boolean) {
|
|||||||
}
|
}
|
||||||
case 'size': {
|
case 'size': {
|
||||||
// Set Size
|
// Set Size
|
||||||
if (!isExtended) {
|
target.setSize(safeParseInt(args), !isExtended);
|
||||||
target.setSize(safeParseInt(args));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'vtable-size': {
|
case 'vtable-size': {
|
||||||
// Set VTable Size
|
// Set VTable Size
|
||||||
if (!isExtended) {
|
target.setVTableSize(safeParseInt(args), !isExtended);
|
||||||
target.setVTableSize(safeParseInt(args));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'vtable': {
|
case 'vtable': {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { INDENT, MIN_SIZE, formatType, STRUCTURE_FILES, toHex, getAssertFunction, assertSize, stripArrayData } from './common';
|
import { INDENT, MIN_SIZE, formatType, STRUCTURE_FILES, toHex, getAssertFunction, assertSize, stripArrayData, Size } 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';
|
||||||
@ -8,7 +8,7 @@ export class Struct {
|
|||||||
#vtable: VTable | null;
|
#vtable: VTable | null;
|
||||||
readonly #methods: Method[];
|
readonly #methods: Method[];
|
||||||
readonly #properties: Property[];
|
readonly #properties: Property[];
|
||||||
#size: number | null;
|
readonly #size: Size;
|
||||||
readonly #dependencies: string[];
|
readonly #dependencies: string[];
|
||||||
readonly #staticProperties: StaticProperty[];
|
readonly #staticProperties: StaticProperty[];
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ export class Struct {
|
|||||||
this.#methods = [];
|
this.#methods = [];
|
||||||
this.#properties = [];
|
this.#properties = [];
|
||||||
this.#vtable = null;
|
this.#vtable = null;
|
||||||
this.#size = null;
|
this.#size = new Size(false);
|
||||||
this.#dependencies = [];
|
this.#dependencies = [];
|
||||||
this.#staticProperties = [];
|
this.#staticProperties = [];
|
||||||
}
|
}
|
||||||
@ -40,28 +40,36 @@ export class Struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
setSize(size: number) {
|
setSize(size: number, isExact: boolean) {
|
||||||
this.#size = size;
|
this.#size.set(size, isExact);
|
||||||
}
|
}
|
||||||
// Getters
|
// Getters
|
||||||
#roundSize(size: number) {
|
#roundSize(size: number) {
|
||||||
const alignment = this.getAlignment();
|
const alignment = this.getAlignment();
|
||||||
return Math.ceil(size / alignment) * alignment;
|
return Math.ceil(size / alignment) * alignment;
|
||||||
}
|
}
|
||||||
getSize(round: boolean) {
|
#getRealSize() {
|
||||||
if (this.#isUndefined()) {
|
// Get Size Computed Purely From Properties
|
||||||
return 0;
|
let size = MIN_SIZE;
|
||||||
|
for (const property of this.#properties) {
|
||||||
|
const newSize = property.propertyOffset() + property.propertySize();
|
||||||
|
if (newSize > size) {
|
||||||
|
size = newSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
getSize(round: boolean) {
|
||||||
let size;
|
let size;
|
||||||
if (this.#size !== null) {
|
if (this.#size.isExact()) {
|
||||||
size = this.#size;
|
// Exact Size Is Specified
|
||||||
|
size = this.#size.get()!;
|
||||||
} else {
|
} else {
|
||||||
size = MIN_SIZE;
|
// Specified Size Is A Lower Bound
|
||||||
for (const property of this.#properties) {
|
size = this.#getRealSize();
|
||||||
const newSize = property.propertyOffset() + property.propertySize();
|
const specifiedSize = this.#size.get();
|
||||||
if (newSize > size) {
|
if (specifiedSize !== null && specifiedSize > size) {
|
||||||
size = newSize;
|
size = specifiedSize;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (round) {
|
if (round) {
|
||||||
@ -110,40 +118,33 @@ export class Struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure VTable
|
// Configure VTable
|
||||||
setVTableSize(size: number) {
|
setVTableSize(size: number, isExact: boolean) {
|
||||||
this.#ensureVTable();
|
this.#ensureVTable();
|
||||||
this.#vtable!.setSize(size);
|
this.#vtable!.setSize(size, isExact);
|
||||||
}
|
}
|
||||||
setVTableAddress(address: number) {
|
setVTableAddress(address: number) {
|
||||||
this.#ensureVTable();
|
this.#ensureVTable();
|
||||||
this.#vtable!.setAddress(address);
|
this.#vtable!.setAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check If Structure Is Undefined
|
|
||||||
#isUndefined() {
|
|
||||||
return this.#properties.length === 0 && this.#size === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check
|
// Check
|
||||||
#check() {
|
#check() {
|
||||||
// Sort Properties
|
// Sort Properties
|
||||||
this.#properties.sort((a, b) => a.propertyOffset() - b.propertyOffset());
|
this.#properties.sort((a, b) => a.propertyOffset() - b.propertyOffset());
|
||||||
|
|
||||||
// Check Size
|
// Check Size
|
||||||
const size = this.getSize(true);
|
if (this.#size.isExact()) {
|
||||||
if (this.#size !== null) {
|
const size = this.getSize(true);
|
||||||
// Check Alignment
|
// Check Alignment
|
||||||
if (size !== this.#size) {
|
const specifiedSize = this.#size.get()!;
|
||||||
|
if (size !== specifiedSize) {
|
||||||
throw new Error('Size Misaligned');
|
throw new Error('Size Misaligned');
|
||||||
}
|
}
|
||||||
// Check If Size Is Too Small
|
// Check If Size Is Too Small
|
||||||
const lastProperty = this.#properties[this.#properties.length - 1];
|
let realSize = this.#getRealSize();
|
||||||
if (lastProperty) {
|
realSize = this.#roundSize(realSize);
|
||||||
let realSize = lastProperty.propertyOffset() + lastProperty.propertySize();
|
if (realSize > specifiedSize) {
|
||||||
realSize = this.#roundSize(realSize);
|
throw new Error(`Structure Size Too Small: ${toHex(specifiedSize)}`);
|
||||||
if (realSize > this.#size) {
|
|
||||||
throw new Error(`Structure Size Too Small: ${this.#size}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,26 +154,21 @@ export class Struct {
|
|||||||
// Null A = Start Of Structure
|
// Null A = Start Of Structure
|
||||||
// Null B = End Of Structure
|
// Null B = End Of Structure
|
||||||
let neededPadding = 0;
|
let neededPadding = 0;
|
||||||
|
const size = this.getSize(true);
|
||||||
if (a === null) {
|
if (a === null) {
|
||||||
// Start Of Structure Padding
|
// Start Of Structure Padding
|
||||||
if (b !== null) {
|
if (b !== null) {
|
||||||
neededPadding = b.propertyOffset();
|
neededPadding = b.propertyOffset();
|
||||||
} else {
|
} else {
|
||||||
// Both A And B Are Null
|
// Both A And B Are Null
|
||||||
if (this.#size !== null) {
|
neededPadding = size;
|
||||||
neededPadding = this.#size;
|
|
||||||
} else {
|
|
||||||
neededPadding = MIN_SIZE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (b === null) {
|
} else if (b === null) {
|
||||||
// End Of Structure Padding
|
// End Of Structure Padding
|
||||||
if (this.#size !== null) {
|
const realSize = this.#getRealSize();
|
||||||
const realSize = a.propertyOffset() + a.propertySize();
|
const realRoundedSize = this.#roundSize(realSize);
|
||||||
const realRoundedSize = this.#roundSize(realSize);
|
if (realRoundedSize !== size) {
|
||||||
if (realRoundedSize !== this.#size) {
|
neededPadding = size - realSize;
|
||||||
neededPadding = this.#size - realSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Inner Structure Padding
|
// Inner Structure Padding
|
||||||
@ -180,7 +176,7 @@ export class Struct {
|
|||||||
neededPadding = b.propertyOffset() - realSizeSoFar;
|
neededPadding = b.propertyOffset() - realSizeSoFar;
|
||||||
}
|
}
|
||||||
if (neededPadding < 0) {
|
if (neededPadding < 0) {
|
||||||
throw new Error('Overlapping properties detected!');
|
throw new Error('Overlapping Properties Detected!');
|
||||||
}
|
}
|
||||||
return neededPadding;
|
return neededPadding;
|
||||||
}
|
}
|
||||||
@ -204,11 +200,6 @@ export class Struct {
|
|||||||
out += `extern ${method.generateDefinition()}`;
|
out += `extern ${method.generateDefinition()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Early Exit For Undefined Structures
|
|
||||||
if (this.#isUndefined()) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VTable
|
// VTable
|
||||||
if (this.#vtable !== null) {
|
if (this.#vtable !== null) {
|
||||||
out += this.#vtable.generate();
|
out += this.#vtable.generate();
|
||||||
@ -252,7 +243,7 @@ export class Struct {
|
|||||||
|
|
||||||
// Sanity Check Size
|
// Sanity Check Size
|
||||||
const size = this.getSize(true);
|
const size = this.getSize(true);
|
||||||
const isSizeDefined = this.#size !== null;
|
const isSizeDefined = this.#size.isExact();
|
||||||
out += assertSize(this.#name, size, isSizeDefined);
|
out += assertSize(this.#name, size, isSizeDefined);
|
||||||
|
|
||||||
// Allocation Function
|
// Allocation Function
|
||||||
@ -292,7 +283,7 @@ export class Struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocation Function
|
// Allocation Function
|
||||||
if (this.#size !== null) {
|
if (this.#size.isExact()) {
|
||||||
declarations += `${this.#name} *alloc_${this.#name}() {\n`;
|
declarations += `${this.#name} *alloc_${this.#name}() {\n`;
|
||||||
declarations += `${INDENT}return new ${this.#name};\n`;
|
declarations += `${INDENT}return new ${this.#name};\n`;
|
||||||
declarations += '}\n';
|
declarations += '}\n';
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
import { INDENT, POINTER_SIZE, assertSize, toHex } from './common';
|
import { INDENT, POINTER_SIZE, Size, assertSize, getSelfArg, toHex } from './common';
|
||||||
import { Method } from './method';
|
import { Method } from './method';
|
||||||
import { Property } from './property';
|
import { Property } from './property';
|
||||||
|
|
||||||
export class VTable implements Property {
|
export class VTable implements Property {
|
||||||
readonly #name: string;
|
readonly #name: string;
|
||||||
#address: number | null;
|
#address: number | null;
|
||||||
#size: number | null;
|
readonly #size: Size;
|
||||||
readonly #methods: Method[];
|
readonly #methods: Method[];
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor(name: string) {
|
constructor(name: string) {
|
||||||
this.#name = name;
|
this.#name = name;
|
||||||
this.#address = null;
|
this.#address = null;
|
||||||
this.#size = null;
|
this.#size = new Size(true);
|
||||||
this.#methods = [];
|
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));
|
||||||
|
this.add(new Method(name, 'destructor_deleting', destructor_return, destructor_args, 0x4));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Property Information
|
// Property Information
|
||||||
@ -37,11 +42,8 @@ export class VTable implements Property {
|
|||||||
setAddress(address: number) {
|
setAddress(address: number) {
|
||||||
this.#address = address;
|
this.#address = address;
|
||||||
}
|
}
|
||||||
setSize(size: number) {
|
setSize(size: number, isExact: boolean) {
|
||||||
this.#size = size;
|
this.#size.set(size, isExact);
|
||||||
if ((this.#size % POINTER_SIZE) !== 0) {
|
|
||||||
throw new Error(`Invalid VTable Size: ${this.#size}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add To VTable
|
// Add To VTable
|
||||||
@ -49,14 +51,13 @@ export class VTable implements Property {
|
|||||||
// Check Offset
|
// Check Offset
|
||||||
const offset = method.address;
|
const offset = method.address;
|
||||||
if ((offset % POINTER_SIZE) !== 0) {
|
if ((offset % POINTER_SIZE) !== 0) {
|
||||||
throw new Error(`Invalid VTable Offset: ${offset}`);
|
throw new Error(`Invalid VTable Offset: ${toHex(offset)}`);
|
||||||
}
|
|
||||||
// Check Size
|
|
||||||
if (this.#size !== null && (offset + POINTER_SIZE) > this.#size) {
|
|
||||||
throw new Error(`VTable Offset Too Large: ${offset}`);
|
|
||||||
}
|
}
|
||||||
// Add
|
// Add
|
||||||
const index = offset / POINTER_SIZE;
|
const index = offset / POINTER_SIZE;
|
||||||
|
if (this.#methods[index]) {
|
||||||
|
throw new Error(`Duplicate Virtual Method At Offset: ${toHex(offset)}`);
|
||||||
|
}
|
||||||
this.#methods[index] = method;
|
this.#methods[index] = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,12 +69,15 @@ export class VTable implements Property {
|
|||||||
// Check
|
// Check
|
||||||
#check() {
|
#check() {
|
||||||
// Check Size
|
// Check Size
|
||||||
if (this.#size !== null) {
|
const size = this.#size.get();
|
||||||
const maxMethodCount = this.#size / POINTER_SIZE;
|
if (size !== null) {
|
||||||
if (maxMethodCount < this.#methods.length) {
|
const maxMethodCount = size / POINTER_SIZE;
|
||||||
throw new Error(`VTable Size Too Small: ${this.#size}`);
|
if (this.#size.isExact() && maxMethodCount < this.#methods.length) {
|
||||||
|
throw new Error(`VTable Size Too Small: ${toHex(size)}`);
|
||||||
|
}
|
||||||
|
if (maxMethodCount > this.#methods.length) {
|
||||||
|
this.#methods.length = maxMethodCount;
|
||||||
}
|
}
|
||||||
this.#methods.length = maxMethodCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,8 +112,9 @@ export class VTable implements Property {
|
|||||||
out += `};\n`;
|
out += `};\n`;
|
||||||
|
|
||||||
// Sanity Check Size
|
// Sanity Check Size
|
||||||
const isSizeDefined = this.#size !== null;
|
const rawSize = this.#size.get();
|
||||||
const size = isSizeDefined ? this.#size! : (this.#methods.length * POINTER_SIZE);
|
const isSizeDefined = this.#size.isExact();
|
||||||
|
const size = isSizeDefined ? rawSize! : (this.#methods.length * POINTER_SIZE);
|
||||||
out += assertSize(this.#getName(), size, isSizeDefined);
|
out += assertSize(this.#getName(), size, isSizeDefined);
|
||||||
|
|
||||||
// Pointers
|
// Pointers
|
||||||
@ -164,7 +169,7 @@ export class VTable implements Property {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Duplication Method
|
// Duplication Method
|
||||||
if (this.#size !== null) {
|
if (this.#size.isExact()) {
|
||||||
declarations += `${this.#getName()} *dup_${this.#getName()}(${this.#getName()} *vtable) {\n`;
|
declarations += `${this.#getName()} *dup_${this.#getName()}(${this.#getName()} *vtable) {\n`;
|
||||||
declarations += `${INDENT}${this.#getName()} *obj = new ${this.#getName()};\n`;
|
declarations += `${INDENT}${this.#getName()} *obj = new ${this.#getName()};\n`;
|
||||||
declarations += `${INDENT}if (obj == NULL) {\n`;
|
declarations += `${INDENT}if (obj == NULL) {\n`;
|
||||||
|
Loading…
Reference in New Issue
Block a user