Getters & setters (static); Instantiate compiler tests; Cleanup

This commit is contained in:
dcodeIO
2017-12-27 02:37:53 +01:00
parent 5c4bf1af76
commit ba61a5e414
49 changed files with 2359 additions and 1952 deletions

View File

@ -7,7 +7,11 @@ import {
} from "./compiler";
import {
PATH_DELIMITER
PATH_DELIMITER,
GETTER_PREFIX,
SETTER_PREFIX,
STATIC_DELIMITER,
INSTANCE_DELIMITER
} from "./constants";
import {
@ -60,7 +64,8 @@ import {
VariableDeclaration,
VariableStatement,
hasModifier
hasModifier,
mangleInternalName
} from "./ast";
import {
@ -88,7 +93,7 @@ export class Program extends DiagnosticEmitter {
sources: Source[];
/** Diagnostic offset used where sequentially obtaining the next diagnostic. */
diagnosticsOffset: i32 = 0;
/** WASM target. */
/** WebAssembly target. */
target: Target = Target.WASM32; // set on initialization
/** Elements by internal name. */
elements: Map<string,Element> = new Map();
@ -102,7 +107,7 @@ export class Program extends DiagnosticEmitter {
/** Constructs a new program, optionally inheriting parser diagnostics. */
constructor(diagnostics: DiagnosticMessage[] | null = null) {
super(diagnostics);
this.sources = new Array();
this.sources = [];
}
/** Initializes the program and its elements prior to compilation. */
@ -130,15 +135,15 @@ export class Program extends DiagnosticEmitter {
initializeBuiltins(this);
const queuedExports: Map<string,QueuedExport> = new Map();
const queuedImports: QueuedImport[] = new Array();
const queuedExports = new Map<string,QueuedExport>();
const queuedImports = new Array<QueuedImport>();
// build initial lookup maps of internal names to declarations
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
const source: Source = this.sources[i];
const statements: Statement[] = source.statements;
for (let j: i32 = 0, l: i32 = statements.length; j < l; ++j) {
const statement: Statement = statements[j];
for (let i = 0, k = this.sources.length; i < k; ++i) {
const source = this.sources[i];
const statements = source.statements;
for (let j = 0, l = statements.length; j < l; ++j) {
const statement = statements[j];
switch (statement.kind) {
case NodeKind.CLASS:
@ -183,15 +188,15 @@ export class Program extends DiagnosticEmitter {
let element: Element | null;
// queued imports should be resolvable now through traversing exports and queued exports
for (let i: i32 = 0; i < queuedImports.length;) {
const queuedImport: QueuedImport = queuedImports[i];
for (let j = 0; j < queuedImports.length;) {
const queuedImport = queuedImports[j];
element = this.tryResolveImport(queuedImport.referencedName, queuedExports);
if (element) {
this.elements.set(queuedImport.internalName, element);
queuedImports.splice(i, 1);
queuedImports.splice(j, 1);
} else {
this.error(DiagnosticCode.Module_0_has_no_exported_member_1, queuedImport.declaration.range, (<ImportStatement>queuedImport.declaration.parent).path.value, queuedImport.declaration.externalIdentifier.name);
++i;
++j;
}
}
@ -200,16 +205,16 @@ export class Program extends DiagnosticEmitter {
let currentExport: QueuedExport | null = queuedExport;
do {
if (currentExport.isReExport) {
element = <Element | null>this.exports.get(currentExport.referencedName);
element = this.exports.get(currentExport.referencedName);
if (element) {
this.exports.set(exportName, element);
break;
}
currentExport = <QueuedExport | null>queuedExports.get(currentExport.referencedName);
currentExport = queuedExports.get(currentExport.referencedName);
if (!currentExport)
this.error(DiagnosticCode.Module_0_has_no_exported_member_1, queuedExport.member.externalIdentifier.range, (<StringLiteralExpression>(<ExportStatement>queuedExport.member.parent).path).value, queuedExport.member.externalIdentifier.name);
} else {
element = <Element | null>this.elements.get(currentExport.referencedName);
element = this.elements.get(currentExport.referencedName);
if (element)
this.exports.set(exportName, element);
else
@ -224,29 +229,30 @@ export class Program extends DiagnosticEmitter {
private tryResolveImport(referencedName: string, queuedExports: Map<string,QueuedExport>): Element | null {
let element: Element | null;
do {
element = <Element | null>this.exports.get(referencedName);
element = this.exports.get(referencedName);
if (element)
return element;
const queuedExport: QueuedExport | null = <QueuedExport | null>queuedExports.get(referencedName);
const queuedExport = queuedExports.get(referencedName);
if (!queuedExport)
return null;
if (queuedExport.isReExport) {
referencedName = queuedExport.referencedName;
continue;
}
return <Element | null>this.elements.get(queuedExport.referencedName);
return this.elements.get(queuedExport.referencedName);
} while (true);
}
private initializeClass(declaration: ClassDeclaration, namespace: Element | null = null): void {
const internalName: string = declaration.internalName;
const internalName = declaration.internalName;
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
return;
}
const prototype: ClassPrototype = new ClassPrototype(this, declaration.name.name, internalName, declaration);
const prototype = new ClassPrototype(this, declaration.name.name, internalName, declaration);
this.elements.set(internalName, prototype);
// add program-level alias if annotated as @global
if (hasDecorator("global", declaration.decorators)) {
if (this.elements.has(declaration.name.name))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
@ -254,6 +260,7 @@ export class Program extends DiagnosticEmitter {
this.elements.set(declaration.name.name, prototype);
}
// add as namespace member if applicable
if (namespace) {
if (namespace.members) {
if (namespace.members.has(declaration.name.name)) {
@ -263,6 +270,8 @@ export class Program extends DiagnosticEmitter {
} else
namespace.members = new Map();
namespace.members.set(declaration.name.name, prototype);
// otherwise add to file-level exports if exported
} else if (prototype.isExported) {
if (this.exports.has(internalName)) {
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.name.range, internalName);
@ -271,27 +280,28 @@ export class Program extends DiagnosticEmitter {
this.exports.set(internalName, prototype);
}
const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
switch (memberDeclarations[j].kind) {
// initialize members
const memberDeclarations = declaration.members;
for (let i = 0, k = memberDeclarations.length; i < k; ++i) {
switch (memberDeclarations[i].kind) {
case NodeKind.FIELD:
this.initializeField(<FieldDeclaration>memberDeclarations[j], prototype);
this.initializeField(<FieldDeclaration>memberDeclarations[i], prototype);
break;
case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>memberDeclarations[j], prototype);
case NodeKind.METHOD: // also getter/setter
this.initializeMethod(<MethodDeclaration>memberDeclarations[i], prototype);
break;
default:
throw new Error("unexpected class member");
throw new Error("class member expected");
}
}
}
private initializeField(declaration: FieldDeclaration, classPrototype: ClassPrototype): void {
const name: string = declaration.name.name;
const internalName: string = declaration.internalName;
const name = declaration.name.name;
const internalName = declaration.internalName;
// static fields become global variables
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
@ -306,7 +316,7 @@ export class Program extends DiagnosticEmitter {
}
} else
classPrototype.members = new Map();
const staticField: Global = new Global(this, internalName, declaration, null);
const staticField = new Global(this, internalName, declaration, null);
classPrototype.members.set(name, staticField);
this.elements.set(internalName, staticField);
@ -325,11 +335,17 @@ export class Program extends DiagnosticEmitter {
}
private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void {
let name: string = declaration.name.name;
const internalName: string = declaration.internalName;
let isGetter = false;
if ((isGetter = hasModifier(ModifierKind.GET, declaration.modifiers)) || hasModifier(ModifierKind.SET, declaration.modifiers)) {
this.initializeAccessor(declaration, classPrototype, isGetter);
return;
}
const name = declaration.name.name;
const internalName = declaration.internalName;
// static methods become global functions
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
return;
@ -359,13 +375,64 @@ export class Program extends DiagnosticEmitter {
}
}
private initializeAccessor(declaration: MethodDeclaration, classPrototype: ClassPrototype, isGetter: bool): void {
const propertyName = declaration.name.name;
const internalPropertyName = declaration.internalName;
let propertyElement = this.elements.get(internalPropertyName);
if (propertyElement) {
if (propertyElement.kind != ElementKind.PROPERTY || (isGetter ? (<Property>propertyElement).getterPrototype : (<Property>propertyElement).setterPrototype)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalPropertyName);
return;
}
} else
propertyElement = new Property(this, propertyName, internalPropertyName, classPrototype);
let name = (isGetter ? GETTER_PREFIX : SETTER_PREFIX) + propertyName;
// static accessors become global functions
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
const internalStaticName = classPrototype.internalName + STATIC_DELIMITER + name;
if (this.elements.has(internalStaticName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalStaticName);
return;
}
const staticPrototype = new FunctionPrototype(this, name, internalStaticName, declaration, null);
if (isGetter)
(<Property>propertyElement).getterPrototype = staticPrototype;
else
(<Property>propertyElement).setterPrototype = staticPrototype;
if (!classPrototype.members)
classPrototype.members = new Map();
classPrototype.members.set(propertyName, propertyElement); // checked above
this.elements.set(internalPropertyName, propertyElement);
// instance accessors are remembered until resolved
} else {
const internalInstanceName = classPrototype.internalName + INSTANCE_DELIMITER + name;
if (classPrototype.instanceMembers) {
if (classPrototype.instanceMembers.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
return;
}
} else
classPrototype.instanceMembers = new Map();
const instancePrototype = new FunctionPrototype(this, name, internalInstanceName, declaration, classPrototype);
if (isGetter)
(<Property>propertyElement).getterPrototype = instancePrototype;
else
(<Property>propertyElement).setterPrototype = instancePrototype;
classPrototype.instanceMembers.set(name, propertyElement);
}
}
private initializeEnum(declaration: EnumDeclaration, namespace: Element | null = null): void {
const internalName: string = declaration.internalName;
const internalName = declaration.internalName;
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
return;
}
const enm: Enum = new Enum(this, internalName, declaration);
const enm = new Enum(this, internalName, declaration);
this.elements.set(internalName, enm);
if (namespace) {
@ -385,14 +452,14 @@ export class Program extends DiagnosticEmitter {
this.exports.set(internalName, enm);
}
const values: EnumValueDeclaration[] = declaration.values;
const values = declaration.values;
for (let i: i32 = 0, k: i32 = values.length; i < k; ++i)
this.initializeEnumValue(values[i], enm);
}
private initializeEnumValue(declaration: EnumValueDeclaration, enm: Enum): void {
const name: string = declaration.name.name;
const internalName: string = declaration.internalName;
const name = declaration.name.name;
const internalName = declaration.internalName;
if (enm.members) {
if (enm.members.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
@ -400,13 +467,13 @@ export class Program extends DiagnosticEmitter {
}
} else
enm.members = new Map();
const value: EnumValue = new EnumValue(enm, this, internalName, declaration);
const value = new EnumValue(enm, this, internalName, declaration);
enm.members.set(name, value);
}
private initializeExports(statement: ExportStatement, queuedExports: Map<string,QueuedExport>): void {
const members: ExportMember[] = statement.members;
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
const members = statement.members;
for (let i = 0, k = members.length; i < k; ++i)
this.initializeExport(members[i], statement.internalPath, queuedExports);
}
@ -433,7 +500,7 @@ export class Program extends DiagnosticEmitter {
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, externalName);
return;
}
const queuedExport: QueuedExport = new QueuedExport();
const queuedExport = new QueuedExport();
queuedExport.isReExport = false;
queuedExport.referencedName = referencedName; // -> internal name
queuedExport.member = member;
@ -450,9 +517,9 @@ export class Program extends DiagnosticEmitter {
}
// walk already known queued exports
const seen: Set<QueuedExport> = new Set();
while (queuedExports.has(referencedName)) {
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(referencedName);
const seen = new Set<QueuedExport>();
let queuedExport: QueuedExport | null;
while (queuedExport = queuedExports.get(referencedName)) {
if (queuedExport.isReExport) {
if (this.exports.has(queuedExport.referencedName)) {
this.exports.set(externalName, <Element>this.exports.get(referencedName));
@ -476,7 +543,7 @@ export class Program extends DiagnosticEmitter {
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, externalName);
return;
}
const queuedReExport: QueuedExport = new QueuedExport();
const queuedReExport = new QueuedExport();
queuedReExport.isReExport = true;
queuedReExport.referencedName = referencedName; // -> export name
queuedReExport.member = member;
@ -485,12 +552,12 @@ export class Program extends DiagnosticEmitter {
}
private initializeFunction(declaration: FunctionDeclaration, namespace: Element | null = null): void {
const internalName: string = declaration.internalName;
const internalName = declaration.internalName;
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
return;
}
const prototype: FunctionPrototype = new FunctionPrototype(this, declaration.name.name, internalName, declaration, null);
const prototype = new FunctionPrototype(this, declaration.name.name, internalName, declaration, null);
this.elements.set(internalName, prototype);
if (hasDecorator("global", declaration.decorators)) {
@ -519,14 +586,12 @@ export class Program extends DiagnosticEmitter {
}
private initializeImports(statement: ImportStatement, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
const declarations: ImportDeclaration[] | null = statement.declarations;
const declarations = statement.declarations;
if (declarations) {
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) {
const declaration: ImportDeclaration = declarations[i];
this.initializeImport(declaration, statement.internalPath, queuedExports, queuedImports);
}
for (let i = 0, k = declarations.length; i < k; ++i)
this.initializeImport(declarations[i], statement.internalPath, queuedExports, queuedImports);
} else if (statement.namespaceName) {
const internalName: string = statement.range.source.internalPath + "/" + statement.namespaceName.name;
const internalName = statement.range.source.internalPath + "/" + statement.namespaceName.name;
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, statement.namespaceName.range, internalName);
return;
@ -537,13 +602,13 @@ export class Program extends DiagnosticEmitter {
}
private initializeImport(declaration: ImportDeclaration, internalPath: string, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
const internalName: string = declaration.internalName;
const internalName = declaration.internalName;
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
return;
}
let referencedName: string = internalPath + PATH_DELIMITER + declaration.externalIdentifier.name;
let referencedName = internalPath + PATH_DELIMITER + declaration.externalIdentifier.name;
// resolve right away if the export exists
if (this.exports.has(referencedName)) {
@ -552,9 +617,9 @@ export class Program extends DiagnosticEmitter {
}
// walk already known queued exports
const seen: Set<QueuedExport> = new Set();
while (queuedExports.has(referencedName)) {
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(referencedName);
const seen = new Set<QueuedExport>();
let queuedExport: QueuedExport | null;
while (queuedExport = queuedExports.get(referencedName)) {
if (queuedExport.isReExport) {
if (this.exports.has(queuedExport.referencedName)) {
this.elements.set(internalName, <Element>this.exports.get(referencedName));
@ -574,7 +639,7 @@ export class Program extends DiagnosticEmitter {
}
// otherwise queue it
const queuedImport: QueuedImport = new QueuedImport();
const queuedImport = new QueuedImport();
queuedImport.internalName = internalName;
queuedImport.referencedName = referencedName;
queuedImport.declaration = declaration;
@ -582,8 +647,8 @@ export class Program extends DiagnosticEmitter {
}
private initializeInterface(declaration: InterfaceDeclaration, namespace: Element | null = null): void {
const internalName: string = declaration.internalName;
const prototype: InterfacePrototype = new InterfacePrototype(this, declaration.name.name, internalName, declaration);
const internalName = declaration.internalName;
const prototype = new InterfacePrototype(this, declaration.name.name, internalName, declaration);
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
@ -608,8 +673,8 @@ export class Program extends DiagnosticEmitter {
this.exports.set(internalName, prototype);
}
const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let i: i32 = 0, k: i32 = memberDeclarations.length; i < k; ++i) {
const memberDeclarations = declaration.members;
for (let i = 0, k = memberDeclarations.length; i < k; ++i) {
switch (memberDeclarations[i].kind) {
case NodeKind.FIELD:
@ -621,15 +686,15 @@ export class Program extends DiagnosticEmitter {
break;
default:
throw new Error("unexpected interface member");
throw new Error("interface member expected");
}
}
}
private initializeNamespace(declaration: NamespaceDeclaration, parentNamespace: Element | null = null): void {
const internalName: string = declaration.internalName;
const internalName = declaration.internalName;
let namespace: Element | null = this.elements.get(internalName);
let namespace = this.elements.get(internalName);
if (!namespace) {
namespace = new Namespace(this, internalName, declaration);
this.elements.set(internalName, namespace);
@ -652,8 +717,8 @@ export class Program extends DiagnosticEmitter {
this.exports.set(internalName, namespace);
}
const members: Statement[] = declaration.members;
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
const members = declaration.members;
for (let i = 0, k = members.length; i < k; ++i) {
switch (members[i].kind) {
case NodeKind.CLASS:
@ -692,7 +757,7 @@ export class Program extends DiagnosticEmitter {
private initializeType(declaration: TypeDeclaration, namespace: Element | null = null): void {
// type aliases are program globals
const name: string = declaration.name.name;
const name = declaration.name.name;
if (this.types.has(name) || this.typeAliases.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, name);
return;
@ -701,16 +766,16 @@ export class Program extends DiagnosticEmitter {
}
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 declarations = statement.declarations;
for (let i = 0, k = declarations.length; i < k; ++i) {
const declaration = declarations[i];
const internalName = declaration.internalName;
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
continue;
}
const global: Global = new Global(this, internalName, declaration, null);
const global = new Global(this, internalName, declaration, null);
this.elements.set(internalName, global);
if (hasDecorator("global", declaration.decorators)) {
@ -742,20 +807,20 @@ export class Program extends DiagnosticEmitter {
resolveType(node: TypeNode, contextualTypeArguments: Map<string,Type> | null = null, reportNotFound: bool = true): Type | null {
// resolve parameters
const k: i32 = node.typeArguments.length;
const paramTypes: Type[] = new Array(k);
for (let i: i32 = 0; i < k; ++i) {
const paramType: Type | null = this.resolveType(node.typeArguments[i], contextualTypeArguments, reportNotFound);
const k = node.typeArguments.length;
const paramTypes = new Array<Type>(k);
for (let i = 0; i < k; ++i) {
const paramType = this.resolveType(node.typeArguments[i], contextualTypeArguments, reportNotFound);
if (!paramType)
return null;
paramTypes[i] = <Type>paramType;
paramTypes[i] = paramType;
}
let globalName: string = node.identifier.name;
let globalName = node.identifier.name;
if (k) // can't be a placeholder if it has parameters
globalName += typesToString(paramTypes);
else if (contextualTypeArguments) {
const placeholderType: Type | null = <Type | null>contextualTypeArguments.get(globalName);
const placeholderType = contextualTypeArguments.get(globalName);
if (placeholderType)
return placeholderType;
}
@ -763,15 +828,15 @@ export class Program extends DiagnosticEmitter {
let type: Type | null;
// check file-global type
if (type = <Type | null>this.types.get(node.range.source.internalPath + PATH_DELIMITER + globalName))
if (type = this.types.get(node.range.source.internalPath + PATH_DELIMITER + globalName))
return type;
// check program-global type
if (type = <Type | null>this.types.get(globalName))
if (type = this.types.get(globalName))
return type;
// check type alias
let alias: TypeNode | null = <TypeNode | null>this.typeAliases.get(globalName);
let alias = this.typeAliases.get(globalName);
if (alias && (type = this.resolveType(alias, null, reportNotFound)))
return type;
@ -783,8 +848,8 @@ export class Program extends DiagnosticEmitter {
/** 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;
const parameterCount = typeParameters.length;
const argumentCount = typeArgumentNodes ? typeArgumentNodes.length : 0;
if (parameterCount != argumentCount) {
if (argumentCount)
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, Range.join((<TypeNode[]>typeArgumentNodes)[0].range, (<TypeNode[]>typeArgumentNodes)[argumentCount - 1].range), parameterCount.toString(10), argumentCount.toString(10));
@ -792,9 +857,9 @@ export class Program extends DiagnosticEmitter {
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, alternativeReportNode.range.atEnd, parameterCount.toString(10), "0");
return null;
}
const typeArguments: Type[] = new Array(parameterCount);
for (let i: i32 = 0; i < parameterCount; ++i) {
const type: Type | null = this.resolveType((<TypeNode[]>typeArgumentNodes)[i], contextualTypeArguments, true); // reports
const typeArguments = new Array<Type>(parameterCount);
for (let i = 0; i < parameterCount; ++i) {
const type = this.resolveType((<TypeNode[]>typeArgumentNodes)[i], contextualTypeArguments, true); // reports
if (!type)
return null;
// TODO: check extendsType
@ -805,8 +870,8 @@ export class Program extends DiagnosticEmitter {
/** 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);
const name = identifier.name;
const local = contextualFunction.locals.get(name);
if (local)
return local;
let element: Element | null;
@ -820,7 +885,7 @@ export class Program extends DiagnosticEmitter {
/** Resolves a property access the element it refers to. */
resolvePropertyAccess(propertyAccess: PropertyAccessExpression, contextualFunction: Function): Element | null {
const expression: Expression = propertyAccess.expression;
const expression = propertyAccess.expression;
let target: Element | null = null;
if (expression.kind == NodeKind.IDENTIFIER) {
target = this.resolveIdentifier(<IdentifierExpression>expression, contextualFunction);
@ -830,9 +895,9 @@ export class Program extends DiagnosticEmitter {
throw new Error("unexpected target kind");
if (!target)
return null;
const propertyName: string = propertyAccess.property.name;
const propertyName = propertyAccess.property.name;
if (target.members) {
const member: Element | null = target.members.get(propertyName);
const member = target.members.get(propertyName);
if (member)
return member;
}
@ -869,11 +934,10 @@ export class Program extends DiagnosticEmitter {
function hasDecorator(name: string, decorators: Decorator[] | null): bool {
if (decorators)
for (let i: i32 = 0, k: i32 = decorators.length; i < k; ++i) {
const decorator: Decorator = decorators[i];
const expression: Expression = decorator.name;
const args: Expression[] = decorator.arguments;
if (expression.kind == NodeKind.IDENTIFIER && args.length <= 1 && (<IdentifierExpression>expression).name == name)
for (let i = 0, k = decorators.length; i < k; ++i) {
const decorator = decorators[i];
const expression = decorator.name;
if (expression.kind == NodeKind.IDENTIFIER && decorator.arguments.length <= 1 && (<IdentifierExpression>expression).name == name)
return true;
}
return false;
@ -905,9 +969,7 @@ export enum ElementKind {
FIELD_PROTOTYPE,
/** A {@link Field}. */
FIELD,
/** A {@link PropertyPrototype}. */
PROPERTY_PROTOTYPE,
/** A {@link Property}. */
/** A {@link PropertyContainer}. */
PROPERTY,
/** A {@link Namespace}. */
NAMESPACE
@ -1028,7 +1090,7 @@ export class Namespace extends Element {
constructor(program: Program, internalName: string, declaration: NamespaceDeclaration | null = null) {
super(program, internalName);
if ((this.declaration = declaration) && this.declaration.modifiers) {
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
for (let i = 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;
@ -1052,7 +1114,7 @@ export class Enum extends Element {
constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) {
super(program, internalName);
if ((this.declaration = declaration) && this.declaration.modifiers) {
for (let i: i32 = 0, k = this.declaration.modifiers.length; i < k; ++i) {
for (let i = 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;
@ -1102,7 +1164,7 @@ export class Global extends Element {
super(program, internalName);
if (this.declaration = declaration) {
if (this.declaration.modifiers) {
for (let i: i32 = 0, k = this.declaration.modifiers.length; i < k; ++i) {
for (let i = 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;
@ -1191,7 +1253,7 @@ export class FunctionPrototype extends Element {
this.simpleName = simpleName;
if (this.declaration = declaration) {
if (this.declaration.modifiers)
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
for (let i = 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;
@ -1222,18 +1284,21 @@ export class FunctionPrototype extends Element {
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 {
const instanceKey: string = typesToString(typeArguments, "", "");
let instance: Function | null = <Function | null>this.instances.get(instanceKey);
// Whether a getter/setter function or not.
get isAccessor(): bool { return (this.flags & (ElementFlags.GETTER | ElementFlags.SETTER)) != 0; }
resolve(typeArguments: Type[] | null = null, contextualTypeArguments: Map<string,Type> | null = null): Function | null {
const instanceKey = typeArguments ? typesToString(typeArguments, "", "") : "";
let instance = this.instances.get(instanceKey);
if (instance)
return instance;
const declaration: FunctionDeclaration | null = this.declaration;
const declaration = this.declaration;
if (!declaration)
throw new Error("unexpected instantiation of internal function");
throw new Error("declaration expected"); // cannot resolve built-ins
// override call specific contextual type arguments
let i: i32, k: i32 = typeArguments.length;
if (k) {
let i: i32, k: i32;
if (typeArguments && (k = typeArguments.length)) {
const inheritedTypeArguments: Map<string,Type> | null = contextualTypeArguments;
contextualTypeArguments = new Map();
if (inheritedTypeArguments)
@ -1245,15 +1310,15 @@ export class FunctionPrototype extends Element {
// resolve parameters
k = declaration.parameters.length;
const parameters: Parameter[] = new Array(k);
const parameterTypes: Type[] = new Array(k);
let typeNode: TypeNode | null ;
const parameters = new Array<Parameter>(k);
const parameterTypes = new Array<Type>(k);
let typeNode: TypeNode | null;
for (i = 0; i < k; ++i) {
if (typeNode = declaration.parameters[i].type) {
const type: Type | null = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
const type = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
if (type) {
parameters[i] = new Parameter(declaration.parameters[i].name.name, type);
parameterTypes[i] = <Type>type;
parameterTypes[i] = type;
} else
return null;
} else
@ -1262,16 +1327,20 @@ export class FunctionPrototype extends Element {
// resolve return type
let returnType: Type;
if (typeNode = declaration.returnType) {
const type: Type | null = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
if (type)
returnType = <Type>type;
else
if (this.isSetter) {
returnType = Type.void; // not annotated
} else {
if (typeNode = declaration.returnType) {
const type = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
if (type)
returnType = type;
else
return null;
} else
return null;
} else
return null;
}
let internalName: string = this.internalName;
let internalName = this.internalName;
if (instanceKey.length)
internalName += "<" + instanceKey + ">";
instance = new Function(this, internalName, typeArguments, parameters, returnType, null); // TODO: class
@ -1284,7 +1353,7 @@ export class FunctionPrototype extends Element {
if (this.isGeneric) {
assert(typeArgumentNodes != null && typeArgumentNodes.length != 0);
if (!this.declaration)
throw new Error("missing declaration");
throw new Error("declaration expected");
resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode);
if (!resolvedTypeArguments)
return null;
@ -1306,7 +1375,7 @@ export class Function extends Element {
/** Prototype reference. */
prototype: FunctionPrototype;
/** Concrete type arguments. */
typeArguments: Type[];
typeArguments: Type[] | null;
/** Concrete function parameters. Excluding `this` if an instance method. */
parameters: Parameter[];
/** Concrete return type. */
@ -1326,7 +1395,7 @@ export class Function extends Element {
private breakStack: i32[] | null = null;
/** 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[] | null, parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) {
super(prototype.program, internalName);
this.prototype = prototype;
this.typeArguments = typeArguments;
@ -1334,7 +1403,7 @@ export class Function extends Element {
this.returnType = returnType;
this.instanceMethodOf = instanceMethodOf;
this.flags = prototype.flags;
let localIndex: i32 = 0;
let localIndex = 0;
if (instanceMethodOf) {
assert(this.isInstance);
this.locals.set("this", new Local(prototype.program, "this", localIndex++, instanceMethodOf.type));
@ -1345,8 +1414,8 @@ export class Function extends Element {
}
} else
assert(!this.isInstance);
for (let i: i32 = 0, k: i32 = parameters.length; i < k; ++i) {
const parameter: Parameter = parameters[i];
for (let i = 0, k = parameters.length; i < k; ++i) {
const parameter = parameters[i];
this.locals.set(parameter.name, new Local(prototype.program, parameter.name, localIndex++, parameter.type));
}
}
@ -1356,11 +1425,11 @@ export class Function extends Element {
// if it has a name, check previously as this method will throw otherwise
let localIndex = this.parameters.length + this.additionalLocals.length;
if (this.isInstance) localIndex++; // plus 'this'
const local: Local = new Local(this.prototype.program, name ? name : "anonymous$" + localIndex.toString(10), localIndex, type);
const local = new Local(this.prototype.program, name ? name : "anonymous$" + localIndex.toString(10), localIndex, type);
if (name) {
if (this.locals.has(<string>name))
if (this.locals.has(name))
throw new Error("unexpected duplicate local name");
this.locals.set(<string>name, local);
this.locals.set(name, local);
}
this.additionalLocals.push(type);
return local;
@ -1418,7 +1487,7 @@ export class Function extends Element {
/** Enters a(nother) break context. */
enterBreakContext(): string {
const id: i32 = this.nextBreakId++;
const id = this.nextBreakId++;
if (!this.breakStack)
this.breakStack = [ id ];
else
@ -1429,7 +1498,7 @@ export class Function extends Element {
/** Leaves the current break context. */
leaveBreakContext(): void {
assert(this.breakStack != null);
const length: i32 = (<i32[]>this.breakStack).length;
const length = (<i32[]>this.breakStack).length;
assert(length > 0);
(<i32[]>this.breakStack).pop();
if (length > 1) {
@ -1470,7 +1539,7 @@ export class FieldPrototype extends Element {
super(classPrototype.program, internalName);
this.classPrototype = classPrototype;
if ((this.declaration = declaration) && this.declaration.modifiers) {
for (let i: i32 = 0, k = this.declaration.modifiers.length; i < k; ++i) {
for (let i = 0, k = this.declaration.modifiers.length; i < k; ++i) {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.READONLY: this.isReadonly = true; break;
@ -1511,73 +1580,25 @@ export class Field extends Element {
}
}
/** A yet unresolved property. */
export class PropertyPrototype extends Element {
kind = ElementKind.PROPERTY_PROTOTYPE;
/** Simple name. */
simpleName: string;
/** Parent class prototype. */
classPrototype: ClassPrototype;
/** Getter declaration reference. */
getterDeclaration: FunctionDeclaration | null;
/** Setter declaration reference. */
setterDeclaration: FunctionDeclaration | null;
/** Constructs a new propery prototype. */
constructor(classPrototype: ClassPrototype, simpleName: string, internalName: string, getterDeclaration: FunctionDeclaration | null = null, setterDeclaration: FunctionDeclaration | null = null) {
super(classPrototype.program, internalName);
this.simpleName = simpleName;
this.classPrototype = classPrototype;
let i: i32, k: i32;
if ((this.getterDeclaration = getterDeclaration) && this.getterDeclaration.modifiers) {
assert(this.getterDeclaration.typeParameters.length == 0);
assert(this.getterDeclaration.parameters.length == 0);
for (i = 0, k = this.getterDeclaration.modifiers.length; i < k; ++i) {
switch (this.getterDeclaration.modifiers[i].modifierKind) {
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.GET:
case ModifierKind.STATIC: break; // already handled
default: assert(false);
}
}
}
if ((this.setterDeclaration = setterDeclaration) && this.setterDeclaration.modifiers) {
assert(this.setterDeclaration.typeParameters.length == 0);
assert(this.setterDeclaration.parameters.length == 1);
for (i = 0, k = this.setterDeclaration.modifiers.length; i < k; ++i) {
switch (this.setterDeclaration.modifiers[i].modifierKind) {
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.SET:
case ModifierKind.STATIC: break; // already handled
default: assert(false);
}
}
}
}
}
/** A resolved property. */
/** A property comprised of a getter and a setter function. */
export class Property extends Element {
kind = ElementKind.PROPERTY;
/** Prototype reference. */
prototype: PropertyPrototype;
/** Property type. */
type: Type;
/** Getter function. */
getter: Function | null = null;
/** Setter function. */
setter: Function | null = null;
/** Simple name. */
simpleName: string;
/** Parent class prototype. */
parent: ClassPrototype;
/** Getter prototype. */
getterPrototype: FunctionPrototype | null = null;
/** Setter prototype. */
setterPrototype: FunctionPrototype | null = null;
/** Constructs a new property. */
constructor(prototype: PropertyPrototype, internalName: string, type: Type) {
super(prototype.program, internalName);
this.flags = prototype.flags;
this.type = type;
/** Constructs a new property prototype. */
constructor(program: Program, simpleName: string, internalName: string, parent: ClassPrototype) {
super(program, internalName);
this.simpleName = simpleName;
this.parent = parent;
}
}
@ -1600,7 +1621,7 @@ export class ClassPrototype extends Element {
this.simpleName = simpleName;
if (this.declaration = declaration) {
if (this.declaration.modifiers) {
for (let i: i32 = 0, k: i32 = this.declaration.modifiers.length; i < k; ++i) {
for (let i = 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;
@ -1615,16 +1636,16 @@ export class ClassPrototype extends Element {
}
resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Class {
const instanceKey: string = typesToString(typeArguments, "", "");
let instance: Class | null = <Class | null>this.instances.get(instanceKey);
const instanceKey = typesToString(typeArguments, "", "");
let instance = this.instances.get(instanceKey);
if (instance)
return instance;
const declaration: ClassDeclaration | null = this.declaration;
const declaration = this.declaration;
if (!declaration)
throw new Error("unexpected instantiation of internal class");
throw new Error("declaration expected"); // cannot resolve built-ins
// override call specific contextual type arguments
let i: i32, k: i32 = typeArguments.length;
let i: i32, k = typeArguments.length;
if (k) {
const inheritedTypeArguments: Map<string,Type> | null = contextualTypeArguments;
contextualTypeArguments = new Map();
@ -1645,7 +1666,7 @@ export class ClassPrototype extends Element {
}
}
let internalName: string = this.internalName;
let internalName = this.internalName;
if (instanceKey.length)
internalName += "<" + instanceKey + ">";
instance = new Class(this, internalName, typeArguments, null); // TODO: base class
@ -1659,7 +1680,7 @@ export class ClassPrototype extends Element {
if (this.isGeneric) {
assert(typeArgumentNodes != null && typeArgumentNodes.length != 0);
if (!this.declaration)
throw new Error("missing declaration"); // generic built-in
throw new Error("declaration expected"); // generic built-in
resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode);
if (!resolvedTypeArguments)
return null;
@ -1711,10 +1732,11 @@ export class Class extends Element {
const typeParameters: TypeParameter[] = declaration.typeParameters;
if (typeParameters.length != typeArguments.length)
throw new Error("unexpected type argument count mismatch");
const k: i32 = typeArguments.length;
const k = typeArguments.length;
if (k) {
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
for (let i: i32 = 0; i < k; ++i)
if (!this.contextualTypeArguments)
this.contextualTypeArguments = new Map();
for (let i = 0; i < k; ++i)
this.contextualTypeArguments.set(typeParameters[i].identifier.name, typeArguments[i]);
}
}