Namespaces

This commit is contained in:
dcodeIO
2017-12-13 23:24:13 +01:00
parent 7d85b0cc7f
commit 99b0fdf7a8
30 changed files with 514 additions and 128 deletions

View File

@ -1809,12 +1809,16 @@ export function mangleInternalName(declaration: DeclarationStatement): string {
}
}
}
if (!declaration.parent)
let parent: Node | null = declaration.parent;
if (!parent)
return name;
if (declaration.parent.kind == NodeKind.CLASS)
return (<ClassDeclaration>declaration.parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
if (declaration.parent.kind == NodeKind.NAMESPACE || declaration.parent.kind == NodeKind.ENUM)
return (<DeclarationStatement>declaration.parent).internalName + STATIC_DELIMITER + name;
if (declaration.kind == NodeKind.VARIABLEDECLARATION && parent.kind == NodeKind.VARIABLE) // skip over
if (!(parent = parent.parent))
return name;
if (parent.kind == NodeKind.CLASS)
return (<ClassDeclaration>parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
if (parent.kind == NodeKind.NAMESPACE || parent.kind == NodeKind.ENUM)
return (<DeclarationStatement>parent).internalName + STATIC_DELIMITER + name;
return declaration.range.source.internalPath + PATH_DELIMITER + name;
}

View File

@ -494,7 +494,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
return module.createUnreachable();
arg0 = compiler.compileExpression(operands[0], Type.i32); // reports
compiler.currentType = Type.void;
return compiler.options.noDebug
return compiler.options.noAssert
? module.createNop()
: module.createIf(
module.createUnary(UnaryOp.EqzI32, arg0),

View File

@ -119,7 +119,7 @@ export class Options {
/** If true, compiles everything instead of just reachable code. */
noTreeShaking: bool = false;
/** If true, replaces assertions with nops. */
noDebug: bool = false;
noAssert: bool = false;
}
const enum ConversionKind {
@ -306,7 +306,7 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("unexpected missing global");
if (!this.compileGlobal(<Global>element))
return null;
if (declaration.range.source.isEntry && (<VariableStatement>declaration.parent).parent == declaration.range.source && hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (isModuleExport(element, declaration)) {
if ((<Global>element).hasConstantValue)
this.module.addGlobalExport(element.internalName, declaration.identifier.name);
else
@ -470,7 +470,7 @@ export class Compiler extends DiagnosticEmitter {
const instance: Function | null = this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, typeArguments, contextualTypeArguments, alternativeReportNode);
if (!instance)
return;
if (declaration.range.source.isEntry && declaration.parent == declaration.range.source && hasModifier(ModifierKind.EXPORT, declaration.modifiers))
if (isModuleExport(instance, declaration))
this.module.addFunctionExport(instance.internalName, declaration.identifier.name);
}
@ -527,7 +527,7 @@ export class Compiler extends DiagnosticEmitter {
// create the function
const internalName: string = instance.internalName;
if (instance.isDeclared) {
if (instance.isDeclared) { // TODO: use parent namespace as externalModuleName, if applicable
this.module.addFunctionImport(internalName, "env", declaration.identifier.name, typeRef);
} else {
this.module.addFunction(internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, <ExpressionRef[]>stmts, NativeType.None));
@ -573,7 +573,6 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("unexpected namespace member");
}
}
throw new Error("not implemented");
}
compileNamespace(ns: Namespace): void {
@ -1834,7 +1833,115 @@ export class Compiler extends DiagnosticEmitter {
return this.compileExpression(expression.expression, contextualType, ConversionKind.NONE);
}
compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): ExpressionRef {
compilePropertyAccessExpression(propertyAccess: PropertyAccessExpression, contextualType: Type): ExpressionRef {
const expression: Expression = propertyAccess.expression;
const propertyName: string = propertyAccess.property.name;
// the lhs expression is either 'this', 'super', an identifier or another property access
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);
return this.module.createUnreachable();
}
target = this.currentFunction.instanceMethodOf;
break;
case NodeKind.SUPER:
if (!(this.currentFunction.instanceMethodOf && this.currentFunction.instanceMethodOf.base)) {
this.error(DiagnosticCode._super_can_only_be_referenced_in_a_derived_class, expression.range);
return this.module.createUnreachable();
}
target = this.currentFunction.instanceMethodOf.base;
break;
case NodeKind.IDENTIFIER:
target = this.program.resolveIdentifier(<IdentifierExpression>expression, this.currentFunction); // reports
break;
case NodeKind.PROPERTYACCESS:
target = this.program.resolvePropertyAccess(<PropertyAccessExpression>expression, this.currentFunction); // reports
break;
}
if (!target)
return this.module.createUnreachable();
// look up the property within the target to obtain the actual element
let element: Element | null;
let expr: ExpressionRef;
switch (target.kind) {
// handle enum value right away
case ElementKind.ENUM:
element = (<Enum>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();
}
this.currentType = Type.i32;
return (<EnumValue>element).hasConstantValue
? this.module.createI32((<EnumValue>element).constantValue)
: this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32);
// postpone everything else
case ElementKind.LOCAL:
element = (<Local>target).type.classType;
if (!element) {
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, (<Local>target).type.toString());
return this.module.createUnreachable();
}
target = element;
break;
case ElementKind.GLOBAL:
if (!this.compileGlobal(<Global>target))
return this.module.createUnreachable();
element = (<Type>(<Global>target).type).classType;
if (!element) {
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, (<Local>target).type.toString());
return this.module.createUnreachable();
}
target = element;
break;
case ElementKind.NAMESPACE:
element = (<Namespace>target).members.get(propertyName);
if (!(element && element.isExported)) {
this.error(DiagnosticCode.Namespace_0_has_no_exported_member_1, propertyAccess.property.range, (<Namespace>target).internalName, propertyName);
return this.module.createUnreachable();
}
target = element;
break;
default:
throw new Error("unexpected target kind");
}
// handle the element
switch (element.kind) {
case ElementKind.LOCAL:
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));
case ElementKind.FUNCTION: // getter
if (!(<Function>element).prototype.isGetter) {
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, element.internalName);
return this.module.createUnreachable();
}
return this.compileCall(<Function>element, [], propertyAccess);
}
this.error(DiagnosticCode.Operation_not_supported, propertyAccess.range);
throw new Error("not implemented");
}
@ -2031,3 +2138,22 @@ function typesToSignatureName(paramTypes: Type[], returnType: Type): string {
sb.push(typeToSignatureNamePart(returnType));
return sb.join("");
}
function isModuleExport(element: Element, declaration: DeclarationStatement): bool {
if (!element.isExported)
return false;
if (declaration.range.source.isEntry)
return true;
let parentNode: Node | null = declaration.parent;
if (!parentNode)
return false;
if (parentNode.kind == NodeKind.VARIABLE)
if (!(parentNode = parentNode.parent))
return false;
if (parentNode.kind != NodeKind.NAMESPACE && parentNode.kind != NodeKind.CLASS)
return false;
let parent: Element | null = element.program.elements.get((<DeclarationStatement>parentNode).internalName);
if (!parent)
return false;
return isModuleExport(parent, <DeclarationStatement>parentNode);
}

