mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-26 07:22:21 +00:00
Add showcase compiler test incl. respective features and fixes
This commit is contained in:
parent
2d0f5f3087
commit
9846f6c033
21
src/ast.ts
21
src/ast.ts
@ -439,11 +439,12 @@ export abstract class Node {
|
||||
return stmt;
|
||||
}
|
||||
|
||||
static createImportStatement(declarations: ImportDeclaration[], path: StringLiteralExpression, range: Range): ImportStatement {
|
||||
static createImportStatement(declarations: ImportDeclaration[] | null, path: StringLiteralExpression, range: Range): ImportStatement {
|
||||
var stmt = new ImportStatement();
|
||||
stmt.range = range;
|
||||
for (var i: i32 = 0, k: i32 = (stmt.declarations = declarations).length; i < k; ++i)
|
||||
declarations[i].parent = stmt;
|
||||
if (stmt.declarations = declarations)
|
||||
for (var i: i32 = 0, k: i32 = (<ImportDeclaration[]>declarations).length; i < k; ++i)
|
||||
(<ImportDeclaration[]>declarations)[i].parent = stmt;
|
||||
stmt.namespaceName = null;
|
||||
stmt.path = path;
|
||||
stmt.normalizedPath = resolvePath(normalizePath(path.value), range.source.normalizedPath);
|
||||
@ -1709,22 +1710,20 @@ export class ImportStatement extends Statement {
|
||||
internalPath: string;
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
sb.push("import ");
|
||||
if (this.declarations) {
|
||||
sb.push("import {\n");
|
||||
sb.push("{\n");
|
||||
for (var i: i32 = 0, k: i32 = this.declarations.length; i < k; ++i) {
|
||||
if (i > 0)
|
||||
sb.push(",\n");
|
||||
this.declarations[i].serialize(sb);
|
||||
}
|
||||
sb.push("\n}");
|
||||
} else {
|
||||
sb.push("import * as ");
|
||||
if (this.namespaceName)
|
||||
sb.push("\n} from ");
|
||||
} else if (this.namespaceName) {
|
||||
sb.push("* as ");
|
||||
this.namespaceName.serialize(sb);
|
||||
else
|
||||
throw new Error("missing asterisk import identifier");
|
||||
}
|
||||
sb.push(" from ");
|
||||
}
|
||||
this.path.serialize(sb);
|
||||
}
|
||||
}
|
||||
|
@ -453,25 +453,26 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (element.isCompiled)
|
||||
return true;
|
||||
|
||||
element.isCompiled = true; // members might reference each other, triggering another compile
|
||||
var previousValue: EnumValue | null = null;
|
||||
if (element.members)
|
||||
for (var member of element.members.values()) {
|
||||
if (member.kind != ElementKind.ENUMVALUE)
|
||||
if (member.kind != ElementKind.ENUMVALUE) // happens if an enum is also a namespace
|
||||
continue;
|
||||
var initInStart = false;
|
||||
var val = <EnumValue>member;
|
||||
if (val.hasConstantValue) {
|
||||
this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue));
|
||||
} else if (val.declaration) {
|
||||
var declaration = val.declaration;
|
||||
var valueDeclaration = val.declaration;
|
||||
var initExpr: ExpressionRef;
|
||||
var initInStart = false;
|
||||
if (declaration.value) {
|
||||
initExpr = this.compileExpression(<Expression>declaration.value, Type.i32);
|
||||
if (valueDeclaration.value) {
|
||||
initExpr = this.compileExpression(<Expression>valueDeclaration.value, Type.i32);
|
||||
if (!this.module.noEmit && _BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
|
||||
initExpr = this.precomputeExpressionRef(initExpr);
|
||||
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
|
||||
if (element.isConstant)
|
||||
this.warning(DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable, declaration.range);
|
||||
this.warning(DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable, valueDeclaration.range);
|
||||
initInStart = true;
|
||||
}
|
||||
}
|
||||
@ -486,7 +487,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.module.createI32(1)
|
||||
);
|
||||
if (element.isConstant)
|
||||
this.warning(DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable, declaration.range);
|
||||
this.warning(DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable, valueDeclaration.range);
|
||||
initInStart = true;
|
||||
}
|
||||
if (initInStart) {
|
||||
@ -506,9 +507,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
} else
|
||||
throw new Error("declaration expected");
|
||||
if (element.declaration && isModuleExport(element, element.declaration) && !initInStart)
|
||||
this.module.addGlobalExport(member.internalName, member.internalName);
|
||||
previousValue = <EnumValue>val;
|
||||
}
|
||||
return element.isCompiled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// functions
|
||||
@ -2167,6 +2170,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createUnreachable();
|
||||
|
||||
var element = resolved.element;
|
||||
var tempLocal: Local;
|
||||
var targetExpr: ExpressionRef;
|
||||
switch (element.kind) {
|
||||
|
||||
case ElementKind.LOCAL:
|
||||
@ -2202,12 +2207,12 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
assert(resolved.targetExpression != null);
|
||||
var targetExpr = this.compileExpression(<Expression>resolved.targetExpression, Type.usize32);
|
||||
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, Type.usize32);
|
||||
this.currentType = select<Type>((<Field>element).type, Type.void, tee);
|
||||
var elementNativeType = (<Field>element).type.toNativeType();
|
||||
if (!tee)
|
||||
return this.module.createStore((<Field>element).type.byteSize, targetExpr, valueWithCorrectType, elementNativeType, (<Field>element).memoryOffset);
|
||||
var tempLocal = this.currentFunction.getAndFreeTempLocal((<Field>element).type);
|
||||
tempLocal = this.currentFunction.getAndFreeTempLocal((<Field>element).type);
|
||||
return this.module.createBlock(null, [ // TODO: simplify if valueWithCorrectType has no side effects
|
||||
this.module.createSetLocal(tempLocal.index, valueWithCorrectType),
|
||||
this.module.createStore((<Field>element).type.byteSize, targetExpr, this.module.createGetLocal(tempLocal.index, elementNativeType), elementNativeType, (<Field>element).memoryOffset),
|
||||
@ -2221,15 +2226,31 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (setterInstance) {
|
||||
assert(setterInstance.parameters.length == 1);
|
||||
if (!tee) {
|
||||
if (setterInstance.isInstance) {
|
||||
assert(resolved.targetExpression != null);
|
||||
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, select<Type>(Type.usize64, Type.usize32, this.options.target == Target.WASM64));
|
||||
this.currentType = Type.void;
|
||||
return this.makeCall(setterInstance, [ targetExpr, valueWithCorrectType ]);
|
||||
} else {
|
||||
this.currentType = Type.void;
|
||||
return this.makeCall(setterInstance, [ valueWithCorrectType ]);
|
||||
}
|
||||
}
|
||||
var getterPrototype = (<Property>element).getterPrototype;
|
||||
assert(getterPrototype != null);
|
||||
var getterInstance = (<FunctionPrototype>getterPrototype).resolve(); // reports
|
||||
if (getterInstance) {
|
||||
assert(getterInstance.parameters.length == 0);
|
||||
this.currentType = getterInstance.returnType;
|
||||
if (setterInstance.isInstance) {
|
||||
assert(resolved.targetExpression != null);
|
||||
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, select<Type>(Type.usize64, Type.usize32, this.options.target == Target.WASM64));
|
||||
tempLocal = this.currentFunction.getAndFreeTempLocal(getterInstance.returnType);
|
||||
return this.module.createBlock(null, [
|
||||
this.makeCall(setterInstance, [ this.module.createTeeLocal(tempLocal.index, targetExpr), valueWithCorrectType ]),
|
||||
this.makeCall(getterInstance, [ this.module.createGetLocal(tempLocal.index, tempLocal.type.toNativeType()) ])
|
||||
], getterInstance.returnType.toNativeType());
|
||||
} else
|
||||
return this.module.createBlock(null, [
|
||||
this.makeCall(setterInstance, [ valueWithCorrectType ]),
|
||||
this.makeCall(getterInstance)
|
||||
@ -2517,6 +2538,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createUnreachable();
|
||||
|
||||
var element = resolved.element;
|
||||
var targetExpr: ExpressionRef;
|
||||
switch (element.kind) {
|
||||
|
||||
case ElementKind.GLOBAL: // static property
|
||||
@ -2542,8 +2564,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
assert(resolved.target != null);
|
||||
assert(resolved.targetExpression != null);
|
||||
assert((<Field>element).memoryOffset >= 0);
|
||||
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, select<Type>(Type.usize64, Type.usize32, this.options.target == Target.WASM64));
|
||||
this.currentType = (<Field>element).type;
|
||||
return this.module.createLoad((<Field>element).type.byteSize, (<Field>element).type.isSignedInteger,
|
||||
this.compileExpression(<Expression>resolved.targetExpression, select<Type>(Type.usize64, Type.usize32, this.options.target == Target.WASM64)),
|
||||
targetExpr,
|
||||
(<Field>element).type.toNativeType(),
|
||||
(<Field>element).memoryOffset
|
||||
);
|
||||
@ -2556,6 +2580,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createUnreachable();
|
||||
assert(getterInstance.parameters.length == 0);
|
||||
this.currentType = getterInstance.returnType;
|
||||
if (getterInstance.isInstance) {
|
||||
var targetExpr = this.compileExpression(<Expression>resolved.targetExpression, select<Type>(Type.usize64, Type.usize32, this.options.target == Target.WASM64))
|
||||
return this.makeCall(getterInstance, [ targetExpr ]);
|
||||
} else
|
||||
return this.makeCall(getterInstance);
|
||||
}
|
||||
this.error(DiagnosticCode.Operation_not_supported, propertyAccess.range);
|
||||
@ -2802,11 +2830,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
function isModuleExport(element: Element, declaration: DeclarationStatement): bool {
|
||||
if (!element.isExported)
|
||||
return false;
|
||||
if (declaration.range.source.isEntry)
|
||||
return true;
|
||||
var parentNode = declaration.parent;
|
||||
if (!parentNode)
|
||||
return false;
|
||||
if (declaration.range.source.isEntry && parentNode.kind != NodeKind.NAMESPACE)
|
||||
return true;
|
||||
if (parentNode.kind == NodeKind.VARIABLE)
|
||||
if (!(parentNode = parentNode.parent))
|
||||
return false;
|
||||
|
@ -910,10 +910,11 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
parseImport(tn: Tokenizer): ImportStatement | null {
|
||||
// at 'import': ('{' (ImportMember (',' ImportMember)*)? '}' | '*' 'as' Identifier) 'from' StringLiteral ';'?
|
||||
// at 'import': ('{' (ImportMember (',' ImportMember)*)? '}' | '*' 'as' Identifier)? 'from' StringLiteral ';'?
|
||||
var startPos = tn.tokenPos;
|
||||
var members: ImportDeclaration[] | null = null;
|
||||
var namespaceName: IdentifierExpression | null = null;
|
||||
var skipFrom = false;
|
||||
if (tn.skip(Token.OPENBRACE)) {
|
||||
members = new Array();
|
||||
if (!tn.skip(Token.CLOSEBRACE)) {
|
||||
@ -940,26 +941,18 @@ export class Parser extends DiagnosticEmitter {
|
||||
this.error(DiagnosticCode._0_expected, tn.range(), "as");
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
this.error(DiagnosticCode._0_expected, tn.range(), "{");
|
||||
return null;
|
||||
}
|
||||
if (tn.skip(Token.FROM)) {
|
||||
} else
|
||||
skipFrom = true;
|
||||
|
||||
if (skipFrom || tn.skip(Token.FROM)) {
|
||||
if (tn.skip(Token.STRINGLITERAL)) {
|
||||
var path = Node.createStringLiteralExpression(tn.readString(), tn.range());
|
||||
var ret: ImportStatement;
|
||||
if (members) {
|
||||
if (!namespaceName)
|
||||
ret = Node.createImportStatement(members, path, tn.range(startPos, tn.pos));
|
||||
else {
|
||||
assert(false);
|
||||
return null;
|
||||
}
|
||||
} else if (namespaceName) {
|
||||
if (namespaceName) {
|
||||
assert(!members);
|
||||
ret = Node.createImportStatementWithWildcard(namespaceName, path, tn.range(startPos, tn.pos));
|
||||
} else {
|
||||
assert(false);
|
||||
return null;
|
||||
ret = Node.createImportStatement(members, path, tn.range(startPos, tn.pos));
|
||||
}
|
||||
if (!this.seenlog.has(ret.normalizedPath)) {
|
||||
this.backlog.push(ret.normalizedPath);
|
||||
|
@ -438,6 +438,7 @@ export class Program extends DiagnosticEmitter {
|
||||
else
|
||||
(<Property>propertyElement).setterPrototype = instancePrototype;
|
||||
classPrototype.instanceMembers.set(name, propertyElement);
|
||||
this.elements.set(internalPropertyName, propertyElement);
|
||||
}
|
||||
}
|
||||
|
||||
@ -614,8 +615,7 @@ export class Program extends DiagnosticEmitter {
|
||||
return;
|
||||
}
|
||||
this.error(DiagnosticCode.Operation_not_supported, statement.range); // TODO
|
||||
} else
|
||||
throw new Error("imports must either define members or a namespace");
|
||||
}
|
||||
}
|
||||
|
||||
private initializeImport(declaration: ImportDeclaration, internalPath: string, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
|
||||
@ -912,26 +912,27 @@ export class Program extends DiagnosticEmitter {
|
||||
var name = identifier.name;
|
||||
var local = contextualFunction.locals.get(name);
|
||||
if (local)
|
||||
return resolvedElement.set(local);
|
||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(local);
|
||||
|
||||
var element: Element | null;
|
||||
var namespace: Element | null;
|
||||
|
||||
// search parent namespaces if applicable
|
||||
// search contextual parent namespaces if applicable
|
||||
if (contextualFunction && (namespace = contextualFunction.prototype.namespace)) {
|
||||
do {
|
||||
if (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name))
|
||||
return resolvedElement.set(element);
|
||||
// if ((namespace.members && (element = namespace.members.get(name))) || (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)))
|
||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
|
||||
} while (namespace = namespace.namespace);
|
||||
}
|
||||
|
||||
// search current file
|
||||
if (element = this.elements.get(identifier.range.source.internalPath + PATH_DELIMITER + name))
|
||||
return resolvedElement.set(element);
|
||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
|
||||
|
||||
// search global scope
|
||||
if (element = this.elements.get(name))
|
||||
return resolvedElement.set(element);
|
||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
|
||||
|
||||
this.error(DiagnosticCode.Cannot_find_name_0, identifier.range, name);
|
||||
return null;
|
||||
@ -939,13 +940,11 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
/** Resolves a property access to the element it refers to. */
|
||||
resolvePropertyAccess(propertyAccess: PropertyAccessExpression, contextualFunction: Function): ResolvedElement | null {
|
||||
var resolved: ResolvedElement | null;
|
||||
|
||||
// start by resolving the lhs target (expression before the last dot)
|
||||
var targetExpression = propertyAccess.expression;
|
||||
if (!(resolved = this.resolveExpression(targetExpression, contextualFunction)))
|
||||
if (!(resolvedElement = this.resolveExpression(targetExpression, contextualFunction)))
|
||||
return null;
|
||||
var target = resolved.element;
|
||||
var target = resolvedElement.element;
|
||||
|
||||
// at this point we know exactly what the target is, so look up the element within
|
||||
var propertyName = propertyAccess.property.name;
|
||||
@ -973,16 +972,22 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
resolveElementAccess(elementAccess: ElementAccessExpression, contextualFunction: Function): ResolvedElement | null {
|
||||
var resolved: ResolvedElement | null;
|
||||
|
||||
// start by resolving the lhs target (expression before the last dot)
|
||||
var targetExpression = elementAccess.expression;
|
||||
if (!(resolved = this.resolveExpression(targetExpression, contextualFunction)))
|
||||
if (!(resolvedElement = this.resolveExpression(targetExpression, contextualFunction)))
|
||||
return null;
|
||||
var target = resolved.element;
|
||||
var target = resolvedElement.element;
|
||||
|
||||
// at this point we know exactly what the target is, so make sure it is an array and look up the element within
|
||||
throw new Error("not implemented");
|
||||
switch (target.kind) {
|
||||
case ElementKind.CLASS:
|
||||
var type = (<Class>target).type;
|
||||
if (type.classType) {
|
||||
// TODO: check if array etc.
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.error(DiagnosticCode.Operation_not_supported, elementAccess.range);
|
||||
return null;
|
||||
}
|
||||
|
||||
resolveExpression(expression: Expression, contextualFunction: Function): ResolvedElement | null {
|
||||
@ -991,13 +996,13 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
case NodeKind.THIS: // -> Class
|
||||
if (classType = contextualFunction.instanceMethodOf)
|
||||
return resolvedElement.set(classType);
|
||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(classType);
|
||||
this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range);
|
||||
return null;
|
||||
|
||||
case NodeKind.SUPER: // -> Class
|
||||
if ((classType = contextualFunction.instanceMethodOf) && (classType = classType.base))
|
||||
return resolvedElement.set(classType);
|
||||
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(classType);
|
||||
this.error(DiagnosticCode._super_can_only_be_referenced_in_a_derived_class, expression.range);
|
||||
return null;
|
||||
|
||||
@ -1009,12 +1014,10 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
case NodeKind.ELEMENTACCESS:
|
||||
return this.resolveElementAccess(<ElementAccessExpression>expression, contextualFunction);
|
||||
|
||||
default:
|
||||
}
|
||||
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Common result structure returned when calling any of the resolve functions on a {@link Program}. */
|
||||
@ -1022,11 +1025,12 @@ export class ResolvedElement {
|
||||
|
||||
/** The target element, if a property or element access */
|
||||
target: Element | null;
|
||||
/** The target element's sub-expression, if a property or element access. */
|
||||
/** The target element's expression, if a property or element access. */
|
||||
targetExpression: Expression | null;
|
||||
/** The element being accessed. */
|
||||
element: Element;
|
||||
|
||||
/** Clears the target and sets the resolved element. */
|
||||
set(element: Element): this {
|
||||
this.target = null;
|
||||
this.targetExpression = null;
|
||||
@ -1034,6 +1038,7 @@ export class ResolvedElement {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets the resolved target in addition to the previously set element. */
|
||||
withTarget(target: Element, targetExpression: Expression): this {
|
||||
this.target = target;
|
||||
this.targetExpression = targetExpression;
|
||||
@ -1041,7 +1046,8 @@ export class ResolvedElement {
|
||||
}
|
||||
}
|
||||
|
||||
var resolvedElement = new ResolvedElement();
|
||||
// Cached result structure instance
|
||||
var resolvedElement: ResolvedElement | null;
|
||||
|
||||
/** Indicates the specific kind of an {@link Element}. */
|
||||
export enum ElementKind {
|
||||
@ -1114,7 +1120,9 @@ export enum ElementFlags {
|
||||
/** Is an abstract member. */
|
||||
ABSTRACT = 1 << 16,
|
||||
/** Is a struct-like class with limited capabilites. */
|
||||
STRUCT = 1 << 17
|
||||
STRUCT = 1 << 17,
|
||||
/** Has already inherited base class static members. */
|
||||
HAS_STATIC_BASE_MEMBERS = 1 << 18
|
||||
}
|
||||
|
||||
/** Base class of all program elements. */
|
||||
@ -1810,11 +1818,22 @@ export class ClassPrototype extends Element {
|
||||
this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range);
|
||||
return null;
|
||||
}
|
||||
if ((this.flags & ElementFlags.HAS_STATIC_BASE_MEMBERS) == 0) { // inherit static base members once
|
||||
this.flags |= ElementFlags.HAS_STATIC_BASE_MEMBERS;
|
||||
if (baseClass.prototype.members) {
|
||||
if (!this.members)
|
||||
this.members = new Map();
|
||||
for (var baseMember of baseClass.prototype.members.values())
|
||||
if (!baseMember.isInstance)
|
||||
this.members.set(baseMember.simpleName, baseMember);
|
||||
}
|
||||
}
|
||||
if (baseClass.prototype.isStruct != this.isStruct) {
|
||||
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else
|
||||
this.flags |= ElementFlags.HAS_STATIC_BASE_MEMBERS; // fwiw
|
||||
|
||||
// override call specific contextual type arguments if provided
|
||||
var i: i32, k: i32;
|
||||
@ -1880,8 +1899,14 @@ export class ClassPrototype extends Element {
|
||||
instance.members.set(member.simpleName, methodPrototype);
|
||||
break;
|
||||
|
||||
case ElementKind.PROPERTY: // instance properties are just copied because there is nothing to partially-resolve
|
||||
if (!instance.members)
|
||||
instance.members = new Map();
|
||||
instance.members.set(member.simpleName, member);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("instance member expected");
|
||||
throw new Error("instance member expected: " + member.kind);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1931,12 +1956,15 @@ export class Class extends Element {
|
||||
this.type = (prototype.program.target == Target.WASM64 ? Type.usize64 : Type.usize32).asClass(this);
|
||||
this.base = base;
|
||||
|
||||
// inherit contextual type arguments from base class
|
||||
if (base && base.contextualTypeArguments) {
|
||||
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
|
||||
// inherit static members and contextual type arguments from base class
|
||||
if (base) {
|
||||
if (base.contextualTypeArguments) {
|
||||
if (!this.contextualTypeArguments)
|
||||
this.contextualTypeArguments = new Map();
|
||||
for (var [baseName, baseType] of base.contextualTypeArguments)
|
||||
this.contextualTypeArguments.set(baseName, baseType);
|
||||
}
|
||||
}
|
||||
|
||||
// apply instance-specific contextual type arguments
|
||||
var declaration = this.prototype.declaration;
|
||||
|
@ -46,14 +46,12 @@ export class CArray<T> {
|
||||
private constructor() {}
|
||||
|
||||
@operator("[]")
|
||||
get(index: i32): T {
|
||||
assert(index >= 0);
|
||||
return load<T>(index * sizeof<T>());
|
||||
get(index: usize): T {
|
||||
return load<T>(changetype<usize>(this) + index * sizeof<T>());
|
||||
}
|
||||
|
||||
@operator("[]=")
|
||||
set(index: i32, value: T): void {
|
||||
assert(index >= 0);
|
||||
store<T>(index * sizeof<T>(), value);
|
||||
set(index: usize, value: T): void {
|
||||
store<T>(changetype<usize>(this) + index * sizeof<T>(), value);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,33 @@
|
||||
(module
|
||||
(type $i (func (result i32)))
|
||||
(type $v (func))
|
||||
(global $enum/Implicit.ZERO i32 (i32.const 0))
|
||||
(global $enum/Implicit.ONE i32 (i32.const 1))
|
||||
(global $enum/Implicit.TWO i32 (i32.const 2))
|
||||
(global $enum/Implicit.THREE i32 (i32.const 3))
|
||||
(global $enum/Explicit.ZERO i32 (i32.const 0))
|
||||
(global $enum/Explicit.ONE i32 (i32.const 1))
|
||||
(global $enum/Explicit.TWO i32 (i32.const 2))
|
||||
(global $enum/Explicit.THREE i32 (i32.const 3))
|
||||
(global $enum/Mixed.ZERO i32 (i32.const 0))
|
||||
(global $enum/Mixed.ONE i32 (i32.const 1))
|
||||
(global $enum/Mixed.THREE i32 (i32.const 3))
|
||||
(global $enum/Mixed.FOUR i32 (i32.const 4))
|
||||
(global $enum/NonConstant.ZERO (mut i32) (i32.const 0))
|
||||
(global $enum/NonConstant.ONE (mut i32) (i32.const 0))
|
||||
(memory $0 1)
|
||||
(export "enum/Implicit.ZERO" (global $enum/Implicit.ZERO))
|
||||
(export "enum/Implicit.ONE" (global $enum/Implicit.ONE))
|
||||
(export "enum/Implicit.TWO" (global $enum/Implicit.TWO))
|
||||
(export "enum/Implicit.THREE" (global $enum/Implicit.THREE))
|
||||
(export "enum/Explicit.ZERO" (global $enum/Explicit.ZERO))
|
||||
(export "enum/Explicit.ONE" (global $enum/Explicit.ONE))
|
||||
(export "enum/Explicit.TWO" (global $enum/Explicit.TWO))
|
||||
(export "enum/Explicit.THREE" (global $enum/Explicit.THREE))
|
||||
(export "enum/Mixed.ZERO" (global $enum/Mixed.ZERO))
|
||||
(export "enum/Mixed.ONE" (global $enum/Mixed.ONE))
|
||||
(export "enum/Mixed.THREE" (global $enum/Mixed.THREE))
|
||||
(export "enum/Mixed.FOUR" (global $enum/Mixed.FOUR))
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $start (; 0 ;) (type $v)
|
||||
|
@ -1,9 +1,33 @@
|
||||
(module
|
||||
(type $i (func (result i32)))
|
||||
(type $v (func))
|
||||
(global $enum/Implicit.ZERO i32 (i32.const 0))
|
||||
(global $enum/Implicit.ONE i32 (i32.const 1))
|
||||
(global $enum/Implicit.TWO i32 (i32.const 2))
|
||||
(global $enum/Implicit.THREE i32 (i32.const 3))
|
||||
(global $enum/Explicit.ZERO i32 (i32.const 0))
|
||||
(global $enum/Explicit.ONE i32 (i32.const 1))
|
||||
(global $enum/Explicit.TWO i32 (i32.const 2))
|
||||
(global $enum/Explicit.THREE i32 (i32.const 3))
|
||||
(global $enum/Mixed.ZERO i32 (i32.const 0))
|
||||
(global $enum/Mixed.ONE i32 (i32.const 1))
|
||||
(global $enum/Mixed.THREE i32 (i32.const 3))
|
||||
(global $enum/Mixed.FOUR i32 (i32.const 4))
|
||||
(global $enum/NonConstant.ZERO (mut i32) (i32.const 0))
|
||||
(global $enum/NonConstant.ONE (mut i32) (i32.const 0))
|
||||
(memory $0 1)
|
||||
(export "enum/Implicit.ZERO" (global $enum/Implicit.ZERO))
|
||||
(export "enum/Implicit.ONE" (global $enum/Implicit.ONE))
|
||||
(export "enum/Implicit.TWO" (global $enum/Implicit.TWO))
|
||||
(export "enum/Implicit.THREE" (global $enum/Implicit.THREE))
|
||||
(export "enum/Explicit.ZERO" (global $enum/Explicit.ZERO))
|
||||
(export "enum/Explicit.ONE" (global $enum/Explicit.ONE))
|
||||
(export "enum/Explicit.TWO" (global $enum/Explicit.TWO))
|
||||
(export "enum/Explicit.THREE" (global $enum/Explicit.THREE))
|
||||
(export "enum/Mixed.ZERO" (global $enum/Mixed.ZERO))
|
||||
(export "enum/Mixed.ONE" (global $enum/Mixed.ONE))
|
||||
(export "enum/Mixed.THREE" (global $enum/Mixed.THREE))
|
||||
(export "enum/Mixed.FOUR" (global $enum/Mixed.FOUR))
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $enum/getZero (; 0 ;) (type $i) (result i32)
|
||||
|
@ -17,6 +17,18 @@
|
||||
(global $enum/NonConstant.ONE (mut i32) (i32.const 0))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "enum/Implicit.ZERO" (global $enum/Implicit.ZERO))
|
||||
(export "enum/Implicit.ONE" (global $enum/Implicit.ONE))
|
||||
(export "enum/Implicit.TWO" (global $enum/Implicit.TWO))
|
||||
(export "enum/Implicit.THREE" (global $enum/Implicit.THREE))
|
||||
(export "enum/Explicit.ZERO" (global $enum/Explicit.ZERO))
|
||||
(export "enum/Explicit.ONE" (global $enum/Explicit.ONE))
|
||||
(export "enum/Explicit.TWO" (global $enum/Explicit.TWO))
|
||||
(export "enum/Explicit.THREE" (global $enum/Explicit.THREE))
|
||||
(export "enum/Mixed.ZERO" (global $enum/Mixed.ZERO))
|
||||
(export "enum/Mixed.ONE" (global $enum/Mixed.ONE))
|
||||
(export "enum/Mixed.THREE" (global $enum/Mixed.THREE))
|
||||
(export "enum/Mixed.FOUR" (global $enum/Mixed.FOUR))
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $enum/getZero (; 0 ;) (type $i) (result i32)
|
||||
|
4337
tests/compiler/showcase.optimized-inlined.wast
Normal file
4337
tests/compiler/showcase.optimized-inlined.wast
Normal file
File diff suppressed because it is too large
Load Diff
4307
tests/compiler/showcase.optimized.wast
Normal file
4307
tests/compiler/showcase.optimized.wast
Normal file
File diff suppressed because it is too large
Load Diff
127
tests/compiler/showcase.ts
Normal file
127
tests/compiler/showcase.ts
Normal file
@ -0,0 +1,127 @@
|
||||
// This test case compiles to WebAssembly today, highlights some of the features that have been
|
||||
// implemented already and gives a quick outlook on the road ahead.
|
||||
|
||||
// Global variables can be constant
|
||||
const aConstantGlobal: i32 = 42;
|
||||
|
||||
// Constant globals can be exported to JS from the entry file
|
||||
export const anExportedConstantGlobal: f32 = 42.0;
|
||||
|
||||
// Global variables can be mutable
|
||||
var aMutableGlobal: i32 = 42;
|
||||
|
||||
// Variables can infer their type
|
||||
var anInferredI32 = 42; // infers i32 by default
|
||||
var anInferredI64 = 0x100000000; // infers i64 because the value doesn't fit in 32 bits
|
||||
var anInferredF64 = 42.0; // infers f64 because it is notated as a float
|
||||
var anInferredF32 = <f32>42.0; // infers f32 by evaluating its initializer
|
||||
|
||||
// Unary expressions just work
|
||||
import "./unary";
|
||||
|
||||
// Binary expressions just work
|
||||
import "./binary";
|
||||
|
||||
// Logical expressions just work
|
||||
import "./logical";
|
||||
|
||||
// Several WebAssembly and some common JavaScript built-ins are supported and compile to opcodes directly
|
||||
import "./builtins";
|
||||
|
||||
// Speaking of imports: Exports and re-exports are supported as well
|
||||
export { aConstantGlobal };
|
||||
export { anExportedConstantGlobal as anAliasedConstantGlobal } from "./showcase";
|
||||
|
||||
// Elements can be arranged in namespaces
|
||||
namespace ANamespace {
|
||||
export var aNamespacedGlobal: i32 = 42;
|
||||
export function aNamespacedFunction(a: i32): i32 { return a; } // functions just work
|
||||
}
|
||||
|
||||
// The compiler supports built-in assertions (--noAssert disables them globally)
|
||||
assert(ANamespace.aNamespacedFunction(ANamespace.aNamespacedGlobal) == 42);
|
||||
|
||||
// Enums become constant globals and thus can be exported from the entry file
|
||||
export enum AnEnum {
|
||||
ONE = 1, // values can use explicit initializers
|
||||
TWO, // or assume the previous value + 1
|
||||
// or be omitted
|
||||
FOUR = AnEnum.TWO + 2, // or reference other values (and remain constant through precomputation)
|
||||
FIVE, // and continue from there
|
||||
FOURTYTWO = aMutableGlobal, // or reference mutable values but then can't be exported
|
||||
FOURTYTHREE // and even continue from there without being exported (tsc doesn't allow this)
|
||||
}
|
||||
assert(AnEnum.ONE == 1);
|
||||
assert(AnEnum.TWO == 2);
|
||||
assert(AnEnum.FOUR == 4);
|
||||
assert(AnEnum.FIVE == 5);
|
||||
assert(AnEnum.FOURTYTWO == 42);
|
||||
assert(AnEnum.FOURTYTHREE == 43);
|
||||
|
||||
// In fact, there are a couple of things asc just waves through where tsc refuses to
|
||||
1, 2, 3; // for example not-so-useful comma expressions
|
||||
function addGeneric<T>(left: T, right: T): T {
|
||||
return left + right; // or maybe-useful generic math
|
||||
}
|
||||
|
||||
// Speaking of generics: While there is no type inference yet, it just works
|
||||
addGeneric<i32>(1, 2); // compiles and calls the i32 version
|
||||
addGeneric<f32>(1, 2); // compiles and calls the f32 version
|
||||
clz<i64>(0x8000); // most built-ins are generic as well
|
||||
|
||||
// Type aliases work but must be declared in the global scope for now
|
||||
type double = f64;
|
||||
addGeneric<double>(1, 2); // compiles and calls the f64 version
|
||||
|
||||
// Speaking of lazy compilation: Stuff that's not used is considered dead code and not compiled by default
|
||||
function anUnusedFunction(): void { }
|
||||
|
||||
// That is, unless exported from the entry file, so it is considered reachable
|
||||
export function anExportedFunction(): void { }
|
||||
|
||||
// Or, of course, `--noTreeShaking` is specified
|
||||
|
||||
// As you see, while classes, strings and arrays are still in the works, pretty much everything can
|
||||
// be implemented already. Here are a few more sophisitcated examples of code that'll most likely
|
||||
// make it into the standard library eventually:
|
||||
import "./memcpy"; // until replaced by the proposed `move_memory` intrinsic (sic.)
|
||||
import "./fmod"; // for floating point modulus support, e.g., `1.5 % 1.0`
|
||||
|
||||
// Speaking of classes: Some preliminary work has already been done, so while we can't properly
|
||||
// instantiate them yet, we can point them at some raw memory
|
||||
class AClass {
|
||||
static aStaticField: AClass | null = null;
|
||||
aField: i32;
|
||||
}
|
||||
class ADerivedClass extends AClass {
|
||||
aNotherField: f32;
|
||||
get aWildAccessorAppears(): f32 { return this.aNotherField; }
|
||||
set aWildAccessorAppears(val: f32) { this.aNotherField = val; }
|
||||
}
|
||||
var aClassInstance = changetype<ADerivedClass>(<usize>8);
|
||||
aClassInstance.aField = 42;
|
||||
aClassInstance.aNotherField = 9000;
|
||||
assert(load<i32>(8) == 42);
|
||||
assert(load<f32>(12) == 9000);
|
||||
|
||||
aClassInstance.aWildAccessorAppears = 123;
|
||||
assert(aClassInstance.aWildAccessorAppears == 123);
|
||||
|
||||
AClass.aStaticField = aClassInstance;
|
||||
assert(ADerivedClass.aStaticField == aClassInstance);
|
||||
|
||||
// yet that's pretty much a work in progress, until...
|
||||
|
||||
// Speaking of the standard library:
|
||||
|
||||
// Ultimately, a memory manager should still be present regardless of the GC spec because making
|
||||
// everything a GC-managed object impacts performance where GC isn't necessary. TLSF appears to
|
||||
// be a viable candidate because it's relatively fast and small and an ARC-variant seems like a
|
||||
// good internal alternative to a general-purpose GC if we can figure out reference cycles.
|
||||
|
||||
// With GC (and earlier: host-bindings) it will technically be possible to declare classes whose
|
||||
// instances can cross the JS/WASM boundary natively, but then aren't stored in linear memory.
|
||||
|
||||
// Have a nice day!
|
||||
|
||||
// P.S: Interested in compilers? Nothing cooler to do with your spare time? Say hi!
|
6394
tests/compiler/showcase.wast
Normal file
6394
tests/compiler/showcase.wast
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,3 +5,5 @@ import { A, B, C } from "./other";
|
||||
import { A as B, C, D as E, F } from "./other";
|
||||
|
||||
import * as A from "./other";
|
||||
|
||||
import "./other";
|
||||
|
@ -13,3 +13,4 @@ D as E,
|
||||
F
|
||||
} from "./other";
|
||||
import * as A from "./other";
|
||||
import "./other";
|
||||
|
Loading…
x
Reference in New Issue
Block a user