Skip to content

Commit 99b0fdf

Browse files
committed
Namespaces
1 parent 7d85b0c commit 99b0fdf

30 files changed

+514
-128
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ AssemblyScript NEXT
55

66
**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.
77

8-
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.
8+
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.
99

1010
The compiler itself utilizes "portable definitions" so it can be compiled to both JavaScript using `tsc` and, eventually, to WebAssembly using `asc`.
1111

bin/asc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ args._.forEach(filename => {
130130
var options = assemblyscript.createOptions();
131131
assemblyscript.setTarget(options, 0);
132132
assemblyscript.setNoTreeShaking(options, args.noTreeShaking);
133-
assemblyscript.setNoDebug(options, args.noDebug);
133+
assemblyscript.setNoAssert(options, args.noAssert);
134+
// TODO: noDebug binaryen feature, removing names the debug section
134135

135136
var module = assemblyscript.compile(parser, options);
136137
checkDiagnostics(parser);

bin/asc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"desc": "Disables tree-shaking.",
4444
"type": "boolean"
4545
},
46-
"noDebug": {
46+
"noAssert": {
4747
"desc": "Disables assertions.",
4848
"type": "boolean"
4949
},

src/ast.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,12 +1809,16 @@ export function mangleInternalName(declaration: DeclarationStatement): string {
18091809
}
18101810
}
18111811
}
1812-
if (!declaration.parent)
1812+
let parent: Node | null = declaration.parent;
1813+
if (!parent)
18131814
return name;
1814-
if (declaration.parent.kind == NodeKind.CLASS)
1815-
return (<ClassDeclaration>declaration.parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
1816-
if (declaration.parent.kind == NodeKind.NAMESPACE || declaration.parent.kind == NodeKind.ENUM)
1817-
return (<DeclarationStatement>declaration.parent).internalName + STATIC_DELIMITER + name;
1815+
if (declaration.kind == NodeKind.VARIABLEDECLARATION && parent.kind == NodeKind.VARIABLE) // skip over
1816+
if (!(parent = parent.parent))
1817+
return name;
1818+
if (parent.kind == NodeKind.CLASS)
1819+
return (<ClassDeclaration>parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
1820+
if (parent.kind == NodeKind.NAMESPACE || parent.kind == NodeKind.ENUM)
1821+
return (<DeclarationStatement>parent).internalName + STATIC_DELIMITER + name;
18181822
return declaration.range.source.internalPath + PATH_DELIMITER + name;
18191823
}
18201824

src/builtins.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
494494
return module.createUnreachable();
495495
arg0 = compiler.compileExpression(operands[0], Type.i32); // reports
496496
compiler.currentType = Type.void;
497-
return compiler.options.noDebug
497+
return compiler.options.noAssert
498498
? module.createNop()
499499
: module.createIf(
500500
module.createUnary(UnaryOp.EqzI32, arg0),

src/compiler.ts

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export class Options {
119119
/** If true, compiles everything instead of just reachable code. */
120120
noTreeShaking: bool = false;
121121
/** If true, replaces assertions with nops. */
122-
noDebug: bool = false;
122+
noAssert: bool = false;
123123
}
124124

125125
const enum ConversionKind {
@@ -306,7 +306,7 @@ export class Compiler extends DiagnosticEmitter {
306306
throw new Error("unexpected missing global");
307307
if (!this.compileGlobal(<Global>element))
308308
return null;
309-
if (declaration.range.source.isEntry && (<VariableStatement>declaration.parent).parent == declaration.range.source && hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
309+
if (isModuleExport(element, declaration)) {
310310
if ((<Global>element).hasConstantValue)
311311
this.module.addGlobalExport(element.internalName, declaration.identifier.name);
312312
else
@@ -470,7 +470,7 @@ export class Compiler extends DiagnosticEmitter {
470470
const instance: Function | null = this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, typeArguments, contextualTypeArguments, alternativeReportNode);
471471
if (!instance)
472472
return;
473-
if (declaration.range.source.isEntry && declaration.parent == declaration.range.source && hasModifier(ModifierKind.EXPORT, declaration.modifiers))
473+
if (isModuleExport(instance, declaration))
474474
this.module.addFunctionExport(instance.internalName, declaration.identifier.name);
475475
}
476476

@@ -527,7 +527,7 @@ export class Compiler extends DiagnosticEmitter {
527527

528528
// create the function
529529
const internalName: string = instance.internalName;
530-
if (instance.isDeclared) {
530+
if (instance.isDeclared) { // TODO: use parent namespace as externalModuleName, if applicable
531531
this.module.addFunctionImport(internalName, "env", declaration.identifier.name, typeRef);
532532
} else {
533533
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 {
573573
throw new Error("unexpected namespace member");
574574
}
575575
}
576-
throw new Error("not implemented");
577576
}
578577

579578
compileNamespace(ns: Namespace): void {
@@ -1834,7 +1833,115 @@ export class Compiler extends DiagnosticEmitter {
18341833
return this.compileExpression(expression.expression, contextualType, ConversionKind.NONE);
18351834
}
18361835

1837-
compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): ExpressionRef {
1836+
compilePropertyAccessExpression(propertyAccess: PropertyAccessExpression, contextualType: Type): ExpressionRef {
1837+
const expression: Expression = propertyAccess.expression;
1838+
const propertyName: string = propertyAccess.property.name;
1839+
1840+
// the lhs expression is either 'this', 'super', an identifier or another property access
1841+
let target: Element | null;
1842+
switch (expression.kind) {
1843+
1844+
default:
1845+
throw new Error("unexpected expression kind");
1846+
1847+
case NodeKind.THIS:
1848+
if (!this.currentFunction.instanceMethodOf) {
1849+
this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range);
1850+
return this.module.createUnreachable();
1851+
}
1852+
target = this.currentFunction.instanceMethodOf;
1853+
break;
1854+
1855+
case NodeKind.SUPER:
1856+
if (!(this.currentFunction.instanceMethodOf && this.currentFunction.instanceMethodOf.base)) {
1857+
this.error(DiagnosticCode._super_can_only_be_referenced_in_a_derived_class, expression.range);
1858+
return this.module.createUnreachable();
1859+
}
1860+
target = this.currentFunction.instanceMethodOf.base;
1861+
break;
1862+
1863+
case NodeKind.IDENTIFIER:
1864+
target = this.program.resolveIdentifier(<IdentifierExpression>expression, this.currentFunction); // reports
1865+
break;
1866+
1867+
case NodeKind.PROPERTYACCESS:
1868+
target = this.program.resolvePropertyAccess(<PropertyAccessExpression>expression, this.currentFunction); // reports
1869+
break;
1870+
}
1871+
if (!target)
1872+
return this.module.createUnreachable();
1873+
1874+
// look up the property within the target to obtain the actual element
1875+
let element: Element | null;
1876+
let expr: ExpressionRef;
1877+
switch (target.kind) {
1878+
1879+
// handle enum value right away
1880+
1881+
case ElementKind.ENUM:
1882+
element = (<Enum>target).members.get(propertyName);
1883+
if (!element) {
1884+
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName);
1885+
return this.module.createUnreachable();
1886+
}
1887+
this.currentType = Type.i32;
1888+
return (<EnumValue>element).hasConstantValue
1889+
? this.module.createI32((<EnumValue>element).constantValue)
1890+
: this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32);
1891+
1892+
// postpone everything else
1893+
1894+
case ElementKind.LOCAL:
1895+
element = (<Local>target).type.classType;
1896+
if (!element) {
1897+
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, (<Local>target).type.toString());
1898+
return this.module.createUnreachable();
1899+
}
1900+
target = element;
1901+
break;
1902+
1903+
case ElementKind.GLOBAL:
1904+
if (!this.compileGlobal(<Global>target))
1905+
return this.module.createUnreachable();
1906+
element = (<Type>(<Global>target).type).classType;
1907+
if (!element) {
1908+
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, (<Local>target).type.toString());
1909+
return this.module.createUnreachable();
1910+
}
1911+
target = element;
1912+
break;
1913+
1914+
case ElementKind.NAMESPACE:
1915+
element = (<Namespace>target).members.get(propertyName);
1916+
if (!(element && element.isExported)) {
1917+
this.error(DiagnosticCode.Namespace_0_has_no_exported_member_1, propertyAccess.property.range, (<Namespace>target).internalName, propertyName);
1918+
return this.module.createUnreachable();
1919+
}
1920+
target = element;
1921+
break;
1922+
1923+
default:
1924+
throw new Error("unexpected target kind");
1925+
}
1926+
1927+
// handle the element
1928+
switch (element.kind) {
1929+
1930+
case ElementKind.LOCAL:
1931+
return this.module.createGetLocal((<Local>element).index, typeToNativeType(this.currentType = (<Local>element).type));
1932+
1933+
case ElementKind.GLOBAL:
1934+
this.compileGlobal(<Global>element);
1935+
return this.module.createGetGlobal((<Global>element).internalName, typeToNativeType(this.currentType = <Type>(<Global>element).type));
1936+
1937+
case ElementKind.FUNCTION: // getter
1938+
if (!(<Function>element).prototype.isGetter) {
1939+
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, element.internalName);
1940+
return this.module.createUnreachable();
1941+
}
1942+
return this.compileCall(<Function>element, [], propertyAccess);
1943+
}
1944+
this.error(DiagnosticCode.Operation_not_supported, propertyAccess.range);
18381945
throw new Error("not implemented");
18391946
}
18401947

@@ -2031,3 +2138,22 @@ function typesToSignatureName(paramTypes: Type[], returnType: Type): string {
20312138
sb.push(typeToSignatureNamePart(returnType));
20322139
return sb.join("");
20332140
}
2141+
2142+
function isModuleExport(element: Element, declaration: DeclarationStatement): bool {
2143+
if (!element.isExported)
2144+
return false;
2145+
if (declaration.range.source.isEntry)
2146+
return true;
2147+
let parentNode: Node | null = declaration.parent;
2148+
if (!parentNode)
2149+
return false;
2150+
if (parentNode.kind == NodeKind.VARIABLE)
2151+
if (!(parentNode = parentNode.parent))
2152+
return false;
2153+
if (parentNode.kind != NodeKind.NAMESPACE && parentNode.kind != NodeKind.CLASS)
2154+
return false;
2155+
let parent: Element | null = element.program.elements.get((<DeclarationStatement>parentNode).internalName);
2156+
if (!parent)
2157+
return false;
2158+
return isModuleExport(parent, <DeclarationStatement>parentNode);
2159+
}

src/diagnosticMessages.generated.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export enum DiagnosticCode {
5757
Type_0_is_not_generic = 2315,
5858
Type_0_is_not_assignable_to_type_1 = 2322,
5959
_this_cannot_be_referenced_in_current_location = 2332,
60+
_super_can_only_be_referenced_in_a_derived_class = 2335,
6061
Property_0_does_not_exist_on_type_1 = 2339,
6162
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
6263
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
@@ -69,6 +70,7 @@ export enum DiagnosticCode {
6970
Expected_0_arguments_but_got_1 = 2554,
7071
Expected_at_least_0_arguments_but_got_1 = 2555,
7172
Expected_0_type_arguments_but_got_1 = 2558,
73+
Namespace_0_has_no_exported_member_1 = 2694,
7274
File_0_not_found = 6054
7375
}
7476

@@ -130,6 +132,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
130132
case 2315: return "Type '{0}' is not generic.";
131133
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
132134
case 2332: return "'this' cannot be referenced in current location.";
135+
case 2335: return "'super' can only be referenced in a derived class.";
133136
case 2339: return "Property '{0}' does not exist on type '{1}'.";
134137
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
135138
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 {
142145
case 2554: return "Expected {0} arguments, but got {1}.";
143146
case 2555: return "Expected at least {0} arguments, but got {1}.";
144147
case 2558: return "Expected {0} type arguments, but got {1}.";
148+
case 2694: return "Namespace '{0}' has no exported member '{1}'.";
145149
case 6054: return "File '{0}' not found.";
146150
default: return "";
147151
}

src/diagnosticMessages.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"Type '{0}' is not generic.": 2315,
5858
"Type '{0}' is not assignable to type '{1}'.": 2322,
5959
"'this' cannot be referenced in current location.": 2332,
60+
"'super' can only be referenced in a derived class.": 2335,
6061
"Property '{0}' does not exist on type '{1}'.": 2339,
6162
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": 2349,
6263
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
@@ -69,6 +70,7 @@
6970
"Expected {0} arguments, but got {1}.": 2554,
7071
"Expected at least {0} arguments, but got {1}.": 2555,
7172
"Expected {0} type arguments, but got {1}.": 2558,
73+
"Namespace '{0}' has no exported member '{1}'.": 2694,
7274

7375
"File '{0}' not found.": 6054
7476
}

src/diagnostics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export abstract class DiagnosticEmitter {
155155
const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
156156
this.diagnostics.push(message);
157157
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
158-
// console.log(new Error().stack);
158+
console.log(<string>new Error("stack").stack);
159159
}
160160

161161
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {

src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ export function setNoTreeShaking(options: Options, noTreeShaking: bool): void {
8484
options.noTreeShaking = noTreeShaking;
8585
}
8686

87-
/** Sets the `noDebug` option. */
88-
export function setNoDebug(options: Options, noDebug: bool): void {
89-
options.noDebug = noDebug;
87+
/** Sets the `noAssert` option. */
88+
export function setNoAssert(options: Options, noAssert: bool): void {
89+
options.noAssert = noAssert;
9090
}
9191

9292
/** Compiles the sources computed by the parser to a module. */

0 commit comments

Comments
 (0)