use gc interface directly, document

This commit is contained in:
dcode
2019-03-26 23:35:08 +01:00
parent 7c0dc66849
commit 3146f8f9e0
95 changed files with 17360 additions and 13504 deletions

View File

@ -4079,277 +4079,6 @@ export function compileIterateRoots(compiler: Compiler): void {
);
}
export function compileBuiltinArrayGet(
compiler: Compiler,
target: Class,
thisExpression: Expression,
elementExpression: Expression,
contextualType: Type
): ExpressionRef {
var type = assert(compiler.program.determineBuiltinArrayType(target));
var module = compiler.module;
var outType = (
type.is(TypeFlags.INTEGER) &&
contextualType.is(TypeFlags.INTEGER) &&
contextualType.size > type.size
) ? contextualType : type;
var dataStart = assert(target.lookupInSelf("dataStart"));
assert(dataStart.kind == ElementKind.FIELD);
var dataLength = assert(target.lookupInSelf("dataLength"));
assert(dataLength.kind == ElementKind.FIELD);
// compile the index expression and shift it to become the actual byteOffset
var dynamicOffset = compiler.compileExpression(
elementExpression,
Type.i32,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
var alignLog2 = type.alignLog2;
if (alignLog2) {
dynamicOffset = module.createBinary(BinaryOp.ShlI32,
dynamicOffset,
module.createI32(alignLog2)
);
}
var usizeType = compiler.options.usizeType;
var nativeSizeType = compiler.options.nativeSizeType;
var ptrExpr: ExpressionRef;
var constantOffset: i32 = 0;
// precompute byteOffset into a constant and a dynamic part
dynamicOffset = module.precomputeExpression(dynamicOffset);
if (getExpressionId(dynamicOffset) == ExpressionId.Const) {
constantOffset = getConstValueI32(dynamicOffset);
dynamicOffset = 0;
} else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) {
if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) {
let left = getBinaryLeft(dynamicOffset);
let right = getBinaryRight(dynamicOffset);
if (getExpressionId(left) == ExpressionId.Const) {
constantOffset = getConstValueI32(left);
dynamicOffset = right;
} else if (getExpressionId(right) == ExpressionId.Const) {
constantOffset = getConstValueI32(right);
dynamicOffset = left;
}
}
}
// ptr = this.dataStart
ptrExpr = module.createLoad(usizeType.byteSize, true,
compiler.compileExpression(
thisExpression,
target.type,
ConversionKind.IMPLICIT,
WrapMode.NONE
),
nativeSizeType, (<Field>dataStart).memoryOffset
);
// ptr = ptr + <usize>dynamicOffset
if (dynamicOffset) {
if (nativeSizeType == NativeType.I64) {
ptrExpr = module.createBinary(BinaryOp.AddI64,
ptrExpr,
module.createUnary(UnaryOp.ExtendU32, dynamicOffset)
);
} else {
ptrExpr = module.createBinary(BinaryOp.AddI32,
ptrExpr,
dynamicOffset
);
}
}
compiler.currentType = outType;
return module.createLoad(
type.byteSize,
type.is(TypeFlags.SIGNED),
ptrExpr,
outType.toNativeType(),
constantOffset
);
}
export function compileBuiltinArraySet(
compiler: Compiler,
target: Class,
thisExpression: Expression,
elementExpression: Expression,
valueExpression: Expression,
contextualType: Type
): ExpressionRef {
var type = assert(compiler.program.determineBuiltinArrayType(target));
return compileBuiltinArraySetWithValue(
compiler,
target,
thisExpression,
elementExpression,
compiler.compileExpression(
valueExpression,
type.is(TypeFlags.INTEGER | TypeFlags.VALUE)
? type.is(TypeFlags.LONG)
? type.is(TypeFlags.SIGNED)
? Type.i64
: Type.u64
: type.is(TypeFlags.SIGNED)
? Type.i32
: Type.u32
: type,
ConversionKind.IMPLICIT,
WrapMode.NONE
),
contextualType != Type.void
);
}
export function compileBuiltinArraySetWithValue(
compiler: Compiler,
target: Class,
thisExpression: Expression,
elementExpression: Expression,
valueExpr: ExpressionRef,
tee: bool
): ExpressionRef {
var type = assert(compiler.program.determineBuiltinArrayType(target));
var module = compiler.module;
var dataStart = assert(target.lookupInSelf("dataStart"));
assert(dataStart.kind == ElementKind.FIELD);
var dataLength = assert(target.lookupInSelf("dataLength"));
assert(dataLength.kind == ElementKind.FIELD);
var constantOffset: i32 = 0;
var dynamicOffset = module.precomputeExpression(
compiler.compileExpression(
elementExpression,
Type.i32,
ConversionKind.IMPLICIT,
WrapMode.NONE
)
);
if (getExpressionId(dynamicOffset) == ExpressionId.Const) {
constantOffset = getConstValueI32(dynamicOffset);
dynamicOffset = 0;
} else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) {
if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) {
let left = getBinaryLeft(dynamicOffset);
let right = getBinaryRight(dynamicOffset);
if (getExpressionId(left) == ExpressionId.Const) {
constantOffset = getConstValueI32(left);
dynamicOffset = right;
} else if (getExpressionId(right) == ExpressionId.Const) {
constantOffset = getConstValueI32(right);
dynamicOffset = left;
}
}
}
var program = compiler.program;
var isManaged = type.isManaged(program) && target.type.isManaged(program);
var usizeType = compiler.options.usizeType;
var nativeSizeType = compiler.options.nativeSizeType;
var thisExpr = compiler.compileExpression(
thisExpression,
target.type,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
var tempThis: Local | null = null;
if (isManaged) {
tempThis = compiler.currentFlow.getTempLocal(target.type, false);
thisExpr = module.createTeeLocal(tempThis.index, thisExpr);
}
var dataStartExpr = module.createLoad(usizeType.byteSize, true,
thisExpr, nativeSizeType, (<Field>dataStart).memoryOffset
);
var typeAlignLog2 = type.alignLog2;
constantOffset <<= typeAlignLog2;
if (dynamicOffset) {
if (typeAlignLog2) {
dynamicOffset = module.createBinary(BinaryOp.ShlI32,
dynamicOffset,
module.createI32(typeAlignLog2)
);
}
if (nativeSizeType == NativeType.I64) {
dataStartExpr = module.createBinary(BinaryOp.AddI64,
dataStartExpr,
module.createUnary(UnaryOp.ExtendU32, dynamicOffset)
);
} else {
dataStartExpr = module.createBinary(BinaryOp.AddI32,
dataStartExpr,
dynamicOffset
);
}
}
// handle Array<Ref>: value = RETAIN<T, TArray>(value, this)
if (isManaged) {
let program = compiler.program;
let retainInstance = compiler.resolver.resolveFunction(assert(program.retainPrototype), [ type, target.type ]);
if (!retainInstance) return module.createUnreachable();
valueExpr = compiler.makeCallInlinePrechecked(retainInstance, [
valueExpr,
module.createGetLocal(assert(tempThis).index, nativeSizeType)
], 0, true);
// handle Uint8ClampedArray: value = ~(value >> 31) & (((255 - value) >> 31) | value)
} else if (target.internalName == BuiltinSymbols.Uint8ClampedArray) {
let tempLocal = compiler.currentFlow.getAndFreeTempLocal(Type.i32, true);
valueExpr = module.createBinary(BinaryOp.AndI32,
module.createBinary(BinaryOp.XorI32,
module.createBinary(BinaryOp.ShrI32,
module.createTeeLocal(tempLocal.index, valueExpr),
module.createI32(31)
),
module.createI32(-1)
),
module.createBinary(BinaryOp.OrI32,
module.createBinary(BinaryOp.ShrI32,
module.createBinary(BinaryOp.SubI32,
module.createI32(255),
module.createGetLocal(tempLocal.index, NativeType.I32)
),
module.createI32(31)
),
module.createGetLocal(tempLocal.index, NativeType.I32)
)
);
}
assert(!tempThis);
var nativeType = type.toNativeType();
if (!tee) {
compiler.currentType = Type.void;
return module.createStore(
type.byteSize,
dataStartExpr,
valueExpr,
nativeType,
constantOffset
);
} else {
let flow = compiler.currentFlow;
let tempLocal = flow.getAndFreeTempLocal(type, false);
compiler.currentType = type;
return module.createBlock(null, [
module.createStore(
type.byteSize,
dataStartExpr,
module.createTeeLocal(tempLocal.index, valueExpr),
nativeType,
constantOffset
),
module.createGetLocal(tempLocal.index, nativeType)
], nativeType);
}
}
/** Ensures that the specified class's GC hook exists and returns its function table index. */
export function ensureGCHook(
compiler: Compiler,

281
src/codegen/array.ts Normal file
View File

@ -0,0 +1,281 @@
// TBD: managed reference handling makes this cumbersome, and there is a binaryen pass that can
// help propagating constant offsets. ideally, using operator overloads would be enough because
// it's the most flexible way to do this.
// import { Compiler, ConversionKind, WrapMode } from "../compiler";
// import { Class, ElementKind, Field, Local } from "../program";
// import { Expression } from "../ast";
// import { Type, TypeFlags } from "../types";
// import { ExpressionRef, getExpressionId, getBinaryOp, getBinaryLeft, getBinaryRight, getConstValueI32, ExpressionId, BinaryOp, NativeType, UnaryOp } from "../module";
// import { BuiltinSymbols } from "../builtins";
// export function makeArrayGet(
// compiler: Compiler,
// target: Class,
// thisExpression: Expression,
// elementExpression: Expression,
// contextualType: Type
// ): ExpressionRef {
// var type = assert(compiler.program.determineBuiltinArrayType(target));
// var module = compiler.module;
// var outType = (
// type.is(TypeFlags.INTEGER) &&
// contextualType.is(TypeFlags.INTEGER) &&
// contextualType.size > type.size
// ) ? contextualType : type;
// var dataStart = assert(target.lookupInSelf("dataStart"));
// assert(dataStart.kind == ElementKind.FIELD);
// var dataLength = assert(target.lookupInSelf("dataLength"));
// assert(dataLength.kind == ElementKind.FIELD);
// // compile the index expression and shift it to become the actual byteOffset
// var dynamicOffset = compiler.compileExpression(
// elementExpression,
// Type.i32,
// ConversionKind.IMPLICIT,
// WrapMode.NONE
// );
// var alignLog2 = type.alignLog2;
// if (alignLog2) {
// dynamicOffset = module.createBinary(BinaryOp.ShlI32,
// dynamicOffset,
// module.createI32(alignLog2)
// );
// }
// var usizeType = compiler.options.usizeType;
// var nativeSizeType = compiler.options.nativeSizeType;
// var ptrExpr: ExpressionRef;
// var constantOffset: i32 = 0;
// // precompute byteOffset into a constant and a dynamic part
// dynamicOffset = module.precomputeExpression(dynamicOffset);
// if (getExpressionId(dynamicOffset) == ExpressionId.Const) {
// constantOffset = getConstValueI32(dynamicOffset);
// dynamicOffset = 0;
// } else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) {
// if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) {
// let left = getBinaryLeft(dynamicOffset);
// let right = getBinaryRight(dynamicOffset);
// if (getExpressionId(left) == ExpressionId.Const) {
// constantOffset = getConstValueI32(left);
// dynamicOffset = right;
// } else if (getExpressionId(right) == ExpressionId.Const) {
// constantOffset = getConstValueI32(right);
// dynamicOffset = left;
// }
// }
// }
// // ptr = this.dataStart
// ptrExpr = module.createLoad(usizeType.byteSize, true,
// compiler.compileExpression(
// thisExpression,
// target.type,
// ConversionKind.IMPLICIT,
// WrapMode.NONE
// ),
// nativeSizeType, (<Field>dataStart).memoryOffset
// );
// // ptr = ptr + <usize>dynamicOffset
// if (dynamicOffset) {
// if (nativeSizeType == NativeType.I64) {
// ptrExpr = module.createBinary(BinaryOp.AddI64,
// ptrExpr,
// module.createUnary(UnaryOp.ExtendU32, dynamicOffset)
// );
// } else {
// ptrExpr = module.createBinary(BinaryOp.AddI32,
// ptrExpr,
// dynamicOffset
// );
// }
// }
// compiler.currentType = outType;
// return module.createLoad(
// type.byteSize,
// type.is(TypeFlags.SIGNED),
// ptrExpr,
// outType.toNativeType(),
// constantOffset
// );
// }
// export function makeArraySet(
// compiler: Compiler,
// target: Class,
// thisExpression: Expression,
// elementExpression: Expression,
// valueExpression: Expression,
// contextualType: Type
// ): ExpressionRef {
// var type = assert(compiler.program.determineBuiltinArrayType(target));
// return makeArraySetWithValue(
// compiler,
// target,
// thisExpression,
// elementExpression,
// compiler.compileExpression(
// valueExpression,
// type.is(TypeFlags.INTEGER | TypeFlags.VALUE)
// ? type.is(TypeFlags.LONG)
// ? type.is(TypeFlags.SIGNED)
// ? Type.i64
// : Type.u64
// : type.is(TypeFlags.SIGNED)
// ? Type.i32
// : Type.u32
// : type,
// ConversionKind.IMPLICIT,
// WrapMode.NONE
// ),
// contextualType != Type.void
// );
// }
// export function makeArraySetWithValue(
// compiler: Compiler,
// target: Class,
// thisExpression: Expression,
// elementExpression: Expression,
// valueExpr: ExpressionRef,
// tee: bool
// ): ExpressionRef {
// var type = assert(compiler.program.determineBuiltinArrayType(target));
// var module = compiler.module;
// var dataStart = assert(target.lookupInSelf("dataStart"));
// assert(dataStart.kind == ElementKind.FIELD);
// var dataLength = assert(target.lookupInSelf("dataLength"));
// assert(dataLength.kind == ElementKind.FIELD);
// var constantOffset: i32 = 0;
// var dynamicOffset = module.precomputeExpression(
// compiler.compileExpression(
// elementExpression,
// Type.i32,
// ConversionKind.IMPLICIT,
// WrapMode.NONE
// )
// );
// if (getExpressionId(dynamicOffset) == ExpressionId.Const) {
// constantOffset = getConstValueI32(dynamicOffset);
// dynamicOffset = 0;
// } else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) {
// if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) {
// let left = getBinaryLeft(dynamicOffset);
// let right = getBinaryRight(dynamicOffset);
// if (getExpressionId(left) == ExpressionId.Const) {
// constantOffset = getConstValueI32(left);
// dynamicOffset = right;
// } else if (getExpressionId(right) == ExpressionId.Const) {
// constantOffset = getConstValueI32(right);
// dynamicOffset = left;
// }
// }
// }
// var program = compiler.program;
// var isManaged = type.isManaged(program) && target.type.isManaged(program);
// var usizeType = compiler.options.usizeType;
// var nativeSizeType = compiler.options.nativeSizeType;
// var thisExpr = compiler.compileExpression(
// thisExpression,
// target.type,
// ConversionKind.IMPLICIT,
// WrapMode.NONE
// );
// var tempThis: Local | null = null;
// if (isManaged) {
// tempThis = compiler.currentFlow.getTempLocal(target.type, false);
// thisExpr = module.createTeeLocal(tempThis.index, thisExpr);
// }
// var dataStartExpr = module.createLoad(usizeType.byteSize, true,
// thisExpr, nativeSizeType, (<Field>dataStart).memoryOffset
// );
// var typeAlignLog2 = type.alignLog2;
// constantOffset <<= typeAlignLog2;
// if (dynamicOffset) {
// if (typeAlignLog2) {
// dynamicOffset = module.createBinary(BinaryOp.ShlI32,
// dynamicOffset,
// module.createI32(typeAlignLog2)
// );
// }
// if (nativeSizeType == NativeType.I64) {
// dataStartExpr = module.createBinary(BinaryOp.AddI64,
// dataStartExpr,
// module.createUnary(UnaryOp.ExtendU32, dynamicOffset)
// );
// } else {
// dataStartExpr = module.createBinary(BinaryOp.AddI32,
// dataStartExpr,
// dynamicOffset
// );
// }
// }
// // handle Array<Ref>: value = RETAIN<T, TArray>(value, this)
// if (isManaged) {
// let program = compiler.program;
// let retainInstance = compiler.resolver.resolveFunction(assert(program.retainPrototype), [ type, target.type ]);
// if (!retainInstance) return module.createUnreachable();
// valueExpr = compiler.makeCallInlinePrechecked(retainInstance, [
// valueExpr,
// module.createGetLocal(assert(tempThis).index, nativeSizeType)
// ], 0, true);
// // handle Uint8ClampedArray: value = ~(value >> 31) & (((255 - value) >> 31) | value)
// } else if (target.internalName == BuiltinSymbols.Uint8ClampedArray) {
// let tempLocal = compiler.currentFlow.getAndFreeTempLocal(Type.i32, true);
// valueExpr = module.createBinary(BinaryOp.AndI32,
// module.createBinary(BinaryOp.XorI32,
// module.createBinary(BinaryOp.ShrI32,
// module.createTeeLocal(tempLocal.index, valueExpr),
// module.createI32(31)
// ),
// module.createI32(-1)
// ),
// module.createBinary(BinaryOp.OrI32,
// module.createBinary(BinaryOp.ShrI32,
// module.createBinary(BinaryOp.SubI32,
// module.createI32(255),
// module.createGetLocal(tempLocal.index, NativeType.I32)
// ),
// module.createI32(31)
// ),
// module.createGetLocal(tempLocal.index, NativeType.I32)
// )
// );
// }
// assert(!tempThis);
// var nativeType = type.toNativeType();
// if (!tee) {
// compiler.currentType = Type.void;
// return module.createStore(
// type.byteSize,
// dataStartExpr,
// valueExpr,
// nativeType,
// constantOffset
// );
// } else {
// let flow = compiler.currentFlow;
// let tempLocal = flow.getAndFreeTempLocal(type, false);
// compiler.currentType = type;
// return module.createBlock(null, [
// module.createStore(
// type.byteSize,
// dataStartExpr,
// module.createTeeLocal(tempLocal.index, valueExpr),
// nativeType,
// constantOffset
// ),
// module.createGetLocal(tempLocal.index, nativeType)
// ], nativeType);
// }
// }

156
src/codegen/gc.ts Normal file
View File

@ -0,0 +1,156 @@
import { Compiler } from "../compiler";
import { ExpressionRef, NativeType, BinaryOp } from "../module";
import { Local, Function, Class } from "../program";
import { Type } from "../types";
/** Prepares the insertion of a reference into an _uninitialized_ parent using the GC interface. */
export function makeInsertRef(
compiler: Compiler,
valueExpr: ExpressionRef,
tempParent: Local,
nullable: bool
): ExpressionRef {
var module = compiler.module;
var program = compiler.program;
var usizeType = compiler.options.usizeType;
var nativeSizeType = compiler.options.nativeSizeType;
var flow = compiler.currentFlow;
var tempValue = flow.getTempLocal(usizeType, false);
var handle: ExpressionRef;
var fn: Function | null;
if (fn = program.linkRef) { // tracing
handle = module.createCall(fn.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType),
module.createGetLocal(tempParent.index, nativeSizeType)
], NativeType.None);
} else if (fn = program.retainRef) { // arc
handle = module.createCall(fn.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType)
], NativeType.None);
} else {
assert(false);
return module.createUnreachable();
}
flow.freeTempLocal(tempValue);
if (!compiler.compileFunction(fn)) return module.createUnreachable();
// {
// [if (value !== null)] link/retain(value[, parent])
// } -> value
return module.createBlock(null, [
module.createSetLocal(tempValue.index, valueExpr),
nullable
? module.createIf(
module.createGetLocal(tempValue.index, nativeSizeType),
handle
)
: handle,
module.createGetLocal(tempValue.index, nativeSizeType)
], nativeSizeType);
}
/** Prepares the replaces a reference hold by an _initialized_ parent using the GC interface. */
export function makeReplaceRef(
compiler: Compiler,
valueExpr: ExpressionRef,
oldValueExpr: ExpressionRef,
tempParent: Local,
nullable: bool
): ExpressionRef {
var module = compiler.module;
var program = compiler.program;
var usizeType = compiler.options.usizeType;
var nativeSizeType = compiler.options.nativeSizeType;
var flow = compiler.currentFlow;
var tempValue = flow.getTempLocal(usizeType, false);
var tempOldValue = flow.getTempLocal(usizeType, false);
var handleOld: ExpressionRef;
var handleNew: ExpressionRef;
var fn1: Function | null, fn2: Function | null;
if (fn1 = program.linkRef) { // tracing
fn2 = assert(program.unlinkRef);
handleOld = module.createCall(fn2.internalName, [
module.createGetLocal(tempOldValue.index, nativeSizeType),
module.createGetLocal(tempParent.index, nativeSizeType)
], NativeType.None);
handleNew = module.createCall(fn1.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType),
module.createGetLocal(tempParent.index, nativeSizeType)
], NativeType.None);
} else if (fn1 = program.retainRef) { // arc
fn2 = assert(program.releaseRef);
handleOld = module.createCall(fn2.internalName, [
module.createGetLocal(tempOldValue.index, nativeSizeType)
], NativeType.None);
handleNew = module.createCall(fn1.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType)
], NativeType.None);
} else {
assert(false);
return module.createUnreachable();
}
flow.freeTempLocal(tempValue);
flow.freeTempLocal(tempOldValue);
if (!compiler.compileFunction(fn1) || !compiler.compileFunction(fn2)) return module.createUnreachable();
// if (value != oldValue) {
// if (oldValue !== null) unlink/release(oldValue[, parent])
// [if (value !== null)] link/retain(value[, parent])
// } -> value
return module.createIf(
module.createBinary(nativeSizeType == NativeType.I32 ? BinaryOp.NeI32 : BinaryOp.NeI64,
module.createTeeLocal(tempValue.index, valueExpr),
module.createTeeLocal(tempOldValue.index, oldValueExpr)
),
module.createBlock(null, [
module.createIf(
module.createGetLocal(tempOldValue.index, nativeSizeType),
handleOld
),
nullable
? module.createIf(
module.createGetLocal(tempValue.index, nativeSizeType),
handleNew
)
: handleNew,
module.createGetLocal(tempValue.index, nativeSizeType)
], nativeSizeType),
module.createGetLocal(tempValue.index, nativeSizeType)
);
}
export function makeInstanceOfClass(
compiler: Compiler,
expr: ExpressionRef,
classInstance: Class
): ExpressionRef {
var module = compiler.module;
var flow = compiler.currentFlow;
var idTemp = flow.getTempLocal(Type.i32, false);
var idExpr = module.createLoad(4, false,
module.createBinary(BinaryOp.SubI32,
expr,
module.createI32(compiler.program.runtimeHeaderSize)
),
NativeType.I32
);
var label = "instanceof_" + classInstance.name + "|" + flow.pushBreakLabel();
var conditions: ExpressionRef[] = [];
conditions.push(
module.createDrop( // br_if returns the value too
module.createBreak(label,
module.createBinary(BinaryOp.EqI32, // classId == class.id
module.createTeeLocal(idTemp.index, idExpr),
module.createI32(classInstance.id)
),
module.createI32(1) // ? true
)
)
);
// TODO: insert conditions for all possible subclasses (i.e. cat is also animal)
// TODO: simplify if there are none
conditions.push(
module.createI32(0) // : false
);
flow.freeTempLocal(idTemp);
flow.popBreakLabel();
return module.createBlock(label, conditions, NativeType.I32);
}

