1
0
mirror of https://github.com/fluencelabs/assemblyscript synced 2025-05-05 11:52:27 +00:00

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

@ -5,7 +5,7 @@ AssemblyScript NEXT
**AssemblyScript** is a new compiler targeting [WebAssembly](http://webassembly.org) while utilizing [TypeScript](http://www.typescriptlang.org)'s syntax and [node](https://nodejs.org)'s vibrant ecosystem. Instead of requiring complex toolchains to set up, you can simply `npm install` it - or run it in a browser.
By compiling syntactially but not necessarily semantically valid TypeScript to [Binaryen](https://github.com/WebAssembly/binaryen) IR, the resulting module can be validated, optimized, emitted in WebAssembly text or binary format and converted to [asm.js](http://asmjs.org) as a polyfill.
By compiling syntactially (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`.

@ -130,7 +130,8 @@ args._.forEach(filename => {
var options = assemblyscript.createOptions();
assemblyscript.setTarget(options, 0);
assemblyscript.setNoTreeShaking(options, args.noTreeShaking);
assemblyscript.setNoDebug(options, args.noDebug);
assemblyscript.setNoAssert(options, args.noAssert);
// TODO: noDebug binaryen feature, removing names the debug section
var module = assemblyscript.compile(parser, options);
checkDiagnostics(parser);

@ -43,7 +43,7 @@
"desc": "Disables tree-shaking.",
"type": "boolean"
},
"noDebug": {
"noAssert": {
"desc": "Disables assertions.",
"type": "boolean"
},

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

@ -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),

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

@ -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 "";
}

@ -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
}

@ -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 {

@ -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. */

@ -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;

@ -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) {

24
std/portable.d.ts vendored

@ -87,30 +87,31 @@ declare class String {
static fromCodePoint(cp: i32): string;
static fromCodePoints(arr: i32[]): string;
readonly length: i32;
private constructor();
indexOf(subject: string): i32;
lastIndexOf(subject: string): i32;
charCodeAt(index: i32): i32;
substring(from: i32, to?: i32): string;
startsWith(subject: string): bool;
endsWith(subject: string): bool;
replace(search: string, replacement: string): string;
toString(): string;
}
declare class Boolean {}
interface Boolean {}
declare class Number {
toString(radix: i32): string;
private constructor();
toString(radix?: i32): string;
}
declare class Object {}
interface Object {}
declare class Function {
/** @deprecated */
apply(subject: any): any;
}
interface Function {}
declare class RegExp {}
interface RegExp {}
declare interface IArguments {}
interface IArguments {}
declare class Error {
constructor(message: string);
@ -119,7 +120,8 @@ declare class Error {
}
declare class Symbol {
static iterator: symbol;
private constructor();
static readonly iterator: symbol;
}
declare class Set<T> {
@ -139,7 +141,7 @@ declare class Map<K,V> {
[Symbol.iterator](): Iterator<[K, V]>;
}
declare interface Iterator<T> {}
interface Iterator<T> {}
declare namespace JSON {
/** @deprecated */

@ -1,5 +1,6 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $v (func))
(global $export/a i32 (i32.const 1))
(global $export/b i32 (i32.const 2))
(memory $0 1)
@ -7,6 +8,7 @@
(export "renamed_sub" (func $export/sub))
(export "a" (global $export/a))
(export "renamed_b" (global $export/b))
(export "two" (func $export/ns.two))
(export "memory" (memory $0))
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add
@ -20,4 +22,7 @@
(get_local $1)
)
)
(func $export/ns.two (; 2 ;) (type $v)
(nop)
)
)

@ -14,7 +14,7 @@ const b: i32 = 2;
export { b as renamed_b };
/* export namespace ns {
export namespace ns {
function one(): void {}
export function two(): void {}
} */
}

@ -1,5 +1,6 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $v (func))
(global $export/a i32 (i32.const 1))
(global $export/b i32 (i32.const 2))
(global $HEAP_START i32 (i32.const 4))
@ -8,6 +9,7 @@
(export "renamed_sub" (func $export/sub))
(export "a" (global $export/a))
(export "renamed_b" (global $export/b))
(export "two" (func $export/ns.two))
(export "memory" (memory $0))
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
@ -25,6 +27,8 @@
)
)
)
(func $export/ns.two (; 2 ;) (type $v)
)
)
(;
[program.elements]
@ -60,9 +64,13 @@
export/sub
export/a
export/b
export/ns
export/ns.one
export/ns.two
[program.exports]
export/add
export/renamed_sub
export/a
export/renamed_b
export/ns
;)

@ -41,5 +41,10 @@
)
)
)
(block
(block $__inlined_func$export/ns.two
(nop)
)
)
)
)

@ -16,7 +16,10 @@
(get_local $1)
)
)
(func $start (; 2 ;) (type $v)
(func $export/ns.two (; 2 ;) (type $v)
(nop)
)
(func $start (; 3 ;) (type $v)
(drop
(i32.add
(call $export/add
@ -29,5 +32,6 @@
)
)
)
(call $export/ns.two)
)
)

@ -1,3 +1,5 @@
import { add, renamed_sub as sub, a, renamed_b as b } from "./export";
import { add, renamed_sub as sub, a, renamed_b as b, ns as renamed_ns } from "./export";
add(a, b) + sub(b, a);
renamed_ns.two();

@ -23,7 +23,9 @@
)
)
)
(func $start (; 2 ;) (type $v)
(func $export/ns.two (; 2 ;) (type $v)
)
(func $start (; 3 ;) (type $v)
(drop
(i32.add
(call $export/add
@ -36,6 +38,7 @@
)
)
)
(call $export/ns.two)
)
)
(;
@ -72,13 +75,18 @@
export/sub
export/a
export/b
export/ns
export/ns.one
export/ns.two
import/add
import/sub
import/a
import/b
import/renamed_ns
[program.exports]
export/add
export/renamed_sub
export/a
export/renamed_b
export/ns
;)

@ -0,0 +1,11 @@
(module
(type $v (func))
(memory $0 1)
(export "test" (func $namespace/test))
(export "memory" (memory $0))
(func $namespace/test (; 0 ;) (type $v)
(block $__inlined_func$namespace/Outer.Inner.aFunc
(nop)
)
)
)

@ -0,0 +1,12 @@
(module
(type $v (func))
(memory $0 1)
(export "test" (func $namespace/test))
(export "memory" (memory $0))
(func $namespace/Outer.Inner.aFunc (; 0 ;) (type $v)
(nop)
)
(func $namespace/test (; 1 ;) (type $v)
(call $namespace/Outer.Inner.aFunc)
)
)

@ -0,0 +1,13 @@
namespace Outer {
export namespace Inner {
export let aVar: i32;
export function aFunc(): void {}
export enum anEnum { ONE = 1 }
}
}
export function test(): void {
Outer.Inner.aVar;
Outer.Inner.aFunc();
Outer.Inner.anEnum.ONE;
}

@ -0,0 +1,58 @@
(module
(type $v (func))
(global $namespace/Outer.Inner.aVar (mut i32) (i32.const 0))
(global $HEAP_START i32 (i32.const 4))
(memory $0 1)
(export "test" (func $namespace/test))
(export "memory" (memory $0))
(func $namespace/Outer.Inner.aFunc (; 0 ;) (type $v)
)
(func $namespace/test (; 1 ;) (type $v)
(drop
(get_global $namespace/Outer.Inner.aVar)
)
(call $namespace/Outer.Inner.aFunc)
(drop
(get_global $namespace/Outer.Inner.anEnum.ONE)
)
)
)
(;
[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
namespace/Outer
namespace/Outer.Inner
namespace/Outer.Inner.aVar
namespace/Outer.Inner.aFunc
namespace/Outer.Inner.anEnum
namespace/test
[program.exports]
namespace/test
;)

@ -1,7 +1,9 @@
export { add, renamed_sub, a as renamed_a, renamed_b as rerenamed_b } from "./export";
import { add as imported_add, renamed_sub as imported_sub } from "./export";
import { add as imported_add, renamed_sub as imported_sub, ns as imported_ns } from "./export";
export { imported_add as renamed_add, imported_sub as rerenamed_sub };
imported_add(1, 2) + imported_sub(3, 4);
export { ns as renamed_ns } from "./export";

@ -29,7 +29,9 @@
)
)
)
(func $start (; 2 ;) (type $v)
(func $export/ns.two (; 2 ;) (type $v)
)
(func $start (; 3 ;) (type $v)
(drop
(i32.add
(call $export/add
@ -78,17 +80,23 @@
export/sub
export/a
export/b
export/ns
export/ns.one
export/ns.two
reexport/imported_add
reexport/imported_sub
reexport/imported_ns
[program.exports]
export/add
export/renamed_sub
export/a
export/renamed_b
export/ns
reexport/add
reexport/renamed_sub
reexport/renamed_a
reexport/rerenamed_b
reexport/renamed_add
reexport/rerenamed_sub
reexport/renamed_ns
;)

11
tests/parser/namespace.ts Normal file

@ -0,0 +1,11 @@
declare namespace A {
namespace B {
export namespace C {
let aVar: i32;
const aConst: i32;
function aFunc(): void {}
enum AnEnum {}
class AClass {}
}
}
}

@ -0,0 +1,15 @@
declare namespace A {
namespace B {
export namespace C {
let aVar: i32;
const aConst: i32;
function aFunc(): void {
}
enum AnEnum {
}
class AClass {
}
}
}
}

3
tests/parser/var.ts Normal file

@ -0,0 +1,3 @@
var a: i32;
let b: i32;
const c: i32;

@ -0,0 +1,3 @@
let a: i32;
let b: i32;
const c: i32;