This commit is contained in:
dcodeIO 2017-12-13 04:46:05 +01:00
parent ce57820f59
commit 7d85b0cc7f
4 changed files with 318 additions and 120 deletions

View File

@ -7,7 +7,7 @@ AssemblyScript NEXT
By compiling syntactially but 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. By compiling syntactially but 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.
The compiler itself utilizies "portable definitions" so it can be compiled to both JavaScript using `tsc` and, eventually, to WebAssembly using `asc`. The compiler itself utilizes "portable definitions" so it can be compiled to both JavaScript using `tsc` and, eventually, to WebAssembly using `asc`.
Development status Development status
------------------ ------------------
@ -32,7 +32,7 @@ $> cd next
$> npm install $> npm install
``` ```
Author your module either using Author your module using either
* the [assembly definitions](./std/assembly.d.ts) ([base config](./std/assembly.json)) if all you care about is targeting WebAssembly/asm.js or * the [assembly definitions](./std/assembly.d.ts) ([base config](./std/assembly.json)) if all you care about is targeting WebAssembly/asm.js or
* the [portable definitions](./std/portable.d.ts) ([base config](./std/portable.json)) if you also want to compile to JavaScript using `tsc` * the [portable definitions](./std/portable.d.ts) ([base config](./std/portable.json)) if you also want to compile to JavaScript using `tsc`

View File

