mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-16 16:31:32 +00:00
Initial GC integration (#196)
This commit is contained in:
193
src/builtins.ts
193
src/builtins.ts
@ -134,7 +134,7 @@ export function compileCall(
|
||||
compiler.currentType = Type.bool;
|
||||
if (!type) return module.createUnreachable();
|
||||
let classType = type.classReference;
|
||||
return classType != null && classType.lookupOverload(OperatorKind.INDEXED_GET) != null
|
||||
return classType !== null && classType.lookupOverload(OperatorKind.INDEXED_GET) !== null
|
||||
? module.createI32(1)
|
||||
: module.createI32(0);
|
||||
}
|
||||
@ -175,6 +175,19 @@ export function compileCall(
|
||||
compiler.currentType = Type.bool;
|
||||
return module.createI32(getExpressionId(expr) == ExpressionId.Const ? 1 : 0);
|
||||
}
|
||||
case "isManaged": { // isManaged<T>() -> bool
|
||||
if (!compiler.program.hasGC) {
|
||||
compiler.currentType = Type.bool;
|
||||
return module.createI32(0);
|
||||
}
|
||||
let type = evaluateConstantType(compiler, typeArguments, operands, reportNode);
|
||||
compiler.currentType = Type.bool;
|
||||
if (!type) return module.createUnreachable();
|
||||
let classType = type.classReference;
|
||||
return classType !== null && !classType.hasDecorator(DecoratorFlags.UNMANAGED)
|
||||
? module.createI32(1)
|
||||
: module.createI32(0);
|
||||
}
|
||||
|
||||
// math
|
||||
|
||||
@ -2881,25 +2894,46 @@ export function compileAllocate(
|
||||
assert(classInstance.program == program);
|
||||
var module = compiler.module;
|
||||
var options = compiler.options;
|
||||
var allocateInstance = program.memoryAllocateInstance;
|
||||
if (!allocateInstance) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_find_name_0,
|
||||
reportNode.range, "memory.allocate"
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
|
||||
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
allocateInstance.internalName, [
|
||||
options.isWasm64
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset)
|
||||
],
|
||||
options.nativeSizeType
|
||||
);
|
||||
// __gc_allocate(size, markFn)
|
||||
if (program.hasGC && classInstance.type.isManaged(program)) {
|
||||
let allocateInstance = assert(program.gcAllocateInstance);
|
||||
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
allocateInstance.internalName, [
|
||||
options.isWasm64
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset),
|
||||
module.createI32(
|
||||
ensureGCHook(compiler, classInstance)
|
||||
)
|
||||
],
|
||||
options.nativeSizeType
|
||||
);
|
||||
|
||||
// memory.allocate(size)
|
||||
} else {
|
||||
let allocateInstance = program.memoryAllocateInstance;
|
||||
if (!allocateInstance) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_find_name_0,
|
||||
reportNode.range, "memory.allocate"
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
|
||||
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
allocateInstance.internalName, [
|
||||
options.isWasm64
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset)
|
||||
],
|
||||
options.nativeSizeType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Compiles an abort wired to the conditionally imported 'abort' function. */
|
||||
@ -2921,7 +2955,7 @@ export function compileAbort(
|
||||
? compiler.compileExpression(message, stringType, ConversionKind.IMPLICIT, WrapMode.NONE)
|
||||
: stringType.toNativeZero(module);
|
||||
|
||||
var filenameArg = compiler.compileStaticString(reportNode.range.source.normalizedPath);
|
||||
var filenameArg = compiler.ensureStaticString(reportNode.range.source.normalizedPath);
|
||||
|
||||
compiler.currentType = Type.void;
|
||||
return module.createBlock(null, [
|
||||
@ -2988,3 +3022,122 @@ export function compileIterateRoots(compiler: Compiler): void {
|
||||
: module.createNop()
|
||||
);
|
||||
}
|
||||
|
||||
/** 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 internals)
|
||||
var members = classInstance.members;
|
||||
if (classInstance.prototype.declaration.range.source.isLibrary) {
|
||||
if (members !== null && members.has("__gc")) {
|
||||
let gcPrototype = assert(members.get("__gc"));
|
||||
assert(gcPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let gcInstance = assert(program.resolver.resolveFunction(<FunctionPrototype>gcPrototype, null));
|
||||
assert(gcInstance.is(CommonFlags.PRIVATE | CommonFlags.INSTANCE));
|
||||
assert(!gcInstance.isAny(CommonFlags.AMBIENT | CommonFlags.VIRTUAL));
|
||||
assert(gcInstance.signature.parameterTypes.length == 0);
|
||||
assert(gcInstance.signature.returnType == Type.void);
|
||||
gcInstance.internalName = classInstance.internalName + "~gc";
|
||||
assert(compiler.compileFunction(gcInstance));
|
||||
let index = compiler.ensureFunctionTableEntry(gcInstance);
|
||||
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(0);
|
||||
classInstance.gcHookIndex = gcHookIndex;
|
||||
|
||||
// if the class extends a base class, call its hook first (calls mark)
|
||||
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)
|
||||
],
|
||||
nativeSizeType == NativeType.I64 ? "Iv" : "iv"
|
||||
)
|
||||
);
|
||||
|
||||
// if this class is the top-most base class, mark the instance
|
||||
} else {
|
||||
body.push(
|
||||
module.createCall(assert(program.gcMarkInstance).internalName, [
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
], NativeType.None)
|
||||
);
|
||||
}
|
||||
|
||||
// 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(
|
||||
module.createCall(assert(program.gcMarkInstance).internalName, [
|
||||
module.createLoad(
|
||||
nativeSizeSize,
|
||||
false,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeSizeType,
|
||||
offset
|
||||
)
|
||||
], NativeType.None)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the function to the module and return its table index
|
||||
functionTable[gcHookIndex] = module.addFunction(
|
||||
classInstance.internalName + "~gc",
|
||||
compiler.ensureFunctionType(null, Type.void, options.usizeType),
|
||||
null,
|
||||
module.createBlock(null, body)
|
||||
);
|
||||
return gcHookIndex;
|
||||
}
|
||||
|
522
src/compiler.ts
522
src/compiler.ts
@ -36,7 +36,8 @@ import {
|
||||
getGetLocalIndex,
|
||||
getBlockChildCount,
|
||||
getBlockChild,
|
||||
getBlockName
|
||||
getBlockName,
|
||||
needsExplicitUnreachable
|
||||
} from "./module";
|
||||
|
||||
import {
|
||||
@ -74,7 +75,7 @@ import {
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
Resolver
|
||||
Resolver, ReportMode
|
||||
} from "./resolver";
|
||||
|
||||
import {
|
||||
@ -155,6 +156,8 @@ import {
|
||||
} from "./types";
|
||||
|
||||
import {
|
||||
writeI8,
|
||||
writeI16,
|
||||
writeI32,
|
||||
writeI64,
|
||||
writeF32,
|
||||
@ -282,7 +285,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
/** Map of already compiled static string segments. */
|
||||
stringSegments: Map<string,MemorySegment> = new Map();
|
||||
/** Function table being compiled. */
|
||||
functionTable: Function[] = [];
|
||||
functionTable: FunctionRef[] = [];
|
||||
/** Argument count helper global. */
|
||||
argcVar: GlobalRef = 0;
|
||||
/** Argument count helper setter. */
|
||||
@ -391,11 +394,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var functionTableSize = functionTable.length;
|
||||
var functionTableExported = false;
|
||||
if (functionTableSize) {
|
||||
let entries = new Array<FunctionRef>(functionTableSize);
|
||||
for (let i = 0; i < functionTableSize; ++i) {
|
||||
entries[i] = functionTable[i].ref;
|
||||
}
|
||||
module.setFunctionTable(entries);
|
||||
module.setFunctionTable(functionTable);
|
||||
module.addTableExport("0", "table");
|
||||
functionTableExported = true;
|
||||
}
|
||||
@ -1474,7 +1473,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// insert the trampoline if the function has optional parameters
|
||||
func = this.ensureTrampoline(func);
|
||||
}
|
||||
functionTable.push(func);
|
||||
functionTable.push(func.ref);
|
||||
func.functionTableIndex = index;
|
||||
return index;
|
||||
}
|
||||
@ -1581,7 +1580,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
default: stmts.push(stmt);
|
||||
case ExpressionId.Nop:
|
||||
}
|
||||
if (flow.isAny(FlowFlags.ANY_TERMINATING)) break;
|
||||
if (flow.isAny(FlowFlags.ANY_TERMINATING)) {
|
||||
if (needsExplicitUnreachable(stmt)) stmts.push(this.module.createUnreachable());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return stmts;
|
||||
}
|
||||
@ -1682,7 +1684,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
parentFlow.inherit(flow);
|
||||
|
||||
return module.createBlock(breakLabel, [
|
||||
var block: ExpressionRef[] = [
|
||||
module.createLoop(continueLabel,
|
||||
terminated
|
||||
? body // skip trailing continue if unnecessary
|
||||
@ -1691,7 +1693,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
module.createBreak(continueLabel, condExpr)
|
||||
], NativeType.None)
|
||||
)
|
||||
], terminated ? NativeType.Unreachable : NativeType.None);
|
||||
];
|
||||
if (terminated) block.push(module.createUnreachable());
|
||||
return module.createBlock(breakLabel, block);
|
||||
}
|
||||
|
||||
compileEmptyStatement(statement: EmptyStatement): ExpressionRef {
|
||||
@ -1754,7 +1758,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var incrExpr = statement.incrementor
|
||||
? this.compileExpression(<Expression>statement.incrementor, Type.void, ConversionKind.IMPLICIT, WrapMode.NONE)
|
||||
: 0;
|
||||
var bodyExpr = this.compileStatement(statement.statement);
|
||||
var bodyStatement = statement.statement;
|
||||
var bodyExpr = bodyStatement.kind == NodeKind.BLOCK && (<BlockStatement>bodyStatement).statements.length == 1
|
||||
? this.compileStatement((<BlockStatement>bodyStatement).statements[0])
|
||||
: this.compileStatement(bodyStatement);
|
||||
|
||||
// Switch back to the parent flow
|
||||
currentFunction.flow = flow.free();
|
||||
@ -1766,7 +1773,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
FlowFlags.CONTINUES |
|
||||
FlowFlags.CONDITIONALLY_CONTINUES
|
||||
);
|
||||
var terminated = alwaysTrue && flow.isAny(FlowFlags.ANY_TERMINATING);
|
||||
if (alwaysTrue) parentFlow.inherit(flow);
|
||||
else parentFlow.inheritConditional(flow);
|
||||
|
||||
@ -1793,18 +1799,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
|
||||
breakBlock.push(
|
||||
module.createLoop(repeatLabel,
|
||||
module.createBlock(null, repeatBlock, NativeType.None)
|
||||
)
|
||||
module.createLoop(repeatLabel, module.createBlock(null, repeatBlock, NativeType.None))
|
||||
);
|
||||
|
||||
return module.createBlock(
|
||||
breakLabel,
|
||||
breakBlock,
|
||||
terminated
|
||||
? NativeType.Unreachable
|
||||
: NativeType.None
|
||||
);
|
||||
return module.createBlock(breakLabel, breakBlock);
|
||||
}
|
||||
|
||||
compileIfStatement(statement: IfStatement): ExpressionRef {
|
||||
@ -2249,7 +2247,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
var body = this.compileStatement(statement.statement);
|
||||
var alwaysTrue = false; // TODO
|
||||
var alwaysReturns = alwaysTrue && flow.is(FlowFlags.RETURNS);
|
||||
var terminated = flow.isAny(FlowFlags.ANY_TERMINATING);
|
||||
|
||||
// Switch back to the parent flow
|
||||
@ -2264,7 +2261,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (alwaysTrue) parentFlow.inherit(flow);
|
||||
else parentFlow.inheritConditional(flow);
|
||||
|
||||
var expr = module.createBlock(breakLabel, [
|
||||
return module.createBlock(breakLabel, [
|
||||
module.createLoop(continueLabel,
|
||||
module.createIf(condExpr,
|
||||
terminated
|
||||
@ -2275,8 +2272,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
], NativeType.None)
|
||||
)
|
||||
)
|
||||
], alwaysReturns ? NativeType.Unreachable : NativeType.None);
|
||||
return expr;
|
||||
]);
|
||||
}
|
||||
|
||||
// expressions
|
||||
@ -4808,6 +4804,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
let tempLocalIndex = tempLocal.index;
|
||||
// TODO: simplify if valueWithCorrectType has no side effects
|
||||
// TODO: call __gc_link here if a GC is present
|
||||
return module.createBlock(null, [
|
||||
module.createSetLocal(tempLocalIndex, valueWithCorrectType),
|
||||
module.createStore(
|
||||
@ -4820,6 +4817,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
module.createGetLocal(tempLocalIndex, nativeType)
|
||||
], nativeType);
|
||||
} else {
|
||||
// TODO: call __gc_link here if a GC is present
|
||||
return module.createStore(
|
||||
type.byteSize,
|
||||
thisExpr,
|
||||
@ -5107,7 +5105,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// indirect call: index argument with signature (non-generic, can't be inlined)
|
||||
case ElementKind.LOCAL: {
|
||||
if (signature = (<Local>target).type.signatureReference) {
|
||||
indexArg = module.createGetLocal((<Local>target).index, NativeType.I32);
|
||||
if ((<Local>target).is(CommonFlags.INLINED)) {
|
||||
indexArg = module.createI32(i64_low((<Local>target).constantIntegerValue));
|
||||
} else {
|
||||
indexArg = module.createGetLocal((<Local>target).index, NativeType.I32);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
this.error(
|
||||
@ -6094,6 +6096,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.compileArrayLiteral(
|
||||
assert(classType.typeArguments)[0],
|
||||
(<ArrayLiteralExpression>expression).elementExpressions,
|
||||
false, // TODO: isConst?
|
||||
expression
|
||||
);
|
||||
}
|
||||
@ -6204,7 +6207,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
case LiteralKind.STRING: {
|
||||
assert(!implicitNegate);
|
||||
return this.compileStaticString((<StringLiteralExpression>expression).value);
|
||||
return this.compileStringLiteral(<StringLiteralExpression>expression);
|
||||
}
|
||||
case LiteralKind.OBJECT: {
|
||||
assert(!implicitNegate);
|
||||
@ -6220,241 +6223,282 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return module.createUnreachable();
|
||||
}
|
||||
|
||||
compileStaticString(stringValue: string): ExpressionRef {
|
||||
/** Ensures that the specified string exists in static memory and returns a pointer to it. */
|
||||
ensureStaticString(stringValue: string): ExpressionRef {
|
||||
var program = this.program;
|
||||
var module = this.module;
|
||||
var options = this.options;
|
||||
var stringSegments = this.stringSegments;
|
||||
var needsGCHeader = program.hasGC;
|
||||
|
||||
var stringSegment: MemorySegment | null = stringSegments.get(stringValue);
|
||||
if (!stringSegment) {
|
||||
var stringSegment: MemorySegment;
|
||||
var stringOffset: I64;
|
||||
if (!stringSegments.has(stringValue)) {
|
||||
let stringLength = stringValue.length;
|
||||
let stringBuffer = new Uint8Array(4 + stringLength * 2);
|
||||
stringBuffer[0] = stringLength & 0xff;
|
||||
stringBuffer[1] = (stringLength >>> 8) & 0xff;
|
||||
stringBuffer[2] = (stringLength >>> 16) & 0xff;
|
||||
stringBuffer[3] = (stringLength >>> 24) & 0xff;
|
||||
let stringSize = 4 + stringLength * 2;
|
||||
let offset = 0;
|
||||
let gcHeaderSize = program.gcHeaderSize;
|
||||
if (needsGCHeader) {
|
||||
stringSize += gcHeaderSize;
|
||||
offset += gcHeaderSize;
|
||||
}
|
||||
let stringBuffer = new Uint8Array(stringSize);
|
||||
stringBuffer[offset ] = stringLength & 0xff;
|
||||
stringBuffer[offset + 1] = (stringLength >>> 8) & 0xff;
|
||||
stringBuffer[offset + 2] = (stringLength >>> 16) & 0xff;
|
||||
stringBuffer[offset + 3] = (stringLength >>> 24) & 0xff;
|
||||
for (let i = 0; i < stringLength; ++i) {
|
||||
stringBuffer[4 + i * 2] = stringValue.charCodeAt(i) & 0xff;
|
||||
stringBuffer[5 + i * 2] = (stringValue.charCodeAt(i) >>> 8) & 0xff;
|
||||
stringBuffer[offset + 4 + i * 2] = stringValue.charCodeAt(i) & 0xff;
|
||||
stringBuffer[offset + 5 + i * 2] = (stringValue.charCodeAt(i) >>> 8) & 0xff;
|
||||
}
|
||||
stringSegment = this.addMemorySegment(stringBuffer, options.usizeType.byteSize);
|
||||
stringSegments.set(stringValue, stringSegment);
|
||||
if (needsGCHeader) {
|
||||
stringOffset = i64_add(stringSegment.offset, i64_new(gcHeaderSize, 0));
|
||||
} else {
|
||||
stringOffset = stringSegment.offset;
|
||||
}
|
||||
} else {
|
||||
stringSegment = <MemorySegment>stringSegments.get(stringValue);
|
||||
stringOffset = stringSegment.offset;
|
||||
}
|
||||
if (program.typesLookup.has("string")) {
|
||||
let stringType = <Type>program.typesLookup.get("string");
|
||||
this.currentType = stringType;
|
||||
} else {
|
||||
this.currentType = options.usizeType;
|
||||
}
|
||||
var stringOffset = stringSegment.offset;
|
||||
var stringType = this.program.typesLookup.get("string");
|
||||
this.currentType = stringType ? stringType : options.usizeType;
|
||||
if (options.isWasm64) {
|
||||
return module.createI64(i64_low(stringOffset), i64_high(stringOffset));
|
||||
} else {
|
||||
assert(i64_is_i32(stringOffset));
|
||||
return module.createI32(i64_low(stringOffset));
|
||||
}
|
||||
assert(i64_is_i32(stringOffset));
|
||||
return module.createI32(i64_low(stringOffset));
|
||||
}
|
||||
|
||||
compileArrayLiteral(elementType: Type, expressions: (Expression | null)[], reportNode: Node): ExpressionRef {
|
||||
var isStatic = true;
|
||||
compileStringLiteral(expression: StringLiteralExpression): ExpressionRef {
|
||||
return this.ensureStaticString(expression.value);
|
||||
}
|
||||
|
||||
/** Ensures that the specified array exists in static memory and returns a pointer to it. */
|
||||
ensureStaticArray(elementType: Type, values: ExpressionRef[]): ExpressionRef {
|
||||
var length = values.length;
|
||||
var byteSize = elementType.byteSize;
|
||||
var byteLength = length * byteSize;
|
||||
var usizeTypeSize = this.options.usizeType.byteSize;
|
||||
|
||||
// determine the size of the Array header
|
||||
var arrayHeaderSize = (usizeTypeSize + 4 + 7) & ~7; // .buffer_ + .length_ + alignment
|
||||
var arrayTotalSize = arrayHeaderSize;
|
||||
|
||||
// determine the size of the ArrayBuffer
|
||||
var bufferHeaderSize = (4 + 7) & ~7; // .byteLength + alignment
|
||||
var bufferTotalSize = 1 << (32 - clz(byteLength + bufferHeaderSize - 1)); // see internals
|
||||
|
||||
var program = this.program;
|
||||
var needsGC = program.hasGC;
|
||||
var gcHeaderSize = program.gcHeaderSize;
|
||||
|
||||
var offset = 0;
|
||||
if (needsGC) {
|
||||
offset += gcHeaderSize; // start writing after GC header
|
||||
arrayTotalSize += gcHeaderSize;
|
||||
bufferTotalSize += gcHeaderSize;
|
||||
}
|
||||
|
||||
// create a compound segment holding both the the Array header and the ArrayBuffer
|
||||
var buffer = new Uint8Array(arrayHeaderSize + bufferTotalSize);
|
||||
var segment = this.addMemorySegment(buffer);
|
||||
|
||||
// write the Array header first
|
||||
if (usizeTypeSize == 8) {
|
||||
writeI64(i64_add(segment.offset, i64_new(arrayHeaderSize)), buffer, offset); // .buffer_
|
||||
offset += 8;
|
||||
} else {
|
||||
assert(i64_is_u32(segment.offset));
|
||||
writeI32(i64_low(segment.offset) + arrayHeaderSize, buffer, offset); // .buffer_
|
||||
offset += 4;
|
||||
}
|
||||
writeI32(length, buffer, offset); // .length_
|
||||
offset += 4;
|
||||
assert(((offset + 7) & ~7) == arrayTotalSize); // incl. GC header if applicable
|
||||
|
||||
// append the ArrayBuffer
|
||||
offset = arrayTotalSize;
|
||||
if (needsGC) offset += gcHeaderSize;
|
||||
writeI32(byteLength, buffer, offset); // .byteLength
|
||||
offset += bufferHeaderSize; // align
|
||||
var nativeType = elementType.toNativeType();
|
||||
switch (nativeType) {
|
||||
case NativeType.I32: {
|
||||
switch (byteSize) {
|
||||
case 1: {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let value = values[i];
|
||||
assert(getExpressionType(value) == nativeType);
|
||||
assert(getExpressionId(value) == ExpressionId.Const);
|
||||
writeI8(getConstValueI32(value), buffer, offset);
|
||||
offset += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let value = values[i];
|
||||
assert(getExpressionType(value) == nativeType);
|
||||
assert(getExpressionId(value) == ExpressionId.Const);
|
||||
writeI16(getConstValueI32(value), buffer, offset);
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let value = values[i];
|
||||
assert(getExpressionType(value) == nativeType);
|
||||
assert(getExpressionId(value) == ExpressionId.Const);
|
||||
writeI32(getConstValueI32(value), buffer, offset);
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: assert(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let value = values[i];
|
||||
assert(getExpressionType(value) == nativeType);
|
||||
assert(getExpressionId(value) == ExpressionId.Const);
|
||||
writeI64(i64_new(getConstValueI64Low(value), getConstValueI64High(value)), buffer, offset);
|
||||
offset += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let value = values[i];
|
||||
assert(getExpressionType(value) == nativeType);
|
||||
assert(getExpressionId(value) == ExpressionId.Const);
|
||||
writeF32(getConstValueF32(value), buffer, offset);
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let value = values[i];
|
||||
assert(getExpressionType(value) == nativeType);
|
||||
assert(getExpressionId(value) == ExpressionId.Const);
|
||||
writeF64(getConstValueF64(value), buffer, offset);
|
||||
offset += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: assert(false);
|
||||
}
|
||||
assert(offset <= arrayTotalSize + bufferTotalSize); // might have empty trailing space
|
||||
|
||||
var arrayPrototype = this.program.arrayPrototype;
|
||||
if (arrayPrototype) {
|
||||
let arrayInstance = this.resolver.resolveClass(arrayPrototype, [ elementType ], null, ReportMode.REPORT);
|
||||
if (!arrayInstance) {
|
||||
this.currentType = this.options.usizeType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
this.currentType = arrayInstance.type;
|
||||
} else {
|
||||
this.currentType = this.options.usizeType;
|
||||
}
|
||||
|
||||
// return a pointer at the array header (skip GC header if present)
|
||||
var address = segment.offset;
|
||||
if (needsGC) address = i64_add(address, i64_new(gcHeaderSize, 0));
|
||||
if (usizeTypeSize == 8) {
|
||||
return this.module.createI64(i64_low(address), i64_high(address));
|
||||
} else {
|
||||
assert(i64_is_u32(address));
|
||||
return this.module.createI32(i64_low(address));
|
||||
}
|
||||
}
|
||||
|
||||
compileArrayLiteral(
|
||||
elementType: Type,
|
||||
expressions: (Expression | null)[],
|
||||
isConst: bool,
|
||||
reportNode: Node
|
||||
): ExpressionRef {
|
||||
var module = this.module;
|
||||
|
||||
// obtain the array type
|
||||
// find out whether all elements are constant (array is static)
|
||||
var length = expressions.length;
|
||||
var values = new Array<ExpressionRef>(length);
|
||||
var nativeElementType = elementType.toNativeType();
|
||||
var isStatic = true;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
values[i] = expressions[i]
|
||||
? this.compileExpression(<Expression>expressions[i], elementType, ConversionKind.IMPLICIT, WrapMode.NONE)
|
||||
: elementType.toNativeZero(module);
|
||||
if (isStatic) {
|
||||
let expr = module.precomputeExpression(values[i]);
|
||||
if (getExpressionId(expr) == ExpressionId.Const) {
|
||||
assert(getExpressionType(expr) == nativeElementType);
|
||||
} else {
|
||||
if (isConst) {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
reportNode.range
|
||||
);
|
||||
}
|
||||
isStatic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make a static array if possible
|
||||
if (isStatic) return this.ensureStaticArray(elementType, values);
|
||||
|
||||
// otherwise obtain the array type
|
||||
var arrayPrototype = assert(this.program.arrayPrototype);
|
||||
if (!arrayPrototype || arrayPrototype.kind != ElementKind.CLASS_PROTOTYPE) return module.createUnreachable();
|
||||
var arrayInstance = this.resolver.resolveClass(<ClassPrototype>arrayPrototype, [ elementType ]);
|
||||
if (!arrayInstance) return module.createUnreachable();
|
||||
var arrayType = arrayInstance.type;
|
||||
|
||||
var elementCount = expressions.length;
|
||||
if (elementCount) { // non-empty static or dynamic
|
||||
let nativeElementType = elementType.toNativeType();
|
||||
let values: usize;
|
||||
let byteLength: usize;
|
||||
switch (nativeElementType) {
|
||||
case NativeType.I32: {
|
||||
values = changetype<usize>(new Int32Array(elementCount));
|
||||
byteLength = elementCount * 4;
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
values = changetype<usize>(new Array<I64>(elementCount));
|
||||
byteLength = elementCount * 8;
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
values = changetype<usize>(new Float32Array(elementCount));
|
||||
byteLength = elementCount * 4;
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
values = changetype<usize>(new Float64Array(elementCount));
|
||||
byteLength = elementCount * 8;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
// precompute value expressions
|
||||
let exprs = new Array<ExpressionRef>(elementCount);
|
||||
let expr: BinaryenExpressionRef;
|
||||
for (let i = 0; i < elementCount; ++i) {
|
||||
exprs[i] = expressions[i]
|
||||
? this.compileExpression(<Expression>expressions[i], elementType, ConversionKind.IMPLICIT, WrapMode.NONE)
|
||||
: elementType.toNativeZero(module);
|
||||
if (isStatic) {
|
||||
expr = module.precomputeExpression(exprs[i]);
|
||||
if (getExpressionId(expr) == ExpressionId.Const) {
|
||||
assert(getExpressionType(expr) == nativeElementType);
|
||||
switch (nativeElementType) {
|
||||
case NativeType.I32: {
|
||||
changetype<i32[]>(values)[i] = getConstValueI32(expr);
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
changetype<I64[]>(values)[i] = i64_new(
|
||||
getConstValueI64Low(expr),
|
||||
getConstValueI64High(expr)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
changetype<f32[]>(values)[i] = getConstValueF32(expr);
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
changetype<f64[]>(values)[i] = getConstValueF64(expr);
|
||||
break;
|
||||
}
|
||||
default: assert(false); // checked above
|
||||
}
|
||||
} else {
|
||||
// TODO: emit a warning if declared 'const'
|
||||
// if (isConst) {
|
||||
// this.warn(
|
||||
// DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
// reportNode.range
|
||||
// );
|
||||
// }
|
||||
isStatic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let usizeTypeSize = this.options.usizeType.byteSize;
|
||||
if (isStatic) { // non-empty, all elements can be precomputed
|
||||
|
||||
// Create a combined static memory segment composed of:
|
||||
// Array struct + ArrayBuffer struct + aligned ArrayBuffer data
|
||||
|
||||
let arraySize = usizeTypeSize + 4; // buffer_ & length_
|
||||
let bufferHeaderSize = (4 + 7) & ~7; // aligned byteLength (8)
|
||||
let bufferTotalSize = 1 << (32 - clz(byteLength + bufferHeaderSize - 1)); // see internals
|
||||
let data = new Uint8Array(arraySize + bufferTotalSize);
|
||||
let segment = this.addMemorySegment(data);
|
||||
let offset = 0;
|
||||
|
||||
// write Array struct
|
||||
if (usizeTypeSize == 8) {
|
||||
writeI64(i64_add(segment.offset, i64_new(arraySize)), data, offset); // buffer_ @ segment[arSize]
|
||||
offset += 8;
|
||||
} else {
|
||||
assert(i64_high(segment.offset) == 0);
|
||||
writeI32(i64_low(segment.offset) + arraySize, data, offset); // buffer_ @ segment[arSize]
|
||||
offset += 4;
|
||||
}
|
||||
writeI32(elementCount, data, offset); // length_
|
||||
offset += 4;
|
||||
assert(offset == arraySize);
|
||||
|
||||
// write ArrayBuffer struct
|
||||
writeI32(byteLength, data, offset);
|
||||
offset += bufferHeaderSize; // incl. alignment
|
||||
|
||||
// write ArrayBuffer data
|
||||
switch (nativeElementType) {
|
||||
case NativeType.I32: {
|
||||
for (let i = 0; i < elementCount; ++i) {
|
||||
writeI32(changetype<i32[]>(values)[i], data, offset); offset += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
for (let i = 0; i < elementCount; ++i) {
|
||||
writeI64(changetype<I64[]>(values)[i], data, offset); offset += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
for (let i = 0; i < elementCount; ++i) {
|
||||
writeF32(changetype<f32[]>(values)[i], data, offset); offset += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
for (let i = 0; i < elementCount; ++i) {
|
||||
writeF64(changetype<f64[]>(values)[i], data, offset); offset += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
}
|
||||
assert(offset <= arraySize + bufferTotalSize);
|
||||
|
||||
this.currentType = arrayType;
|
||||
return usizeTypeSize == 8
|
||||
? module.createI64(
|
||||
i64_low(segment.offset),
|
||||
i64_high(segment.offset)
|
||||
)
|
||||
: module.createI32(
|
||||
i64_low(segment.offset)
|
||||
);
|
||||
|
||||
} else { // non-empty, some elements can't be precomputed
|
||||
|
||||
this.currentType = arrayType;
|
||||
let setter = arrayInstance.lookupOverload(OperatorKind.INDEXED_SET, true);
|
||||
if (!setter) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_in_type_0_only_permits_reading,
|
||||
reportNode.range, arrayInstance.internalName
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let nativeArrayType = arrayType.toNativeType();
|
||||
let currentFunction = this.currentFunction;
|
||||
let tempLocal = currentFunction.getTempLocal(arrayType, false);
|
||||
let stmts = new Array<ExpressionRef>(2 + elementCount);
|
||||
let index = 0;
|
||||
stmts[index++] = module.createSetLocal(tempLocal.index,
|
||||
this.makeCallDirect(assert(arrayInstance.constructorInstance), [
|
||||
module.createI32(0), // this
|
||||
module.createI32(elementCount)
|
||||
])
|
||||
);
|
||||
for (let i = 0; i < elementCount; ++i) {
|
||||
stmts[index++] = this.makeCallDirect(setter, [
|
||||
module.createGetLocal(tempLocal.index, nativeArrayType), // this
|
||||
module.createI32(i),
|
||||
exprs[i]
|
||||
]);
|
||||
}
|
||||
assert(index + 1 == stmts.length);
|
||||
stmts[index] = module.createGetLocal(tempLocal.index, nativeArrayType);
|
||||
currentFunction.freeTempLocal(tempLocal);
|
||||
this.currentType = arrayType;
|
||||
return module.createBlock(null, stmts, nativeArrayType);
|
||||
}
|
||||
|
||||
} else { // empty, TBD: cache this somehow?
|
||||
this.currentType = arrayType;
|
||||
return this.makeCallDirect(assert(arrayInstance.constructorInstance), [
|
||||
// and compile an explicit instantiation
|
||||
this.currentType = arrayType;
|
||||
var setter = arrayInstance.lookupOverload(OperatorKind.INDEXED_SET, true);
|
||||
if (!setter) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_in_type_0_only_permits_reading,
|
||||
reportNode.range, arrayInstance.internalName
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
var nativeArrayType = arrayType.toNativeType();
|
||||
var currentFunction = this.currentFunction;
|
||||
var tempLocal = currentFunction.getTempLocal(arrayType, false);
|
||||
var stmts = new Array<ExpressionRef>(2 + length);
|
||||
var index = 0;
|
||||
stmts[index++] = module.createSetLocal(tempLocal.index,
|
||||
this.makeCallDirect(assert(arrayInstance.constructorInstance), [
|
||||
module.createI32(0), // this
|
||||
module.createI32(0)
|
||||
module.createI32(length)
|
||||
])
|
||||
);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
stmts[index++] = this.makeCallDirect(setter, [
|
||||
module.createGetLocal(tempLocal.index, nativeArrayType), // this
|
||||
module.createI32(i),
|
||||
values[i]
|
||||
]);
|
||||
}
|
||||
assert(index + 1 == stmts.length);
|
||||
stmts[index] = module.createGetLocal(tempLocal.index, nativeArrayType);
|
||||
currentFunction.freeTempLocal(tempLocal);
|
||||
this.currentType = arrayType;
|
||||
return module.createBlock(null, stmts, nativeArrayType);
|
||||
}
|
||||
|
||||
compileObjectLiteral(expression: ObjectLiteralExpression, contextualType: Type): ExpressionRef {
|
||||
|
@ -16,14 +16,14 @@ export type ImportRef = usize;
|
||||
export type ExportRef = usize;
|
||||
export type Index = u32;
|
||||
|
||||
export enum NativeType {
|
||||
None = _BinaryenTypeNone(),
|
||||
I32 = _BinaryenTypeInt32(),
|
||||
I64 = _BinaryenTypeInt64(),
|
||||
F32 = _BinaryenTypeFloat32(),
|
||||
F64 = _BinaryenTypeFloat64(),
|
||||
Unreachable = _BinaryenTypeUnreachable(),
|
||||
Auto = _BinaryenTypeAuto()
|
||||
export const enum NativeType {
|
||||
None = 0, // _BinaryenTypeNone(),
|
||||
I32 = 1, // _BinaryenTypeInt32(),
|
||||
I64 = 2, // _BinaryenTypeInt64(),
|
||||
F32 = 3, // _BinaryenTypeFloat32(),
|
||||
F64 = 4, // _BinaryenTypeFloat64(),
|
||||
Unreachable = 5, // _BinaryenTypeUnreachable(),
|
||||
Auto = -1 // _BinaryenTypeAuto()
|
||||
}
|
||||
|
||||
export enum ExpressionId {
|
||||
@ -1580,3 +1580,26 @@ export class BinaryModule {
|
||||
/** Source map, if generated. */
|
||||
sourceMap: string | null;
|
||||
}
|
||||
|
||||
/** Tests if an expression needs an explicit 'unreachable' when it is the terminating statement. */
|
||||
export function needsExplicitUnreachable(expr: ExpressionRef): bool {
|
||||
// not applicable if pushing a value to the stack
|
||||
switch (_BinaryenExpressionGetType(expr)) {
|
||||
case NativeType.I32:
|
||||
case NativeType.I64:
|
||||
case NativeType.F32:
|
||||
case NativeType.F64: return false;
|
||||
}
|
||||
switch (_BinaryenExpressionGetId(expr)) {
|
||||
case ExpressionId.Unreachable:
|
||||
case ExpressionId.Return: return false;
|
||||
case ExpressionId.Break: return _BinaryenBreakGetCondition(expr) != 0;
|
||||
case ExpressionId.Block: {
|
||||
if (!_BinaryenBlockGetName(expr)) { // can't break out of it
|
||||
let numChildren = _BinaryenBlockGetNumChildren(expr); // last child needs unreachable
|
||||
return numChildren > 0 && needsExplicitUnreachable(_BinaryenBlockGetChild(expr, numChildren - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -341,6 +341,17 @@ export class Program extends DiagnosticEmitter {
|
||||
/** Memory allocation function. */
|
||||
memoryAllocateInstance: Function | null = null;
|
||||
|
||||
/** Whether a garbage collector is present or not. */
|
||||
hasGC: bool = false;
|
||||
/** Garbage collector allocation function. */
|
||||
gcAllocateInstance: Function | null = null;
|
||||
/** Garbage collector link function called when a managed object is referenced from a parent. */
|
||||
gcLinkInstance: Function | null = null;
|
||||
/** Garbage collector mark function called to on reachable managed objects. */
|
||||
gcMarkInstance: Function | null = null;
|
||||
/** Size of a managed object header. */
|
||||
gcHeaderSize: u32 = 0;
|
||||
|
||||
/** Currently processing filespace. */
|
||||
currentFilespace: Filespace;
|
||||
|
||||
@ -658,6 +669,48 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register GC hooks if present
|
||||
if (
|
||||
this.elementsLookup.has("__gc_allocate") &&
|
||||
this.elementsLookup.has("__gc_link") &&
|
||||
this.elementsLookup.has("__gc_mark")
|
||||
) {
|
||||
// __gc_allocate(usize, (ref: usize) => void): usize
|
||||
let element = <Element>this.elementsLookup.get("__gc_allocate");
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let gcAllocateInstance = assert(this.resolver.resolveFunction(<FunctionPrototype>element, null));
|
||||
let signature = gcAllocateInstance.signature;
|
||||
assert(signature.parameterTypes.length == 2);
|
||||
assert(signature.parameterTypes[0] == this.options.usizeType);
|
||||
assert(signature.parameterTypes[1].signatureReference);
|
||||
assert(signature.returnType == this.options.usizeType);
|
||||
|
||||
// __gc_link(usize, usize): void
|
||||
element = <Element>this.elementsLookup.get("__gc_link");
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let gcLinkInstance = assert(this.resolver.resolveFunction(<FunctionPrototype>element, null));
|
||||
signature = gcLinkInstance.signature;
|
||||
assert(signature.parameterTypes.length == 2);
|
||||
assert(signature.parameterTypes[0] == this.options.usizeType);
|
||||
assert(signature.parameterTypes[1] == this.options.usizeType);
|
||||
assert(signature.returnType == Type.void);
|
||||
|
||||
// __gc_mark(usize): void
|
||||
element = <Element>this.elementsLookup.get("__gc_mark");
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let gcMarkInstance = assert(this.resolver.resolveFunction(<FunctionPrototype>element, null));
|
||||
signature = gcMarkInstance.signature;
|
||||
assert(signature.parameterTypes.length == 1);
|
||||
assert(signature.parameterTypes[0] == this.options.usizeType);
|
||||
assert(signature.returnType == Type.void);
|
||||
|
||||
this.gcAllocateInstance = gcAllocateInstance;
|
||||
this.gcLinkInstance = gcLinkInstance;
|
||||
this.gcMarkInstance = gcMarkInstance;
|
||||
this.gcHeaderSize = (2 * options.usizeType.byteSize + 4 + 7) & ~7; // TODO: hardcoded atm
|
||||
this.hasGC = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets a constant integer value. */
|
||||
@ -2771,6 +2824,8 @@ export class Class extends Element {
|
||||
constructorInstance: Function | null = null;
|
||||
/** Operator overloads. */
|
||||
overloads: Map<OperatorKind,Function> | null = null;
|
||||
/** Function index of the GC hook. */
|
||||
gcHookIndex: u32 = <u32>-1;
|
||||
|
||||
/** Constructs a new class. */
|
||||
constructor(
|
||||
|
@ -377,7 +377,9 @@ export class Resolver extends DiagnosticEmitter {
|
||||
case ElementKind.GLOBAL:
|
||||
case ElementKind.LOCAL:
|
||||
case ElementKind.FIELD: {
|
||||
let classReference = (<VariableLikeElement>target).type.classReference;
|
||||
let type = (<VariableLikeElement>target).type;
|
||||
assert(type != Type.void);
|
||||
let classReference = type.classReference;
|
||||
if (!classReference) {
|
||||
this.error(
|
||||
DiagnosticCode.Property_0_does_not_exist_on_type_1,
|
||||
|
13
src/types.ts
13
src/types.ts
@ -5,7 +5,9 @@
|
||||
|
||||
import {
|
||||
Class,
|
||||
FunctionTarget
|
||||
FunctionTarget,
|
||||
Program,
|
||||
DecoratorFlags
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
@ -134,6 +136,15 @@ export class Type {
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests if this is a managed type that needs GC hooks. */
|
||||
isManaged(program: Program): bool {
|
||||
if (program.hasGC) {
|
||||
let classReference = this.classReference;
|
||||
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Computes the sign-extending shift in the target type. */
|
||||
computeSmallIntegerShift(targetType: Type): u32 {
|
||||
return targetType.size - this.size;
|
||||
|
@ -1,5 +1,27 @@
|
||||
/** @module util *//***/
|
||||
|
||||
/** Reads an 8-bit integer from the specified buffer. */
|
||||
export function readI8(buffer: Uint8Array, offset: i32): i32 {
|
||||
return buffer[offset];
|
||||
}
|
||||
|
||||
/** Writes an 8-bit integer to the specified buffer. */
|
||||
export function writeI8(value: i32, buffer: Uint8Array, offset: i32): void {
|
||||
buffer[offset] = value;
|
||||
}
|
||||
|
||||
/** Reads a 16-bit integer from the specified buffer. */
|
||||
export function readI16(buffer: Uint8Array, offset: i32): i32 {
|
||||
return buffer[offset ]
|
||||
| buffer[offset + 1] << 8;
|
||||
}
|
||||
|
||||
/** Writes a 16-bit integer to the specified buffer. */
|
||||
export function writeI16(value: i32, buffer: Uint8Array, offset: i32): void {
|
||||
buffer[offset ] = value;
|
||||
buffer[offset + 1] = value >>> 8;
|
||||
}
|
||||
|
||||
/** Reads a 32-bit integer from the specified buffer. */
|
||||
export function readI32(buffer: Uint8Array, offset: i32): i32 {
|
||||
return buffer[offset ]
|
||||
|
Reference in New Issue
Block a user