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

@ -1,16 +1,19 @@
AssemblyScript NEXT
===================
![](https://s.gravatar.com/avatar/f105de3decfafc734b8eabe9a960b25d?size=64) AssemblyScript NEXT
=================
[![Build Status](https://travis-ci.org/AssemblyScript/next.svg?branch=master)](https://travis-ci.org/AssemblyScript/next)
[![Build Status](https://travis-ci.org/AssemblyScript/assemblyscript.svg?branch=master)](https://travis-ci.org/AssemblyScript/assemblyscript)
**AssemblyScript** is a new compiler targeting [WebAssembly](http://webassembly.org) while utilizing [TypeScript](http://www.typescriptlang.org)'s syntax and [node](https://nodejs.org)'s vibrant ecosystem. Instead of requiring complex toolchains to set up, you can simply `npm install` it - or run it in a browser.
By compiling syntactially (not necessarily semantically) valid TypeScript to [Binaryen](https://github.com/WebAssembly/binaryen) IR, the resulting module can be validated, optimized, emitted in WebAssembly text or binary format and converted to [asm.js](http://asmjs.org) as a polyfill.
Note, though, that this version of the compiler (0.5.0, NEXT) is relatively new and does not yet support some features a TypeScript programmer might expect, e.g., strings, arrays and classes.
See [the AssemblyScript wiki](https://github.com/AssemblyScript/assemblyscript/wiki) for additional information and documentation.
How does it work?
-----------------
By compiling syntactially (not necessarily semantically) valid TypeScript to [Binaryen](https://github.com/WebAssembly/binaryen) IR, the resulting module can be validated, optimized, emitted in WebAssembly text or binary format and converted to [asm.js](http://asmjs.org) as a polyfill.
Examples
--------
@ -22,7 +25,7 @@ A few early examples to get an idea:
Getting started
---------------
If you'd like to try it today or even plan to contribute, this is how you do it:
This version of the compiler is not on [npm](https://www.npmjs.com/package/assemblyscript), yet, but if you'd like to try it today or even plan to contribute, this is how you do it:
```
$> git clone https://github.com/AssemblyScript/next.git

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);
}
}

79
std/assembly.d.ts vendored
View File

@ -29,6 +29,85 @@ declare type f32 = number;
/** A 64-bit float. */
declare type f64 = number;
/** Converts any other numeric value to an 8-bit signed integer. */
declare function i8(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i8;
declare namespace i8 {
export const MIN_VALUE: i8;
export const MAX_VALUE: i8;
}
/** Converts any other numeric value to a 16-bit signed integer. */
declare function i16(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i8;
declare namespace i16 {
export const MIN_VALUE: i16;
export const MAX_VALUE: i16;
}
/** Converts any other numeric value to a 32-bit signed integer. */
declare function i32(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i32;
declare namespace i32 {
export const MIN_VALUE: i32;
export const MAX_VALUE: i32;
}
/** Converts any other numeric value to a 64-bit signed integer. */
declare function i64(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i64;
declare namespace i64 {
export const MIN_VALUE: i64;
export const MAX_VALUE: i64;
}
/** Converts any other numeric value to a 32-bit (in WASM32) respectivel 64-bit (in WASM64) signed integer. */
declare function isize(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): isize;
declare namespace isize {
export const MIN_VALUE: isize;
export const MAX_VALUE: isize;
}
/** Converts any other numeric value to an 8-bit unsigned integer. */
declare function u8(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i8;
declare namespace u8 {
export const MIN_VALUE: u8;
export const MAX_VALUE: u8;
}
/** Converts any other numeric value to a 16-bit unsigned integer. */
declare function u16(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i8;
declare namespace u16 {
export const MIN_VALUE: u16;
export const MAX_VALUE: u16;
}
/** Converts any other numeric value to a 32-bit unsigned integer. */
declare function u32(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i32;
declare namespace u32 {
export const MIN_VALUE: u32;
export const MAX_VALUE: u32;
}
/** Converts any other numeric value to a 64-bit unsigned integer. */
declare function u64(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i64;
declare namespace u64 {
export const MIN_VALUE: u64;
export const MAX_VALUE: u64;
}
/** Converts any other numeric value to a 32-bit (in WASM32) respectivel 64-bit (in WASM64) unsigned integer. */
declare function usize(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): isize;
declare namespace usize {
export const MIN_VALUE: usize;
export const MAX_VALUE: usize;
}
/** Converts any other numeric value to a 1-bit unsigned integer. */
declare function bool(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): bool;
declare namespace bool {
export const MIN_VALUE: bool;
export const MAX_VALUE: bool;
}
/** Converts any other numeric value to a 32-bit float. */
declare function f32(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): f32;
declare namespace f32 {
export const MIN_SAFE_INTEGER: f32;
export const MAX_SAFE_INTEGER: f32;
}
/** Converts any other numeric value to a 64-bit float. */
declare function f64(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): f64;
declare namespace f64 {
export const MIN_SAFE_INTEGER: f64;
export const MAX_SAFE_INTEGER: f64;
}
// Built-ins
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */

75
std/portable.d.ts vendored
View File

@ -10,16 +10,83 @@
// of 2^53-1) and instead require a compatibility layer to work in JS as well. See: src/util/i64.ts
declare type i8 = number;
declare type u8 = number;
declare type i16 = number;
declare type u16 = number;
declare type i32 = number;
declare type u32 = number;
declare type isize = number;
declare type u8 = number;
declare type u16 = number;
declare type u32 = number;
declare type bool = boolean;
declare type usize = number;
declare type f32 = number;
declare type f64 = number;
declare type bool = boolean;
/** Converts any other numeric value to an 8-bit signed integer. */
declare function i8(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): i8;
declare namespace i8 {
export const MIN_VALUE: i8;
export const MAX_VALUE: i8;
}
/** Converts any other numeric value to a 16-bit signed integer. */
declare function i16(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): i8;
declare namespace i16 {
export const MIN_VALUE: i16;
export const MAX_VALUE: i16;
}
/** Converts any other numeric value to a 32-bit signed integer. */
declare function i32(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): i32;
declare namespace i32 {
export const MIN_VALUE: i32;
export const MAX_VALUE: i32;
}
/** Converts any other numeric value to a 32-bit (in WASM32) respectivel 64-bit (in WASM64) signed integer. */
declare function isize(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): isize;
declare namespace isize {
export const MIN_VALUE: isize;
export const MAX_VALUE: isize;
}
/** Converts any other numeric value to an 8-bit unsigned integer. */
declare function u8(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): i8;
declare namespace u8 {
export const MIN_VALUE: u8;
export const MAX_VALUE: u8;
}
/** Converts any other numeric value to a 16-bit unsigned integer. */
declare function u16(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): i8;
declare namespace u16 {
export const MIN_VALUE: u16;
export const MAX_VALUE: u16;
}
/** Converts any other numeric value to a 32-bit unsigned integer. */
declare function u32(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): i32;
declare namespace u32 {
export const MIN_VALUE: u32;
export const MAX_VALUE: u32;
}
/** Converts any other numeric value to a 32-bit (in WASM32) respectivel 64-bit (in WASM64) unsigned integer. */
declare function usize(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): isize;
declare namespace usize {
export const MIN_VALUE: usize;
export const MAX_VALUE: usize;
}
/** Converts any other numeric value to a 1-bit unsigned integer. */
declare function bool(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): bool;
declare namespace bool {
export const MIN_VALUE: bool;
export const MAX_VALUE: bool;
}
/** Converts any other numeric value to a 32-bit float. */
declare function f32(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): f32;
declare namespace f32 {
export const MIN_SAFE_INTEGER: f32;
export const MAX_SAFE_INTEGER: f32;
}
/** Converts any other numeric value to a 64-bit float. */
declare function f64(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): f64;
declare namespace f64 {
export const MIN_SAFE_INTEGER: f64;
export const MAX_SAFE_INTEGER: f64;
}
// Portable built-ins

View File

@ -1,5 +1,60 @@
var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self;
Object.defineProperties(
globalScope["i8"] = function i8(value) { return value << 24 >> 24; }
, {
"MIN_VALUE": { value: -128, writable: false },
"MAX_VALUE": { value: 127, writable: false }
});
Object.defineProperties(
globalScope["i16"] = function i16(value) { return value << 16 >> 16; }
, {
"MIN_VALUE": { value: -32768, writable: false },
"MAX_VALUE": { value: 32767, writable: false }
});
Object.defineProperties(
globalScope["i32"] = globalScope["isize"] = function i32(value) { return value | 0; }
, {
"MIN_VALUE": { value: -2147483648, writable: false },
"MAX_VALUE": { value: 2147483647, writable: false }
});
Object.defineProperties(
globalScope["u8"] = function u8(value) { return value & 0xff; }
, {
"MIN_VALUE": { value: 0, writable: false },
"MAX_VALUE": { value: 255, writable: false }
});
Object.defineProperties(
globalScope["u16"] = function u16(value) { return value & 0xffff; }
, {
"MIN_VALUE": { value: 0, writable: false },
"MAX_VALUE": { value: 65535, writable: false }
});
Object.defineProperties(
globalScope["u32"] = globalScope["usize"] = function u32(value) { return value >>> 0; }
, {
"MIN_VALUE": { value: 0, writable: false },
"MAX_VALUE": { value: 4294967295, writable: false }
});
Object.defineProperties(
globalScope["bool"] = function bool(value) { return Boolean(value); }
, {
"MIN_VALUE": { value: 0, writable: false },
"MAX_VALUE": { value: 1, writable: false }
});
Object.defineProperties(
globalScope["f32"] = function f32(value) { return Math.fround(value); }
, {
"MIN_SAFE_INTEGER": { value: -16777215, writable: false },
"MAX_SAFE_INTEGER": { value: 16777215, writable: false }
});
Object.defineProperties(
globalScope["f64"] = function f64(value) { return +value; }
, {
"MIN_SAFE_INTEGER": { value: -9007199254740991, writable: false },
"MAX_SAFE_INTEGER": { value: 9007199254740991, writable: false }
});
globalScope["clz"] = Math.clz32;
globalScope["abs"] = Math.abs;
globalScope["max"] = Math.max;

3
std/tsconfig.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "../tsconfig-base.json"
}

View File

@ -43,6 +43,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
[program.exports]
;)

View File

@ -846,6 +846,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
binary/b
binary/i
binary/I

View File

@ -1063,6 +1063,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
builtins/b
builtins/i
builtins/I

View File

@ -71,6 +71,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
class/Animal
class/Animal.MAX
class/Animal.add

View File

@ -36,6 +36,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
declare/external
[program.exports]
declare/external

View File

@ -81,6 +81,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
do/loopDo
do/loopDoInDo
[program.exports]

View File

@ -66,6 +66,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
enum/Implicit
enum/Explicit
enum/Mixed

View File

@ -60,6 +60,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
export/add
export/sub
export/a

View File

@ -177,6 +177,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
for/i
[program.exports]

View File

@ -338,6 +338,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
../../examples/game-of-life/assembly/game-of-life/w
../../examples/game-of-life/assembly/game-of-life/h
../../examples/game-of-life/assembly/game-of-life/s

View File

@ -1219,6 +1219,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
../../examples/i64-polyfill/assembly/i64/lo
../../examples/i64-polyfill/assembly/i64/hi
../../examples/i64-polyfill/assembly/i64/getLo

View File

@ -76,6 +76,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
if/ifThenElse
if/ifThen
if/ifThenElseBlock

View File

@ -71,6 +71,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
export/add
export/sub
export/a

View File

@ -0,0 +1,9 @@
(module
(type $v (func))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $start (; 0 ;) (type $v)
(nop)
)
)

26
tests/compiler/limits.ts Normal file
View File

@ -0,0 +1,26 @@
i8.MIN_VALUE;
i8.MAX_VALUE;
i16.MIN_VALUE;
i16.MAX_VALUE;
i32.MIN_VALUE;
i32.MAX_VALUE;
i64.MIN_VALUE;
i64.MAX_VALUE;
isize.MIN_VALUE;
isize.MAX_VALUE;
u8.MIN_VALUE;
u8.MAX_VALUE;
u16.MIN_VALUE;
u16.MAX_VALUE;
u32.MIN_VALUE;
u32.MAX_VALUE;
u64.MIN_VALUE;
u64.MAX_VALUE;
usize.MIN_VALUE;
usize.MAX_VALUE;
bool.MIN_VALUE;
bool.MAX_VALUE;
f32.MIN_SAFE_INTEGER;
f32.MAX_SAFE_INTEGER;
f64.MIN_SAFE_INTEGER;
f64.MAX_SAFE_INTEGER;

155
tests/compiler/limits.wast Normal file
View File

@ -0,0 +1,155 @@
(module
(type $v (func))
(global $i8.MIN_VALUE (mut i32) (i32.const -128))
(global $i8.MAX_VALUE (mut i32) (i32.const 127))
(global $i16.MIN_VALUE (mut i32) (i32.const -32768))
(global $i16.MAX_VALUE (mut i32) (i32.const 32767))
(global $i32.MIN_VALUE (mut i32) (i32.const -2147483648))
(global $i32.MAX_VALUE (mut i32) (i32.const 2147483647))
(global $i64.MIN_VALUE (mut i64) (i64.const -9223372036854775808))
(global $i64.MAX_VALUE (mut i64) (i64.const 9223372036854775807))
(global $u8.MIN_VALUE (mut i32) (i32.const 0))
(global $u8.MAX_VALUE (mut i32) (i32.const 255))
(global $u16.MIN_VALUE (mut i32) (i32.const 0))
(global $u16.MAX_VALUE (mut i32) (i32.const 65535))
(global $u32.MIN_VALUE (mut i32) (i32.const 0))
(global $u32.MAX_VALUE (mut i32) (i32.const -1))
(global $u64.MIN_VALUE (mut i64) (i64.const 0))
(global $u64.MAX_VALUE (mut i64) (i64.const -1))
(global $bool.MIN_VALUE (mut i32) (i32.const 0))
(global $bool.MAX_VALUE (mut i32) (i32.const 1))
(global $f32.MIN_SAFE_INTEGER (mut f32) (f32.const -16777215))
(global $f32.MAX_SAFE_INTEGER (mut f32) (f32.const 16777215))
(global $f64.MIN_SAFE_INTEGER (mut f64) (f64.const -9007199254740991))
(global $f64.MAX_SAFE_INTEGER (mut f64) (f64.const 9007199254740991))
(global $HEAP_START i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $start (; 0 ;) (type $v)
(drop
(i32.const -128)
)
(drop
(i32.const 127)
)
(drop
(i32.const -32768)
)
(drop
(i32.const 32767)
)
(drop
(i32.const -2147483648)
)
(drop
(i32.const 2147483647)
)
(drop
(i64.const -9223372036854775808)
)
(drop
(i64.const 9223372036854775807)
)
(drop
(i32.const -2147483648)
)
(drop
(i32.const 2147483647)
)
(drop
(i32.const 0)
)
(drop
(i32.const 255)
)
(drop
(i32.const 0)
)
(drop
(i32.const 65535)
)
(drop
(i32.const 0)
)
(drop
(i32.const -1)
)
(drop
(i64.const 0)
)
(drop
(i64.const -1)
)
(drop
(i32.const 0)
)
(drop
(i32.const -1)
)
(drop
(i32.const 0)
)
(drop
(i32.const 1)
)
(drop
(f32.const -16777215)
)
(drop
(f32.const 16777215)
)
(drop
(f64.const -9007199254740991)
)
(drop
(f64.const 9007199254740991)
)
)
)
(;
[program.elements]
clz
ctz
popcnt
rotl
rotr
abs
ceil
copysign
floor
max
min
nearest
sqrt
trunc
current_memory
grow_memory
unreachable
load
store
reinterpret
select
sizeof
changetype
isNaN
isFinite
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
[program.exports]
;)

View File

@ -169,6 +169,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
[program.exports]
;)

View File

@ -285,6 +285,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
logical/i
logical/I
logical/f

View File

@ -2083,6 +2083,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
memcpy/memcpy
memcpy/base
memcpy/dest

View File

@ -47,6 +47,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
namespace/Outer
namespace/Outer.Inner
namespace/Outer.Inner.aVar

View File

@ -76,6 +76,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
export/add
export/sub
export/a

View File

@ -175,6 +175,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
switch/doSwitch
switch/doSwitchDefaultFirst
switch/doSwitchDefaultOmitted

View File

@ -63,6 +63,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
ternary/a
[program.exports]

View File

@ -358,6 +358,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
tlsf/fls
tlsf/ffs
tlsf/ALIGN_SIZE_LOG2

View File

@ -651,6 +651,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
unary/i
unary/I
unary/f

View File

@ -90,6 +90,19 @@
assert
parseInt
parseFloat
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
while/loopWhile
while/loopWhileInWhile
[program.exports]