View File

@ -189,6 +189,8 @@ export namespace LibrarySymbols {
export const REGISTER = "REGISTER";
export const RETAIN = "RETAIN";
export const RELEASE = "RELEASE";
export const MOVE = "MOVE";
export const REPLACE = "REPLACE";
export const MAKEARRAY = "MAKEARRAY";
// other
export const length = "length";

View File

@ -7,10 +7,7 @@ import {
compileCall as compileBuiltinCall,
compileAbort,
compileIterateRoots,
BuiltinSymbols,
compileBuiltinArrayGet,
compileBuiltinArraySet,
compileBuiltinArraySetWithValue
BuiltinSymbols
} from "./builtins";
import {
@ -174,6 +171,8 @@ import {
makeMap
} from "./util";
import { makeInsertRef, makeReplaceRef } from "./codegen/gc";
/** Compilation target. */
export enum Target {
/** WebAssembly with 32-bit pointers. */
@ -1010,7 +1009,7 @@ export class Compiler extends DiagnosticEmitter {
if (initInStart) {
module.addGlobal(val.internalName, NativeType.I32, true, module.createI32(0));
this.currentBody.push(
module.createSetGlobal(val.internalName, initExpr)
this.makeGlobalAssignment(val, initExpr, false)
);
previousValueIsMut = true;
} else {
@ -4745,19 +4744,19 @@ export class Compiler extends DiagnosticEmitter {
let elementExpression = resolver.currentElementExpression;
if (elementExpression) { // indexed access
let isUnchecked = flow.is(FlowFlags.UNCHECKED_CONTEXT);
if (isUnchecked) {
let arrayType = this.program.determineBuiltinArrayType(<Class>target);
if (arrayType) {
return compileBuiltinArraySet(
this,
<Class>target,
assert(this.resolver.currentThisExpression),
elementExpression,
valueExpression,
contextualType
);
}
}
// if (isUnchecked) {
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
// if (arrayType) {
// return compileBuiltinArraySet(
// this,
// <Class>target,
// assert(this.resolver.currentThisExpression),
// elementExpression,
// valueExpression,
// contextualType
// );
// }
// }
let indexedSet = (<Class>target).lookupOverload(OperatorKind.INDEXED_SET, isUnchecked);
if (!indexedSet) {
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET, isUnchecked);
@ -4801,7 +4800,7 @@ export class Compiler extends DiagnosticEmitter {
compileAssignmentWithValue(
expression: Expression,
valueWithCorrectType: ExpressionRef,
valueExpr: ExpressionRef,
tee: bool = false
): ExpressionRef {
var module = this.module;
@ -4811,49 +4810,28 @@ export class Compiler extends DiagnosticEmitter {
switch (target.kind) {
case ElementKind.LOCAL: {
let type = (<Local>target).type;
assert(type != Type.void);
this.currentType = tee ? type : Type.void;
if ((<Local>target).is(CommonFlags.CONST)) {
if (target.is(CommonFlags.CONST)) {
this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
expression.range, target.internalName
);
this.currentType = tee ? (<Local>target).type : Type.void;
return module.createUnreachable();
}
let localIndex = (<Local>target).index;
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
if (!flow.canOverflow(valueWithCorrectType, type)) flow.setLocalFlag(localIndex, LocalFlags.WRAPPED);
else flow.unsetLocalFlag(localIndex, LocalFlags.WRAPPED);
}
return tee
? module.createTeeLocal(localIndex, valueWithCorrectType)
: module.createSetLocal(localIndex, valueWithCorrectType);
return this.makeLocalAssignment(<Local>target, valueExpr, tee);
}
case ElementKind.GLOBAL: {
if (!this.compileGlobal(<Global>target)) return module.createUnreachable();
let type = (<Global>target).type;
assert(type != Type.void);
this.currentType = tee ? type : Type.void;
if ((<Local>target).is(CommonFlags.CONST)) {
if (target.is(CommonFlags.CONST)) {
this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
expression.range,
target.internalName
);
this.currentType = tee ? (<Global>target).type : Type.void;
return module.createUnreachable();
}
valueWithCorrectType = this.ensureSmallIntegerWrap(valueWithCorrectType, type); // guaranteed
if (tee) {
let nativeType = type.toNativeType();
let internalName = target.internalName;
return module.createBlock(null, [ // emulated teeGlobal
module.createSetGlobal(internalName, valueWithCorrectType),
module.createGetGlobal(internalName, nativeType)
], nativeType);
} else {
return module.createSetGlobal(target.internalName, valueWithCorrectType);
}
return this.makeGlobalAssignment(<Global>target, valueExpr, tee);
}
case ElementKind.FIELD: {
let initializerNode = (<Field>target).initializerNode;
@ -4870,66 +4848,15 @@ export class Compiler extends DiagnosticEmitter {
);
return module.createUnreachable();
}
let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,
WrapMode.NONE
return this.makeFieldAssignment(<Field>target,
valueExpr,
this.compileExpressionRetainType(
assert(this.resolver.currentThisExpression),
// FIXME: explicit type (currently fails due to missing null checking)
this.options.usizeType, WrapMode.NONE
),
tee
);
let thisType = this.currentType;
let type = (<Field>target).type;
let nativeType = type.toNativeType();
if (type.kind == TypeKind.BOOL) {
// make sure bools are wrapped (usually are) when storing as 8 bits
valueWithCorrectType = this.ensureSmallIntegerWrap(valueWithCorrectType, type);
}
let program = this.program;
let tempThis: Local | null = null;
if (type.isManaged(program) && thisType.isManaged(program)) {
let retainInstance = this.resolver.resolveFunction(assert(program.retainPrototype), [ type, thisType ]);
if (!retainInstance) {
this.currentType = tee ? type : Type.void;
return module.createUnreachable();
}
tempThis = this.currentFlow.getTempLocal(thisType, false);
// this = (tempThis = this)
thisExpr = module.createTeeLocal(tempThis.index, thisExpr);
// value = RETAIN(value, tempThis)
valueWithCorrectType = this.makeCallInlinePrechecked(retainInstance, [
valueWithCorrectType,
module.createGetLocal(tempThis.index, this.options.nativeSizeType)
], 0, true);
}
if (tee) {
let tempValue = this.currentFlow.getAndFreeTempLocal(
type,
!flow.canOverflow(valueWithCorrectType, type)
);
if (tempThis) this.currentFlow.freeTempLocal(tempThis);
this.currentType = type;
// (this.field = (tempValue = value)), tempValue
return module.createBlock(null, [
module.createStore(
type.byteSize,
thisExpr,
module.createTeeLocal(tempValue.index, valueWithCorrectType),
nativeType,
(<Field>target).memoryOffset
),
module.createGetLocal(tempValue.index, nativeType)
], nativeType);
} else {
if (tempThis) this.currentFlow.freeTempLocal(tempThis);
this.currentType = Type.void;
// this.field = value
return module.createStore(
type.byteSize,
thisExpr,
valueWithCorrectType,
nativeType,
(<Field>target).memoryOffset
);
}
}
case ElementKind.PROPERTY_PROTOTYPE: { // static property
let setterPrototype = (<PropertyPrototype>target).setterPrototype;
@ -4943,7 +4870,7 @@ export class Compiler extends DiagnosticEmitter {
let setterInstance = this.resolver.resolveFunction(setterPrototype, null, makeMap(), ReportMode.REPORT);
if (!setterInstance) return module.createUnreachable();
// call just the setter if the return value isn't of interest
if (!tee) return this.makeCallDirect(setterInstance, [ valueWithCorrectType ], expression);
if (!tee) return this.makeCallDirect(setterInstance, [ valueExpr ], expression);
// otherwise call the setter first, then the getter
let getterPrototype = assert((<PropertyPrototype>target).getterPrototype); // must be present
let getterInstance = this.resolver.resolveFunction(getterPrototype, null, makeMap(), ReportMode.REPORT);
@ -4951,7 +4878,7 @@ export class Compiler extends DiagnosticEmitter {
let returnType = getterInstance.signature.returnType;
let nativeReturnType = returnType.toNativeType();
return module.createBlock(null, [
this.makeCallDirect(setterInstance, [ valueWithCorrectType ], expression),
this.makeCallDirect(setterInstance, [ valueExpr ], expression),
this.makeCallDirect(getterInstance, null, expression) // sets currentType
], nativeReturnType);
}
@ -4968,10 +4895,9 @@ export class Compiler extends DiagnosticEmitter {
if (!tee) {
let thisExpr = this.compileExpressionRetainType(
assert(this.resolver.currentThisExpression),
this.options.usizeType,
WrapMode.NONE
this.options.usizeType, WrapMode.NONE
);
return this.makeCallDirect(setterInstance, [ thisExpr, valueWithCorrectType ], expression);
return this.makeCallDirect(setterInstance, [ thisExpr, valueExpr ], expression);
}
// otherwise call the setter first, then the getter
let getterInstance = assert((<Property>target).getterInstance); // must be present
@ -4987,7 +4913,7 @@ export class Compiler extends DiagnosticEmitter {
return module.createBlock(null, [
this.makeCallDirect(setterInstance, [ // set and remember the target
module.createTeeLocal(tempLocalIndex, thisExpr),
valueWithCorrectType
valueExpr
], expression),
this.makeCallDirect(getterInstance, [ // get from remembered target
module.createGetLocal(tempLocalIndex, nativeReturnType)
@ -4998,19 +4924,19 @@ export class Compiler extends DiagnosticEmitter {
let elementExpression = this.resolver.currentElementExpression;
if (elementExpression) {
let isUnchecked = flow.is(FlowFlags.UNCHECKED_CONTEXT);
if (isUnchecked) {
let arrayType = this.program.determineBuiltinArrayType(<Class>target);
if (arrayType) {
return compileBuiltinArraySetWithValue(
this,
<Class>target,
assert(this.resolver.currentThisExpression),
elementExpression,
valueWithCorrectType,
tee
);
}
}
// if (isUnchecked) {
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
// if (arrayType) {
// return compileBuiltinArraySetWithValue(
// this,
// <Class>target,
// assert(this.resolver.currentThisExpression),
// elementExpression,
// valueExpr,
// tee
// );
// }
// }
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET, isUnchecked);
if (!indexedGet) {
this.error(
@ -5050,7 +4976,7 @@ export class Compiler extends DiagnosticEmitter {
this.makeCallDirect(indexedSet, [
module.createTeeLocal(tempLocalTarget.index, thisExpr),
module.createTeeLocal(tempLocalElement.index, elementExpr),
valueWithCorrectType
valueExpr
], expression),
this.makeCallDirect(indexedGet, [
module.createGetLocal(tempLocalTarget.index, tempLocalTarget.type.toNativeType()),
@ -5061,7 +4987,7 @@ export class Compiler extends DiagnosticEmitter {
return this.makeCallDirect(indexedSet, [
thisExpr,
elementExpr,
valueWithCorrectType
valueExpr
], expression);
}
}
@ -5075,6 +5001,126 @@ export class Compiler extends DiagnosticEmitter {
return module.createUnreachable();
}
makeLocalAssignment(local: Local, valueExpr: ExpressionRef, tee: bool): ExpressionRef {
// TBD: use REPLACE macro to keep track of managed refcounts? or can the compiler evaluate
// this statically in closed contexts like functions in order to safe the extra work?
var type = local.type;
assert(type != Type.void);
var localIndex = local.index;
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
let flow = this.currentFlow;
if (!flow.canOverflow(valueExpr, type)) flow.setLocalFlag(localIndex, LocalFlags.WRAPPED);
else flow.unsetLocalFlag(localIndex, LocalFlags.WRAPPED);
}
if (tee) {
this.currentType = type;
return this.module.createTeeLocal(localIndex, valueExpr);
} else {
this.currentType = Type.void;
return this.module.createSetLocal(localIndex, valueExpr)
}
}
makeGlobalAssignment(global: Global, valueExpr: ExpressionRef, tee: bool): ExpressionRef {
// TBD: use REPLACE macro to keep track of managed refcounts? currently this doesn't work
// because there isn't a parent ref here. a tracing GC wouldn't need the hook at all while
// a reference counting gc doesn't need a parent.
var type = global.type;
assert(type != Type.void);
valueExpr = this.ensureSmallIntegerWrap(valueExpr, type); // global values must be wrapped
if (tee) {
let module = this.module;
let nativeType = type.toNativeType();
let tempValue = this.currentFlow.getAndFreeTempLocal(type, true);
this.currentType = type;
return module.createBlock(null, [
module.createSetGlobal(global.internalName,
module.createTeeLocal(tempValue.index, valueExpr)
),
module.createGetLocal(tempValue.index, nativeType)
], nativeType);
} else {
this.currentType = Type.void;
return this.module.createSetGlobal(global.internalName, valueExpr);
}
}
makeFieldAssignment(field: Field, valueExpr: ExpressionRef, thisExpr: ExpressionRef, tee: bool): ExpressionRef {
var program = this.program;
var module = this.module;
var flow = this.currentFlow;
var fieldType = field.type;
var nativeFieldType = fieldType.toNativeType();
assert(field.parent.kind == ElementKind.CLASS);
var thisType = (<Class>field.parent).type;
var nativeThisType = thisType.toNativeType();
// MANAGED: this.field = replace(value, this.field)
if (fieldType.isManaged(program)) {
let tempThis = flow.getTempLocal(thisType, false);
let expr: ExpressionRef;
if (tee) { // tee value to a temp local and make it the block's result
let tempValue = flow.getTempLocal(fieldType, !flow.canOverflow(valueExpr, fieldType));
expr = module.createBlock(null, [
module.createStore(fieldType.byteSize,
module.createTeeLocal(tempThis.index, thisExpr),
makeReplaceRef(this,
module.createTeeLocal(tempValue.index, valueExpr),
module.createLoad(fieldType.byteSize, fieldType.is(TypeFlags.SIGNED),
module.createGetLocal(tempThis.index, nativeThisType),
nativeFieldType, field.memoryOffset
),
tempThis,
fieldType.is(TypeFlags.NULLABLE)
),
nativeFieldType, field.memoryOffset
),
module.createGetLocal(tempValue.index, nativeFieldType)
], nativeFieldType);
flow.freeTempLocal(tempValue);
this.currentType = fieldType;
} else { // no need for a temp local
expr = module.createStore(fieldType.byteSize,
module.createTeeLocal(tempThis.index, thisExpr),
makeReplaceRef(this,
valueExpr,
module.createLoad(fieldType.byteSize, fieldType.is(TypeFlags.SIGNED),
module.createGetLocal(tempThis.index, nativeThisType),
nativeFieldType, field.memoryOffset
),
tempThis,
fieldType.is(TypeFlags.NULLABLE)
),
nativeFieldType, field.memoryOffset
);
this.currentType = Type.void;
}
flow.freeTempLocal(tempThis);
return expr;
}
// UNMANAGED: this.field = value
if (tee) {
this.currentType = fieldType;
let tempValue = flow.getAndFreeTempLocal(fieldType, !flow.canOverflow(valueExpr, fieldType));
return module.createBlock(null, [
module.createStore(fieldType.byteSize,
thisExpr,
module.createTeeLocal(tempValue.index, valueExpr),
nativeFieldType, field.memoryOffset
),
module.createGetLocal(tempValue.index, nativeFieldType)
], nativeFieldType);
} else {
this.currentType = Type.void;
return module.createStore(fieldType.byteSize,
thisExpr,
valueExpr,
nativeFieldType, field.memoryOffset
);
}
}
compileCallExpression(
expression: CallExpression,
contextualType: Type,
@ -6072,18 +6118,18 @@ export class Compiler extends DiagnosticEmitter {
switch (target.kind) {
case ElementKind.CLASS: {
let isUnchecked = this.currentFlow.is(FlowFlags.UNCHECKED_CONTEXT);
if (isUnchecked) {
let arrayType = this.program.determineBuiltinArrayType(<Class>target);
if (arrayType) {
return compileBuiltinArrayGet(
this,
<Class>target,
expression.expression,
expression.elementExpression,
contextualType
);
}
}
// if (isUnchecked) {
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
// if (arrayType) {
// return compileBuiltinArrayGet(
// this,
// <Class>target,
// expression.expression,
// expression.elementExpression,
// contextualType
// );
// }
// }
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET, isUnchecked);
if (!indexedGet) {
this.error(
@ -6814,26 +6860,19 @@ export class Compiler extends DiagnosticEmitter {
)
)
);
var isManaged = elementType.isManaged(program) && arrayType.isManaged(program);
var retainInstance = isManaged
? this.resolver.resolveFunction(assert(program.retainPrototype), [ elementType, arrayType ])
: null;
var isManaged = elementType.isManaged(program);
for (let i = 0, alignLog2 = elementType.alignLog2; i < length; ++i) {
let valueExpression = expressions[i];
let valueExpr = valueExpression
? this.compileExpression(valueExpression, elementType, ConversionKind.IMPLICIT, WrapMode.NONE)
: elementType.toNativeZero(module);
if (isManaged) {
if (!retainInstance) {
valueExpr = module.createUnreachable();
} else {
// value = RETAIN(value, tempThis)
valueExpr = this.makeCallInlinePrechecked(retainInstance, [
valueExpr,
module.createGetLocal(tempThis.index, nativeArrayType)
], 0, true);
this.currentFlow = flow;
}
// value = link/retain(value[, tempThis])
valueExpr = makeInsertRef(this,
valueExpr,
tempThis,
elementType.is(TypeFlags.NULLABLE)
);
}
// store<T>(tempData, value, immOffset)
stmts.push(
@ -6849,7 +6888,7 @@ export class Compiler extends DiagnosticEmitter {
stmts.push(
module.createGetLocal(tempThis.index, nativeArrayType)
);
flow.freeTempLocal(tempThis); // but can be reused now
flow.freeTempLocal(tempThis);
flow.freeTempLocal(tempDataStart);
this.currentType = arrayType;
return module.createBlock(null, stmts, nativeArrayType);
@ -7212,43 +7251,6 @@ export class Compiler extends DiagnosticEmitter {
return module.createUnreachable();
}
private compileGetter(target: PropertyPrototype, reportNode: Node): ExpressionRef {
var prototype = target.getterPrototype;
if (prototype) {
let instance = this.resolver.resolveFunction(prototype, null);
if (!instance) return this.module.createUnreachable();
let signature = instance.signature;
if (!this.checkCallSignature( // reports
signature,
0,
instance.is(CommonFlags.INSTANCE),
reportNode
)) {
return this.module.createUnreachable();
}
if (instance.is(CommonFlags.INSTANCE)) {
let classInstance = assert(instance.parent); assert(classInstance.kind == ElementKind.CLASS);
let thisExpression = assert(this.resolver.currentThisExpression); //!!!
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType,
WrapMode.NONE
);
this.currentType = signature.returnType;
return this.compileCallDirect(instance, [], reportNode, thisExpr);
} else {
this.currentType = signature.returnType;
return this.compileCallDirect(instance, [], reportNode, 0);
}
} else {
this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1,
reportNode.range, (<PropertyPrototype>target).name, (<PropertyPrototype>target).parent.toString()
);
return this.module.createUnreachable();
}
}
compileTernaryExpression(expression: TernaryExpression, contextualType: Type): ExpressionRef {
var ifThen = expression.ifThen;
var ifElse = expression.ifElse;

View File

@ -156,7 +156,30 @@ export enum LocalFlags {
export namespace LocalFlags {
export function join(left: LocalFlags, right: LocalFlags): LocalFlags {
return ((left & LocalFlags.ANY_CATEGORICAL) & (right & LocalFlags.ANY_CATEGORICAL))
| (left & LocalFlags.ANY_CONDITIONAL) | (right & LocalFlags.ANY_CONDITIONAL);
| (left & LocalFlags.ANY_CONDITIONAL) | (right & LocalFlags.ANY_CONDITIONAL);
}
}
/** Flags indicating the current state of a field. */
export enum FieldFlags {
/** No specific conditions. */
NONE = 0,
/** Field is initialized. Relevant in constructors. */
INITIALIZED = 1 << 0,
/** Field is conditionally initialized. Relevant in constructors. */
CONDITIONALLY_INITIALIZED = 1 << 1,
/** Any categorical flag. */
ANY_CATEGORICAL = INITIALIZED,
/** Any conditional flag. */
ANY_CONDITIONAL = CONDITIONALLY_INITIALIZED
}
export namespace FieldFlags {
export function join(left: FieldFlags, right: FieldFlags): FieldFlags {
return ((left & FieldFlags.ANY_CATEGORICAL) & (right & FieldFlags.ANY_CATEGORICAL))
| (left & FieldFlags.ANY_CONDITIONAL) | (right & FieldFlags.ANY_CONDITIONAL);
}
}
@ -181,6 +204,8 @@ export class Flow {
scopedLocals: Map<string,Local> | null = null;
/** Local flags. */
localFlags: LocalFlags[];
/** Field flags. Relevant in constructors. */
fieldFlags: Map<string,FieldFlags> | null = null;
/** Function being inlined, when inlining. */
inlineFunction: Function | null;
/** The label we break to when encountering a return statement, when inlining. */

View File

@ -354,23 +354,25 @@ export class Program extends DiagnosticEmitter {
stringInstance: Class | null = null;
/** Abort function reference, if present. */
abortInstance: Function | null = null;
/** Runtime allocation function. */
/** Runtime allocation macro. `ALLOCATE(payloadSize: usize): usize` */
allocateInstance: Function | null = null;
/** Unmanaged allocation function. */
/** Unmanaged allocation macro. `ALLOCATE_UNMANAGED(size: usize): usize` */
allocateUnmanagedInstance: Function | null = null;
/** Runtime reallocation function. */
/** Runtime reallocation macro. `REALLOCATE(ref: usize, newPayloadSize: usize): usize` */
reallocateInstance: Function | null = null;
/** Runtime discard function. */
/** Runtime discard macro. `DISCARD(ref: usize): void` */
discardInstance: Function | null = null;
/** Runtime register function. */
/** Runtime register macro. `REGISTER<T>(ref: usize): T` */
registerPrototype: FunctionPrototype | null = null;
/** Runtime retain function. */
retainPrototype: FunctionPrototype | null = null;
/** Runtime release function. */
releasePrototype: FunctionPrototype | null = null;
/** Runtime make array function. */
/** Runtime make array macro. `MAKEARRAY<V>(capacity: i32, source: usize = 0): Array<V>` */
makeArrayPrototype: FunctionPrototype | null = null;
linkRef: Function | null = null;
unlinkRef: Function | null = null;
retainRef: Function | null = null;
releaseRef: Function | null = null;
/** Next class id. */
nextClassId: u32 = 1;
@ -378,7 +380,7 @@ export class Program extends DiagnosticEmitter {
/** Whether a garbage collector is present or not. */
get gcImplemented(): bool {
return this.lookupGlobal("__gc_register") !== null;
return this.lookupGlobal("__ref_collect") !== null;
}
/** Garbage collector mark function called to on reachable managed objects. */
gcMarkInstance: Function | null = null; // FIXME
@ -830,18 +832,25 @@ export class Program extends DiagnosticEmitter {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.registerPrototype = <FunctionPrototype>element;
}
if (element = this.lookupGlobal(LibrarySymbols.RETAIN)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.retainPrototype = <FunctionPrototype>element;
}
if (element = this.lookupGlobal(LibrarySymbols.RELEASE)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.releasePrototype = <FunctionPrototype>element;
}
if (element = this.lookupGlobal(LibrarySymbols.MAKEARRAY)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.makeArrayPrototype = <FunctionPrototype>element;
}
if (this.lookupGlobal("__ref_collect")) {
if (element = this.lookupGlobal("__ref_link")) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.linkRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
element = assert(this.lookupGlobal("__ref_unlink"));
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.unlinkRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
} 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);
}
}
}
// mark module exports, i.e. to apply proper wrapping behavior on the boundaries
@ -1726,31 +1735,31 @@ export class Program extends DiagnosticEmitter {
}
/** Determines the element type of a built-in array. */
determineBuiltinArrayType(target: Class): Type | null {
switch (target.internalName) {
case BuiltinSymbols.Int8Array: return Type.i8;
case BuiltinSymbols.Uint8ClampedArray:
case BuiltinSymbols.Uint8Array: return Type.u8;
case BuiltinSymbols.Int16Array: return Type.i16;
case BuiltinSymbols.Uint16Array: return Type.u16;
case BuiltinSymbols.Int32Array: return Type.i32;
case BuiltinSymbols.Uint32Array: return Type.u32;
case BuiltinSymbols.Int64Array: return Type.i64;
case BuiltinSymbols.Uint64Array: return Type.u64;
case BuiltinSymbols.Float32Array: return Type.f32;
case BuiltinSymbols.Float64Array: return Type.f64;
}
var current: Class | null = target;
var arrayPrototype = this.arrayPrototype;
do {
if (current.prototype == arrayPrototype) { // Array<T>
let typeArguments = assert(current.typeArguments);
assert(typeArguments.length == 1);
return typeArguments[0];
}
} while (current = current.base);
return null;
}
// determineBuiltinArrayType(target: Class): Type | null {
// switch (target.internalName) {
// case BuiltinSymbols.Int8Array: return Type.i8;
// case BuiltinSymbols.Uint8ClampedArray:
// case BuiltinSymbols.Uint8Array: return Type.u8;
// case BuiltinSymbols.Int16Array: return Type.i16;
// case BuiltinSymbols.Uint16Array: return Type.u16;
// case BuiltinSymbols.Int32Array: return Type.i32;
// case BuiltinSymbols.Uint32Array: return Type.u32;
// case BuiltinSymbols.Int64Array: return Type.i64;
// case BuiltinSymbols.Uint64Array: return Type.u64;
// case BuiltinSymbols.Float32Array: return Type.f32;
// case BuiltinSymbols.Float64Array: return Type.f64;
// }
// var current: Class | null = target;
// var arrayPrototype = this.arrayPrototype;
// do {
// if (current.prototype == arrayPrototype) { // Array<T>
// let typeArguments = assert(current.typeArguments);
// assert(typeArguments.length == 1);
// return typeArguments[0];
// }
// } while (current = current.base);
// return null;
// }
}
/** Indicates the specific kind of an {@link Element}. */

View File

@ -372,12 +372,26 @@ export class Resolver extends DiagnosticEmitter {
);
// recoverable
}
return this.resolveType(
let type = this.resolveType(
(<TypeDefinition>element).typeNode,
element,
contextualTypeArguments,
reportMode
);
if (!type) return null;
if (node.isNullable) {
if (!type.is(TypeFlags.REFERENCE)) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Basic_type_0_cannot_be_nullable,
typeNode.name.range, typeName.identifier.text
);
}
} else {
return type.asNullable();
}
}
return type;
}
if (reportMode == ReportMode.REPORT) {
this.error(
@ -614,8 +628,8 @@ export class Resolver extends DiagnosticEmitter {
case ElementKind.CLASS: { // property access on element access?
let elementExpression = this.currentElementExpression;
if (elementExpression) {
let arrayType = this.program.determineBuiltinArrayType(<Class>target);
if (!arrayType) {
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
// if (!arrayType) {
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
if (!indexedGet) {
this.error(
@ -624,8 +638,8 @@ export class Resolver extends DiagnosticEmitter {
);
return null;
}
arrayType = indexedGet.signature.returnType;
}
let arrayType = indexedGet.signature.returnType;
// }
if (!(target = arrayType.classReference)) {
this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1,
@ -726,8 +740,8 @@ export class Resolver extends DiagnosticEmitter {
break;
}
case ElementKind.CLASS: {
let arrayType = this.program.determineBuiltinArrayType(<Class>target);
if (!arrayType) {
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
// if (!arrayType) {
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
if (!indexedGet) {
if (reportMode == ReportMode.REPORT) {
@ -738,8 +752,8 @@ export class Resolver extends DiagnosticEmitter {
}
return null;
}
arrayType = indexedGet.signature.returnType;
}
let arrayType = indexedGet.signature.returnType;
// }
if (targetExpression.kind == NodeKind.ELEMENTACCESS) { // nested element access
if (target = arrayType.classReference) {
this.currentThisExpression = targetExpression;

View File

@ -212,7 +212,9 @@ export class Type {
var targetFunction: Signature | null;
if (this.is(TypeFlags.REFERENCE)) {
if (target.is(TypeFlags.REFERENCE)) {
if (!this.is(TypeFlags.NULLABLE) || target.is(TypeFlags.NULLABLE)) {
// FIXME: turned out resolveType didn't handle nullability properly, and fixing it there
// leads to this check failing all over the place due to not yet implemented null states.
// if (!this.is(TypeFlags.NULLABLE) || target.is(TypeFlags.NULLABLE)) {
if (currentClass = this.classReference) {
if (targetClass = target.classReference) {
return currentClass.isAssignableTo(targetClass);
@ -222,7 +224,7 @@ export class Type {
return currentFunction.isAssignableTo(targetFunction);
}
}
}
// }
}
} else if (!target.is(TypeFlags.REFERENCE)) {
if (this.is(TypeFlags.INTEGER)) {