Type limits

This commit is contained in:
dcodeIO
2017-12-15 15:00:19 +01:00
parent 8085a02df3
commit 7cf879fb4b
35 changed files with 911 additions and 86 deletions

View File

@ -3,7 +3,7 @@ import { DiagnosticCode } from "./diagnostics";
import { Node, Expression } from "./ast";
import { Type } from "./types";
import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef } from "./module";
import { Program, ElementFlags, FunctionPrototype, Local } from "./program";
import { Program, ElementFlags, Element, Global, FunctionPrototype, Local } from "./program";
/** Initializes the specified program with built-in functions. */
export function initialize(program: Program): void {
@ -35,14 +35,67 @@ export function initialize(program: Program): void {
addFunction(program, "assert");
addFunction(program, "parseInt");
addFunction(program, "parseFloat");
addFunction(program, "i8").members = new Map([
[ "MIN_VALUE", new Global(program, "i8.MIN_VALUE", null, Type.i8).withConstantIntegerValue(-128, -1) ],
[ "MAX_VALUE", new Global(program, "i8.MAX_VALUE", null, Type.i8).withConstantIntegerValue(127, 0) ]
]);
addFunction(program, "i16").members = new Map([
[ "MIN_VALUE", new Global(program, "i16.MIN_VALUE", null, Type.i16).withConstantIntegerValue(-32768, -1) ],
[ "MAX_VALUE", new Global(program, "i16.MAX_VALUE", null, Type.i16).withConstantIntegerValue(32767, 0) ]
]);
addFunction(program, "i32").members = new Map([
[ "MIN_VALUE", new Global(program, "i32.MIN_VALUE", null, Type.i32).withConstantIntegerValue(-2147483648, -1) ],
[ "MAX_VALUE", new Global(program, "i32.MAX_VALUE", null, Type.i32).withConstantIntegerValue(2147483647, 0) ]
]);
addFunction(program, "i64").members = new Map([
[ "MIN_VALUE", new Global(program, "i64.MIN_VALUE", null, Type.i64).withConstantIntegerValue(0, -2147483648) ],
[ "MAX_VALUE", new Global(program, "i64.MAX_VALUE", null, Type.i64).withConstantIntegerValue(-1, 2147483647) ]
]);
addFunction(program, "u8").members = new Map([
[ "MIN_VALUE", new Global(program, "u8.MIN_VALUE", null, Type.u8).withConstantIntegerValue(0, 0) ],
[ "MAX_VALUE", new Global(program, "u8.MAX_VALUE", null, Type.u8).withConstantIntegerValue(255, 0) ]
]);
addFunction(program, "u16").members = new Map([
[ "MIN_VALUE", new Global(program, "u16.MIN_VALUE", null, Type.u16).withConstantIntegerValue(0, 0) ],
[ "MAX_VALUE", new Global(program, "u16.MAX_VALUE", null, Type.u16).withConstantIntegerValue(65535, 0) ]
]);
addFunction(program, "u32").members = new Map([
[ "MIN_VALUE", new Global(program, "u32.MIN_VALUE", null, Type.u32).withConstantIntegerValue(0, 0) ],
[ "MAX_VALUE", new Global(program, "u32.MAX_VALUE", null, Type.u32).withConstantIntegerValue(-1, 0) ]
]);
addFunction(program, "u64").members = new Map([
[ "MIN_VALUE", new Global(program, "u64.MIN_VALUE", null, Type.u64).withConstantIntegerValue(0, 0) ],
[ "MAX_VALUE", new Global(program, "u64.MAX_VALUE", null, Type.i64).withConstantIntegerValue(-1, -1) ]
]);
addFunction(program, "bool").members = new Map([
[ "MIN_VALUE", new Global(program, "bool.MIN_VALUE", null, Type.bool).withConstantIntegerValue(0, 0) ],
[ "MAX_VALUE", new Global(program, "bool.MAX_VALUE", null, Type.bool).withConstantIntegerValue(1, 0) ]
]);
addFunction(program, "f32").members = new Map([
[ "MIN_SAFE_INTEGER", new Global(program, "f32.MIN_SAFE_INTEGER", null, Type.f32).withConstantFloatValue(-16777215) ],
[ "MAX_SAFE_INTEGER", new Global(program, "f32.MAX_SAFE_INTEGER", null, Type.f32).withConstantFloatValue(16777215) ]
]);
addFunction(program, "f64").members = new Map([
[ "MIN_SAFE_INTEGER", new Global(program, "f64.MIN_SAFE_INTEGER", null, Type.f64).withConstantFloatValue(-9007199254740991) ],
[ "MAX_SAFE_INTEGER", new Global(program, "f64.MAX_SAFE_INTEGER", null, Type.f64).withConstantFloatValue(9007199254740991) ]
]);
if (program.target == Target.WASM64) {
program.elements.set("isize", <Element>program.elements.get("i64"));
program.elements.set("usize", <Element>program.elements.get("u64"));
} else {
program.elements.set("isize", <Element>program.elements.get("i32"));
program.elements.set("usize", <Element>program.elements.get("u32"));
}
}
/** Adds a built-in function to the specified program. */
function addFunction(program: Program, name: string, isGeneric: bool = false): void {
let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null);
function addFunction(program: Program, name: string, isGeneric: bool = false): FunctionPrototype {
let prototype: FunctionPrototype = new FunctionPrototype(program, name, name, null, null);
prototype.isBuiltIn = true;
if (isGeneric) prototype.isGeneric = true;
program.elements.set(name, prototype);
return prototype;
}
/** Compiles a call to a built-in function. */

View File

@ -93,7 +93,8 @@ import {
UnaryPrefixExpression,
// utility
hasModifier
hasModifier,
InterfaceDeclaration
} from "./ast";
import {
@ -179,7 +180,7 @@ export class Compiler extends DiagnosticEmitter {
this.program = program;
this.options = options ? options : new Options();
this.module = this.options.noEmit ? Module.createStub() : Module.create();
const startFunctionTemplate: FunctionPrototype = new FunctionPrototype(program, "start", null);
const startFunctionTemplate: FunctionPrototype = new FunctionPrototype(program, "start", "start", null);
const startFunctionInstance: Function = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null);
this.currentFunction = this.startFunction = startFunctionInstance;
this.memoryOffset = new U64(this.options.target == Target.WASM64 ? 8 : 4, 0); // leave space for `null`
@ -544,6 +545,7 @@ export class Compiler extends DiagnosticEmitter {
} else {
this.module.addFunction(internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, <ExpressionRef[]>stmts, NativeType.None));
}
instance.finalize();
return true;
}
@ -561,6 +563,11 @@ export class Compiler extends DiagnosticEmitter {
this.compileClassDeclaration(<ClassDeclaration>member, []);
break;
case NodeKind.INTERFACE:
if ((noTreeShaking || hasModifier(ModifierKind.EXPORT, (<InterfaceDeclaration>member).modifiers)) && !(<InterfaceDeclaration>member).typeParameters.length)
this.compileInterfaceDeclaration(<InterfaceDeclaration>member, []);
break;
case NodeKind.ENUM:
if (noTreeShaking || hasModifier(ModifierKind.EXPORT, (<EnumDeclaration>member).modifiers))
this.compileEnumDeclaration(<EnumDeclaration>member);
@ -684,6 +691,10 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("not implemented");
}
compileInterfaceDeclaration(declaration: InterfaceDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): void {
throw new Error("not implemented");
}
// memory
/** Adds a static memory segment with the specified data. */
@ -1896,30 +1907,6 @@ export class Compiler extends DiagnosticEmitter {
let expr: ExpressionRef;
switch (target.kind) {
case ElementKind.ENUM:
case ElementKind.CLASS_PROTOTYPE:
case ElementKind.CLASS:
case ElementKind.NAMESPACE:
if (target.members) {
element = target.members.get(propertyName);
if (!element) {
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName);
return this.module.createUnreachable();
}
// handle enum values right away
if (element.kind == ElementKind.ENUMVALUE) {
this.currentType = Type.i32;
return (<EnumValue>element).hasConstantValue
? this.module.createI32((<EnumValue>element).constantValue)
: this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32);
}
} else {
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName);
return this.module.createUnreachable();
}
break;
case ElementKind.LOCAL:
element = (<Local>target).type.classType;
if (!element) {
@ -1941,7 +1928,25 @@ export class Compiler extends DiagnosticEmitter {
break;
default:
throw new Error("unexpected target kind");
if (target.members) {
element = target.members.get(propertyName);
if (!element) {
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName);
return this.module.createUnreachable();
}
// handle enum values right away
if (element.kind == ElementKind.ENUMVALUE) {
this.currentType = Type.i32;
return (<EnumValue>element).hasConstantValue
? this.module.createI32((<EnumValue>element).constantValue)
: this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32);
}
} else {
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName);
return this.module.createUnreachable();
}
break;
}
// handle the element
@ -1951,8 +1956,18 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createGetLocal((<Local>element).index, typeToNativeType(this.currentType = (<Local>element).type));
case ElementKind.GLOBAL:
this.compileGlobal(<Global>element);
return this.module.createGetGlobal((<Global>element).internalName, typeToNativeType(this.currentType = <Type>(<Global>element).type));
if (!this.compileGlobal(<Global>element))
return this.module.createUnreachable();
this.currentType = <Type>(<Global>element).type;
if ((<Global>element).hasConstantValue)
return this.currentType== Type.f32
? this.module.createF32((<Global>element).constantFloatValue)
: this.currentType == Type.f64
? this.module.createF64((<Global>element).constantFloatValue)
: this.currentType.isLongInteger
? this.module.createI64((<I64>(<Global>element).constantIntegerValue).lo, (<I64>(<Global>element).constantIntegerValue).hi)
: this.module.createI32((<I64>(<Global>element).constantIntegerValue).lo);
return this.module.createGetGlobal((<Global>element).internalName, typeToNativeType(this.currentType));
case ElementKind.FUNCTION: // getter
if (!(<Function>element).prototype.isGetter) {

View File

@ -47,7 +47,6 @@ export class Decompiler {
this.push("(");
let k: Index = _BinaryenFunctionGetNumParams(func);
for (let i: Index = 0; i < k; ++i) {
console.log("rat");
if (i > 0)
this.push(", ");
this.push("$");

View File

@ -216,7 +216,7 @@ export class Program extends DiagnosticEmitter {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
return;
}
const prototype: ClassPrototype = new ClassPrototype(this, internalName, declaration);
const prototype: ClassPrototype = new ClassPrototype(this, declaration.identifier.name, internalName, declaration);
this.elements.set(internalName, prototype);
if (namespace) {
@ -306,7 +306,7 @@ export class Program extends DiagnosticEmitter {
}
} else
classPrototype.members = new Map();
const staticPrototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null);
const staticPrototype: FunctionPrototype = new FunctionPrototype(this, name, internalName, declaration, null);
classPrototype.members.set(name, staticPrototype);
this.elements.set(internalName, staticPrototype);
@ -319,7 +319,7 @@ export class Program extends DiagnosticEmitter {
}
} else
classPrototype.instanceMembers = new Map();
const instancePrototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, classPrototype);
const instancePrototype: FunctionPrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype);
classPrototype.instanceMembers.set(name, instancePrototype);
}
}
@ -455,7 +455,7 @@ export class Program extends DiagnosticEmitter {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
return;
}
const prototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null);
const prototype: FunctionPrototype = new FunctionPrototype(this, declaration.identifier.name, internalName, declaration, null);
this.elements.set(internalName, prototype);
if (namespace) {
@ -531,7 +531,7 @@ export class Program extends DiagnosticEmitter {
private initializeInterface(declaration: InterfaceDeclaration, namespace: Element | null = null): void {
const internalName: string = declaration.internalName;
const prototype: InterfacePrototype = new InterfacePrototype(this, internalName, declaration);
const prototype: InterfacePrototype = new InterfacePrototype(this, declaration.identifier.name, internalName, declaration);
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
@ -1024,6 +1024,18 @@ export class Global extends Element {
}
this.type = type; // resolved later if `null`
}
withConstantIntegerValue(lo: i32, hi: i32): this {
this.constantIntegerValue = new I64(lo, hi);
this.hasConstantValue = true;
return this;
}
withConstantFloatValue(value: f64): this {
this.constantFloatValue = value;
this.hasConstantValue = true;
return this;
}
}
/** A function parameter. */
@ -1070,14 +1082,17 @@ export class FunctionPrototype extends Element {
/** Declaration reference. */
declaration: FunctionDeclaration | null;
/** Class prototype reference. */
/** If an instance method, the class prototype reference. */
classPrototype: ClassPrototype | null;
/** Resolved instances. */
instances: Map<string,Function> = new Map();
/** Simple name. */
simpleName: string;
/** Constructs a new function prototype. */
constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) {
constructor(program: Program, simpleName: string, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) {
super(program, internalName);
this.simpleName = simpleName;
if (this.declaration = declaration) {
if (this.declaration.modifiers)
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
@ -1142,7 +1157,7 @@ export class FunctionPrototype extends Element {
} else
return null;
} else
return null; // TODO: infer type? (currently reported by parser)
return null;
}
// resolve return type
@ -1155,7 +1170,7 @@ export class FunctionPrototype extends Element {
else
return null;
} else
return null; // TODO: infer type? (currently reported by parser)
return null;
let internalName: string = this.internalName;
if (instanceKey.length)
@ -1168,17 +1183,20 @@ export class FunctionPrototype extends Element {
resolveInclTypeArguments(typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null, alternativeReportNode: Node | null): Function | null {
let resolvedTypeArguments: Type[] | null;
if (this.isGeneric) {
assert(typeArgumentNodes != null && typeArgumentNodes.length != 0);
if (!this.declaration)
throw new Error("missing declaration");
resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode);
if (!resolvedTypeArguments)
return null;
} else {
// TODO: check typeArgumentNodes being empty
assert(typeArgumentNodes == null || typeArgumentNodes.length == 0);
resolvedTypeArguments = [];
}
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
}
toString(): string { return this.simpleName; }
}
/** A resolved function. */
@ -1194,7 +1212,7 @@ export class Function extends Element {
parameters: Parameter[];
/** Concrete return type. */
returnType: Type;
/** If a method, the concrete class it is a member of. */
/** If an instance method, the concrete class it is a member of. */
instanceMethodOf: Class | null;
/** Map of locals by name. */
locals: Map<string,Local> = new Map();
@ -1203,7 +1221,7 @@ export class Function extends Element {
/** Current break context label. */
breakContext: string | null = null;
/** Contextual type arguments. */
contextualTypeArguments: Map<string,Type> = new Map();
contextualTypeArguments: Map<string,Type> | null;
private nextBreakId: i32 = 0;
private breakStack: i32[] | null = null;
@ -1219,19 +1237,21 @@ export class Function extends Element {
this.flags = prototype.flags;
let localIndex: i32 = 0;
if (instanceMethodOf) {
assert(this.isInstance);
this.locals.set("this", new Local(prototype.program, "this", localIndex++, instanceMethodOf.type));
for (let [name, type] of instanceMethodOf.contextualTypeArguments)
this.contextualTypeArguments.set(name, type);
}
if (instanceMethodOf.contextualTypeArguments) {
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
for (let [name, type] of instanceMethodOf.contextualTypeArguments)
this.contextualTypeArguments.set(name, type);
}
} else
assert(!this.isInstance);
for (let i: i32 = 0, k: i32 = parameters.length; i < k; ++i) {
const parameter: Parameter = parameters[i];
this.locals.set(parameter.name, new Local(prototype.program, parameter.name, localIndex++, parameter.type));
}
}
/** Tests if this function is an instance method. */
get isInstance(): bool { return this.instanceMethodOf != null; }
/** Adds a local of the specified type, with an optional name. */
addLocal(type: Type, name: string | null = null): Local {
// if it has a name, check previously as this method will throw otherwise
@ -1320,6 +1340,16 @@ export class Function extends Element {
this.breakStack = null;
}
}
/** Finalizes the function once compiled, releasing no longer needed resources. */
finalize(): void {
assert(!this.breakStack || !this.breakStack.length, "break stack is not empty");
this.breakStack = null;
this.breakContext = null;
this.tempI32s = this.tempI64s = this.tempF32s = this.tempF64s = null;
}
toString(): string { return this.prototype.simpleName; }
}
/** A yet unresolved instance field prototype. */
@ -1380,9 +1410,12 @@ export class ClassPrototype extends Element {
instances: Map<string,Class> = new Map();
/** Instance member prototypes. */
instanceMembers: Map<string,Element> | null = null;
/** Simple name. */
simpleName: string;
constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) {
constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration | null = null) {
super(program, internalName);
this.simpleName = simpleName;
if (this.declaration = declaration) {
if (this.declaration.modifiers) {
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
@ -1400,29 +1433,62 @@ export class ClassPrototype extends Element {
}
resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Class {
const key: string = typesToString(typeArguments, "", "");
let instance: Class | null = <Class | null>this.instances.get(key);
const instanceKey: string = typesToString(typeArguments, "", "");
let instance: Class | null = <Class | null>this.instances.get(instanceKey);
if (instance)
return instance;
if (!this.declaration)
const declaration: ClassDeclaration | null = this.declaration;
if (!declaration)
throw new Error("unexpected instantiation of internal class");
throw new Error("not implemented");
// override call specific contextual type arguments
let i: i32, k: i32 = typeArguments.length;
if (k) {
const inheritedTypeArguments: Map<string,Type> | null = contextualTypeArguments;
contextualTypeArguments = new Map();
if (inheritedTypeArguments)
for (let [name, type] of inheritedTypeArguments)
contextualTypeArguments.set(name, type);
for (i = 0; i < k; ++i)
contextualTypeArguments.set(declaration.typeParameters[i].identifier.name, typeArguments[i]);
}
// TODO: set up instance fields and methods
if (this.instanceMembers)
for (let [key, member] of this.instanceMembers) {
switch (member.kind) {
case ElementKind.FIELD_PROTOTYPE: break;
case ElementKind.FUNCTION_PROTOTYPE: break;
default: throw new Error("unexpected instance member");
}
}
let internalName: string = this.internalName;
if (instanceKey.length)
internalName += "<" + instanceKey + ">";
instance = new Class(this, internalName, typeArguments, null); // TODO: base class
instance.contextualTypeArguments = contextualTypeArguments;
this.instances.set(instanceKey, instance);
return instance;
}
resolveInclTypeArguments(typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null, alternativeReportNode: Node | null): Class | null {
let resolvedTypeArguments: Type[] | null;
if (this.isGeneric) {
assert(typeArgumentNodes != null && typeArgumentNodes.length != 0);
if (!this.declaration)
throw new Error("not implemented"); // generic built-in
throw new Error("missing declaration"); // generic built-in
resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode);
if (!resolvedTypeArguments)
return null;
} else {
// TODO: check typeArgumentNodes being empty
assert(typeArgumentNodes == null || !typeArgumentNodes.length);
resolvedTypeArguments = [];
}
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
}
toString(): string { return this.simpleName; }
}
/** A resolved class. */
@ -1438,16 +1504,8 @@ export class Class extends Element {
type: Type;
/** Base class, if applicable. */
base: Class | null;
/** Contextual type argumentsfor fields and methods. */
contextualTypeArguments: Map<string,Type> = new Map();
/** Instance fields. */
instanceFields: Map<string,Field> | null = null;
/** Instance methods. */
instanceMethods: Map<string,Function> | null = null;
/** Instance getters. */
instanceGetters: Map<string,Function> | null = null;
/** Instance setters. */
instanceSetters: Map<string,Function> | null = null;
/** Contextual type arguments for fields and methods. */
contextualTypeArguments: Map<string,Type> | null = null;
/** Constructs a new class. */
constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] = [], base: Class | null = null) {
@ -1459,9 +1517,11 @@ export class Class extends Element {
this.base = base;
// inherit contextual type arguments from base class
if (base)
if (base && base.contextualTypeArguments) {
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
for (let [name, type] of base.contextualTypeArguments)
this.contextualTypeArguments.set(name, type);
}
// apply instance-specific contextual type arguments
const declaration: ClassDeclaration | null = this.prototype.declaration;
@ -1469,14 +1529,16 @@ export class Class extends Element {
const typeParameters: TypeParameter[] = declaration.typeParameters;
if (typeParameters.length != typeArguments.length)
throw new Error("unexpected type argument count mismatch");
for (let i: i32 = 0, k: i32 = typeArguments.length; i < k; ++i)
this.contextualTypeArguments.set(typeParameters[i].identifier.name, typeArguments[i]);
const k: i32 = typeArguments.length;
if (k) {
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
for (let i: i32 = 0; i < k; ++i)
this.contextualTypeArguments.set(typeParameters[i].identifier.name, typeArguments[i]);
}
}
}
toString(): string {
throw new Error("not implemented");
}
toString(): string { return this.prototype.simpleName; }
}
/** A yet unresolved interface. */
@ -1488,8 +1550,8 @@ export class InterfacePrototype extends ClassPrototype {
declaration: InterfaceDeclaration | null; // more specific
/** Constructs a new interface prototype. */
constructor(program: Program, internalName: string, declaration: InterfaceDeclaration | null = null) {
super(program, internalName, declaration);
constructor(program: Program, simpleName: string, internalName: string, declaration: InterfaceDeclaration | null = null) {
super(program, simpleName, internalName, declaration);
}
}