mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-22 19:21:47 +00:00
finalize GC wiring
This commit is contained in:
142
src/builtins.ts
142
src/builtins.ts
@ -2370,6 +2370,15 @@ export function compileCall(
|
||||
let typeRef = module.getFunctionTypeBySignature(nativeReturnType, nativeParamTypes);
|
||||
if (!typeRef) typeRef = module.addFunctionType(typeName, nativeReturnType, nativeParamTypes);
|
||||
compiler.currentType = returnType;
|
||||
// if the index expression is precomputable to a constant value, emit a direct call
|
||||
if (getExpressionId(arg0 = module.precomputeExpression(arg0)) == ExpressionId.Const) {
|
||||
assert(getExpressionType(arg0) == NativeType.I32);
|
||||
let index = getConstValueI32(arg0);
|
||||
let functionTable = compiler.functionTable;
|
||||
if (index >= 0 && index < functionTable.length) {
|
||||
return module.createCall(functionTable[index], operandExprs, nativeReturnType);
|
||||
}
|
||||
}
|
||||
// of course this can easily result in a 'RuntimeError: function signature mismatch' trap and
|
||||
// thus must be used with care. it exists because it *might* be useful in specific scenarios.
|
||||
return module.createCallIndirect(arg0, operandExprs, typeName);
|
||||
@ -3620,8 +3629,16 @@ export function compileCall(
|
||||
compiler.currentType = Type.u32;
|
||||
if (!type) return module.createUnreachable();
|
||||
let classReference = type.classReference;
|
||||
if (!classReference) return module.createUnreachable();
|
||||
return module.createI32(classReference.id);
|
||||
if (!classReference || classReference.hasDecorator(DecoratorFlags.UNMANAGED)) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
reportNode.range
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let classId = classReference.ensureClassId(compiler); // involves compile steps
|
||||
compiler.currentType = Type.u32;
|
||||
return module.createI32(classId);
|
||||
}
|
||||
case BuiltinSymbols.iterateRoots: {
|
||||
if (
|
||||
@ -4093,127 +4110,6 @@ export function compileIterateRoots(compiler: Compiler): void {
|
||||
);
|
||||
}
|
||||
|
||||
/** Ensures that the specified class's GC hook exists and returns its function table index. */
|
||||
export function ensureGCHook(
|
||||
compiler: Compiler,
|
||||
classInstance: Class
|
||||
): u32 {
|
||||
var program = compiler.program;
|
||||
assert(classInstance.type.isManaged(program));
|
||||
|
||||
// check if the GC hook has already been created
|
||||
{
|
||||
let existingIndex = classInstance.gcHookIndex;
|
||||
if (existingIndex != <u32>-1) return existingIndex;
|
||||
}
|
||||
|
||||
// check if the class implements a custom GC function (only valid for library elements)
|
||||
var members = classInstance.members;
|
||||
if (classInstance.isDeclaredInLibrary) {
|
||||
if (members !== null && members.has("__iter")) {
|
||||
let iterPrototype = assert(members.get("__iter"));
|
||||
assert(iterPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let iterInstance = assert(program.resolver.resolveFunction(<FunctionPrototype>iterPrototype, null));
|
||||
assert(iterInstance.is(CommonFlags.PRIVATE | CommonFlags.INSTANCE));
|
||||
assert(!iterInstance.isAny(CommonFlags.AMBIENT | CommonFlags.VIRTUAL));
|
||||
let signature = iterInstance.signature;
|
||||
let parameterTypes = signature.parameterTypes;
|
||||
assert(parameterTypes.length == 1);
|
||||
assert(parameterTypes[0].signatureReference);
|
||||
assert(signature.returnType == Type.void);
|
||||
iterInstance.internalName = classInstance.internalName + "~iter";
|
||||
assert(compiler.compileFunction(iterInstance));
|
||||
let index = compiler.ensureFunctionTableEntry(iterInstance);
|
||||
classInstance.gcHookIndex = index;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
var module = compiler.module;
|
||||
var options = compiler.options;
|
||||
var nativeSizeType = options.nativeSizeType;
|
||||
var nativeSizeSize = options.usizeType.byteSize;
|
||||
var body = new Array<ExpressionRef>();
|
||||
|
||||
// nothing to mark if 'this' is null
|
||||
body.push(
|
||||
module.createIf(
|
||||
module.createUnary(
|
||||
options.isWasm64
|
||||
? UnaryOp.EqzI64
|
||||
: UnaryOp.EqzI32,
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
),
|
||||
module.createReturn()
|
||||
)
|
||||
);
|
||||
|
||||
// remember the function index so we don't recurse infinitely
|
||||
var functionTable = compiler.functionTable;
|
||||
var gcHookIndex = functionTable.length;
|
||||
functionTable.push("<placeholder>");
|
||||
classInstance.gcHookIndex = gcHookIndex;
|
||||
|
||||
// if the class extends a base class, call its hook first
|
||||
var baseInstance = classInstance.base;
|
||||
if (baseInstance) {
|
||||
assert(baseInstance.type.isManaged(program));
|
||||
body.push(
|
||||
module.createCallIndirect(
|
||||
module.createI32(
|
||||
ensureGCHook(compiler, <Class>baseInstance.type.classReference)
|
||||
),
|
||||
[
|
||||
module.createGetLocal(0, nativeSizeType), // this
|
||||
module.createGetLocal(1, NativeType.I32) // fn
|
||||
],
|
||||
"FUNCSIG$" + (nativeSizeType == NativeType.I64 ? "vji" : "vii")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// mark instances assigned to own fields that are again references
|
||||
if (members) {
|
||||
for (let member of members.values()) {
|
||||
if (member.kind == ElementKind.FIELD) {
|
||||
if ((<Field>member).parent === classInstance) {
|
||||
let type = (<Field>member).type;
|
||||
if (type.isManaged(program)) {
|
||||
let offset = (<Field>member).memoryOffset;
|
||||
assert(offset >= 0);
|
||||
body.push( // fn(fieldValue)
|
||||
module.createCallIndirect(
|
||||
module.createGetLocal(1, NativeType.I32),
|
||||
[
|
||||
module.createLoad(
|
||||
nativeSizeSize,
|
||||
false,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeSizeType,
|
||||
offset
|
||||
),
|
||||
],
|
||||
"FUNCSIG$vi"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the function to the module and return its table index
|
||||
var funcName = classInstance.internalName + "~iter";
|
||||
module.addFunction(
|
||||
funcName,
|
||||
compiler.ensureFunctionType(null, Type.void, options.usizeType),
|
||||
null,
|
||||
module.createBlock(null, body)
|
||||
);
|
||||
functionTable[gcHookIndex] = funcName;
|
||||
return gcHookIndex;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
/** Evaluates the constant type of a type argument *or* expression. */
|
||||
|
147
src/compiler.ts
147
src/compiler.ts
@ -71,7 +71,8 @@ import {
|
||||
DecoratorFlags,
|
||||
PropertyPrototype,
|
||||
File,
|
||||
mangleInternalName
|
||||
mangleInternalName,
|
||||
CollectorKind
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
@ -448,7 +449,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// expose module capabilities
|
||||
var capabilities = Capability.NONE;
|
||||
if (program.options.isWasm64) capabilities |= Capability.WASM64;
|
||||
if (program.gcImplemented) capabilities |= Capability.GC;
|
||||
if (program.collectorKind != CollectorKind.NONE) capabilities |= Capability.GC;
|
||||
if (capabilities != 0) {
|
||||
module.addGlobal(BuiltinSymbols.capabilities, NativeType.I32, false, module.createI32(capabilities));
|
||||
module.addGlobalExport(BuiltinSymbols.capabilities, ".capabilities");
|
||||
@ -1408,7 +1409,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
} else {
|
||||
let length = stringValue.length;
|
||||
let buffer = new Uint8Array(rtHeaderSize + (length << 1));
|
||||
program.writeRuntimeHeader(buffer, 0, stringInstance, length << 1);
|
||||
program.writeRuntimeHeader(buffer, 0, stringInstance.ensureClassId(this), length << 1);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
writeI16(stringValue.charCodeAt(i), buffer, rtHeaderSize + (i << 1));
|
||||
}
|
||||
@ -1434,7 +1435,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var runtimeHeaderSize = program.runtimeHeaderSize;
|
||||
|
||||
var buf = new Uint8Array(runtimeHeaderSize + byteLength);
|
||||
program.writeRuntimeHeader(buf, 0, bufferInstance, byteLength);
|
||||
program.writeRuntimeHeader(buf, 0, bufferInstance.ensureClassId(this), byteLength);
|
||||
var pos = runtimeHeaderSize;
|
||||
var nativeType = elementType.toNativeType();
|
||||
switch (nativeType) {
|
||||
@ -1521,7 +1522,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var arrayLength = i32(bufferLength / elementType.byteSize);
|
||||
|
||||
var buf = new Uint8Array(runtimeHeaderSize + arrayInstanceSize);
|
||||
program.writeRuntimeHeader(buf, 0, arrayInstance, arrayInstanceSize);
|
||||
program.writeRuntimeHeader(buf, 0, arrayInstance.ensureClassId(this), arrayInstanceSize);
|
||||
|
||||
var bufferAddress32 = i64_low(bufferSegment.offset) + runtimeHeaderSize;
|
||||
assert(!program.options.isWasm64); // TODO
|
||||
@ -6820,7 +6821,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// makeArray(length, classId, alignLog2, staticBuffer)
|
||||
let expr = this.makeCallDirect(assert(program.makeArrayInstance), [
|
||||
module.createI32(length),
|
||||
module.createI32(arrayInstance.id),
|
||||
module.createI32(arrayInstance.ensureClassId(this)),
|
||||
program.options.isWasm64
|
||||
? module.createI64(elementType.alignLog2)
|
||||
: module.createI32(elementType.alignLog2),
|
||||
@ -6854,7 +6855,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
module.createSetLocal(tempThis.index,
|
||||
this.makeCallDirect(makeArrayInstance, [
|
||||
module.createI32(length),
|
||||
module.createI32(arrayInstance.id),
|
||||
module.createI32(arrayInstance.ensureClassId(this)),
|
||||
program.options.isWasm64
|
||||
? module.createI64(elementType.alignLog2)
|
||||
: module.createI32(elementType.alignLog2),
|
||||
@ -8115,7 +8116,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset)
|
||||
], reportNode),
|
||||
module.createI32(classInstance.id)
|
||||
module.createI32(classInstance.ensureClassId(this))
|
||||
], reportNode);
|
||||
}
|
||||
}
|
||||
@ -8320,7 +8321,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
module.createBreak(label,
|
||||
module.createBinary(BinaryOp.EqI32, // classId == class.id
|
||||
module.createTeeLocal(idTemp.index, idExpr),
|
||||
module.createI32(classInstance.id)
|
||||
module.createI32(classInstance.ensureClassId(this))
|
||||
),
|
||||
module.createI32(1) // ? true
|
||||
)
|
||||
@ -8335,6 +8336,134 @@ export class Compiler extends DiagnosticEmitter {
|
||||
flow.popBreakLabel();
|
||||
return module.createBlock(label, conditions, NativeType.I32);
|
||||
}
|
||||
|
||||
/** Reserves the function index / class id for the following `makeIterate` operation. */
|
||||
makeIterateReserve(classInstance: Class): u32 {
|
||||
var functionTable = this.functionTable;
|
||||
var functionIndex = functionTable.length;
|
||||
functionTable.push(classInstance.iterateName);
|
||||
return functionIndex;
|
||||
}
|
||||
|
||||
/** Makes the managed iteration function of the specified class. */
|
||||
makeIterate(classInstance: Class, functionIndex: i32): void {
|
||||
var program = this.program;
|
||||
assert(classInstance.type.isManaged(program));
|
||||
|
||||
// check if the class implements a custom iteration function (only valid for library elements)
|
||||
var members = classInstance.members;
|
||||
if (classInstance.isDeclaredInLibrary) {
|
||||
if (members !== null && members.has("__iterate")) {
|
||||
let iterPrototype = members.get("__iterate")!;
|
||||
assert(iterPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let iterInstance = assert(program.resolver.resolveFunction(<FunctionPrototype>iterPrototype, null));
|
||||
assert(iterInstance.is(CommonFlags.PRIVATE | CommonFlags.INSTANCE));
|
||||
assert(iterInstance.hasDecorator(DecoratorFlags.UNSAFE));
|
||||
assert(!iterInstance.isAny(CommonFlags.AMBIENT | CommonFlags.VIRTUAL));
|
||||
let signature = iterInstance.signature;
|
||||
let parameterTypes = signature.parameterTypes;
|
||||
assert(parameterTypes.length == 1);
|
||||
assert(parameterTypes[0].signatureReference);
|
||||
assert(signature.returnType == Type.void);
|
||||
iterInstance.internalName = classInstance.iterateName;
|
||||
assert(this.compileFunction(iterInstance));
|
||||
this.ensureFunctionTableEntry(iterInstance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var module = this.module;
|
||||
var options = this.options;
|
||||
var nativeSizeType = options.nativeSizeType;
|
||||
var nativeSizeSize = options.usizeType.byteSize;
|
||||
// var signatureStr = Signature.makeSignatureString([ Type.u32 ], Type.void, options.usizeType);
|
||||
var body = new Array<ExpressionRef>();
|
||||
|
||||
// nothing to mark if 'this' is null (should not happen)
|
||||
// body.push(
|
||||
// module.createIf(
|
||||
// module.createUnary(
|
||||
// options.isWasm64
|
||||
// ? UnaryOp.EqzI64
|
||||
// : UnaryOp.EqzI32,
|
||||
// module.createGetLocal(0, nativeSizeType)
|
||||
// ),
|
||||
// module.createReturn()
|
||||
// )
|
||||
// );
|
||||
|
||||
// remember the function index so we don't recurse infinitely
|
||||
var functionTable = this.functionTable;
|
||||
var functionName = classInstance.iterateName;
|
||||
assert(functionIndex < functionTable.length);
|
||||
assert(functionTable[functionIndex] == functionName);
|
||||
|
||||
// if the class extends a base class, call its hook first
|
||||
var baseInstance = classInstance.base;
|
||||
if (baseInstance) {
|
||||
let baseType = baseInstance.type;
|
||||
let baseClassId = baseInstance.ensureClassId(this);
|
||||
assert(baseType.isManaged(program));
|
||||
body.push(
|
||||
// BASECLASS~iterate.call(this, fn)
|
||||
module.createCall(functionTable[baseClassId], [
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
module.createGetLocal(1, NativeType.I32)
|
||||
], NativeType.None)
|
||||
);
|
||||
}
|
||||
|
||||
// iterate references assigned to own fields
|
||||
if (members) {
|
||||
for (let member of members.values()) {
|
||||
if (member.kind == ElementKind.FIELD) {
|
||||
if ((<Field>member).parent === classInstance) {
|
||||
let fieldType = (<Field>member).type;
|
||||
if (fieldType.isManaged(program)) {
|
||||
let fieldClass = fieldType.classReference!;
|
||||
let fieldClassId = fieldClass.ensureClassId(this);
|
||||
let fieldOffset = (<Field>member).memoryOffset;
|
||||
assert(fieldOffset >= 0);
|
||||
body.push(
|
||||
// if ($2 = value) { fn($2); FIELDCLASS~iterate($2, fn); }
|
||||
module.createIf(
|
||||
module.createTeeLocal(2,
|
||||
module.createLoad(
|
||||
nativeSizeSize,
|
||||
false,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeSizeType,
|
||||
fieldOffset
|
||||
)
|
||||
),
|
||||
module.createBlock(null, [
|
||||
module.createCallIndirect(
|
||||
module.createGetLocal(1, NativeType.I32),
|
||||
[
|
||||
module.createGetLocal(2, nativeSizeType)
|
||||
], "FUNCSIG$vi"
|
||||
),
|
||||
module.createCall(functionTable[fieldClassId], [
|
||||
module.createGetLocal(2, nativeSizeType),
|
||||
module.createGetLocal(1, NativeType.I32)
|
||||
], NativeType.None)
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the function to the module and return its table index
|
||||
module.addFunction(
|
||||
functionName,
|
||||
this.ensureFunctionType([ Type.u32 ], Type.void, options.usizeType),
|
||||
members ? [ nativeSizeType ] : null,
|
||||
module.createBlock(null, body)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
@ -18,7 +18,8 @@ import {
|
||||
|
||||
import {
|
||||
Options,
|
||||
Feature
|
||||
Feature,
|
||||
Compiler
|
||||
} from "./compiler";
|
||||
|
||||
import {
|
||||
@ -308,6 +309,13 @@ function operatorKindFromDecorator(decoratorKind: DecoratorKind, arg: string): O
|
||||
return OperatorKind.INVALID;
|
||||
}
|
||||
|
||||
/** Garbage collector kind present. */
|
||||
export enum CollectorKind {
|
||||
NONE,
|
||||
TRACING,
|
||||
COUNTING
|
||||
}
|
||||
|
||||
/** Represents an AssemblyScript program. */
|
||||
export class Program extends DiagnosticEmitter {
|
||||
|
||||
@ -367,25 +375,24 @@ export class Program extends DiagnosticEmitter {
|
||||
/** Runtime make array function. `makeArray(capacity: i32, source: usize = 0, cid: u32): usize` */
|
||||
makeArrayInstance: Function | null = null;
|
||||
|
||||
/** The kind of garbage collector being present. */
|
||||
collectorKind: CollectorKind = CollectorKind.NONE;
|
||||
/** Memory allocation implementation, if present: `__mem_allocate(size: usize): usize` */
|
||||
allocateMem: Function | null = null;
|
||||
/** Memory free implementation, if present: `__mem_free(ref: usize): void` */
|
||||
freeMem: Function | null = null;
|
||||
/** Reference link implementation, if present: `__ref_link(ref: usize, parentRef: usize): void` */
|
||||
linkRef: Function | null = null;
|
||||
/** Reference unlink implementation, if present: `__ref_unlink(ref: usize, parentRef: usize): void` */
|
||||
unlinkRef: Function | null = null;
|
||||
/** Reference retain implementation, if present: `__ref_retain(ref: usize): void` */
|
||||
retainRef: Function | null = null;
|
||||
/** Reference release implementation, if present: `__ref_release(ref: usize): void` */
|
||||
releaseRef: Function | null = null;
|
||||
|
||||
/** Next class id. */
|
||||
nextClassId: u32 = 1;
|
||||
|
||||
// gc integration
|
||||
|
||||
/** Whether a garbage collector is present or not. */
|
||||
get gcImplemented(): bool {
|
||||
return this.lookupGlobal("__ref_collect") !== null;
|
||||
}
|
||||
/** Garbage collector mark function called to on reachable managed objects. */
|
||||
gcMarkInstance: Function | null = null; // FIXME
|
||||
|
||||
/** Constructs a new program, optionally inheriting parser diagnostics. */
|
||||
constructor(
|
||||
/** Shared array of diagnostic messages (emitted so far). */
|
||||
@ -402,12 +409,12 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
/** Gets the size of a common runtime header. */
|
||||
get runtimeHeaderSize(): i32 {
|
||||
return this.gcImplemented ? 16 : 8;
|
||||
return this.collectorKind ? 16 : 8;
|
||||
}
|
||||
|
||||
/** Writes a common runtime header to the specified buffer. */
|
||||
writeRuntimeHeader(buffer: Uint8Array, offset: i32, classInstance: Class, payloadSize: u32): void {
|
||||
writeI32(classInstance.id, buffer, offset);
|
||||
writeRuntimeHeader(buffer: Uint8Array, offset: i32, classId: i32, payloadSize: u32): void {
|
||||
writeI32(classId, buffer, offset);
|
||||
writeI32(payloadSize, buffer, offset + 4);
|
||||
}
|
||||
|
||||
@ -854,12 +861,14 @@ export class Program extends DiagnosticEmitter {
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
this.unlinkRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
|
||||
}
|
||||
this.collectorKind = CollectorKind.TRACING;
|
||||
} else if (element = this.lookupGlobal("__ref_retain")) {
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
this.retainRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
|
||||
element = assert(this.lookupGlobal("__ref_release"));
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
this.releaseRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
|
||||
this.collectorKind = CollectorKind.COUNTING;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2999,14 +3008,27 @@ export class Class extends TypedElement {
|
||||
constructorInstance: Function | null = null;
|
||||
/** Operator overloads. */
|
||||
overloads: Map<OperatorKind,Function> | null = null;
|
||||
/** Function index of the GC hook. */
|
||||
gcHookIndex: u32 = <u32>-1;
|
||||
/** Unique class id. */
|
||||
private _id: u32 = 0;
|
||||
/** Gets the unique id of this class. */
|
||||
get id(): u32 {
|
||||
|
||||
/** Ensures that this class has an id. */
|
||||
ensureClassId(compiler: Compiler): i32 {
|
||||
var id = this._id;
|
||||
if (!id) this._id = id = this.program.nextClassId++;
|
||||
if (!id) {
|
||||
assert(!this.hasDecorator(DecoratorFlags.UNMANAGED));
|
||||
let program = this.program;
|
||||
if (program.collectorKind == CollectorKind.TRACING) {
|
||||
// tracing GC uses the function index of the iteration function as the
|
||||
// class's id so it can call the id directly, which avoids to generate
|
||||
// a helper function with a big switch mapping ids to function indexes.
|
||||
// here: might be called recursively in makeIterate, so reserve the id.
|
||||
this._id = id = compiler.makeIterateReserve(this);
|
||||
compiler.makeIterate(this, id);
|
||||
} else {
|
||||
// counting GC or none just increments without any iterate functions
|
||||
this._id = id = program.nextClassId++;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -3031,6 +3053,11 @@ export class Class extends TypedElement {
|
||||
);
|
||||
}
|
||||
|
||||
/** Gets the name of this class's GC iteration function. */
|
||||
get iterateName(): string {
|
||||
return this.internalName + "~iterate";
|
||||
}
|
||||
|
||||
/** Constructs a new class. */
|
||||
constructor(
|
||||
/** Name incl. type parameters, i.e. `Foo<i32>`. */
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
Class,
|
||||
FunctionTarget,
|
||||
Program,
|
||||
DecoratorFlags
|
||||
DecoratorFlags,
|
||||
CollectorKind
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
@ -152,7 +153,7 @@ export class Type {
|
||||
|
||||
/** Tests if this is a managed type that needs GC hooks. */
|
||||
isManaged(program: Program): bool {
|
||||
if (program.gcImplemented) {
|
||||
if (program.collectorKind != CollectorKind.NONE) {
|
||||
let classReference = this.classReference;
|
||||
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
|
||||
}
|
||||
|
Reference in New Issue
Block a user