@ -3,7 +3,7 @@ import { DiagnosticCode } from "./diagnostics";
import { Node, Expression } from "./ast"; import { Node, Expression } from "./ast";
import { Type } from "./types"; import { Type } from "./types";
import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef } from "./module"; import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef } from "./module";
import { Program, FunctionPrototype, Local } from "./program"; import { Program, ElementFlags, FunctionPrototype, Local } from "./program";
/** Initializes the specified program with built-in functions. */ /** Initializes the specified program with built-in functions. */
export function initialize(program: Program): void { export function initialize(program: Program): void {
@ -41,7 +41,7 @@ export function initialize(program: Program): void {
function addFunction(program: Program, name: string, isGeneric: bool = false): void { function addFunction(program: Program, name: string, isGeneric: bool = false): void {
let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null); let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null);
prototype.isBuiltIn = true; prototype.isBuiltIn = true;
prototype.isGeneric = isGeneric; if (isGeneric) prototype.isGeneric = true;
program.elements.set(name, prototype); program.elements.set(name, prototype);
} }

View File

@ -20,6 +20,7 @@ import {
ClassPrototype, ClassPrototype,
Class, Element, Class, Element,
ElementKind, ElementKind,
ElementFlags,
Enum, Enum,
FunctionPrototype, FunctionPrototype,
Function, Function,
@ -201,7 +202,7 @@ export class Compiler extends DiagnosticEmitter {
if (!typeRef) if (!typeRef)
typeRef = this.module.addFunctionType("v", NativeType.None, []); typeRef = this.module.addFunctionType("v", NativeType.None, []);
this.module.setStart( this.module.setStart(
this.module.addFunction(this.startFunction.template.internalName, typeRef, typesToNativeTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody)) this.module.addFunction(this.startFunction.prototype.internalName, typeRef, typesToNativeTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody))
); );
} }
@ -314,11 +315,11 @@ export class Compiler extends DiagnosticEmitter {
return <Global>element; return <Global>element;
} }
compileGlobal(element: Global): bool { compileGlobal(global: Global): bool {
if (element.isCompiled) if (global.isCompiled)
return true; return true;
const declaration: VariableLikeDeclarationStatement | null = element.declaration; const declaration: VariableLikeDeclarationStatement | null = global.declaration;
let type: Type | null = element.type; let type: Type | null = global.type;
if (!type) { if (!type) {
if (!declaration) if (!declaration)
throw new Error("unexpected missing declaration"); throw new Error("unexpected missing declaration");
@ -327,33 +328,33 @@ export class Compiler extends DiagnosticEmitter {
type = this.program.resolveType(declaration.type); // reports type = this.program.resolveType(declaration.type); // reports
if (!type) if (!type)
return false; return false;
element.type = type; global.type = type;
} }
if (this.module.noEmit) if (this.module.noEmit)
return true; return true;
const nativeType: NativeType = typeToNativeType(<Type>type); const nativeType: NativeType = typeToNativeType(<Type>type);
let initializer: ExpressionRef; let initializer: ExpressionRef;
let initializeInStart: bool = false; let initializeInStart: bool = false;
if (element.hasConstantValue) { if (global.hasConstantValue) {
if (type.isLongInteger) if (type.isLongInteger)
initializer = element.constantIntegerValue ? this.module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi) : this.module.createI64(0, 0); initializer = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0);
else if (type.kind == TypeKind.F32) else if (type.kind == TypeKind.F32)
initializer = this.module.createF32(element.constantFloatValue); initializer = this.module.createF32(global.constantFloatValue);
else if (type.kind == TypeKind.F64) else if (type.kind == TypeKind.F64)
initializer = this.module.createF64(element.constantFloatValue); initializer = this.module.createF64(global.constantFloatValue);
else if (type.isSmallInteger) { else if (type.isSmallInteger) {
if (type.isSignedInteger) { if (type.isSignedInteger) {
const shift: i32 = type.smallIntegerShift; const shift: i32 = type.smallIntegerShift;
initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0); initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() << shift >> shift : 0);
} else } else
initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & type.smallIntegerMask: 0); initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() & type.smallIntegerMask: 0);
} else } else
initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() : 0); initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() : 0);
} else if (declaration) { } else if (declaration) {
if (declaration.initializer) { if (declaration.initializer) {
initializer = this.compileExpression(declaration.initializer, type); initializer = this.compileExpression(declaration.initializer, type);
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
if (!element.isMutable) { if (!global.isMutable) {
initializer = this.precomputeExpressionRef(initializer); initializer = this.precomputeExpressionRef(initializer);
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range); this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range);
@ -366,34 +367,35 @@ export class Compiler extends DiagnosticEmitter {
initializer = typeToNativeZero(this.module, type); initializer = typeToNativeZero(this.module, type);
} else } else
throw new Error("unexpected missing declaration or constant value"); throw new Error("unexpected missing declaration or constant value");
const internalName: string = element.internalName; const internalName: string = global.internalName;
if (initializeInStart) { if (initializeInStart) {
this.module.addGlobal(internalName, nativeType, true, typeToNativeZero(this.module, type)); this.module.addGlobal(internalName, nativeType, true, typeToNativeZero(this.module, type));
this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer)); this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer));
} else { } else {
this.module.addGlobal(internalName, nativeType, element.isMutable, initializer); this.module.addGlobal(internalName, nativeType, global.isMutable, initializer);
if (!element.isMutable) { if (!global.isMutable) {
element.hasConstantValue = true;
const exprType: NativeType = _BinaryenExpressionGetType(initializer); const exprType: NativeType = _BinaryenExpressionGetType(initializer);
switch (exprType) { switch (exprType) {
case NativeType.I32: case NativeType.I32:
element.constantIntegerValue = new I64(_BinaryenConstGetValueI32(initializer), 0); global.constantIntegerValue = new I64(_BinaryenConstGetValueI32(initializer), 0);
break; break;
case NativeType.I64: case NativeType.I64:
element.constantIntegerValue = new I64(_BinaryenConstGetValueI64Low(initializer), _BinaryenConstGetValueI64High(initializer)); global.constantIntegerValue = new I64(_BinaryenConstGetValueI64Low(initializer), _BinaryenConstGetValueI64High(initializer));
break; break;
case NativeType.F32: case NativeType.F32:
element.constantFloatValue = _BinaryenConstGetValueF32(initializer); global.constantFloatValue = _BinaryenConstGetValueF32(initializer);
break; break;
case NativeType.F64: case NativeType.F64:
element.constantFloatValue = _BinaryenConstGetValueF64(initializer); global.constantFloatValue = _BinaryenConstGetValueF64(initializer);
break; break;
default: default:
throw new Error("unexpected initializer type"); throw new Error("unexpected initializer type");
} }
global.hasConstantValue = true;
} }
} }
return element.isCompiled = true; global.isCompiled = true;
return true;
} }
// enums // enums
@ -446,8 +448,8 @@ export class Compiler extends DiagnosticEmitter {
} else { } else {
this.module.addGlobal(val.internalName, NativeType.I32, false, initializer); this.module.addGlobal(val.internalName, NativeType.I32, false, initializer);
if (_BinaryenExpressionGetType(initializer) == NativeType.I32) { if (_BinaryenExpressionGetType(initializer) == NativeType.I32) {
val.hasConstantValue = true;
val.constantValue = _BinaryenConstGetValueI32(initializer); val.constantValue = _BinaryenConstGetValueI32(initializer);
val.hasConstantValue = true;
} else } else
throw new Error("unexpected initializer type"); throw new Error("unexpected initializer type");
} }
@ -483,11 +485,11 @@ export class Compiler extends DiagnosticEmitter {
if (instance.isCompiled) if (instance.isCompiled)
return true; return true;
const declaration: FunctionDeclaration | null = instance.template.declaration; const declaration: FunctionDeclaration | null = instance.prototype.declaration;
if (!declaration) if (!declaration)
throw new Error("unexpected missing declaration"); throw new Error("unexpected missing declaration");
if (instance.isDeclare) { if (instance.isDeclared) {
if (declaration.statements) { if (declaration.statements) {
this.error(DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts, declaration.identifier.range); this.error(DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts, declaration.identifier.range);
return false; return false;
@ -502,7 +504,7 @@ export class Compiler extends DiagnosticEmitter {
// compile statements // compile statements
let stmts: ExpressionRef[] | null = null; let stmts: ExpressionRef[] | null = null;
if (!instance.isDeclare) { if (!instance.isDeclared) {
const previousFunction: Function = this.currentFunction; const previousFunction: Function = this.currentFunction;
this.currentFunction = instance; this.currentFunction = instance;
stmts = this.compileStatements(<Statement[]>declaration.statements); stmts = this.compileStatements(<Statement[]>declaration.statements);
@ -525,7 +527,7 @@ export class Compiler extends DiagnosticEmitter {
// create the function // create the function
const internalName: string = instance.internalName; const internalName: string = instance.internalName;
if (instance.isDeclare) { if (instance.isDeclared) {
this.module.addFunctionImport(internalName, "env", declaration.identifier.name, typeRef); this.module.addFunctionImport(internalName, "env", declaration.identifier.name, typeRef);
} else { } else {
this.module.addFunction(internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, <ExpressionRef[]>stmts, NativeType.None)); this.module.addFunction(internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, <ExpressionRef[]>stmts, NativeType.None));
@ -580,7 +582,7 @@ export class Compiler extends DiagnosticEmitter {
switch (element.kind) { switch (element.kind) {
case ElementKind.CLASS_PROTOTYPE: case ElementKind.CLASS_PROTOTYPE:
if ((noTreeShaking || (<ClassPrototype>element).isExport) && !(<ClassPrototype>element).isGeneric) if ((noTreeShaking || (<ClassPrototype>element).isExported) && !(<ClassPrototype>element).isGeneric)
this.compileClassUsingTypeArguments(<ClassPrototype>element, []); this.compileClassUsingTypeArguments(<ClassPrototype>element, []);
break; break;
@ -589,7 +591,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
case ElementKind.FUNCTION_PROTOTYPE: case ElementKind.FUNCTION_PROTOTYPE:
if ((noTreeShaking || (<FunctionPrototype>element).isExport) && !(<FunctionPrototype>element).isGeneric) if ((noTreeShaking || (<FunctionPrototype>element).isExported) && !(<FunctionPrototype>element).isGeneric)
this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, []); this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, []);
break; break;
@ -1680,7 +1682,7 @@ export class Compiler extends DiagnosticEmitter {
this.compileFunction(functionInstance); this.compileFunction(functionInstance);
// imported function // imported function
if (functionInstance.isDeclare) if (functionInstance.isDeclared)
return this.module.createCallImport(functionInstance.internalName, operands, typeToNativeType(functionInstance.returnType)); return this.module.createCallImport(functionInstance.internalName, operands, typeToNativeType(functionInstance.returnType));
// internal function // internal function

View File

@ -61,6 +61,7 @@ class QueuedImport {
const noTypesYet: Map<string,Type> = new Map(); const noTypesYet: Map<string,Type> = new Map();
/** Represents an AssemblyScript program. */
export class Program extends DiagnosticEmitter { export class Program extends DiagnosticEmitter {
/** Array of source files. */ /** Array of source files. */
@ -76,6 +77,7 @@ export class Program extends DiagnosticEmitter {
/** Exports of individual files by internal name. Not global exports. */ /** Exports of individual files by internal name. Not global exports. */
exports: Map<string,Element> = new Map(); exports: Map<string,Element> = new Map();
/** Constructs a new program, optionally inheriting parser diagnostics. */
constructor(diagnostics: DiagnosticMessage[] | null = null) { constructor(diagnostics: DiagnosticMessage[] | null = null) {
super(diagnostics); super(diagnostics);
this.sources = new Array(); this.sources = new Array();
@ -152,7 +154,7 @@ export class Program extends DiagnosticEmitter {
let element: Element | null; let element: Element | null;
// queued imports should be resolvable now // queued imports should be resolvable now through traversing exports and queued exports
for (let i: i32 = 0; i < queuedImports.length;) { for (let i: i32 = 0; i < queuedImports.length;) {
const queuedImport: QueuedImport = queuedImports[i]; const queuedImport: QueuedImport = queuedImports[i];
element = this.tryResolveImport(queuedImport.referencedName, queuedExports); element = this.tryResolveImport(queuedImport.referencedName, queuedExports);
@ -165,7 +167,7 @@ export class Program extends DiagnosticEmitter {
} }
} }
// queued exports should be resolvable noww // queued exports should be resolvable now that imports are finalized
for (let [exportName, queuedExport] of queuedExports) { for (let [exportName, queuedExport] of queuedExports) {
let currentExport: QueuedExport | null = queuedExport; let currentExport: QueuedExport | null = queuedExport;
do { do {
@ -190,6 +192,7 @@ export class Program extends DiagnosticEmitter {
} }
} }
/** Tries to resolve an import by traversing exports and queued exports. */
private tryResolveImport(referencedName: string, queuedExports: Map<string,QueuedExport>): Element | null { private tryResolveImport(referencedName: string, queuedExports: Map<string,QueuedExport>): Element | null {
let element: Element | null; let element: Element | null;
do { do {
@ -215,7 +218,7 @@ export class Program extends DiagnosticEmitter {
} }
const prototype: ClassPrototype = new ClassPrototype(this, internalName, declaration); const prototype: ClassPrototype = new ClassPrototype(this, internalName, declaration);
this.elements.set(internalName, prototype); this.elements.set(internalName, prototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (prototype.isExported) {
if (this.exports.has(internalName)) if (this.exports.has(internalName))
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else else
@ -281,7 +284,7 @@ export class Program extends DiagnosticEmitter {
} }
const enm: Enum = new Enum(this, internalName, declaration); const enm: Enum = new Enum(this, internalName, declaration);
this.elements.set(internalName, enm); this.elements.set(internalName, enm);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (enm.isExported) {
if (this.exports.has(internalName)) if (this.exports.has(internalName))
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else else
@ -311,12 +314,10 @@ export class Program extends DiagnosticEmitter {
private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map<string,QueuedExport>): void { private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map<string,QueuedExport>): void {
const externalName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name; const externalName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name;
if (this.exports.has(externalName)) { if (this.exports.has(externalName)) {
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, externalName); this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, externalName);
return; return;
} }
let referencedName: string; let referencedName: string;
// export local element // export local element
@ -393,15 +394,12 @@ export class Program extends DiagnosticEmitter {
return; return;
} }
this.elements.set(internalName, prototype); this.elements.set(internalName, prototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (prototype.isExported) {
if (this.exports.has(internalName)) if (this.exports.has(internalName))
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else else
this.exports.set(internalName, prototype); this.exports.set(internalName, prototype);
} }
if (hasModifier(ModifierKind.DECLARE, declaration.modifiers)) {
prototype.isDeclare = true;
}
} }
private initializeImports(statement: ImportStatement, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void { private initializeImports(statement: ImportStatement, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
@ -459,16 +457,17 @@ export class Program extends DiagnosticEmitter {
private initializeInterface(declaration: InterfaceDeclaration): void { private initializeInterface(declaration: InterfaceDeclaration): void {
const internalName: string = declaration.internalName; const internalName: string = declaration.internalName;
const interfacePrototype: InterfacePrototype = new InterfacePrototype(this, internalName, declaration); const prototype: InterfacePrototype = new InterfacePrototype(this, internalName, declaration);
if (this.elements.has(internalName)) if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else else
this.elements.set(internalName, interfacePrototype); this.elements.set(internalName, prototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (prototype.isExported) {
if (this.exports.has(internalName)) if (this.exports.has(internalName))
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else else
this.exports.set(internalName, interfacePrototype); this.exports.set(internalName, prototype);
} }
const memberDeclarations: DeclarationStatement[] = declaration.members; const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) { for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
@ -476,11 +475,11 @@ export class Program extends DiagnosticEmitter {
switch (memberDeclaration.kind) { switch (memberDeclaration.kind) {
case NodeKind.FIELD: case NodeKind.FIELD:
this.initializeField(<FieldDeclaration>memberDeclaration, interfacePrototype); this.initializeField(<FieldDeclaration>memberDeclaration, prototype);
break; break;
case NodeKind.METHOD: case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>memberDeclaration, interfacePrototype); this.initializeMethod(<MethodDeclaration>memberDeclaration, prototype);
break; break;
default: default:
@ -492,11 +491,12 @@ export class Program extends DiagnosticEmitter {
private initializeNamespace(declaration: NamespaceDeclaration): void { private initializeNamespace(declaration: NamespaceDeclaration): void {
const internalName: string = declaration.internalName; const internalName: string = declaration.internalName;
const namespace: Namespace = new Namespace(this, internalName, declaration); const namespace: Namespace = new Namespace(this, internalName, declaration);
if (this.elements.has(internalName)) if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else { else {
this.elements.set(internalName, namespace); this.elements.set(internalName, namespace);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (namespace.isExported) {
if (this.exports.has(internalName)) if (this.exports.has(internalName))
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else else
@ -504,8 +504,8 @@ export class Program extends DiagnosticEmitter {
} }
} }
const members: Statement[] = declaration.members; const members: Statement[] = declaration.members;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) { for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
const statement: Statement = members[j]; const statement: Statement = members[i];
switch (statement.kind) { switch (statement.kind) {
case NodeKind.CLASS: case NodeKind.CLASS:
@ -540,7 +540,6 @@ export class Program extends DiagnosticEmitter {
private initializeVariables(statement: VariableStatement, isNamespaceMember: bool = false): void { private initializeVariables(statement: VariableStatement, isNamespaceMember: bool = false): void {
const declarations: VariableDeclaration[] = statement.declarations; const declarations: VariableDeclaration[] = statement.declarations;
const isExport: bool = !isNamespaceMember && hasModifier(ModifierKind.EXPORT, statement.modifiers);
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) { for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) {
const declaration: VariableDeclaration = declarations[i]; const declaration: VariableDeclaration = declarations[i];
const internalName: string = declaration.internalName; const internalName: string = declaration.internalName;
@ -549,7 +548,7 @@ export class Program extends DiagnosticEmitter {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else { else {
this.elements.set(internalName, global); this.elements.set(internalName, global);
if (isExport) { if (global.isExported) {
if (this.exports.has(internalName)) if (this.exports.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else else
@ -559,6 +558,7 @@ export class Program extends DiagnosticEmitter {
} }
} }
/** Resolves a {@link TypeNode} to a concrete {@link Type}. */
resolveType(node: TypeNode, contextualTypeArguments: Map<string,Type> | null = null, reportNotFound: bool = true): Type | null { resolveType(node: TypeNode, contextualTypeArguments: Map<string,Type> | null = null, reportNotFound: bool = true): Type | null {
// resolve parameters // resolve parameters
@ -596,6 +596,7 @@ export class Program extends DiagnosticEmitter {
return null; return null;
} }
/** Resolves {@link TypeParameter}s to concrete {@link Type}s. */
resolveTypeArguments(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Type[] | null { resolveTypeArguments(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Type[] | null {
const parameterCount: i32 = typeParameters.length; const parameterCount: i32 = typeParameters.length;
const argumentCount: i32 = typeArgumentNodes ? typeArgumentNodes.length : 0; const argumentCount: i32 = typeArgumentNodes ? typeArgumentNodes.length : 0;
@ -684,83 +685,193 @@ function checkGlobalDecorator(decorators: Decorator[]): string | null {
return null; return null;
} }
/** Indicates the specific kind of an {@link Element}. */
export enum ElementKind { export enum ElementKind {
/** A {@link ClassPrototype}. */
CLASS_PROTOTYPE, CLASS_PROTOTYPE,
/** A {@link Class}. */
CLASS, CLASS,
/** An {@link Enum}. */
ENUM, ENUM,
/** An {@link EnumValue}. */
ENUMVALUE, ENUMVALUE,
/** A {@link FieldPrototype}. */
FIELD_PROTOTYPE, FIELD_PROTOTYPE,
/** A {@link Field}. */
FIELD, FIELD,
/** A {@link FunctionPrototype}. */
FUNCTION_PROTOTYPE, FUNCTION_PROTOTYPE,
/** A {@link Function}. */
FUNCTION, FUNCTION,
/** A {@link Global}. */
GLOBAL, GLOBAL,
/** An {@link InterfacePrototype}. */
INTERFACE_PROTOTYPE, INTERFACE_PROTOTYPE,
/** An {@link Interface}. */
INTERFACE, INTERFACE,
/** A {@link Local}. */
LOCAL, LOCAL,
/** A {@link Namespace}. */
NAMESPACE NAMESPACE
} }
/** Indicates traits of an {@link Element}. */
export enum ElementFlags {
/** No flags set. */
NONE = 0,
/** Is compiled. */
COMPILED = 1 << 0,
/** Is an import. */
IMPORTED = 1 << 1,
/** Is an export. */
EXPORTED = 1 << 2,
/** Is built-in. */
BUILTIN = 1 << 3,
/** Is declared. */
DECLARED = 1 << 4,
/** Is generic. */
GENERIC = 1 << 5,
/** Is constant. */
CONSTANT = 1 << 6,
/** Has constant value. */
CONSTANT_VALUE = 1 << 7,
/** Is instance member. */
INSTANCE = 1 << 8,
/** Is getter. */
GETTER = 1 << 9,
/** Is setter. */
SETTER = 1 << 10
}
/** Base class of all program elements. */ /** Base class of all program elements. */
export abstract class Element { export abstract class Element {
/** Specific element kind. */
kind: ElementKind; kind: ElementKind;
/** Containing {@link Program}. */
program: Program; program: Program;
/** Internal name referring to this element. */
internalName: string; internalName: string;
isCompiled: bool = false; /** Element flags. */
isImport: bool = false; flags: ElementFlags = ElementFlags.NONE;
isBuiltIn: bool = false;
isDeclare: bool = false;
constructor(program: Program, internalName: string) { /** Constructs a new element, linking it to its containing {@link Program}. */
protected constructor(program: Program, internalName: string) {
this.program = program; this.program = program;
this.internalName = internalName; this.internalName = internalName;
} }
/** Whether compiled or not. */
get isCompiled(): bool { return (this.flags & ElementFlags.COMPILED) != 0; }
set isCompiled(is: bool) { if (is) this.flags |= ElementFlags.COMPILED; else this.flags &= ~ElementFlags.COMPILED; }
/** Whether imported or not. */
get isImported(): bool { return (this.flags & ElementFlags.IMPORTED) != 0; }
set isImported(is: bool) { if (is) this.flags |= ElementFlags.IMPORTED; else this.flags &= ~ElementFlags.IMPORTED; }
/** Whether exported or not. */
get isExported(): bool { return (this.flags & ElementFlags.EXPORTED) != 0; }
set isExported(is: bool) { if (is) this.flags |= ElementFlags.EXPORTED; else this.flags &= ~ElementFlags.EXPORTED; }
/** Whether built-in or not. */
get isBuiltIn(): bool { return (this.flags & ElementFlags.BUILTIN) != 0; }
set isBuiltIn(is: bool) { if (is) this.flags |= ElementFlags.BUILTIN; else this.flags &= ~ElementFlags.BUILTIN; }
/** Whether declared or not. */
get isDeclared(): bool { return (this.flags & ElementFlags.DECLARED) != 0; }
set isDeclared(is: bool) { if (is) this.flags |= ElementFlags.DECLARED; else this.flags &= ~ElementFlags.DECLARED; }
/** Whether generic or not. */
get isGeneric(): bool { return (this.flags & ElementFlags.GENERIC) != 0; }
set isGeneric(is: bool) { if (is) this.flags |= ElementFlags.GENERIC; else this.flags &= ~ElementFlags.GENERIC; }
/** Whether constant or not. */
get isConstant(): bool { return (this.flags & ElementFlags.CONSTANT) != 0; }
set isConstant(is: bool) { if (is) this.flags |= ElementFlags.CONSTANT; else this.flags &= ~ElementFlags.CONSTANT; }
/** Whether mutable or not. */
get isMutable(): bool { return !(this.flags & ElementFlags.CONSTANT); } // reuses constant flag
set isMutable(is: bool) { if (is) this.flags &= ~ElementFlags.CONSTANT; else this.flags |= ElementFlags.CONSTANT; }
/** Whether this element has a constant value or not. */
get hasConstantValue(): bool { return (this.flags & ElementFlags.CONSTANT_VALUE) != 0; }
set hasConstantValue(is: bool) { if (is) this.flags |= ElementFlags.CONSTANT_VALUE; else this.flags &= ~ElementFlags.CONSTANT_VALUE; }
/** Whether an instance member or not. */
get isInstance(): bool { return (this.flags & ElementFlags.INSTANCE) != 0; }
set isInstance(is: bool) { if (is) this.flags |= ElementFlags.INSTANCE; else this.flags &= ~ElementFlags.INSTANCE; }
} }
/** A namespace. Also the base class of other namespace-like program elements. */ /** A namespace. Also the base class of other namespace-like program elements. */
export class Namespace extends Element { export class Namespace extends Element {
kind = ElementKind.NAMESPACE; kind = ElementKind.NAMESPACE;
/** Declaration reference. */
declaration: NamespaceDeclaration | null; declaration: NamespaceDeclaration | null;
/** Member elements. */
members: Map<string,Element> = new Map(); members: Map<string,Element> = new Map();
constructor(program: Program, internalName: string, declaration: NamespaceDeclaration | null) { /** Constructs a new namespace. */
constructor(program: Program, internalName: string, declaration: NamespaceDeclaration | null = null) {
super(program, internalName); super(program, internalName);
this.declaration = declaration; if ((this.declaration = declaration) && this.declaration.modifiers) {
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.IMPORT: this.isImported = true; break;
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.DECLARE: this.isDeclared = true; break;
default: throw new Error("unexpected modifier");
}
}
}
} }
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : false; }
} }
/** An enum. */ /** An enum. */
export class Enum extends Namespace { export class Enum extends Namespace {
kind = ElementKind.ENUM; kind = ElementKind.ENUM;
/** Declaration reference. */
declaration: EnumDeclaration | null; declaration: EnumDeclaration | null;
/** Enum members. */
members: Map<string,EnumValue> = new Map(); // more specific members: Map<string,EnumValue> = new Map(); // more specific
/** Constructs a new enum. */
constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) { constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) {
super(program, internalName, null); super(program, internalName, null);
this.declaration = declaration; if ((this.declaration = declaration) && this.declaration.modifiers) {
for (let i: i32 = 0, k = this.declaration.modifiers.length; i < k; ++i) {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.IMPORT: this.isImported = true; break;
case ModifierKind.DECLARE: this.isDeclared = true; break;
case ModifierKind.CONST: this.isConstant = true; break;
default: throw new Error("unexpected modifier");
}
}
}
} }
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
get isConstant(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are const */ true; }
} }
/** An enum value. */ /** An enum value. */
export class EnumValue extends Element { export class EnumValue extends Element {
kind = ElementKind.ENUMVALUE; kind = ElementKind.ENUMVALUE;
/** Declaration reference. */
declaration: EnumValueDeclaration | null; declaration: EnumValueDeclaration | null;
/** Parent enum. */
enum: Enum; enum: Enum;
hasConstantValue: bool; /** Constant value, if applicable. */
constantValue: i32 = 0; constantValue: i32 = 0;
constructor(enm: Enum, program: Program, internalName: string, declaration: EnumValueDeclaration | null = null) { constructor(enm: Enum, program: Program, internalName: string, declaration: EnumValueDeclaration | null = null) {
super(program, internalName); super(program, internalName);
this.enum = enm; this.enum = enm;
if (!(this.declaration = declaration)) this.hasConstantValue = true; if (!(this.declaration = declaration))
this.hasConstantValue = true; // built-ins have constant values
} }
} }
@ -768,29 +879,50 @@ export class EnumValue extends Element {
export class Global extends Element { export class Global extends Element {
kind = ElementKind.GLOBAL; kind = ElementKind.GLOBAL;
/** Declaration reference. */
declaration: VariableLikeDeclarationStatement | null; declaration: VariableLikeDeclarationStatement | null;
/** Resolved type, if resolved. */
type: Type | null; type: Type | null;
hasConstantValue: bool = false; /** Constant integer value, if applicable. */
constantIntegerValue: I64 | null = null; constantIntegerValue: I64 | null = null;
/** Constant float value, if applicable. */
constantFloatValue: f64 = 0; constantFloatValue: f64 = 0;
constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null, type: Type | null) { constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null = null, type: Type | null = null) {
super(program, internalName); super(program, internalName);
if (!(this.declaration = declaration)) this.hasConstantValue = true; if (this.declaration = declaration) {
this.type = type; // resolved later if `null`, also updates constantKind if (this.declaration.modifiers) {
for (let i: i32 = 0, k = this.declaration.modifiers.length; i < k; ++i) {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.IMPORT: this.isImported = true; break;
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.CONST: this.isConstant = true; break;
case ModifierKind.DECLARE: this.isDeclared = true; break;
default: throw new Error("unexpected modifier");
}
}
}
} else {
this.hasConstantValue = true; // built-ins have constant values
}
this.type = type; // resolved later if `null`
} }
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
get isMutable(): bool { return this.declaration ? !hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; }
} }
/** A function parameter. */ /** A function parameter. */
export class Parameter { export class Parameter {
// not an Element on its own
/** Parameter name. */
name: string; name: string;
/** Parameter type. */
type: Type; type: Type;
/** Parameter initializer. */
initializer: Expression | null; initializer: Expression | null;
/** Constructs a new function parameter. */
constructor(name: string, type: Type, initializer: Expression | null = null) { constructor(name: string, type: Type, initializer: Expression | null = null) {
this.name = name; this.name = name;
this.type = type; this.type = type;
@ -802,7 +934,10 @@ export class Parameter {
export class Local extends Element { export class Local extends Element {
kind = ElementKind.LOCAL; kind = ElementKind.LOCAL;
/** Local index. */
index: i32; index: i32;
/** Local type. */
type: Type; type: Type;
constructor(program: Program, internalName: string, index: i32, type: Type) { constructor(program: Program, internalName: string, index: i32, type: Type) {
@ -816,22 +951,44 @@ export class Local extends Element {
export class FunctionPrototype extends Element { export class FunctionPrototype extends Element {
kind = ElementKind.FUNCTION_PROTOTYPE; kind = ElementKind.FUNCTION_PROTOTYPE;
declaration: FunctionDeclaration | null;
classPrototype: ClassPrototype | null;
instances: Map<string,Function> = new Map();
isGeneric: bool;
/** Declaration reference. */
declaration: FunctionDeclaration | null;
/** Class prototype reference. */
classPrototype: ClassPrototype | null;
/** Resolved instances. */
instances: Map<string,Function> = new Map();
/** Constructs a new function prototype. */
constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) { constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) {
super(program, internalName); super(program, internalName);
this.declaration = declaration; if (this.declaration = declaration) {
this.classPrototype = classPrototype; if (this.declaration.modifiers)
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // built-ins set this for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.IMPORT: this.isImported = true; break;
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.DECLARE: this.isDeclared = true; break;
case ModifierKind.GET: this.isGetter = true; break;
case ModifierKind.SET: this.isSetter = true; break;
default: throw new Error("unexpected modifier");
}
}
if (this.declaration.typeParameters.length)
this.isGeneric = true;
}
if (this.classPrototype = classPrototype) {
this.isInstance = true;
}
} }
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; } /** Whether a getter function or not. */
get isInstance(): bool { return this.classPrototype != null; } get isGetter(): bool { return (this.flags & ElementFlags.GETTER) != 0; }
get isGetter(): bool { return this.declaration ? hasModifier(ModifierKind.GET, this.declaration.modifiers) : /* internals aren't getters */ false; } set isGetter(is: bool) { if (is) this.flags |= ElementFlags.GETTER; else this.flags &= ~ElementFlags.GETTER; }
get isSetter(): bool { return this.declaration ? hasModifier(ModifierKind.SET, this.declaration.modifiers) : /* internals aren't setters */ false; }
/** Whether a setter function or not. */
get isSetter(): bool { return (this.flags & ElementFlags.SETTER) != 0; }
set isSetter(is: bool) { if (is) this.flags |= ElementFlags.SETTER; else this.flags &= ~ElementFlags.SETTER; }
resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Function | null { resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Function | null {
const instanceKey: string = typesToString(typeArguments, "", ""); const instanceKey: string = typesToString(typeArguments, "", "");
@ -912,8 +1069,8 @@ export class Function extends Element {
kind = ElementKind.FUNCTION; kind = ElementKind.FUNCTION;
/** Underlying function template. */ /** Prototype reference. */
template: FunctionPrototype; prototype: FunctionPrototype;
/** Concrete type arguments. */ /** Concrete type arguments. */
typeArguments: Type[]; typeArguments: Type[];
/** Concrete function parameters. Excluding `this` if an instance method. */ /** Concrete function parameters. Excluding `this` if an instance method. */
@ -937,13 +1094,12 @@ export class Function extends Element {
/** Constructs a new concrete function. */ /** Constructs a new concrete function. */
constructor(prototype: FunctionPrototype, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) { constructor(prototype: FunctionPrototype, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) {
super(prototype.program, internalName); super(prototype.program, internalName);
this.template = prototype; this.prototype = prototype;
this.typeArguments = typeArguments; this.typeArguments = typeArguments;
this.parameters = parameters; this.parameters = parameters;
this.returnType = returnType; this.returnType = returnType;
this.instanceMethodOf = instanceMethodOf; this.instanceMethodOf = instanceMethodOf;
this.isBuiltIn = prototype.isBuiltIn; this.flags = prototype.flags;
this.isDeclare = prototype.isDeclare;
let localIndex: i32 = 0; let localIndex: i32 = 0;
if (instanceMethodOf) { if (instanceMethodOf) {
this.locals.set("this", new Local(prototype.program, "this", localIndex++, instanceMethodOf.type)); this.locals.set("this", new Local(prototype.program, "this", localIndex++, instanceMethodOf.type));
@ -964,7 +1120,7 @@ export class Function extends Element {
// if it has a name, check previously as this method will throw otherwise // if it has a name, check previously as this method will throw otherwise
let localIndex = this.parameters.length + this.additionalLocals.length; let localIndex = this.parameters.length + this.additionalLocals.length;
if (this.isInstance) localIndex++; // plus 'this' if (this.isInstance) localIndex++; // plus 'this'
const local: Local = new Local(this.template.program, name ? name : "anonymous$" + localIndex.toString(10), localIndex, type); const local: Local = new Local(this.prototype.program, name ? name : "anonymous$" + localIndex.toString(10), localIndex, type);
if (name) { if (name) {
if (this.locals.has(<string>name)) if (this.locals.has(<string>name))
throw new Error("unexpected duplicate local name"); throw new Error("unexpected duplicate local name");
@ -1050,30 +1206,45 @@ export class Function extends Element {
export class FieldPrototype extends Element { export class FieldPrototype extends Element {
kind = ElementKind.FIELD_PROTOTYPE; kind = ElementKind.FIELD_PROTOTYPE;
/** Declaration reference. */
declaration: FieldDeclaration | null; declaration: FieldDeclaration | null;
/** Parent class prototype. */
classPrototype: ClassPrototype; classPrototype: ClassPrototype;
constructor(classPrototype: ClassPrototype, internalName: string, declaration: FieldDeclaration | null) { /** Constructs a new field prototype. */
constructor(classPrototype: ClassPrototype, internalName: string, declaration: FieldDeclaration | null = null) {
super(classPrototype.program, internalName); super(classPrototype.program, internalName);
this.classPrototype = classPrototype; this.classPrototype = classPrototype;
if ((this.declaration = declaration) && this.declaration.modifiers) {
for (let i: i32 = 0, k = this.declaration.modifiers.length; i < k; ++i) {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.EXPORT: this.isExported = true; break;
default: throw new Error("unexpected modifier");
}
}
}
} }
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; }
} }
/** A resolved instance field. */ /** A resolved instance field. */
export class Field extends Element { export class Field extends Element {
kind = ElementKind.FIELD; kind = ElementKind.FIELD;
template: FieldPrototype;
/** Field prototype reference. */
prototype: FieldPrototype;
/** Resolved type. */
type: Type; type: Type;
hasConstantValue: bool = false; /** Constant integer value, if applicable. */
constantIntegerValue: I64 | null = null; constantIntegerValue: I64 | null = null;
/** Constant float value, if applicable. */
constantFloatValue: f64 = 0; constantFloatValue: f64 = 0;
constructor(template: FieldPrototype, internalName: string, type: Type) { /** Constructs a new field. */
super(template.program, internalName); constructor(prototype: FieldPrototype, internalName: string, type: Type) {
if (!this.template.declaration) this.hasConstantValue = true; super(prototype.program, internalName);
this.flags = prototype.flags;
this.type = type; this.type = type;
} }
} }
@ -1082,19 +1253,31 @@ export class Field extends Element {
export class ClassPrototype extends Namespace { export class ClassPrototype extends Namespace {
kind = ElementKind.CLASS_PROTOTYPE; kind = ElementKind.CLASS_PROTOTYPE;
/** Declaration reference. */
declaration: ClassDeclaration | null; declaration: ClassDeclaration | null;
/** Resolved instances. */
instances: Map<string,Class>; instances: Map<string,Class>;
isGeneric: bool;
constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) { constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) {
super(program, internalName, null); super(program, internalName, null);
this.declaration = declaration; if (this.declaration = declaration) {
if (this.declaration.modifiers) {
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.IMPORT: this.isImported = true; break;
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.DECLARE: this.isDeclared = true; break;
default: throw new Error("unexpected modifier");
}
}
}
if (this.declaration.typeParameters.length)
this.isGeneric = true;
}
this.instances = new Map(); this.instances = new Map();
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins can set this
} }
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; }
resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Class { resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Class {
const key: string = typesToString(typeArguments, "", ""); const key: string = typesToString(typeArguments, "", "");
let instance: Class | null = <Class | null>this.instances.get(key); let instance: Class | null = <Class | null>this.instances.get(key);
@ -1125,20 +1308,26 @@ export class ClassPrototype extends Namespace {
export class Class extends Namespace { export class Class extends Namespace {
kind = ElementKind.CLASS; kind = ElementKind.CLASS;
declaration: ClassDeclaration | null;
template: ClassPrototype; /** Prototype reference. */
prototype: ClassPrototype;
/** Resolved type arguments. */
typeArguments: Type[]; typeArguments: Type[];
base: Class | null; /** Resolved class type. */
type: Type; type: Type;
/** Base class, if applicable. */
base: Class | null;
contextualTypeArguments: Map<string,Type> = new Map(); contextualTypeArguments: Map<string,Type> = new Map();
constructor(template: ClassPrototype, internalName: string, typeArguments: Type[], base: Class | null) { /** Constructs a new class. */
super(template.program, internalName, template.declaration); constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] = [], base: Class | null = null) {
this.template = template; super(prototype.program, internalName, prototype.declaration);
this.prototype = prototype;
this.flags = prototype.flags;
this.typeArguments = typeArguments; this.typeArguments = typeArguments;
this.base = base; this.base = base;
this.type = (template.program.target == Target.WASM64 ? Type.usize64 : Type.usize32).asClass(this); this.type = (prototype.program.target == Target.WASM64 ? Type.usize64 : Type.usize32).asClass(this);
// inherit base class contextual type arguments // inherit base class contextual type arguments
if (base) if (base)
@ -1146,7 +1335,7 @@ export class Class extends Namespace {
this.contextualTypeArguments.set(name, type); this.contextualTypeArguments.set(name, type);
// apply instance-specific contextual type arguments // apply instance-specific contextual type arguments
const declaration: ClassDeclaration | null = this.template.declaration; const declaration: ClassDeclaration | null = this.prototype.declaration;
if (declaration) { // irrelevant for built-ins if (declaration) { // irrelevant for built-ins
const typeParameters: TypeParameter[] = declaration.typeParameters; const typeParameters: TypeParameter[] = declaration.typeParameters;
if (typeParameters.length != typeArguments.length) if (typeParameters.length != typeArguments.length)
@ -1165,9 +1354,12 @@ export class Class extends Namespace {
export class InterfacePrototype extends ClassPrototype { export class InterfacePrototype extends ClassPrototype {
kind = ElementKind.INTERFACE_PROTOTYPE; kind = ElementKind.INTERFACE_PROTOTYPE;
/** Declaration reference. */
declaration: InterfaceDeclaration | null; declaration: InterfaceDeclaration | null;
constructor(program: Program, internalName: string, declaration: InterfaceDeclaration | null) { /** Constructs a new interface prototype. */
constructor(program: Program, internalName: string, declaration: InterfaceDeclaration | null = null) {
super(program, internalName, declaration); super(program, internalName, declaration);
} }
} }
@ -1176,10 +1368,14 @@ export class InterfacePrototype extends ClassPrototype {
export class Interface extends Class { export class Interface extends Class {
kind = ElementKind.INTERFACE; kind = ElementKind.INTERFACE;
template: InterfacePrototype;
/** Prototype reference. */
prototype: InterfacePrototype;
/** Base interface, if applcable. */
base: Interface | null; base: Interface | null;
constructor(template: InterfacePrototype, internalName: string, typeArguments: Type[], base: Interface | null) { /** Constructs a new interface. */
super(template, internalName, typeArguments, base); constructor(prototype: InterfacePrototype, internalName: string, typeArguments: Type[] = [], base: Interface | null = null) {
super(prototype, internalName, typeArguments, base);
} }
} }