View File

@ -57,6 +57,7 @@ export enum DiagnosticCode {
Type_0_is_not_generic = 2315,
Type_0_is_not_assignable_to_type_1 = 2322,
_this_cannot_be_referenced_in_current_location = 2332,
_super_can_only_be_referenced_in_a_derived_class = 2335,
Property_0_does_not_exist_on_type_1 = 2339,
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
@ -69,6 +70,7 @@ export enum DiagnosticCode {
Expected_0_arguments_but_got_1 = 2554,
Expected_at_least_0_arguments_but_got_1 = 2555,
Expected_0_type_arguments_but_got_1 = 2558,
Namespace_0_has_no_exported_member_1 = 2694,
File_0_not_found = 6054
}
@ -130,6 +132,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2315: return "Type '{0}' is not generic.";
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
case 2332: return "'this' cannot be referenced in current location.";
case 2335: return "'super' can only be referenced in a derived class.";
case 2339: return "Property '{0}' does not exist on type '{1}'.";
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
@ -142,6 +145,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2554: return "Expected {0} arguments, but got {1}.";
case 2555: return "Expected at least {0} arguments, but got {1}.";
case 2558: return "Expected {0} type arguments, but got {1}.";
case 2694: return "Namespace '{0}' has no exported member '{1}'.";
case 6054: return "File '{0}' not found.";
default: return "";
}

View File

