mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-19 09:51:33 +00:00
inherent acylic data structure detection
this will become useful for the pure collector eventually
This commit is contained in:
@ -4233,7 +4233,6 @@ function typeToRuntimeFlags(type: Type, program: Program): RTTIFlags {
|
||||
|
||||
/** Compiles runtime type information for use by stdlib. */
|
||||
export function compileRTTI(compiler: Compiler): void {
|
||||
// TODO: only add this if actually accessed?
|
||||
var program = compiler.program;
|
||||
var module = compiler.module;
|
||||
var managedClasses = program.managedClasses;
|
||||
@ -4242,22 +4241,26 @@ export function compileRTTI(compiler: Compiler): void {
|
||||
var data = new Uint8Array(size);
|
||||
writeI32(count, data, 0);
|
||||
var off = 8;
|
||||
var arrayPrototype = assert(program.arrayPrototype);
|
||||
var setPrototype = assert(program.setPrototype);
|
||||
var mapPrototype = assert(program.mapPrototype);
|
||||
var lastId = 0;
|
||||
for (let [id, instance] of managedClasses) {
|
||||
assert(id == ++lastId);
|
||||
let flags: RTTIFlags = 0;
|
||||
if (instance.prototype.extends(program.arrayPrototype)) {
|
||||
let typeArguments = assert(instance.typeArguments);
|
||||
if (instance.isAcyclic) flags |= RTTIFlags.ACYCLIC;
|
||||
if (instance.prototype.extends(arrayPrototype)) {
|
||||
let typeArguments = assert(instance.getTypeArgumentsTo(arrayPrototype));
|
||||
assert(typeArguments.length == 1);
|
||||
flags |= RTTIFlags.ARRAY;
|
||||
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0], program);
|
||||
} else if (instance.prototype.extends(program.setPrototype)) {
|
||||
let typeArguments = assert(instance.typeArguments);
|
||||
} else if (instance.prototype.extends(setPrototype)) {
|
||||
let typeArguments = assert(instance.getTypeArgumentsTo(setPrototype));
|
||||
assert(typeArguments.length == 1);
|
||||
flags |= RTTIFlags.SET;
|
||||
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0], program);
|
||||
} else if (instance.prototype.extends(program.mapPrototype)) {
|
||||
let typeArguments = assert(instance.typeArguments);
|
||||
} else if (instance.prototype.extends(mapPrototype)) {
|
||||
let typeArguments = assert(instance.getTypeArgumentsTo(mapPrototype));
|
||||
assert(typeArguments.length == 2);
|
||||
flags |= RTTIFlags.MAP;
|
||||
flags |= RTTIFlags.KEY_ALIGN_0 * typeToRuntimeFlags(typeArguments[0], program);
|
||||
|
107
src/program.ts
107
src/program.ts
@ -2971,9 +2971,8 @@ export class ClassPrototype extends DeclaredElement {
|
||||
/** Tests if this prototype extends the specified. */
|
||||
extends(basePtototype: ClassPrototype | null): bool {
|
||||
var current: ClassPrototype | null = this;
|
||||
do {
|
||||
if (current === basePtototype) return true;
|
||||
} while (current = current.basePrototype);
|
||||
do if (current === basePtototype) return true;
|
||||
while (current = current.basePrototype);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3022,6 +3021,12 @@ export class ClassPrototype extends DeclaredElement {
|
||||
}
|
||||
}
|
||||
|
||||
const enum AcyclicState {
|
||||
UNKNOWN,
|
||||
ACYCLIC,
|
||||
NOT_ACYCLIC
|
||||
}
|
||||
|
||||
/** A resolved class. */
|
||||
export class Class extends TypedElement {
|
||||
|
||||
@ -3041,6 +3046,8 @@ export class Class extends TypedElement {
|
||||
overloads: Map<OperatorKind,Function> | null = null;
|
||||
/** Unique class id. */
|
||||
private _id: u32 = 0;
|
||||
/** Remembers acyclic state. */
|
||||
private _acyclic: AcyclicState = AcyclicState.UNKNOWN;
|
||||
|
||||
/** Gets the unique runtime id of this class. Must be a managed class. */
|
||||
get id(): u32 {
|
||||
@ -3225,6 +3232,100 @@ export class Class extends TypedElement {
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Tests if this class extends the specified prototype. */
|
||||
extends(prototype: ClassPrototype): bool {
|
||||
return this.prototype.extends(prototype);
|
||||
}
|
||||
|
||||
/** Gets the concrete type arguments to the specified extendend prototype. */
|
||||
getTypeArgumentsTo(extendedPrototype: ClassPrototype): Type[] | null {
|
||||
var current: Class | null = this;
|
||||
do if (current.prototype === extendedPrototype) return current.typeArguments;
|
||||
while (current = current.base);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Tests if this class is inherently acyclic. */
|
||||
get isAcyclic(): bool {
|
||||
var acyclic = this._acyclic;
|
||||
if (acyclic == AcyclicState.UNKNOWN) {
|
||||
let hasCycle = this.cyclesTo(this);
|
||||
if (hasCycle) this._acyclic = acyclic = AcyclicState.NOT_ACYCLIC;
|
||||
else this._acyclic = acyclic = AcyclicState.ACYCLIC;
|
||||
}
|
||||
return acyclic == AcyclicState.ACYCLIC;
|
||||
}
|
||||
|
||||
/** Tests if this class potentially forms a reference cycle to another one. */
|
||||
private cyclesTo(other: Class, except: Set<Class> = new Set()): bool {
|
||||
if (except.has(this)) return false;
|
||||
except.add(this); // don't recurse indefinitely
|
||||
|
||||
// Find out if any field references 'other' directly or indirectly
|
||||
var current: Class | null;
|
||||
var members = this.members;
|
||||
if (members) {
|
||||
for (let member of members.values()) {
|
||||
if (
|
||||
member.kind == ElementKind.FIELD &&
|
||||
(current = (<Field>member).type.classReference) !== null &&
|
||||
(
|
||||
current === other ||
|
||||
current.cyclesTo(other, except)
|
||||
)
|
||||
) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same for non-field data
|
||||
var basePrototype: ClassPrototype | null;
|
||||
|
||||
// Array<T->other?>
|
||||
if ((basePrototype = this.program.arrayPrototype) && this.prototype.extends(basePrototype)) {
|
||||
let typeArguments = assert(this.getTypeArgumentsTo(basePrototype));
|
||||
assert(typeArguments.length == 1);
|
||||
if (
|
||||
(current = typeArguments[0].classReference) !== null &&
|
||||
(
|
||||
current === other ||
|
||||
current.cyclesTo(other, except)
|
||||
)
|
||||
) return true;
|
||||
|
||||
// Set<K->other?>
|
||||
} else if ((basePrototype = this.program.setPrototype) && this.prototype.extends(basePrototype)) {
|
||||
let typeArguments = assert(this.getTypeArgumentsTo(basePrototype));
|
||||
assert(typeArguments.length == 1);
|
||||
if (
|
||||
(current = typeArguments[0].classReference) !== null &&
|
||||
(
|
||||
current === other ||
|
||||
current.cyclesTo(other, except)
|
||||
)
|
||||
) return true;
|
||||
|
||||
// Map<K->other?,V->other?>
|
||||
} else if ((basePrototype = this.program.mapPrototype) && this.prototype.extends(basePrototype)) {
|
||||
let typeArguments = assert(this.getTypeArgumentsTo(basePrototype));
|
||||
assert(typeArguments.length == 2);
|
||||
if (
|
||||
(current = typeArguments[0].classReference) !== null &&
|
||||
(
|
||||
current === other ||
|
||||
current.cyclesTo(other, except)
|
||||
)
|
||||
) return true;
|
||||
if (
|
||||
(current = typeArguments[1].classReference) !== null &&
|
||||
(
|
||||
current === other ||
|
||||
current.cyclesTo(other, except)
|
||||
)
|
||||
) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** A yet unresolved interface. */
|
||||
|
Reference in New Issue
Block a user