diff --git a/README.md b/README.md index 3681b2fd..d58da761 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ AssemblyScript NEXT 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. -The compiler itself utilizes "portable definitions" so it can be compiled to both JavaScript using `tsc` and, eventually, to WebAssembly using `asc`. - 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. diff --git a/src/compiler.ts b/src/compiler.ts index 4eadec75..a3cf552a 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -419,52 +419,56 @@ export class Compiler extends DiagnosticEmitter { if (element.isCompiled) return; let previousValue: EnumValue | null = null; - for (let [key, val] of element.members) { - if (val.hasConstantValue) { - this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue)); - } else if (val.declaration) { - const declaration: EnumValueDeclaration = val.declaration; - let initializer: ExpressionRef; - let initializeInStart: bool = false; - if (declaration.value) { - initializer = this.compileExpression(declaration.value, Type.i32); - if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { - initializer = this.precomputeExpressionRef(initializer); + if (element.members) + for (let [key, member] of element.members) { + if (member.kind != ElementKind.ENUMVALUE) + continue; + const val: EnumValue = member; + if (val.hasConstantValue) { + this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue)); + } else if (val.declaration) { + const declaration: EnumValueDeclaration = val.declaration; + let initializer: ExpressionRef; + let initializeInStart: bool = false; + if (declaration.value) { + initializer = this.compileExpression(declaration.value, Type.i32); if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { - if (element.isConstant) - this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range); - initializeInStart = true; + initializer = this.precomputeExpressionRef(initializer); + if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { + if (element.isConstant) + this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range); + initializeInStart = true; + } } + } else if (previousValue == null) { + initializer = this.module.createI32(0); + } else if (previousValue.hasConstantValue) { + initializer = this.module.createI32(previousValue.constantValue + 1); + } else { + // in TypeScript this errors with TS1061, but actually we can do: + initializer = this.module.createBinary(BinaryOp.AddI32, + this.module.createGetGlobal(previousValue.internalName, NativeType.I32), + this.module.createI32(1) + ); + if (element.isConstant) + this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range); + initializeInStart = true; } - } else if (previousValue == null) { - initializer = this.module.createI32(0); - } else if (previousValue.hasConstantValue) { - initializer = this.module.createI32(previousValue.constantValue + 1); - } else { - // in TypeScript this errors with TS1061, but actually we can do: - initializer = this.module.createBinary(BinaryOp.AddI32, - this.module.createGetGlobal(previousValue.internalName, NativeType.I32), - this.module.createI32(1) - ); - if (element.isConstant) - this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, val.declaration.range); - initializeInStart = true; - } - if (initializeInStart) { - this.module.addGlobal(val.internalName, NativeType.I32, true, this.module.createI32(0)); - this.startFunctionBody.push(this.module.createSetGlobal(val.internalName, initializer)); - } else { - this.module.addGlobal(val.internalName, NativeType.I32, false, initializer); - if (_BinaryenExpressionGetType(initializer) == NativeType.I32) { - val.constantValue = _BinaryenConstGetValueI32(initializer); - val.hasConstantValue = true; - } else - throw new Error("unexpected initializer type"); - } - } else - throw new Error("unexpected missing declaration or constant value"); - previousValue = val; - } + if (initializeInStart) { + this.module.addGlobal(val.internalName, NativeType.I32, true, this.module.createI32(0)); + this.startFunctionBody.push(this.module.createSetGlobal(val.internalName, initializer)); + } else { + this.module.addGlobal(val.internalName, NativeType.I32, false, initializer); + if (_BinaryenExpressionGetType(initializer) == NativeType.I32) { + val.constantValue = _BinaryenConstGetValueI32(initializer); + val.hasConstantValue = true; + } else + throw new Error("unexpected initializer type"); + } + } else + throw new Error("unexpected missing declaration or constant value"); + previousValue = val; + } element.isCompiled = true; } @@ -584,6 +588,7 @@ export class Compiler extends DiagnosticEmitter { } compileNamespace(ns: Namespace): void { + if (!ns.members) return; const noTreeShaking: bool = this.options.noTreeShaking; for (let [name, element] of ns.members) { switch (element.kind) { @@ -1856,9 +1861,6 @@ export class Compiler extends DiagnosticEmitter { let target: Element | null; switch (expression.kind) { - default: - throw new Error("unexpected expression kind"); - case NodeKind.THIS: if (!this.currentFunction.instanceMethodOf) { this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); @@ -1882,6 +1884,9 @@ export class Compiler extends DiagnosticEmitter { case NodeKind.PROPERTYACCESS: target = this.program.resolvePropertyAccess(expression, this.currentFunction); // reports break; + + default: + throw new Error("unexpected expression kind"); } if (!target) return this.module.createUnreachable(); @@ -1891,20 +1896,29 @@ export class Compiler extends DiagnosticEmitter { let expr: ExpressionRef; switch (target.kind) { - // handle enum value right away - case ElementKind.ENUM: - element = (target).members.get(propertyName); - if (!element) { + 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 (element).hasConstantValue + ? this.module.createI32((element).constantValue) + : this.module.createGetGlobal((element).internalName, NativeType.I32); + } + } else { this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName); return this.module.createUnreachable(); } - this.currentType = Type.i32; - return (element).hasConstantValue - ? this.module.createI32((element).constantValue) - : this.module.createGetGlobal((element).internalName, NativeType.I32); - - // postpone everything else + break; case ElementKind.LOCAL: element = (target).type.classType; @@ -1926,15 +1940,6 @@ export class Compiler extends DiagnosticEmitter { target = element; break; - case ElementKind.NAMESPACE: - element = (target).members.get(propertyName); - if (!(element && element.isExported)) { - this.error(DiagnosticCode.Namespace_0_has_no_exported_member_1, propertyAccess.property.range, (target).internalName, propertyName); - return this.module.createUnreachable(); - } - target = element; - break; - default: throw new Error("unexpected target kind"); } diff --git a/src/program.ts b/src/program.ts index 5f8e03bc..d207bfc6 100644 --- a/src/program.ts +++ b/src/program.ts @@ -210,94 +210,144 @@ export class Program extends DiagnosticEmitter { } while (true); } - private initializeClass(declaration: ClassDeclaration, namespace: Namespace | null = null): void { - throw new Error("not implemented"); - /* const internalName: string = declaration.internalName; + private initializeClass(declaration: ClassDeclaration, namespace: Element | null = null): void { + const internalName: string = declaration.internalName; if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); return; } const prototype: ClassPrototype = new ClassPrototype(this, internalName, declaration); this.elements.set(internalName, prototype); - if (prototype.isExported) { - if (this.exports.has(internalName)) + + if (namespace) { + if (namespace.members) { + if (namespace.members.has(declaration.identifier.name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + return; + } + } else + namespace.members = new Map(); + namespace.members.set(declaration.identifier.name, prototype); + } else if (prototype.isExported) { + if (this.exports.has(internalName)) { this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); - else - this.exports.set(internalName, prototype); + return; + } + this.exports.set(internalName, prototype); } + const memberDeclarations: DeclarationStatement[] = declaration.members; for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) { - const memberDeclaration: DeclarationStatement = memberDeclarations[j]; - if (memberDeclaration.kind == NodeKind.FIELD) - this.initializeField(memberDeclaration, prototype); - else if (memberDeclaration.kind == NodeKind.METHOD) - this.initializeMethod(memberDeclaration, prototype); - else - throw new Error("unexpected class member"); - } */ + switch (memberDeclarations[j].kind) { + + case NodeKind.FIELD: + this.initializeField(memberDeclarations[j], prototype); + break; + + case NodeKind.METHOD: + this.initializeMethod(memberDeclarations[j], prototype); + break; + + default: + throw new Error("unexpected class member"); + } + } } private initializeField(declaration: FieldDeclaration, classPrototype: ClassPrototype): void { - throw new Error("not implemented"); - /* const name: string = declaration.identifier.name; - if (classPrototype.members.has(name)) { - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); - return; - } + const name: string = declaration.identifier.name; const internalName: string = declaration.internalName; - if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { // static fields become globals - const global: Global = new Global(this, internalName, declaration, null); - classPrototype.members.set(name, global); + + // static fields become global variables + if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { + if (this.elements.has(internalName)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); + return; + } + if (classPrototype.members) { + if (classPrototype.members.has(name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); + return; + } + } else + classPrototype.members = new Map(); + const staticField: Global = new Global(this, internalName, declaration, null); + classPrototype.members.set(name, staticField); + this.elements.set(internalName, staticField); + + // instance fields are remembered until resolved } else { - const field: FieldPrototype = new FieldPrototype(classPrototype, internalName, declaration); - classPrototype.members.set(name, field); - } */ + if (classPrototype.instanceMembers) { + if (classPrototype.instanceMembers.has(name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); + return; + } + } else + classPrototype.instanceMembers = new Map(); + const instanceField = new FieldPrototype(classPrototype, internalName, declaration); + classPrototype.instanceMembers.set(name, instanceField); + } } private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void { - throw new Error("not implemented"); - /* let name: string = declaration.identifier.name; + let name: string = declaration.identifier.name; const internalName: string = declaration.internalName; - if (classPrototype.members.has(name)) { + + // static methods become global functions + if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { + if (this.elements.has(internalName)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); + return; + } + if (classPrototype.members) { + if (classPrototype.members.has(name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); + return; + } + } else + classPrototype.members = new Map(); + const staticPrototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null); + classPrototype.members.set(name, staticPrototype); + this.elements.set(internalName, staticPrototype); + + // instance methods are remembered until resolved + } else { + if (classPrototype.instanceMembers) { + if (classPrototype.instanceMembers.has(name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); + return; + } + } else + classPrototype.instanceMembers = new Map(); + const instancePrototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, classPrototype); + classPrototype.instanceMembers.set(name, instancePrototype); + } + } + + private initializeEnum(declaration: EnumDeclaration, namespace: Element | null = null): void { + const internalName: string = declaration.internalName; + if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); return; } - const func: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, hasModifier(ModifierKind.STATIC, declaration.modifiers) ? null : classPrototype); - let modifiers: Modifier[] | null; - if (modifiers = declaration.modifiers) { - for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i) { - const modifier: Modifier = modifiers[i]; - if (modifier.modifierKind == ModifierKind.GET) { - name = GETTER_PREFIX + name; - break; - } else if (modifier.modifierKind == ModifierKind.SET) { - name = SETTER_PREFIX + name; - break; - } - } - } - classPrototype.members.set(name, func); */ - } - - private initializeEnum(declaration: EnumDeclaration, namespace: Namespace | null = null): void { - const internalName: string = declaration.internalName; const enm: Enum = new Enum(this, internalName, declaration); - - if (this.elements.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - this.elements.set(internalName, enm); + this.elements.set(internalName, enm); if (namespace) { - if (namespace.members.has(declaration.identifier.name)) { - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + if (namespace.members) { + if (namespace.members.has(declaration.identifier.name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + return; + } } else - namespace.members.set(declaration.identifier.name, enm); + namespace.members = new Map(); + namespace.members.set(declaration.identifier.name, enm); } else 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); - else - this.exports.set(internalName, enm); + return; + } + this.exports.set(internalName, enm); } const values: EnumValueDeclaration[] = declaration.members; @@ -308,10 +358,13 @@ export class Program extends DiagnosticEmitter { private initializeEnumValue(declaration: EnumValueDeclaration, enm: Enum): void { const name: string = declaration.identifier.name; const internalName: string = declaration.internalName; - if (enm.members.has(name)) { - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - return; - } + if (enm.members) { + if (enm.members.has(name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + return; + } + } else + enm.members = new Map(); const value: EnumValue = new EnumValue(enm, this, internalName, declaration); enm.members.set(name, value); } @@ -396,26 +449,30 @@ export class Program extends DiagnosticEmitter { } } - private initializeFunction(declaration: FunctionDeclaration, namespace: Namespace | null = null): void { + private initializeFunction(declaration: FunctionDeclaration, namespace: Element | null = null): void { const internalName: string = declaration.internalName; - const prototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null); - if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); return; } + const prototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null); this.elements.set(internalName, prototype); if (namespace) { - if (namespace.members.has(declaration.identifier.name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - namespace.members.set(declaration.identifier.name, prototype); + if (namespace.members) { + if (namespace.members.has(declaration.identifier.name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + return; + } + } else + namespace.members = new Map(); + namespace.members.set(declaration.identifier.name, prototype); } else 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); - else - this.exports.set(internalName, prototype); + return; + } + this.exports.set(internalName, prototype); } } @@ -472,38 +529,43 @@ export class Program extends DiagnosticEmitter { queuedImports.push(queuedImport); } - private initializeInterface(declaration: InterfaceDeclaration, namespace: Namespace | null = null): void { + private initializeInterface(declaration: InterfaceDeclaration, namespace: Element | null = null): void { const internalName: string = declaration.internalName; 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); - else - this.elements.set(internalName, prototype); + return; + } + this.elements.set(internalName, prototype); if (namespace) { - if (namespace.members.has(prototype.internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - namespace.members.set(prototype.internalName, prototype); + if (namespace.members) { + if (namespace.members.has(prototype.internalName)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + return; + } + } else + namespace.members = new Map(); + namespace.members.set(prototype.internalName, prototype); } else 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); - else - this.exports.set(internalName, prototype); + return; + } + this.exports.set(internalName, prototype); } const memberDeclarations: DeclarationStatement[] = declaration.members; - for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) { - const memberDeclaration: DeclarationStatement = memberDeclarations[j]; - switch (memberDeclaration.kind) { + for (let i: i32 = 0, k: i32 = memberDeclarations.length; i < k; ++i) { + switch (memberDeclarations[i].kind) { case NodeKind.FIELD: - this.initializeField(memberDeclaration, prototype); + this.initializeField(memberDeclarations[i], prototype); break; case NodeKind.METHOD: - this.initializeMethod(memberDeclaration, prototype); + this.initializeMethod(memberDeclarations[i], prototype); break; default: @@ -512,54 +574,58 @@ export class Program extends DiagnosticEmitter { } } - private initializeNamespace(declaration: NamespaceDeclaration, parentNamespace: Namespace | null = null): void { + private initializeNamespace(declaration: NamespaceDeclaration, parentNamespace: Element | null = null): void { const internalName: string = declaration.internalName; - const namespace: Namespace = new Namespace(this, internalName, declaration); - if (this.elements.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else + let namespace: Element | null = this.elements.get(internalName); + if (!namespace) { + namespace = new Namespace(this, internalName, declaration); this.elements.set(internalName, namespace); + } if (parentNamespace) { - if (parentNamespace.members.has(declaration.identifier.name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - parentNamespace.members.set(declaration.identifier.name, namespace); + if (parentNamespace.members) { + if (parentNamespace.members.has(declaration.identifier.name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + return; + } + } else + parentNamespace.members = new Map(); + parentNamespace.members.set(declaration.identifier.name, namespace); } else if (namespace.isExported) { - if (this.exports.has(internalName)) - this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); - else - this.exports.set(internalName, namespace); + if (this.exports.has(internalName)) { + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); + return; + } + this.exports.set(internalName, namespace); } const members: Statement[] = declaration.members; for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) { - const statement: Statement = members[i]; - switch (statement.kind) { + switch (members[i].kind) { case NodeKind.CLASS: - this.initializeClass(statement, namespace); + this.initializeClass(members[i], namespace); break; case NodeKind.ENUM: - this.initializeEnum(statement, namespace); + this.initializeEnum(members[i], namespace); break; case NodeKind.FUNCTION: - this.initializeFunction(statement, namespace); + this.initializeFunction(members[i], namespace); break; case NodeKind.INTERFACE: - this.initializeInterface(statement, namespace); + this.initializeInterface(members[i], namespace); break; case NodeKind.NAMESPACE: - this.initializeNamespace(statement, namespace); + this.initializeNamespace(members[i], namespace); break; case NodeKind.VARIABLE: - this.initializeVariables(statement, namespace); + this.initializeVariables(members[i], namespace); break; default: @@ -568,29 +634,33 @@ export class Program extends DiagnosticEmitter { } } - private initializeVariables(statement: VariableStatement, namespace: Namespace | null = null): void { + private initializeVariables(statement: VariableStatement, namespace: Element | null = null): void { const declarations: VariableDeclaration[] = statement.declarations; for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) { const declaration: VariableDeclaration = declarations[i]; const internalName: string = declaration.internalName; - const global: Global = new Global(this, internalName, declaration, null); - - if (this.elements.has(internalName)) + if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else { - this.elements.set(internalName, global); + continue; + } + + const global: Global = new Global(this, internalName, declaration, null); + this.elements.set(internalName, global); if (namespace) { - if (namespace.members.has(declaration.identifier.name)) + if (namespace.members) { + if (namespace.members.has(declaration.identifier.name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + continue; + } + } else + namespace.members = new Map(); + namespace.members.set(declaration.identifier.name, global); + } else if (global.isExported) { + if (this.exports.has(internalName)) this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); else - namespace.members.set(declaration.identifier.name, global); - } else if (global.isExported) { - if (this.exports.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - this.exports.set(internalName, global); - } + this.exports.set(internalName, global); } } } @@ -683,17 +753,11 @@ export class Program extends DiagnosticEmitter { if (!target) return null; const propertyName: string = propertyAccess.property.name; - let member: Element | null = null; - if (target.kind == ElementKind.ENUM) - member = (target).members.get(propertyName); - else if (target.kind == ElementKind.NAMESPACE) - member = (target).members.get(propertyName); - // else if (target.kind == ElementKind.CLASS_PROTOTYPE) - // member = (target).members.get(propertyName); - // else if (target.kind == ElementKind.CLASS) - // member = (target).members.get(propertyName); - if (member) - return member; + if (target.members) { + const member: Element | null = target.members.get(propertyName); + if (member) + return member; + } this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (expression).property.name, target.internalName); return null; } @@ -807,6 +871,8 @@ export abstract class Element { internalName: string; /** Element flags. */ flags: ElementFlags = ElementFlags.NONE; + /** Namespaced member elements. */ + members: Map | null = null; /** Constructs a new element, linking it to its containing {@link Program}. */ protected constructor(program: Program, internalName: string) { @@ -861,9 +927,7 @@ export class Namespace extends Element { kind = ElementKind.NAMESPACE; /** Declaration reference. */ - declaration: NamespaceDeclaration | null; - /** Member elements. */ - members: Map = new Map(); + declaration: NamespaceDeclaration | null; // more specific /** Constructs a new namespace. */ constructor(program: Program, internalName: string, declaration: NamespaceDeclaration | null = null) { @@ -882,18 +946,16 @@ export class Namespace extends Element { } /** An enum. */ -export class Enum extends Namespace { +export class Enum extends Element { kind = ElementKind.ENUM; /** Declaration reference. */ declaration: EnumDeclaration | null; - /** Enum members. */ - members: Map = new Map(); // more specific /** Constructs a new enum. */ constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) { - super(program, internalName, null); + super(program, internalName); 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) { @@ -952,6 +1014,7 @@ export class Global extends Element { case ModifierKind.EXPORT: this.isExported = true; break; case ModifierKind.CONST: this.isConstant = true; break; case ModifierKind.DECLARE: this.isDeclared = true; break; + case ModifierKind.STATIC: break; // static fields become globals default: throw new Error("unexpected modifier"); } } @@ -1024,6 +1087,7 @@ export class FunctionPrototype extends Element { case ModifierKind.DECLARE: this.isDeclared = true; break; case ModifierKind.GET: this.isGetter = true; break; case ModifierKind.SET: this.isSetter = true; break; + case ModifierKind.STATIC: break; // already handled default: throw new Error("unexpected modifier"); } } @@ -1314,14 +1378,8 @@ export class ClassPrototype extends Element { declaration: ClassDeclaration | null; /** Resolved instances. */ instances: Map = new Map(); - /** Static fields. */ - staticFields: Map | null = null; - /** Static methods. */ - staticMethods: Map | null = null; - /** Static getters. */ - staticGetters: Map | null = null; - /** Static setters. */ - staticSetters: Map | null = null; + /** Instance member prototypes. */ + instanceMembers: Map | null = null; constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) { super(program, internalName); diff --git a/tests/compiler.js b/tests/compiler.js index 3b46c019..3cc3eb43 100644 --- a/tests/compiler.js +++ b/tests/compiler.js @@ -77,14 +77,20 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { } } } else { - var expected = fs.readFileSync(__dirname + "/compiler/" + fixture, { encoding: "utf8" }); - var diffs = diff("compiler/" + fixture, expected, actual); - if (diffs !== null) { + try { + var expected = fs.readFileSync(__dirname + "/compiler/" + fixture, { encoding: "utf8" }); + var diffs = diff("compiler/" + fixture, expected, actual); + if (diffs !== null) { + process.exitCode = 1; + console.log(diffs); + console.log(chalk.default.red("diff ERROR")); + } else { + console.log(chalk.default.green("diff OK")); + } + } catch (e) { process.exitCode = 1; - console.log(diffs); + console.log(e.message); console.log(chalk.default.red("diff ERROR")); - } else { - console.log(chalk.default.green("diff OK")); } } diff --git a/tests/compiler/class.optimized-inlined.wast b/tests/compiler/class.optimized-inlined.wast new file mode 100644 index 00000000..5272998e --- /dev/null +++ b/tests/compiler/class.optimized-inlined.wast @@ -0,0 +1,46 @@ +(module + (type $iii (func (param i32 i32) (result i32))) + (type $fff (func (param f32 f32) (result f32))) + (type $v (func)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $start (; 0 ;) (type $v) + (local $0 i32) + (local $1 i32) + (local $2 f32) + (local $3 f32) + (drop + (block (result i32) + (block $__inlined_func$class/Animal.add (result i32) + (set_local $0 + (i32.const 1) + ) + (set_local $1 + (i32.const 2) + ) + (i32.add + (get_local $0) + (get_local $1) + ) + ) + ) + ) + (drop + (block (result f32) + (block $__inlined_func$class/Animal.sub (result f32) + (set_local $2 + (f32.const 1) + ) + (set_local $3 + (f32.const 2) + ) + (f32.sub + (get_local $2) + (get_local $3) + ) + ) + ) + ) + ) +) diff --git a/tests/compiler/class.optimized.wast b/tests/compiler/class.optimized.wast new file mode 100644 index 00000000..40b48b38 --- /dev/null +++ b/tests/compiler/class.optimized.wast @@ -0,0 +1,34 @@ +(module + (type $iii (func (param i32 i32) (result i32))) + (type $fff (func (param f32 f32) (result f32))) + (type $v (func)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $class/Animal.add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (i32.add + (get_local $0) + (get_local $1) + ) + ) + (func $class/Animal.sub (; 1 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) + (f32.sub + (get_local $0) + (get_local $1) + ) + ) + (func $start (; 2 ;) (type $v) + (drop + (call $class/Animal.add + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (call $class/Animal.sub + (f32.const 1) + (f32.const 2) + ) + ) + ) +) diff --git a/tests/compiler/class.ts b/tests/compiler/class.ts new file mode 100644 index 00000000..7952408e --- /dev/null +++ b/tests/compiler/class.ts @@ -0,0 +1,9 @@ +class Animal { + static MAX: i32 = 1; + static add(a: i32, b: i32): i32 { return a + b; } + static sub(a: T, b: T): T { return a - b; } // tsc does not allow this +} + +Animal.MAX; +Animal.add(1,2); +Animal.sub(1.0, 2.0); diff --git a/tests/compiler/class.wast b/tests/compiler/class.wast new file mode 100644 index 00000000..f31a992e --- /dev/null +++ b/tests/compiler/class.wast @@ -0,0 +1,80 @@ +(module + (type $iii (func (param i32 i32) (result i32))) + (type $fff (func (param f32 f32) (result f32))) + (type $v (func)) + (global $class/Animal.MAX (mut i32) (i32.const 1)) + (global $HEAP_START i32 (i32.const 4)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $class/Animal.add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (return + (i32.add + (get_local $0) + (get_local $1) + ) + ) + ) + (func $class/Animal.sub (; 1 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) + (return + (f32.sub + (get_local $0) + (get_local $1) + ) + ) + ) + (func $start (; 2 ;) (type $v) + (drop + (get_global $class/Animal.MAX) + ) + (drop + (call $class/Animal.add + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (call $class/Animal.sub + (f32.const 1) + (f32.const 2) + ) + ) + ) +) +(; +[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 + class/Animal + class/Animal.MAX + class/Animal.add + class/Animal.sub +[program.exports] + +;)