@ -57,6 +57,7 @@
"Type '{0}' is not generic.": 2315,
"Type '{0}' is not assignable to type '{1}'.": 2322,
"'this' cannot be referenced in current location.": 2332,
"'super' can only be referenced in a derived class.": 2335,
"Property '{0}' does not exist on type '{1}'.": 2339,
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": 2349,
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
@ -69,6 +70,7 @@
"Expected {0} arguments, but got {1}.": 2554,
"Expected at least {0} arguments, but got {1}.": 2555,
"Expected {0} type arguments, but got {1}.": 2558,
"Namespace '{0}' has no exported member '{1}'.": 2694,
"File '{0}' not found.": 6054
}

View File

@ -155,7 +155,7 @@ export abstract class DiagnosticEmitter {
const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
this.diagnostics.push(message);
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
// console.log(new Error().stack);
console.log(<string>new Error("stack").stack);
}
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {

View File

@ -84,9 +84,9 @@ export function setNoTreeShaking(options: Options, noTreeShaking: bool): void {
options.noTreeShaking = noTreeShaking;
}
/** Sets the `noDebug` option. */
export function setNoDebug(options: Options, noDebug: bool): void {
options.noDebug = noDebug;
/** Sets the `noAssert` option. */
export function setNoAssert(options: Options, noAssert: bool): void {
options.noAssert = noAssert;
}
/** Compiles the sources computed by the parser to a module. */

View File

@ -22,6 +22,7 @@ import {
// expressions
AssertionKind,
CallExpression,
Expression,
IdentifierExpression,
StringLiteralExpression,
@ -31,6 +32,7 @@ import {
BreakStatement,
ClassDeclaration,
ContinueStatement,
DeclarationStatement,
Decorator,
DoStatement,
EnumDeclaration,
@ -39,6 +41,7 @@ import {
ExportMember,
ExportStatement,
ExpressionStatement,
FieldDeclaration,
ForStatement,
FunctionDeclaration,
IfStatement,
@ -47,9 +50,8 @@ import {
MethodDeclaration,
Modifier,
ModifierKind,
DeclarationStatement,
NamespaceDeclaration,
Parameter,
FieldDeclaration,
ReturnStatement,
Statement,
SwitchCase,
@ -61,8 +63,7 @@ import {
VariableDeclaration,
WhileStatement,
hasModifier,
NamespaceDeclaration
hasModifier
} from "./ast";
@ -1514,6 +1515,13 @@ export class Parser extends DiagnosticEmitter {
if (token == Token.DOT) {
if (next.kind == NodeKind.IDENTIFIER) {
expr = Expression.createPropertyAccess(<Expression>expr, <IdentifierExpression>next, tn.range(startPos, tn.pos));
} else if (next.kind == NodeKind.CALL) { // amend
let propertyCall: CallExpression = <CallExpression>next;
if (propertyCall.expression.kind == NodeKind.IDENTIFIER) {
propertyCall.expression = Expression.createPropertyAccess(<Expression>expr, <IdentifierExpression>propertyCall.expression, tn.range(startPos, tn.pos));
} else
throw new Error("unexpected expression kind");
expr = propertyCall;
} else {
this.error(DiagnosticCode.Identifier_expected, next.range);
return null;

View File

@ -210,8 +210,9 @@ export class Program extends DiagnosticEmitter {
} while (true);
}
private initializeClass(declaration: ClassDeclaration): void {
const internalName: string = declaration.internalName;
private initializeClass(declaration: ClassDeclaration, namespace: Namespace | null = null): void {
throw new Error("not implemented");
/* const internalName: string = declaration.internalName;
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
return;
@ -233,11 +234,12 @@ export class Program extends DiagnosticEmitter {
this.initializeMethod(<MethodDeclaration>memberDeclaration, prototype);
else
throw new Error("unexpected class member");
}
} */
}
private initializeField(declaration: FieldDeclaration, classPrototype: ClassPrototype): void {
const name: string = declaration.identifier.name;
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;
@ -249,11 +251,12 @@ export class Program extends DiagnosticEmitter {
} else {
const field: FieldPrototype = new FieldPrototype(classPrototype, internalName, declaration);
classPrototype.members.set(name, field);
}
} */
}
private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void {
let name: string = declaration.identifier.name;
throw new Error("not implemented");
/* let name: string = declaration.identifier.name;
const internalName: string = declaration.internalName;
if (classPrototype.members.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
@ -273,23 +276,30 @@ export class Program extends DiagnosticEmitter {
}
}
}
classPrototype.members.set(name, func);
classPrototype.members.set(name, func); */
}
private initializeEnum(declaration: EnumDeclaration): void {
private initializeEnum(declaration: EnumDeclaration, namespace: Namespace | 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 enm: Enum = new Enum(this, internalName, declaration);
this.elements.set(internalName, enm);
if (enm.isExported) {
if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
this.elements.set(internalName, enm);
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, enm);
} else if (enm.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, enm);
}
const values: EnumValueDeclaration[] = declaration.members;
for (let i: i32 = 0, k: i32 = values.length; i < k; ++i)
this.initializeEnumValue(values[i], enm);
@ -386,15 +396,22 @@ export class Program extends DiagnosticEmitter {
}
}
private initializeFunction(declaration: FunctionDeclaration): void {
private initializeFunction(declaration: FunctionDeclaration, namespace: Namespace | 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;
}
this.elements.set(internalName, prototype);
if (prototype.isExported) {
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);
} 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
@ -455,20 +472,27 @@ export class Program extends DiagnosticEmitter {
queuedImports.push(queuedImport);
}
private initializeInterface(declaration: InterfaceDeclaration): void {
private initializeInterface(declaration: InterfaceDeclaration, namespace: Namespace | null = null): void {
const internalName: string = declaration.internalName;
const prototype: InterfacePrototype = new InterfacePrototype(this, internalName, declaration);
if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
this.elements.set(internalName, prototype);
if (prototype.isExported) {
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);
} 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);
}
const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
const memberDeclaration: DeclarationStatement = memberDeclarations[j];
@ -488,48 +512,54 @@ export class Program extends DiagnosticEmitter {
}
}
private initializeNamespace(declaration: NamespaceDeclaration): void {
private initializeNamespace(declaration: NamespaceDeclaration, parentNamespace: Namespace | 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 {
else
this.elements.set(internalName, namespace);
if (namespace.isExported) {
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);
} 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);
}
}
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) {
case NodeKind.CLASS:
this.initializeClass(<ClassDeclaration>statement);
this.initializeClass(<ClassDeclaration>statement, namespace);
break;
case NodeKind.ENUM:
this.initializeEnum(<EnumDeclaration>statement);
this.initializeEnum(<EnumDeclaration>statement, namespace);
break;
case NodeKind.FUNCTION:
this.initializeFunction(<FunctionDeclaration>statement);
this.initializeFunction(<FunctionDeclaration>statement, namespace);
break;
case NodeKind.INTERFACE:
this.initializeInterface(<InterfaceDeclaration>statement);
this.initializeInterface(<InterfaceDeclaration>statement, namespace);
break;
case NodeKind.NAMESPACE:
this.initializeNamespace(<NamespaceDeclaration>statement);
this.initializeNamespace(<NamespaceDeclaration>statement, namespace);
break;
case NodeKind.VARIABLE:
this.initializeVariables(<VariableStatement>statement, true);
this.initializeVariables(<VariableStatement>statement, namespace);
break;
default:
@ -538,17 +568,24 @@ export class Program extends DiagnosticEmitter {
}
}
private initializeVariables(statement: VariableStatement, isNamespaceMember: bool = false): void {
private initializeVariables(statement: VariableStatement, namespace: Namespace | 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))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else {
this.elements.set(internalName, global);
if (global.isExported) {
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, global);
} else if (global.isExported) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
@ -596,7 +633,7 @@ export class Program extends DiagnosticEmitter {
return null;
}
/** Resolves {@link TypeParameter}s to concrete {@link Type}s. */
/** Resolves an array of type parameters to concrete types. */
resolveTypeArguments(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Type[] | null {
const parameterCount: i32 = typeParameters.length;
const argumentCount: i32 = typeArgumentNodes ? typeArgumentNodes.length : 0;
@ -618,6 +655,49 @@ export class Program extends DiagnosticEmitter {
return typeArguments;
}
/** Resolves an identifier to the element is refers to. */
resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function): Element | null {
const name: string = identifier.name;
const local: Local | null = <Local | null>contextualFunction.locals.get(name);
if (local)
return local;
let element: Element | null;
if (element = this.elements.get(identifier.range.source.internalPath + PATH_DELIMITER + name))
return element;
if (element = this.elements.get(name))
return element;
this.error(DiagnosticCode.Cannot_find_name_0, identifier.range, name);
return null;
}
/** Resolves a property access the element it refers to. */
resolvePropertyAccess(propertyAccess: PropertyAccessExpression, contextualFunction: Function): Element | null {
const expression: Expression = propertyAccess.expression;
let target: Element | null = null;
if (expression.kind == NodeKind.IDENTIFIER) {
target = this.resolveIdentifier(<IdentifierExpression>expression, contextualFunction);
} else if (expression.kind == NodeKind.PROPERTYACCESS) {
target = this.resolvePropertyAccess(<PropertyAccessExpression>expression, contextualFunction);
} else
throw new Error("unexpected target kind");
if (!target)
return null;
const propertyName: string = propertyAccess.property.name;
let member: Element | null = null;
if (target.kind == ElementKind.ENUM)
member = (<Enum>target).members.get(propertyName);
else if (target.kind == ElementKind.NAMESPACE)
member = (<Namespace>target).members.get(propertyName);
// else if (target.kind == ElementKind.CLASS_PROTOTYPE)
// member = <Element | null>(<ClassPrototype>target).members.get(propertyName);
// else if (target.kind == ElementKind.CLASS)
// member = <Element | null>(<Class>target).members.get(propertyName);
if (member)
return member;
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (<PropertyAccessExpression>expression).property.name, target.internalName);
return null;
}
resolveElement(expression: Expression, contextualFunction: Function): Element | null {
// this -> Class
@ -630,38 +710,11 @@ export class Program extends DiagnosticEmitter {
// local or global name
if (expression.kind == NodeKind.IDENTIFIER) {
const name: string = (<IdentifierExpression>expression).name;
const local: Local | null = <Local | null>contextualFunction.locals.get(name);
if (local)
return local;
const fileGlobalElement: Element | null = <Element | null>this.elements.get(expression.range.source.internalPath + PATH_DELIMITER + name);
if (fileGlobalElement)
return fileGlobalElement;
const programGlobalElement: Element | null = <Element | null>this.elements.get(name);
if (programGlobalElement)
return programGlobalElement;
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, name);
return null;
return this.resolveIdentifier(<IdentifierExpression>expression, contextualFunction);
// static or instance property (incl. enum values) or method
} else if (expression.kind == NodeKind.PROPERTYACCESS) {
const target: Element | null = this.resolveElement((<PropertyAccessExpression>expression).expression, contextualFunction); // reports
if (!target)
return null;
const propertyName: string = (<PropertyAccessExpression>expression).property.name;
let member: Element | null = null;
if (target.kind == ElementKind.ENUM)
member = <EnumValue | null>(<Enum>target).members.get(propertyName);
else if (target.kind == ElementKind.CLASS_PROTOTYPE)
member = <Element | null>(<ClassPrototype>target).members.get(propertyName);
else if (target.kind == ElementKind.CLASS)
member = <Element | null>(<Class>target).members.get(propertyName);
else if (target.kind == ElementKind.NAMESPACE)
member = <Element | null>(<Namespace>target).members.get(propertyName);
if (member)
return member;
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (<PropertyAccessExpression>expression).property.name, target.internalName);
return null;
return this.resolvePropertyAccess(<PropertyAccessExpression>expression, contextualFunction);
}
throw new Error("not implemented: " + expression.kind);
@ -1130,13 +1183,14 @@ export class Function extends Element {
return local;
}
private tempI32s: Local[] = [];
private tempI64s: Local[] = [];
private tempF32s: Local[] = [];
private tempF64s: Local[] = [];
private tempI32s: Local[] | null = null;
private tempI64s: Local[] | null = null;
private tempF32s: Local[] | null = null;
private tempF64s: Local[] | null = null;
/** Gets a free temporary local of the specified type. */
getTempLocal(type: Type): Local {
let temps: Local[];
let temps: Local[] | null;
switch (typeToNativeType(type)) {
case NativeType.I32: temps = this.tempI32s; break;
case NativeType.I64: temps = this.tempI64s; break;
@ -1144,30 +1198,32 @@ export class Function extends Element {
case NativeType.F64: temps = this.tempF64s; break;
default: throw new Error("unexpected type");
}
if (temps.length > 0)
if (temps && temps.length > 0)
return temps.pop();
return this.addLocal(type);
}
/** Frees the temporary local for reuse. */
freeTempLocal(local: Local): void {
let temps: Local[];
switch (typeToNativeType(local.type)) {
case NativeType.I32: temps = this.tempI32s; break;
case NativeType.I64: temps = this.tempI64s; break;
case NativeType.F32: temps = this.tempF32s; break;
case NativeType.F64: temps = this.tempF64s; break;
case NativeType.I32: temps = this.tempI32s || (this.tempI32s = []); break;
case NativeType.I64: temps = this.tempI64s || (this.tempI64s = []); break;
case NativeType.F32: temps = this.tempF32s || (this.tempF32s = []); break;
case NativeType.F64: temps = this.tempF64s || (this.tempF64s = []); break;
default: throw new Error("unexpected type");
}
temps.push(local);
}
/** Gets and immediately frees a temporary local of the specified type. */
getAndFreeTempLocal(type: Type): Local {
let temps: Local[];
switch (typeToNativeType(type)) {
case NativeType.I32: temps = this.tempI32s; break;
case NativeType.I64: temps = this.tempI64s; break;
case NativeType.F32: temps = this.tempF32s; break;
case NativeType.F64: temps = this.tempF64s; break;
case NativeType.I32: temps = this.tempI32s || (this.tempI32s = []); break;
case NativeType.I64: temps = this.tempI64s || (this.tempI64s = []); break;
case NativeType.F32: temps = this.tempF32s || (this.tempF32s = []); break;
case NativeType.F64: temps = this.tempF64s || (this.tempF64s = []); break;
default: throw new Error("unexpected type");
}
if (temps.length > 0)
@ -1250,17 +1306,25 @@ export class Field extends Element {
}
/** A yet unresolved class prototype. */
export class ClassPrototype extends Namespace {
export class ClassPrototype extends Element {
kind = ElementKind.CLASS_PROTOTYPE;
/** Declaration reference. */
declaration: ClassDeclaration | null;
/** Resolved instances. */
instances: Map<string,Class>;
instances: Map<string,Class> = new Map();
/** Static fields. */
staticFields: Map<string,Global> | null = null;
/** Static methods. */
staticMethods: Map<string,Function> | null = null;
/** Static getters. */
staticGetters: Map<string,Function> | null = null;
/** Static setters. */
staticSetters: Map<string,Function> | null = null;
constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) {
super(program, internalName, null);
super(program, internalName);
if (this.declaration = declaration) {
if (this.declaration.modifiers) {
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
@ -1275,7 +1339,6 @@ export class ClassPrototype extends Namespace {
if (this.declaration.typeParameters.length)
this.isGeneric = true;
}
this.instances = new Map();
}
resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Class {
@ -1305,7 +1368,7 @@ export class ClassPrototype extends Namespace {
}
/** A resolved class. */
export class Class extends Namespace {
export class Class extends Element {
kind = ElementKind.CLASS;
@ -1317,19 +1380,27 @@ export class Class extends Namespace {
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;
/** Constructs a new class. */
constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] = [], base: Class | null = null) {
super(prototype.program, internalName, prototype.declaration);
super(prototype.program, internalName);
this.prototype = prototype;
this.flags = prototype.flags;
this.typeArguments = typeArguments;
this.base = base;
this.type = (prototype.program.target == Target.WASM64 ? Type.usize64 : Type.usize32).asClass(this);
this.base = base;
// inherit base class contextual type arguments
// inherit contextual type arguments from base class
if (base)
for (let [name, type] of base.contextualTypeArguments)
this.contextualTypeArguments.set(name, type);
@ -1350,13 +1421,13 @@ export class Class extends Namespace {
}
}
/** A yet unresvoled interface. */
/** A yet unresolved interface. */
export class InterfacePrototype extends ClassPrototype {
kind = ElementKind.INTERFACE_PROTOTYPE;
/** Declaration reference. */
declaration: InterfaceDeclaration | null;
declaration: InterfaceDeclaration | null; // more specific
/** Constructs a new interface prototype. */
constructor(program: Program, internalName: string, declaration: InterfaceDeclaration | null = null) {
@ -1370,9 +1441,9 @@ export class Interface extends Class {
kind = ElementKind.INTERFACE;
/** Prototype reference. */
prototype: InterfacePrototype;
prototype: InterfacePrototype; // more specific
/** Base interface, if applcable. */
base: Interface | null;
base: Interface | null; // more specific
/** Constructs a new interface. */
constructor(prototype: InterfacePrototype, internalName: string, typeArguments: Type[] = [], base: Interface | null = null) {