diff --git a/assembly.d.ts b/assembly.d.ts index 60c2df7e..a29e6547 100644 --- a/assembly.d.ts +++ b/assembly.d.ts @@ -27,7 +27,7 @@ declare type f32 = number; /** A 64-bit float. */ declare type f64 = number; -// builtins +// built-ins /** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */ declare function clz(value: T): T; @@ -78,6 +78,8 @@ declare const NaN: f32 | f64; declare const Infinity: f32 | f64; /** Determines the byte size of the specified core or class type. Compiles to a constant. */ declare function sizeof(): usize; +/** Changes the type of a value to another one. Useful for casting class instances to their pointer values and vice-versa. */ +declare function changetype(value: T1): T2; /** Tests if a 32-bit or 64-bit float is NaN. */ declare function isNaN(value: T): bool; /** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */ @@ -87,10 +89,15 @@ declare function assert(isTrue: bool): void; // internal decorators +/** Annotates an element being part of the global namespace. */ declare function global(): any; +/** Annotates a function being always inlined. */ declare function inline(): any; +/** Annotates a class using a C-style memory layout. */ +declare function struct(): any; // standard library /// /// +/// diff --git a/bin/asc.js b/bin/asc.js index cafc1281..5610d165 100644 --- a/bin/asc.js +++ b/bin/asc.js @@ -110,7 +110,12 @@ while ((diagnostic = assemblyscript.nextDiagnostic(parser)) != null) { if (hasErrors) process.exit(1); -var module = assemblyscript.compile(parser); +var options = assemblyscript.createOptions(); +assemblyscript.setTarget(options, 0); +assemblyscript.setNoTreeShaking(options, args.noTreeShaking); +assemblyscript.setNoDebug(options, args.noDebug); + +var module = assemblyscript.compile(parser, options); hasErrors = false; while ((diagnostic = assemblyscript.nextDiagnostic(parser)) != null) { diff --git a/bin/asc.json b/bin/asc.json index 9009fbf5..29471888 100644 --- a/bin/asc.json +++ b/bin/asc.json @@ -1,29 +1,37 @@ { "version": { - "desc": "Print the compiler's version.", + "desc": "Prints the compiler's version.", "type": "boolean", "aliases": [ "v" ] }, "help": { - "desc": "Print this message.", + "desc": "Prints this message.", "type": "boolean", "aliases": [ "h" ] }, "optimize": { - "desc": "Optimize the module.", + "desc": "Optimizes the module.", "type": "boolean", "aliases": [ "O" ] }, "validate": { - "desc": "Validate the module.", + "desc": "Validates the module.", "type": "boolean" }, "outFile": { - "desc": "Specify the output file (binary format).", + "desc": "Specifies the output file (binary format).", "type": "string" }, "textFile": { - "desc": "Specify the output file (text format).", + "desc": "Specifies the output file (text format).", "type": "string" + }, + "noTreeShaking": { + "desc": "Disables built-in tree-shaking.", + "type": "boolean" + }, + "noDebug": { + "desc": "Replaces assertions with nops.", + "type": "boolean" } } diff --git a/src/ast.ts b/src/ast.ts index 1ec4723b..31ff983d 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -657,17 +657,17 @@ export abstract class Statement extends Node { return stmt; } - static createClass(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], extendsType: TypeNode | null, implementsTypes: TypeNode[], members: DeclarationStatement[], decorators: DecoratorStatement[], range: Range): ClassDeclaration { + static createClass(identifier: IdentifierExpression, typeParameters: TypeParameter[], extendsType: TypeNode | null, implementsTypes: TypeNode[], members: DeclarationStatement[], modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): ClassDeclaration { const stmt: ClassDeclaration = new ClassDeclaration(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; (stmt.identifier = identifier).parent = stmt; for (i = 0, k = (stmt.typeParameters = typeParameters).length; i < k; ++i) typeParameters[i].parent = stmt; if (stmt.extendsType = extendsType) (extendsType).parent = stmt; for (i = 0, k = (stmt.implementsTypes = implementsTypes).length; i < k; ++i) implementsTypes[i].parent = stmt; for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt; - for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; + if (stmt.decorators = decorators) for (i = 0, k = (decorators).length; i < k; ++i) (decorators)[i].parent = stmt; return stmt; } @@ -678,8 +678,8 @@ export abstract class Statement extends Node { return stmt; } - static createDecorator(expression: Expression, args: Expression[], range: Range): DecoratorStatement { - const stmt: DecoratorStatement = new DecoratorStatement(); + static createDecorator(expression: Expression, args: Expression[], range: Range): Decorator { + const stmt: Decorator = new Decorator(); stmt.range = range; (stmt.expression = expression).parent = stmt; for (let i: i32 = 0, k: i32 = (stmt.arguments = args).length; i < k; ++i) args[i].parent = stmt; @@ -700,13 +700,14 @@ export abstract class Statement extends Node { return stmt; } - static createEnum(modifiers: Modifier[], identifier: IdentifierExpression, members: EnumValueDeclaration[], range: Range): EnumDeclaration { + static createEnum(identifier: IdentifierExpression, members: EnumValueDeclaration[], modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): EnumDeclaration { const stmt: EnumDeclaration = new EnumDeclaration(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; (stmt.identifier = identifier).parent = stmt; for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; + if (stmt.decorators = decorators) for (i = 0, k = (decorators).length; i < k; ++i) (decorators)[i].parent = stmt; return stmt; } @@ -718,15 +719,15 @@ export abstract class Statement extends Node { return stmt; } - static createExport(modifiers: Modifier[], members: ExportMember[], path: StringLiteralExpression | null, range: Range): ExportStatement { + static createExport(members: ExportMember[], path: StringLiteralExpression | null, modifiers: Modifier[] | null, range: Range): ExportStatement { const stmt: ExportStatement = new ExportStatement(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt; stmt.path = path; stmt.normalizedPath = path ? resolvePath(normalizePath(path.value), range.source.normalizedPath) : null; stmt.internalPath = stmt.normalizedPath ? mangleInternalPath(stmt.normalizedPath) : null; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; return stmt; } @@ -780,25 +781,26 @@ export abstract class Statement extends Node { return elem; } - static createInterface(modifiers: Modifier[], extendsType: TypeNode | null, members: DeclarationStatement[], range: Range): InterfaceDeclaration { + static createInterface(identifier: IdentifierExpression, extendsType: TypeNode | null, members: DeclarationStatement[], modifiers: Modifier[] | null, range: Range): InterfaceDeclaration { const stmt: InterfaceDeclaration = new InterfaceDeclaration(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; + (stmt.identifier = identifier).parent = stmt; if (stmt.extendsType = extendsType) (extendsType).parent = stmt; for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; return stmt; } - static createField(modifiers: Modifier[], identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, decorators: DecoratorStatement[], range: Range): FieldDeclaration { + static createField(identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): FieldDeclaration { const stmt: FieldDeclaration = new FieldDeclaration(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; (stmt.identifier = identifier).parent = stmt; if (stmt.type = type) (type).parent = stmt; if (stmt.initializer = initializer) (initializer).parent = stmt; - for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; + if (stmt.decorators = decorators) for (i = 0, k = (decorators).length; i < k; ++i) (decorators)[i].parent = stmt; return stmt; } @@ -830,31 +832,31 @@ export abstract class Statement extends Node { return elem; } - static createFunction(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, decorators: DecoratorStatement[], range: Range): FunctionDeclaration { + static createFunction(identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): FunctionDeclaration { const stmt: FunctionDeclaration = new FunctionDeclaration(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; (stmt.identifier = identifier).parent = stmt; for (i = 0, k = (stmt.typeParameters = typeParameters).length; i < k; ++i) typeParameters[i].parent = stmt; for (i = 0, k = (stmt.parameters = parameters).length; i < k; ++i) parameters[i].parent = stmt; if (stmt.returnType = returnType) (returnType).parent = stmt; if (stmt.statements = statements) for (i = 0, k = (statements).length; i < k; ++i) (statements)[i].parent = stmt; - for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; + if (stmt.decorators = decorators) for (i = 0, k = (decorators).length; i < k; ++i) (decorators)[i].parent = stmt; return stmt; } - static createMethod(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, decorators: DecoratorStatement[], range: Range): MethodDeclaration { + static createMethod(identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): MethodDeclaration { const stmt: MethodDeclaration = new MethodDeclaration(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; (stmt.identifier = identifier).parent = stmt; for (i = 0, k = (stmt.typeParameters = typeParameters).length; i < k; ++i) typeParameters[i].parent = stmt; for (i = 0, k = (stmt.parameters = parameters).length; i < k; ++i) parameters[i].parent = stmt; if (stmt.returnType = returnType) (returnType).parent = stmt;; if (stmt.statements = statements) for (i = 0, k = (statements).length; i < k; ++i) (statements)[i].parent = stmt; - for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; + if (stmt.decorators = decorators) for (i = 0, k = (decorators).length; i < k; ++i) (decorators)[i].parent = stmt; return stmt; } @@ -865,13 +867,14 @@ export abstract class Statement extends Node { return elem; } - static createNamespace(modifiers: Modifier[], identifier: IdentifierExpression, members: DeclarationStatement[], range: Range): NamespaceDeclaration { + static createNamespace(identifier: IdentifierExpression, members: Statement[], modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): NamespaceDeclaration { const stmt: NamespaceDeclaration = new NamespaceDeclaration(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; (stmt.identifier = identifier).parent = stmt; for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; + if (stmt.decorators = decorators) for (i = 0, k = (decorators).length; i < k; ++i) (decorators)[i].parent = stmt; return stmt; } @@ -916,22 +919,24 @@ export abstract class Statement extends Node { return stmt; } - static createVariable(modifiers: Modifier[], declarations: VariableDeclaration[], range: Range): VariableStatement { + static createVariable(declarations: VariableDeclaration[], modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): VariableStatement { const stmt: VariableStatement = new VariableStatement(); stmt.range = range; let i: i32, k: i32; - for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt; for (i = 0, k = (stmt.declarations = declarations).length; i < k; ++i) declarations[i].parent = stmt; + if (stmt.modifiers = modifiers) for (i = 0, k = (modifiers).length; i < k; ++i) (modifiers)[i].parent = stmt; + if (stmt.decorators = decorators) for (i = 0, k = (decorators).length; i < k; ++i) (decorators)[i].parent = stmt; return stmt; } - static createVariableDeclaration(identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, modifiers: Modifier[] | null, range: Range): VariableDeclaration { + static createVariableDeclaration(identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, modifiers: Modifier[] | null, decorators: Decorator[] | null, range: Range): VariableDeclaration { const elem: VariableDeclaration = new VariableDeclaration(); elem.range = range; (elem.identifier = identifier).parent = elem; if (elem.type = type) (type).parent = elem; if (elem.initializer = initializer) (initializer).parent = elem; elem.modifiers = modifiers; + elem.decorators = decorators; return elem; } @@ -987,6 +992,7 @@ export abstract class DeclarationStatement extends Statement { identifier: IdentifierExpression; modifiers: Modifier[] | null; + decorators: Decorator[] | null = null; protected _cachedInternalName: string | null = null; @@ -1037,12 +1043,11 @@ export class ClassDeclaration extends DeclarationStatement { extendsType: TypeNode | null; implementsTypes: TypeNode[]; members: DeclarationStatement[]; - decorators: DecoratorStatement[]; get internalName(): string { if (this._cachedInternalName !== null) return this._cachedInternalName; - const globalDecorator: DecoratorStatement | null = getDecoratorByName("global", this.decorators); + const globalDecorator: Decorator | null = this.decorators ? getDecoratorByName("global", this.decorators) : null; if (globalDecorator && globalDecorator.expression.kind == NodeKind.IDENTIFIER && (globalDecorator.expression).name == "global") return this._cachedInternalName = this.identifier.name; else @@ -1051,10 +1056,11 @@ export class ClassDeclaration extends DeclarationStatement { serialize(sb: string[]): void { let i: i32, k: i32; - for (i = 0, k = this.decorators.length; i < k; ++i) { - this.decorators[i].serialize(sb); - sb.push("\n"); - } + if (this.decorators) + for (i = 0, k = this.decorators.length; i < k; ++i) { + this.decorators[i].serialize(sb); + sb.push("\n"); + } if (this.modifiers) for (i = 0, k = (this.modifiers).length; i < k; ++i) { (this.modifiers)[i].serialize(sb); @@ -1109,7 +1115,7 @@ export class ContinueStatement extends Statement { } } -export class DecoratorStatement extends Statement { +export class Decorator extends Statement { kind = NodeKind.DECORATOR; expression: Expression; @@ -1264,14 +1270,14 @@ export class ExpressionStatement extends Statement { export class FieldDeclaration extends VariableLikeDeclarationStatement { kind = NodeKind.FIELD; - decorators: DecoratorStatement[]; serialize(sb: string[]): void { let i: i32, k: i32; - for (i = 0, k = this.decorators.length; i < k; ++i) { - this.decorators[i].serialize(sb); - sb.push("\n"); - } + if (this.decorators) + for (i = 0, k = this.decorators.length; i < k; ++i) { + this.decorators[i].serialize(sb); + sb.push("\n"); + } if (this.modifiers) for (i = 0, k = (this.modifiers).length; i < k; ++i) { (this.modifiers)[i].serialize(sb); @@ -1323,12 +1329,11 @@ export class FunctionDeclaration extends DeclarationStatement { parameters: Parameter[]; returnType: TypeNode | null; statements: Statement[] | null; - decorators: DecoratorStatement[]; get internalName(): string { if (this._cachedInternalName !== null) return this._cachedInternalName; - const globalDecorator: DecoratorStatement | null = getDecoratorByName("global", this.decorators); + const globalDecorator: Decorator | null = this.decorators ? getDecoratorByName("global", this.decorators) : null; if (globalDecorator && globalDecorator.expression.kind == NodeKind.IDENTIFIER && (globalDecorator.expression).name == "global") return this._cachedInternalName = this.identifier.name; else @@ -1337,10 +1342,11 @@ export class FunctionDeclaration extends DeclarationStatement { serialize(sb: string[]): void { let i: i32, k: i32; - for (i = 0, k = this.decorators.length; i < k; ++i) { - this.decorators[i].serialize(sb); - sb.push("\n"); - } + if (this.decorators) + for (i = 0, k = this.decorators.length; i < k; ++i) { + this.decorators[i].serialize(sb); + sb.push("\n"); + } if (this.modifiers) for (i = 0, k = (this.modifiers).length; i < k; ++i) { (this.modifiers)[i].serialize(sb); @@ -1453,6 +1459,11 @@ export class InterfaceDeclaration extends ClassDeclaration { serialize(sb: string[]): void { let i: i32, k: i32; + if (this.decorators) + for (i = 0, k = this.decorators.length; i < k; ++i) { + this.decorators[i].serialize(sb); + sb.push("\n"); + } if (this.modifiers) for (i = 0, k = (this.modifiers).length; i < k; ++i) { (this.modifiers)[i].serialize(sb); @@ -1491,10 +1502,11 @@ export class MethodDeclaration extends FunctionDeclaration { serialize(sb: string[]): void { let i: i32, k: i32; - for (i = 0, k = this.decorators.length; i < k; ++i) { - this.decorators[i].serialize(sb); - sb.push("\n"); - } + if (this.decorators) + for (i = 0, k = this.decorators.length; i < k; ++i) { + this.decorators[i].serialize(sb); + sb.push("\n"); + } if (this.modifiers) for (i = 0, k = (this.modifiers).length; i < k; ++i) { (this.modifiers)[i].serialize(sb); @@ -1511,6 +1523,11 @@ export class NamespaceDeclaration extends DeclarationStatement { serialize(sb: string[]): void { let i: i32, k: i32; + if (this.decorators) + for (i = 0, k = this.decorators.length; i < k; ++i) { + this.decorators[i].serialize(sb); + sb.push("\n"); + } if (this.modifiers) for (i = 0, k = (this.modifiers).length; i < k; ++i) { (this.modifiers)[i].serialize(sb); @@ -1701,11 +1718,17 @@ export class VariableStatement extends Statement { kind = NodeKind.VARIABLE; modifiers: Modifier[] | null; + decorators: Decorator[] | null; declarations: VariableDeclaration[]; serialize(sb: string[]): void { let isConst: bool = false; let i: i32, k: i32; + if (this.decorators) + for (i = 0, k = this.decorators.length; i < k; ++i) { + this.decorators[i].serialize(sb); + sb.push("\n"); + } if (this.modifiers) for (i = 0, k = (this.modifiers).length; i < k; ++i) { (this.modifiers)[i].serialize(sb); @@ -1745,9 +1768,9 @@ export function hasModifier(kind: ModifierKind, modifiers: Modifier[] | null): b return false; } -export function getDecoratorByName(name: string, decorators: DecoratorStatement[]): DecoratorStatement | null { +export function getDecoratorByName(name: string, decorators: Decorator[]): Decorator | null { for (let i: i32 = 0, k: i32 = decorators.length; i < k; ++i) { - const decorator: DecoratorStatement = decorators[i]; + const decorator: Decorator = decorators[i]; const expression: Expression = decorator.expression; if (expression.kind == NodeKind.IDENTIFIER && (expression).name == name) return decorator; diff --git a/src/builtins.ts b/src/builtins.ts index d26a9736..bcf86ab5 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -2,7 +2,7 @@ import { Compiler, Target, typeToNativeType, typeToNativeOne } from "./compiler" import { DiagnosticCode } from "./diagnostics"; import { Node, Expression } from "./ast"; import { Type } from "./types"; -import { ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType } from "./module"; +import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType } from "./module"; import { Program, FunctionPrototype, Local } from "./program"; /** Initializes the specified program with built-in functions. */ @@ -29,6 +29,7 @@ export function initialize(program: Program): void { addFunction(program, "reinterpret", true); addFunction(program, "select", true); addFunction(program, "sizeof", true); + addFunction(program, "changetype", true); addFunction(program, "isNaN", true); addFunction(program, "isFinite", true); addFunction(program, "assert"); @@ -47,6 +48,7 @@ function addFunction(program: Program, name: string, isGeneric: bool = false, is /** Compiles a call to a built-in function. */ export function compileCall(compiler: Compiler, internalName: string, typeArguments: Type[], operands: Expression[], reportNode: Node): ExpressionRef { + const module: Module = compiler.module; const usizeType: Type = select(Type.usize64, Type.usize32, compiler.options.target == Target.WASM64); let arg0: ExpressionRef, @@ -55,126 +57,127 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume let tempLocal0: Local; let tempLocal1: Local; - let nativeType: NativeType; + + let type: Type; switch (internalName) { case "clz": // clz(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyInteger) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]).isLongInteger // sic - ? compiler.module.createUnary(UnaryOp.ClzI64, arg0) + ? module.createUnary(UnaryOp.ClzI64, arg0) : typeArguments[0].isSmallInteger - ? compiler.module.createBinary(BinaryOp.AndI32, - compiler.module.createUnary(UnaryOp.ClzI32, arg0), - compiler.module.createI32(typeArguments[0].smallIntegerMask) + ? module.createBinary(BinaryOp.AndI32, + module.createUnary(UnaryOp.ClzI32, arg0), + module.createI32(typeArguments[0].smallIntegerMask) ) - : compiler.module.createUnary(UnaryOp.ClzI32, arg0); + : module.createUnary(UnaryOp.ClzI32, arg0); } break; case "ctz": // ctz(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyInteger) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]).isLongInteger // sic - ? compiler.module.createUnary(UnaryOp.CtzI64, arg0) + ? module.createUnary(UnaryOp.CtzI64, arg0) : typeArguments[0].isSmallInteger - ? compiler.module.createBinary(BinaryOp.AndI32, - compiler.module.createUnary(UnaryOp.CtzI32, arg0), - compiler.module.createI32(typeArguments[0].smallIntegerMask) + ? module.createBinary(BinaryOp.AndI32, + module.createUnary(UnaryOp.CtzI32, arg0), + module.createI32(typeArguments[0].smallIntegerMask) ) - : compiler.module.createUnary(UnaryOp.CtzI32, arg0); + : module.createUnary(UnaryOp.CtzI32, arg0); } break; case "popcnt": // popcnt(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyInteger) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]).isLongInteger // sic - ? compiler.module.createUnary(UnaryOp.PopcntI64, arg0) + ? module.createUnary(UnaryOp.PopcntI64, arg0) : typeArguments[0].isSmallInteger - ? compiler.module.createBinary(BinaryOp.AndI32, - compiler.module.createUnary(UnaryOp.PopcntI32, arg0), - compiler.module.createI32(typeArguments[0].smallIntegerMask) + ? module.createBinary(BinaryOp.AndI32, + module.createUnary(UnaryOp.PopcntI32, arg0), + module.createI32(typeArguments[0].smallIntegerMask) ) - : compiler.module.createUnary(UnaryOp.PopcntI32, arg0); + : module.createUnary(UnaryOp.PopcntI32, arg0); } break; case "rotl": // rotl(value: T, shift: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyInteger) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); arg1 = compiler.compileExpression(operands[1], typeArguments[0]); return (compiler.currentType = typeArguments[0]).isLongInteger // sic - ? compiler.module.createBinary(BinaryOp.RotlI64, arg0, arg1) + ? module.createBinary(BinaryOp.RotlI64, arg0, arg1) : typeArguments[0].isSmallInteger - ? compiler.module.createBinary(BinaryOp.AndI32, - compiler.module.createBinary(BinaryOp.RotlI32, arg0, arg1), - compiler.module.createI32(typeArguments[0].smallIntegerMask) + ? module.createBinary(BinaryOp.AndI32, + module.createBinary(BinaryOp.RotlI32, arg0, arg1), + module.createI32(typeArguments[0].smallIntegerMask) ) - : compiler.module.createBinary(BinaryOp.RotlI32, arg0, arg1); + : module.createBinary(BinaryOp.RotlI32, arg0, arg1); } break; case "rotr": // rotr(value: T, shift: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyInteger) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); arg1 = compiler.compileExpression(operands[1], typeArguments[0]); return (compiler.currentType = typeArguments[0]).isLongInteger // sic - ? compiler.module.createBinary(BinaryOp.RotrI64, arg0, arg1) + ? module.createBinary(BinaryOp.RotrI64, arg0, arg1) : typeArguments[0].isSmallInteger - ? compiler.module.createBinary(BinaryOp.AndI32, - compiler.module.createBinary(BinaryOp.RotrI32, arg0, arg1), - compiler.module.createI32(typeArguments[0].smallIntegerMask) + ? module.createBinary(BinaryOp.AndI32, + module.createBinary(BinaryOp.RotrI32, arg0, arg1), + module.createI32(typeArguments[0].smallIntegerMask) ) - : compiler.module.createBinary(BinaryOp.RotrI32, arg0, arg1); + : module.createBinary(BinaryOp.RotrI32, arg0, arg1); } break; case "abs": // abs(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]) != Type.void) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); if ((compiler.currentType = typeArguments[0]).isAnyFloat) // sic return typeArguments[0] == Type.f32 - ? compiler.module.createUnary(UnaryOp.AbsF32, arg0) - : compiler.module.createUnary(UnaryOp.AbsF64, arg0); + ? module.createUnary(UnaryOp.AbsF32, arg0) + : module.createUnary(UnaryOp.AbsF64, arg0); if (typeArguments[0].isAnyInteger) { if (typeArguments[0].isSignedInteger) { tempLocal0 = compiler.currentFunction.addLocal(typeArguments[0]); if (typeArguments[0].isLongInteger) - return compiler.module.createSelect( - compiler.module.createBinary(BinaryOp.SubI64, - compiler.module.createI64(0, 0), - compiler.module.createTeeLocal(tempLocal0.index, arg0) + return module.createSelect( + module.createBinary(BinaryOp.SubI64, + module.createI64(0, 0), + module.createTeeLocal(tempLocal0.index, arg0) ), - compiler.module.createGetLocal(tempLocal0.index, NativeType.I64), - compiler.module.createBinary(BinaryOp.LtI64, - compiler.module.createGetLocal(tempLocal0.index, NativeType.I64), - compiler.module.createI64(0, 0) + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createBinary(BinaryOp.LtI64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createI64(0, 0) ) ); else - return compiler.module.createSelect( - compiler.module.createBinary(BinaryOp.SubI32, - compiler.module.createI32(0), - compiler.module.createTeeLocal(tempLocal0.index, arg0) + return module.createSelect( + module.createBinary(BinaryOp.SubI32, + module.createI32(0), + module.createTeeLocal(tempLocal0.index, arg0) ), - compiler.module.createGetLocal(tempLocal0.index, NativeType.I32), - compiler.module.createBinary(BinaryOp.LtI32, - compiler.module.createGetLocal(tempLocal0.index, NativeType.I32), - compiler.module.createI32(0) + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createBinary(BinaryOp.LtI32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createI32(0) ) ); } else @@ -185,33 +188,33 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume case "max": // max(left: T, right: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]) != Type.void) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); arg1 = compiler.compileExpression(operands[1], typeArguments[0]); if ((compiler.currentType = typeArguments[0]).isAnyFloat) // sic return typeArguments[0] == Type.f32 - ? compiler.module.createBinary(BinaryOp.MaxF32, arg0, arg1) - : compiler.module.createBinary(BinaryOp.MaxF64, arg0, arg1); + ? module.createBinary(BinaryOp.MaxF32, arg0, arg1) + : module.createBinary(BinaryOp.MaxF64, arg0, arg1); if (typeArguments[0].isAnyInteger) { tempLocal0 = compiler.currentFunction.addLocal(typeArguments[0]); tempLocal1 = compiler.currentFunction.addLocal(typeArguments[0]); if (typeArguments[0].isLongInteger) - return compiler.module.createSelect( - compiler.module.createTeeLocal(tempLocal0.index, arg0), - compiler.module.createTeeLocal(tempLocal1.index, arg1), - compiler.module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.GtI64 : BinaryOp.GtU64, - compiler.module.createGetLocal(tempLocal0.index, NativeType.I64), - compiler.module.createGetLocal(tempLocal1.index, NativeType.I64) + return module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.GtI64 : BinaryOp.GtU64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createGetLocal(tempLocal1.index, NativeType.I64) ) ); else - return compiler.module.createSelect( - compiler.module.createTeeLocal(tempLocal0.index, arg0), - compiler.module.createTeeLocal(tempLocal1.index, arg1), - compiler.module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.GtI32 : BinaryOp.GtU32, - compiler.module.createGetLocal(tempLocal0.index, NativeType.I32), - compiler.module.createGetLocal(tempLocal1.index, NativeType.I32) + return module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.GtI32 : BinaryOp.GtU32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createGetLocal(tempLocal1.index, NativeType.I32) ) ); } @@ -220,33 +223,33 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume case "min": // min(left: T, right: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]) != Type.void) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); arg1 = compiler.compileExpression(operands[1], typeArguments[0]); if ((compiler.currentType = typeArguments[0]).isAnyFloat) // sic return typeArguments[0] == Type.f32 - ? compiler.module.createBinary(BinaryOp.MinF32, arg0, arg1) - : compiler.module.createBinary(BinaryOp.MinF64, arg0, arg1); + ? module.createBinary(BinaryOp.MinF32, arg0, arg1) + : module.createBinary(BinaryOp.MinF64, arg0, arg1); if (typeArguments[0].isAnyInteger) { tempLocal0 = compiler.currentFunction.addLocal(typeArguments[0]); tempLocal1 = compiler.currentFunction.addLocal(typeArguments[0]); if (typeArguments[0].isLongInteger) - return compiler.module.createSelect( - compiler.module.createTeeLocal(tempLocal0.index, arg0), - compiler.module.createTeeLocal(tempLocal1.index, arg1), - compiler.module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.LtI64 : BinaryOp.LtU64, - compiler.module.createGetLocal(tempLocal0.index, NativeType.I64), - compiler.module.createGetLocal(tempLocal1.index, NativeType.I64) + return module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.LtI64 : BinaryOp.LtU64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createGetLocal(tempLocal1.index, NativeType.I64) ) ); else - return compiler.module.createSelect( - compiler.module.createTeeLocal(tempLocal0.index, arg0), - compiler.module.createTeeLocal(tempLocal1.index, arg1), - compiler.module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.LtI32 : BinaryOp.LtU32, - compiler.module.createGetLocal(tempLocal0.index, NativeType.I32), - compiler.module.createGetLocal(tempLocal1.index, NativeType.I32) + return module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.LtI32 : BinaryOp.LtU32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createGetLocal(tempLocal1.index, NativeType.I32) ) ); } @@ -255,174 +258,185 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume case "ceil": // ceil(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]) == Type.f32 // sic - ? compiler.module.createUnary(UnaryOp.CeilF32, arg0) - : compiler.module.createUnary(UnaryOp.CeilF64, arg0); + ? module.createUnary(UnaryOp.CeilF32, arg0) + : module.createUnary(UnaryOp.CeilF64, arg0); } break; case "floor": // floor(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]) == Type.f32 // sic - ? compiler.module.createUnary(UnaryOp.FloorF32, arg0) - : compiler.module.createUnary(UnaryOp.FloorF64, arg0); + ? module.createUnary(UnaryOp.FloorF32, arg0) + : module.createUnary(UnaryOp.FloorF64, arg0); } break; case "copysign": // copysign(left: T, right: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); arg1 = compiler.compileExpression(operands[1], typeArguments[0]); return (compiler.currentType = typeArguments[0]) == Type.f32 // sic - ? compiler.module.createBinary(BinaryOp.CopysignF32, arg0, arg1) - : compiler.module.createBinary(BinaryOp.CopysignF64, arg0, arg1); + ? module.createBinary(BinaryOp.CopysignF32, arg0, arg1) + : module.createBinary(BinaryOp.CopysignF64, arg0, arg1); } break; case "nearest": // nearest(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]) == Type.f32 // sic - ? compiler.module.createUnary(UnaryOp.NearestF32, arg0) - : compiler.module.createUnary(UnaryOp.NearestF64, arg0); + ? module.createUnary(UnaryOp.NearestF32, arg0) + : module.createUnary(UnaryOp.NearestF64, arg0); } break; case "sqrt": // sqrt(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]) == Type.f32 // sic - ? compiler.module.createUnary(UnaryOp.SqrtF32, arg0) - : compiler.module.createUnary(UnaryOp.SqrtF64, arg0); + ? module.createUnary(UnaryOp.SqrtF32, arg0) + : module.createUnary(UnaryOp.SqrtF64, arg0); } break; case "trunc": // trunc(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if ((compiler.currentType = typeArguments[0]).isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); return (compiler.currentType = typeArguments[0]) == Type.f32 // sic - ? compiler.module.createUnary(UnaryOp.TruncF32, arg0) - : compiler.module.createUnary(UnaryOp.TruncF64, arg0); + ? module.createUnary(UnaryOp.TruncF32, arg0) + : module.createUnary(UnaryOp.TruncF64, arg0); } break; - case "sizeof": // sizeof() -> usize - compiler.currentType = usizeType; - if (!validateCall(compiler, typeArguments, 1, operands, 0, reportNode)) - return compiler.module.createUnreachable(); - return usizeType.isLongInteger - ? compiler.module.createI64(typeArguments[0].byteSize, 0) - : compiler.module.createI32(typeArguments[0].byteSize); - case "load": // load(offset: usize) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); arg0 = compiler.compileExpression(operands[0], usizeType); // reports if ((compiler.currentType = typeArguments[0]) != Type.void) - return compiler.module.createLoad(typeArguments[0].byteSize, typeArguments[0].isSignedInteger, arg0, typeToNativeType(typeArguments[0])); + return module.createLoad(typeArguments[0].byteSize, typeArguments[0].isSignedInteger, arg0, typeToNativeType(typeArguments[0])); break; case "store": // store(offset: usize, value: T) -> void compiler.currentType = Type.void; if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); arg0 = compiler.compileExpression(operands[0], usizeType); // reports arg1 = compiler.compileExpression(operands[1], typeArguments[0]); // reports compiler.currentType = Type.void; if (typeArguments[0] != Type.void) - return compiler.module.createStore(typeArguments[0].byteSize, arg0, arg1, typeToNativeType(typeArguments[0])); + return module.createStore(typeArguments[0].byteSize, arg0, arg1, typeToNativeType(typeArguments[0])); break; case "reinterpret": // reinterpret(value: T1) -> T2 if (!validateCall(compiler, typeArguments, 2, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); compiler.currentType = typeArguments[1]; if (typeArguments[0].isLongInteger && typeArguments[1] == Type.f64) { arg0 = compiler.compileExpression(operands[0], Type.i64); // reports compiler.currentType = Type.f64; - return compiler.module.createUnary(UnaryOp.ReinterpretI64, arg0); + return module.createUnary(UnaryOp.ReinterpretI64, arg0); } if (typeArguments[0].isAnyInteger && typeArguments[0].byteSize == 4 && typeArguments[1] == Type.f32) { arg0 = compiler.compileExpression(operands[0], Type.i32); // reports compiler.currentType = Type.f32; - return compiler.module.createUnary(UnaryOp.ReinterpretI32, arg0); + return module.createUnary(UnaryOp.ReinterpretI32, arg0); } if (typeArguments[0] == Type.f64 && typeArguments[1].isLongInteger) { arg0 = compiler.compileExpression(operands[0], Type.f64); // reports compiler.currentType = typeArguments[1]; - return compiler.module.createUnary(UnaryOp.ReinterpretF64, arg0); + return module.createUnary(UnaryOp.ReinterpretF64, arg0); } if (typeArguments[0] == Type.f32 && typeArguments[1].isAnyInteger && typeArguments[1].byteSize == 4) { arg0 = compiler.compileExpression(operands[0], Type.f32); // reports compiler.currentType = typeArguments[1]; - return compiler.module.createUnary(UnaryOp.ReinterpretF32, arg0); + return module.createUnary(UnaryOp.ReinterpretF32, arg0); } break; case "select": // select(ifTrue: T, ifFalse: T, condition: bool) -> T if (!validateCall(compiler, typeArguments, 1, operands, 3, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if (typeArguments[0] != Type.void) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports arg1 = compiler.compileExpression(operands[1], typeArguments[0]); // reports arg2 = compiler.compileExpression(operands[2], Type.i32); // reports compiler.currentType = typeArguments[0]; - return compiler.module.createSelect(arg0, arg1, arg2); + return module.createSelect(arg0, arg1, arg2); } break; case "current_memory": // current_memory() -> i32 compiler.currentType = Type.i32; if (!validateCall(compiler, typeArguments, 0, operands, 0, reportNode)) - return compiler.module.createUnreachable(); - return compiler.module.createHost(HostOp.CurrentMemory); + return module.createUnreachable(); + return module.createHost(HostOp.CurrentMemory); case "grow_memory": // grow_memory(pages: i32) -> i32 compiler.currentType = Type.i32; if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); arg0 = compiler.compileExpression(operands[0], Type.i32); - return compiler.module.createHost(HostOp.GrowMemory, null, [ arg0 ]); + return module.createHost(HostOp.GrowMemory, null, [ arg0 ]); case "unreachable": // unreachable() -> * // does not modify currentType validateCall(compiler, typeArguments, 0, operands, 0, reportNode); - return compiler.module.createUnreachable(); + return module.createUnreachable(); + + case "sizeof": // sizeof() -> usize + compiler.currentType = usizeType; + if (!validateCall(compiler, typeArguments, 1, operands, 0, reportNode)) + return module.createUnreachable(); + return usizeType.isLongInteger + ? module.createI64(typeArguments[0].byteSize, 0) + : module.createI32(typeArguments[0].byteSize); + + case "changetype": // changetype(value: T1) -> T2 + if (!validateCall(compiler, typeArguments, 2, operands, 1, reportNode)) + return module.createUnreachable(); + if ((typeArguments[0] == usizeType && typeArguments[1].classType) || (typeArguments[0].classType && typeArguments[1] == usizeType)) { + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + compiler.currentType = typeArguments[1]; + return arg0; + } + compiler.error(DiagnosticCode.Type_0_cannot_be_changed_to_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString()); + return module.createUnreachable(); case "isNaN": // isNaN(value: T) -> bool compiler.currentType = Type.bool; if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if (typeArguments[0].isAnyInteger) - return compiler.module.createI32(0); + return module.createI32(0); if (typeArguments[0].isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports compiler.currentType = Type.bool; if (typeArguments[0] == Type.f32) { tempLocal0 = compiler.currentFunction.addLocal(Type.f32); - return compiler.module.createBinary(BinaryOp.NeF32, - compiler.module.createTeeLocal(tempLocal0.index, arg0), - compiler.module.createGetLocal(tempLocal0.index, NativeType.F32) + return module.createBinary(BinaryOp.NeF32, + module.createTeeLocal(tempLocal0.index, arg0), + module.createGetLocal(tempLocal0.index, NativeType.F32) ); } else { tempLocal0 = compiler.currentFunction.addLocal(Type.f64); - return compiler.module.createBinary(BinaryOp.NeF64, - compiler.module.createTeeLocal(tempLocal0.index, arg0), - compiler.module.createGetLocal(tempLocal0.index, NativeType.F64) + return module.createBinary(BinaryOp.NeF64, + module.createTeeLocal(tempLocal0.index, arg0), + module.createGetLocal(tempLocal0.index, NativeType.F64) ); } } @@ -431,40 +445,40 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume case "isFinite": // isFinite(value: T) -> bool compiler.currentType = Type.bool; if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); if (typeArguments[0].isAnyInteger) - return compiler.module.createI32(1); + return module.createI32(1); if (typeArguments[0].isAnyFloat) { arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports compiler.currentType = Type.bool; if (typeArguments[0] == Type.f32) { tempLocal0 = compiler.currentFunction.addLocal(Type.f32); - return compiler.module.createSelect( - compiler.module.createBinary(BinaryOp.NeF32, - compiler.module.createUnary(UnaryOp.AbsF32, - compiler.module.createTeeLocal(tempLocal0.index, arg0) + return module.createSelect( + module.createBinary(BinaryOp.NeF32, + module.createUnary(UnaryOp.AbsF32, + module.createTeeLocal(tempLocal0.index, arg0) ), - compiler.module.createF32(Infinity) + module.createF32(Infinity) ), - compiler.module.createI32(0), - compiler.module.createBinary(BinaryOp.EqF32, - compiler.module.createGetLocal(tempLocal0.index, NativeType.F32), - compiler.module.createGetLocal(tempLocal0.index, NativeType.F32) + module.createI32(0), + module.createBinary(BinaryOp.EqF32, + module.createGetLocal(tempLocal0.index, NativeType.F32), + module.createGetLocal(tempLocal0.index, NativeType.F32) ) ); } else { tempLocal0 = compiler.currentFunction.addLocal(Type.f64); - return compiler.module.createSelect( - compiler.module.createBinary(BinaryOp.NeF64, - compiler.module.createUnary(UnaryOp.AbsF64, - compiler.module.createTeeLocal(tempLocal0.index, arg0) + return module.createSelect( + module.createBinary(BinaryOp.NeF64, + module.createUnary(UnaryOp.AbsF64, + module.createTeeLocal(tempLocal0.index, arg0) ), - compiler.module.createF64(Infinity) + module.createF64(Infinity) ), - compiler.module.createI32(0), - compiler.module.createBinary(BinaryOp.EqF64, - compiler.module.createGetLocal(tempLocal0.index, NativeType.F64), - compiler.module.createGetLocal(tempLocal0.index, NativeType.F64) + module.createI32(0), + module.createBinary(BinaryOp.EqF64, + module.createGetLocal(tempLocal0.index, NativeType.F64), + module.createGetLocal(tempLocal0.index, NativeType.F64) ) ); } @@ -474,14 +488,14 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume case "assert": // assert(isTrue: bool) -> void compiler.currentType = Type.void; if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) - return compiler.module.createUnreachable(); + return module.createUnreachable(); arg0 = compiler.compileExpression(operands[0], Type.i32); // reports compiler.currentType = Type.void; return compiler.options.noDebug - ? compiler.module.createNop() - : compiler.module.createIf( - compiler.module.createUnary(UnaryOp.EqzI32, arg0), - compiler.module.createUnreachable() + ? module.createNop() + : module.createIf( + module.createUnary(UnaryOp.EqzI32, arg0), + module.createUnreachable() ); // case "fmod": diff --git a/src/compiler.ts b/src/compiler.ts index 9ddf4b11..5781904d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -148,7 +148,7 @@ export class Compiler extends DiagnosticEmitter { const startFunctionTemplate: FunctionPrototype = new FunctionPrototype(program, "start", null); const startFunctionInstance: Function = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null); this.currentFunction = this.startFunction = startFunctionInstance; - this.memoryOffset = new U64(2 * (this.options.target == Target.WASM64 ? 8 : 4), 0); // leave space for `null` and heapStart (both of usize type) + this.memoryOffset = new U64(this.options.target == Target.WASM64 ? 8 : 4, 0); // leave space for `null` } /** Performs compilation of the underlying {@link Program} to a {@link Module}. */ @@ -178,32 +178,12 @@ export class Compiler extends DiagnosticEmitter { } // set up memory - // store heapStart at `sizeof()` (that is right after `null`) as an usize const initial: U64 = this.memoryOffset.clone(); - let heapStartBuffer: Uint8Array; - let heapStartOffset: i32; - if (this.options.target == Target.WASM64) { - heapStartBuffer = new Uint8Array(8); - heapStartOffset = 8; - heapStartBuffer[0] = (initial.lo ) as u8; - heapStartBuffer[1] = (initial.lo >>> 8) as u8; - heapStartBuffer[2] = (initial.lo >>> 16) as u8; - heapStartBuffer[3] = (initial.lo >>> 24) as u8; - heapStartBuffer[4] = (initial.hi ) as u8; - heapStartBuffer[5] = (initial.hi >>> 8) as u8; - heapStartBuffer[6] = (initial.hi >>> 16) as u8; - heapStartBuffer[7] = (initial.hi >>> 24) as u8; - } else { - if (!initial.fitsInU32) - throw new Error("static memory size overflows 32 bits"); - heapStartBuffer = new Uint8Array(4); - heapStartOffset = 4; - heapStartBuffer[0] = (initial.lo ) as u8; - heapStartBuffer[1] = (initial.lo >>> 8) as u8; - heapStartBuffer[2] = (initial.lo >>> 16) as u8; - heapStartBuffer[3] = (initial.lo >>> 24) as u8; - } - this.memorySegments.push(MemorySegment.create(heapStartBuffer, new U64(heapStartOffset, 0))); // TODO: use a global instead? + if (this.options.target == Target.WASM64) + this.module.addGlobal("HEAP_START", NativeType.I64, false, this.module.createI64(initial.lo, initial.hi)); + else + this.module.addGlobal("HEAP_START", NativeType.I32, false, this.module.createI32(initial.lo)); + // determine initial page size const initialOverlaps: U64 = initial.clone(); initialOverlaps.and32(0xffff); @@ -663,7 +643,7 @@ export class Compiler extends DiagnosticEmitter { // memory addMemorySegment(buffer: Uint8Array): MemorySegment { - if (this.memoryOffset.lo & 7) { // align to 8 bytes so any possible data type is aligned here + if (this.memoryOffset.lo & 7) { // align to 8 bytes so any native data type is aligned here this.memoryOffset.or32(7); this.memoryOffset.add32(1); } diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index a043c65c..6c2a899e 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -7,6 +7,7 @@ export enum DiagnosticCode { Operation_is_unsafe = 103, Cannot_export_a_mutable_global = 104, Compiling_constant_global_with_non_constant_initializer_as_mutable = 105, + Type_0_cannot_be_changed_to_type_1 = 106, Unterminated_string_literal = 1002, Identifier_expected = 1003, _0_expected = 1005, @@ -79,6 +80,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 103: return "Operation is unsafe."; case 104: return "Cannot export a mutable global."; case 105: return "Compiling constant global with non-constant initializer as mutable."; + case 106: return "Type '{0}' cannot be changed to type '{1}'."; case 1002: return "Unterminated string literal."; case 1003: return "Identifier expected."; case 1005: return "'{0}' expected."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 98cce5ba..3f137ba6 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -5,6 +5,7 @@ "Operation is unsafe.": 103, "Cannot export a mutable global.": 104, "Compiling constant global with non-constant initializer as mutable.": 105, + "Type '{0}' cannot be changed to type '{1}'.": 106, "Unterminated string literal.": 1002, "Identifier expected.": 1003, diff --git a/src/index.ts b/src/index.ts index b6b3016e..e8e71a76 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,11 +20,12 @@ */ import { Module } from "./module"; -import { Compiler } from "./compiler"; -import { DiagnosticMessage, DiagnosticCategory } from "./diagnostics"; +import { Compiler, Options, Target } from "./compiler"; +import { DiagnosticMessage, DiagnosticCategory, formatDiagnosticMessage } from "./diagnostics"; import { Parser } from "./parser"; import { Program } from "./program"; +/** Parses a single source file. If `parser` has been omitted a new one is created. */ export function parseFile(text: string, path: string, parser: Parser | null = null): Parser { let isEntry: bool = false; if (!parser) { @@ -35,10 +36,12 @@ export function parseFile(text: string, path: string, parser: Parser | null = nu return parser; } +/** Obtains the path to the next file required by the parser. Returns `null` once complete. */ export function nextFile(parser: Parser): string | null { return parser.nextFile(); } +/** Obtains the next diagnostic message. Returns `null` once there are no more messages. */ export function nextDiagnostic(parser: Parser): DiagnosticMessage | null { const program: Program = parser.program; if (program.diagnosticsOffset < program.diagnostics.length) @@ -46,13 +49,49 @@ export function nextDiagnostic(parser: Parser): DiagnosticMessage | null { return null; } +/** Formats a diagnostic message to a string. */ +export function formatDiagnostic(message: DiagnosticMessage, useColors: bool, showContext: bool): string { + return formatDiagnosticMessage(message, useColors, showContext); +} + +/** Tests whether a diagnostic is informatory. */ +export function isInfo(message: DiagnosticMessage): bool { + return message.category == DiagnosticCategory.INFO; +} + +/** Tests whether a diagnostic is a warning. */ +export function isWarning(message: DiagnosticMessage): bool { + return message.category == DiagnosticCategory.WARNING; +} + +/** Tests whether a diagnostic is an error. */ export function isError(message: DiagnosticMessage): bool { return message.category == DiagnosticCategory.ERROR; } -export function compile(parser: Parser): Module { - const program: Program = parser.finish(); - return Compiler.compile(program); +/** Creates a new set of compiler options. */ +export function createOptions(): Options { + return new Options(); } -export { DiagnosticMessage, formatDiagnosticMessage as formatDiagnostic } from "./diagnostics"; +/** Sets the `target` option. */ +export function setTarget(options: Options, target: Target): void { + options.target = target; +} + +/** Sets the `noTreeShaking` option. */ +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; +} + +/** Compiles the sources computed by the parser to a module. */ +export function compile(parser: Parser, options: Options | null = null): Module { + const program: Program = parser.finish(); + const compiler: Compiler = new Compiler(program, options); + return compiler.compile(); +} diff --git a/src/parser.ts b/src/parser.ts index b7522a86..b28256e2 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -13,6 +13,7 @@ import { DiagnosticCode, DiagnosticEmitter } from "./diagnostics"; import { normalizePath, I64 } from "./util"; import { + Node, NodeKind, Source, @@ -30,7 +31,7 @@ import { BreakStatement, ClassDeclaration, ContinueStatement, - DecoratorStatement, + Decorator, DoStatement, EnumDeclaration, EnumValueDeclaration, @@ -60,7 +61,8 @@ import { VariableDeclaration, WhileStatement, - hasModifier + hasModifier, + NamespaceDeclaration } from "./ast"; @@ -89,102 +91,7 @@ export class Parser extends DiagnosticEmitter { source.tokenizer = tn; while (!tn.skip(Token.ENDOFFILE)) { - - let decorators: DecoratorStatement[] | null = null; - - while (tn.skip(Token.AT)) { - const decorator: DecoratorStatement | null = this.parseDecorator(tn); - if (!decorator) - break; - if (!decorators) - decorators = new Array(); - (decorators).push(decorator); - } - - let modifiers: Modifier[] | null = null; - - if (tn.skip(Token.EXPORT)) - modifiers = addModifier(Statement.createModifier(ModifierKind.EXPORT, tn.range()), modifiers); - - if (tn.skip(Token.DECLARE)) { - modifiers = addModifier(Statement.createModifier(ModifierKind.DECLARE, tn.range()), modifiers); - tn.peek(true); - if (tn.nextTokenOnNewLine) - this.error(DiagnosticCode.Line_break_not_permitted_here, tn.range(tn.pos)); // recoverable, compatibility - } - - tn.mark(); - - let statement: Statement | null = null; - switch (tn.next()) { - - case Token.CONST: - modifiers = addModifier(Statement.createModifier(ModifierKind.CONST, tn.range()), modifiers); - - if (tn.skip(Token.ENUM)) { - statement = this.parseEnum(tn, modifiers ? modifiers : createModifiers()); - break; - } - // fall through - - case Token.VAR: - case Token.LET: - statement = this.parseVariable(tn, modifiers ? modifiers : createModifiers()); - break; - - case Token.ENUM: - statement = this.parseEnum(tn, modifiers ? modifiers : createModifiers()); - break; - - case Token.FUNCTION: - statement = this.parseFunction(tn, modifiers ? modifiers : createModifiers(), decorators); - decorators = null; - break; - - case Token.ABSTRACT: - if (!tn.skip(Token.CLASS)) { - this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "class"); - break; - } - modifiers = addModifier(Statement.createModifier(ModifierKind.ABSTRACT, tn.range()), modifiers); - // fall through - - case Token.CLASS: - statement = this.parseClass(tn, modifiers ? modifiers : createModifiers(), decorators); - decorators = null; - break; - - case Token.IMPORT: - if (hasModifier(ModifierKind.EXPORT, modifiers)) { - statement = this.parseExportImport(tn, getModifier(ModifierKind.EXPORT, modifiers).range); - } else - statement = this.parseImport(tn); - if (modifiers) - reusableModifiers = modifiers; - break; - - case Token.TYPE: - // TODO - - default: - if (hasModifier(ModifierKind.EXPORT, modifiers)) { - tn.reset(); - statement = this.parseExport(tn, modifiers ? modifiers : createModifiers()); - } else { - if (modifiers) { - if (hasModifier(ModifierKind.DECLARE, modifiers)) - this.error(DiagnosticCode._0_modifier_cannot_be_used_here, getModifier(ModifierKind.DECLARE, modifiers).range, "declare"); // recoverable - reusableModifiers = modifiers; - } - tn.reset(); - statement = this.parseStatement(tn, true); - } - break; - } - - if (decorators) - for (let i: i32 = 0, k: i32 = (decorators).length; i < k; ++i) - this.error(DiagnosticCode.Decorators_are_not_valid_here, (decorators)[i].range); + const statement: Statement | null = this.parseTopLevelStatement(tn); if (!statement) return; statement.parent = source; @@ -193,6 +100,114 @@ export class Parser extends DiagnosticEmitter { reusableModifiers = null; } + parseTopLevelStatement(tn: Tokenizer, isNamespaceMember: bool = false): Statement | null { + let decorators: Decorator[] | null = null; + + while (tn.skip(Token.AT)) { + const decorator: Decorator | null = this.parseDecorator(tn); + if (!decorator) + break; + if (!decorators) + decorators = new Array(); + (decorators).push(decorator); + } + + let modifiers: Modifier[] | null = null; + + if (tn.skip(Token.EXPORT)) + modifiers = addModifier(Statement.createModifier(ModifierKind.EXPORT, tn.range()), modifiers); + + if (tn.skip(Token.DECLARE)) { + modifiers = addModifier(Statement.createModifier(ModifierKind.DECLARE, tn.range()), modifiers); + tn.peek(true); + if (tn.nextTokenOnNewLine) + this.error(DiagnosticCode.Line_break_not_permitted_here, tn.range(tn.pos)); // recoverable, compatibility + } + + tn.mark(); + + let statement: Statement | null = null; + switch (tn.next()) { + + case Token.CONST: + modifiers = addModifier(Statement.createModifier(ModifierKind.CONST, tn.range()), modifiers); + + if (tn.skip(Token.ENUM)) { + statement = this.parseEnum(tn, modifiers, decorators); + break; + } + // fall through + + case Token.VAR: + case Token.LET: + statement = this.parseVariable(tn, modifiers, decorators); + decorators = null; + break; + + case Token.ENUM: + statement = this.parseEnum(tn, modifiers, decorators); + decorators = null; + break; + + case Token.FUNCTION: + statement = this.parseFunction(tn, modifiers, decorators); + decorators = null; + break; + + case Token.ABSTRACT: + if (!tn.skip(Token.CLASS)) { + this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "class"); + break; + } + modifiers = addModifier(Statement.createModifier(ModifierKind.ABSTRACT, tn.range()), modifiers); + // fall through + + case Token.CLASS: + statement = this.parseClass(tn, modifiers, decorators); + decorators = null; + break; + + case Token.NAMESPACE: + statement = this.parseNamespace(tn, modifiers, decorators); + decorators = null; + break; + + case Token.IMPORT: + if (hasModifier(ModifierKind.EXPORT, modifiers)) { + statement = this.parseExportImport(tn, getModifier(ModifierKind.EXPORT, modifiers).range); + } else + statement = this.parseImport(tn); + if (modifiers) + reusableModifiers = modifiers; + break; + + case Token.TYPE: + // TODO + + default: + if (hasModifier(ModifierKind.EXPORT, modifiers)) { + tn.reset(); + statement = this.parseExport(tn, modifiers); // TODO: why exactly does this have modifiers again? + } else { + if (modifiers) { + if (hasModifier(ModifierKind.DECLARE, modifiers)) + this.error(DiagnosticCode._0_modifier_cannot_be_used_here, getModifier(ModifierKind.DECLARE, modifiers).range, "declare"); // recoverable + reusableModifiers = modifiers; + } + tn.reset(); + if (!isNamespaceMember) + statement = this.parseStatement(tn, true); + } + break; + } + + if (decorators /* not consumed */) + for (let i: i32 = 0, k: i32 = (decorators).length; i < k; ++i) + this.error(DiagnosticCode.Decorators_are_not_valid_here, (decorators)[i].range); + + return statement; + } + nextFile(): string | null { if (this.backlog.length) { const filename: string = this.backlog[0]; @@ -318,7 +333,7 @@ export class Parser extends DiagnosticEmitter { // statements - parseDecorator(tn: Tokenizer): DecoratorStatement | null { + parseDecorator(tn: Tokenizer): Decorator | null { // at '@': Identifier ('.' Identifier)* '(' Arguments const startPos: i32 = tn.tokenPos; if (tn.skip(Token.IDENTIFIER)) { @@ -344,24 +359,24 @@ export class Parser extends DiagnosticEmitter { return null; } - parseVariable(tn: Tokenizer, modifiers: Modifier[]): VariableStatement | null { + parseVariable(tn: Tokenizer, modifiers: Modifier[] | null, decorators: Decorator[] | null): VariableStatement | null { // at ('const' | 'let' | 'var'): VariableDeclaration (',' VariableDeclaration)* ';'? - const startPos: i32 = modifiers.length ? modifiers[0].range.start : tn.tokenPos; + const startPos: i32 = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos; const members: VariableDeclaration[] = new Array(); const isDeclare = hasModifier(ModifierKind.DECLARE, modifiers); do { - const member: VariableDeclaration | null = this.parseVariableDeclaration(tn, isDeclare, modifiers); + const member: VariableDeclaration | null = this.parseVariableDeclaration(tn, isDeclare, modifiers, decorators); if (!member) return null; members.push(member); } while (tn.skip(Token.COMMA)); - const ret: VariableStatement = Statement.createVariable(modifiers, members, tn.range(startPos, tn.pos)); + const ret: VariableStatement = Statement.createVariable(members, modifiers, decorators, tn.range(startPos, tn.pos)); tn.skip(Token.SEMICOLON); return ret; } - parseVariableDeclaration(tn: Tokenizer, isDeclare: bool = false, parentModifiers: Modifier[]): VariableDeclaration | null { + parseVariableDeclaration(tn: Tokenizer, isDeclare: bool = false, parentModifiers: Modifier[] | null, parentDecorators: Decorator[] | null): VariableDeclaration | null { // Identifier (':' Type)? ('=' Expression)? if (!tn.skip(Token.IDENTIFIER)) { this.error(DiagnosticCode.Identifier_expected, tn.range()); @@ -383,12 +398,12 @@ export class Parser extends DiagnosticEmitter { if (!initializer) return null; } - return Statement.createVariableDeclaration(identifier, type, initializer, parentModifiers, Range.join(identifier.range, tn.range())); + return Statement.createVariableDeclaration(identifier, type, initializer, parentModifiers, parentDecorators, Range.join(identifier.range, tn.range())); } - parseEnum(tn: Tokenizer, modifiers: Modifier[]): EnumDeclaration | null { + parseEnum(tn: Tokenizer, modifiers: Modifier[] | null, decorators: Decorator[] | null): EnumDeclaration | null { // at 'enum': Identifier '{' (EnumValueDeclaration (',' EnumValueDeclaration )*)? '}' ';'? - const startPos: i32 = modifiers.length ? modifiers[0].range.start : tn.tokenPos; + const startPos: i32 = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos; if (tn.next() != Token.IDENTIFIER) { this.error(DiagnosticCode.Identifier_expected, tn.range()); return null; @@ -411,7 +426,7 @@ export class Parser extends DiagnosticEmitter { return null; } } - const ret: EnumDeclaration = Statement.createEnum(modifiers, identifier, members, tn.range(startPos, tn.pos)); + const ret: EnumDeclaration = Statement.createEnum(identifier, members, modifiers, decorators, tn.range(startPos, tn.pos)); tn.skip(Token.SEMICOLON); return ret; } @@ -528,13 +543,9 @@ export class Parser extends DiagnosticEmitter { return null; } - parseFunction(tn: Tokenizer, modifiers: Modifier[], decorators: DecoratorStatement[] | null): FunctionDeclaration | null { + parseFunction(tn: Tokenizer, modifiers: Modifier[] | null, decorators: Decorator[] | null): FunctionDeclaration | null { // at 'function': Identifier ('<' TypeParameters)? '(' Parameters (':' Type)? '{' Statement* '}' ';'? - const startPos: i32 = decorators && (decorators).length - ? (decorators)[0].range.start - : modifiers.length - ? modifiers[0].range.start - : tn.tokenPos; + const startPos: i32 = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos; if (!tn.skip(Token.IDENTIFIER)) { this.error(DiagnosticCode.Identifier_expected, tn.range(tn.pos)); @@ -576,16 +587,16 @@ export class Parser extends DiagnosticEmitter { } } else if (!isDeclare) this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, tn.range(tn.pos)); - const ret: FunctionDeclaration = Statement.createFunction(modifiers, identifier, typeParameters, parameters, returnType, statements, decorators ? decorators : [], tn.range(startPos, tn.pos)); + const ret: FunctionDeclaration = Statement.createFunction(identifier, typeParameters, parameters, returnType, statements, modifiers, decorators, tn.range(startPos, tn.pos)); tn.skip(Token.SEMICOLON); return ret; } - parseClass(tn: Tokenizer, modifiers: Modifier[], decorators: DecoratorStatement[] | null): ClassDeclaration | null { + parseClass(tn: Tokenizer, modifiers: Modifier[] | null, decorators: Decorator[] | null): ClassDeclaration | null { // at 'class': Identifier ('<' TypeParameters)? ('extends' Type)? ('implements' Type (',' Type)*)? '{' ClassMember* '}' - const startPos: i32 = decorators && (decorators).length - ? (decorators)[0].range.start - : modifiers.length + const startPos: i32 = decorators && decorators.length + ? decorators[0].range.start + : modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos; @@ -628,7 +639,7 @@ export class Parser extends DiagnosticEmitter { members.push(member); } while (!tn.skip(Token.CLOSEBRACE)); } - return Statement.createClass(modifiers, identifier, typeParameters, extendsType, implementsTypes, members, decorators ? decorators : [], tn.range(startPos, tn.pos)); + return Statement.createClass(identifier, typeParameters, extendsType, implementsTypes, members, modifiers, decorators, tn.range(startPos, tn.pos)); } else this.error(DiagnosticCode._0_expected, tn.range(), "{"); } else @@ -640,13 +651,13 @@ export class Parser extends DiagnosticEmitter { // ('public' | 'private' | 'protected')? ('static' | 'abstract')? ('get' | 'set')? Identifier ... const startRange: Range = tn.range(); - let decorators: DecoratorStatement[] = new Array(); + let decorators: Decorator[] = new Array(); while (tn.skip(Token.AT)) { - const decorator: DecoratorStatement | null = this.parseDecorator(tn); + const decorator: Decorator | null = this.parseDecorator(tn); if (!decorator) break; - decorators.push(decorator); + decorators.push(decorator); } let modifiers: Modifier[] | null = null; @@ -706,7 +717,7 @@ export class Parser extends DiagnosticEmitter { this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, tn.range()); // recoverable } - const ret: MethodDeclaration = Statement.createMethod(modifiers ? modifiers : createModifiers(), identifier, typeParameters, parameters, returnType, statements, decorators, Range.join(startRange, tn.range())); + const ret: MethodDeclaration = Statement.createMethod(identifier, typeParameters, parameters, returnType, statements, modifiers, decorators, Range.join(startRange, tn.range())); tn.skip(Token.SEMICOLON); return ret; @@ -731,7 +742,7 @@ export class Parser extends DiagnosticEmitter { if (!initializer) return null; } - const ret: FieldDeclaration = Statement.createField(modifiers ? modifiers : createModifiers(), identifier, type, initializer, decorators, Range.join(startRange, tn.range())); + const ret: FieldDeclaration = Statement.createField(identifier, type, initializer, modifiers, decorators, Range.join(startRange, tn.range())); tn.skip(Token.SEMICOLON); return ret; } @@ -740,9 +751,32 @@ export class Parser extends DiagnosticEmitter { return null; } - parseExport(tn: Tokenizer, modifiers: Modifier[]): ExportStatement | null { + parseNamespace(tn: Tokenizer, modifiers: Modifier[] | null, decorators: Decorator[] | null): NamespaceDeclaration | null { + // at 'namespace': Identifier '{' (Variable | Function)* '}' + const startRange: Range = modifiers && modifiers.length ? modifiers[0].range : tn.range(); + if (tn.skip(Token.IDENTIFIER)) { + const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range()); + if (tn.skip(Token.OPENBRACE)) { + const members: Statement[] = new Array(); + while (!tn.skip(Token.CLOSEBRACE)) { + const member: Statement | null = this.parseTopLevelStatement(tn, true); + if (!member) + return null; + members.push(member); + } + const ret: NamespaceDeclaration = Statement.createNamespace(identifier, members, modifiers, decorators, Range.join(startRange, tn.range())); + tn.skip(Token.SEMICOLON); + return ret; + } else + this.error(DiagnosticCode._0_expected, tn.range(), "{"); + } else + this.error(DiagnosticCode.Identifier_expected, tn.range()); + return null; + } + + parseExport(tn: Tokenizer, modifiers: Modifier[] | null): ExportStatement | null { // at 'export': '{' ExportMember (',' ExportMember)* }' ('from' StringLiteral)? ';'? - const startRange: Range = modifiers.length ? modifiers[0].range : tn.range(); + const startRange: Range = modifiers && modifiers.length ? modifiers[0].range : tn.range(); if (tn.skip(Token.OPENBRACE)) { const members: ExportMember[] = new Array(); if (!tn.skip(Token.CLOSEBRACE)) { @@ -766,7 +800,7 @@ export class Parser extends DiagnosticEmitter { return null; } } - const ret: ExportStatement = Statement.createExport(modifiers, members, path, Range.join(startRange, tn.range())); + const ret: ExportStatement = Statement.createExport(members, path, modifiers, Range.join(startRange, tn.range())); if (ret.normalizedPath && !this.seenlog.has(ret.normalizedPath)) { this.backlog.push(ret.normalizedPath); this.seenlog.add(ret.normalizedPath); @@ -881,7 +915,7 @@ export class Parser extends DiagnosticEmitter { return this.parseBreak(tn); case Token.CONST: - return this.parseVariable(tn, [ Statement.createModifier(ModifierKind.CONST, tn.range()) ]); + return this.parseVariable(tn, [ Statement.createModifier(ModifierKind.CONST, tn.range()) ], null); case Token.CONTINUE: return this.parseContinue(tn); @@ -897,7 +931,7 @@ export class Parser extends DiagnosticEmitter { case Token.LET: case Token.VAR: - return this.parseVariable(tn, []); + return this.parseVariable(tn, null, null); case Token.OPENBRACE: return this.parseBlockStatement(tn, topLevel); @@ -1007,7 +1041,7 @@ export class Parser extends DiagnosticEmitter { if (tn.skip(Token.OPENPAREN)) { let initializer: Statement | null = null; if (tn.skip(Token.LET) || tn.skip(Token.CONST) || tn.skip(Token.VAR)) { - initializer = this.parseVariable(tn, /* TODO */ createModifiers()); + initializer = this.parseVariable(tn, null, null); } else if (!tn.skip(Token.SEMICOLON)) { initializer = this.parseExpressionStatement(tn); if (!initializer) diff --git a/src/program.ts b/src/program.ts index 42b28a18..b9eb4993 100644 --- a/src/program.ts +++ b/src/program.ts @@ -22,7 +22,7 @@ import { ClassDeclaration, DeclarationStatement, - DecoratorStatement, + Decorator, EnumDeclaration, EnumValueDeclaration, ExportMember, @@ -665,9 +665,9 @@ export class Program extends DiagnosticEmitter { } } -function checkGlobalDecorator(decorators: DecoratorStatement[]): string | null { +function checkGlobalDecorator(decorators: Decorator[]): string | null { for (let i: i32 = 0, k: i32 = decorators.length; i < k; ++i) { - const decorator: DecoratorStatement = decorators[i]; + const decorator: Decorator = decorators[i]; const expression: Expression = decorator.expression; const args: Expression[] = decorator.arguments; if (expression.kind == NodeKind.IDENTIFIER && args.length <= 1 && (expression).name == "global") { diff --git a/std/carray.d.ts b/std/carray.d.ts index 145da600..3cae38e9 100644 --- a/std/carray.d.ts +++ b/std/carray.d.ts @@ -1,6 +1,12 @@ /// +/** A C-compatible array class. */ declare class CArray { [key: number]: T; + + /** Constructs a new C-Array of the specified capacity. */ constructor(capacity: usize); + + /** Disposes this instance and the memory associated with it. */ + dispose(): void; } diff --git a/std/cstring.d.ts b/std/cstring.d.ts index a0fed1ab..c2bcff1f 100644 --- a/std/cstring.d.ts +++ b/std/cstring.d.ts @@ -1,5 +1,12 @@ /// -declare class CString extends CArray { - constructor(text: string); +/** A C-compatible string class. */ +declare class CString { + readonly [key: number]: u8; + + /** Constructs a new C-String from a standard string. */ + constructor(string: string); + + /** Disposes this instance and the memory associated with it. */ + dispose(): void; } diff --git a/std/heap.ts b/std/heap.ts new file mode 100644 index 00000000..7309739a --- /dev/null +++ b/std/heap.ts @@ -0,0 +1,20 @@ +/// + +/** A static class representing the heap. */ +declare class Heap { + + /** Allocates a chunk of memory and returns a pointer to it. */ + static allocate(size: usize): usize; + + /** Disposes a chunk of memory by its pointer. */ + static dispose(ptr: usize): void; + + /** Gets the amount of used heap space, in bytes. */ + static get used(): usize; + + /** Gets the amount of free heap space, in bytes. */ + static get free(): usize; + + /** Gets the size of the heap, in bytes. */ + static get size(): usize; +} diff --git a/std/impl/carray.ts b/std/impl/carray.ts index 5a3b9d96..45ee276b 100644 --- a/std/impl/carray.ts +++ b/std/impl/carray.ts @@ -1,29 +1,25 @@ /// -/** A C-compatible Array class. */ @global() +@struct() class CArray { - /** Constructs a new C-Array of the specified capacity. */ constructor(capacity: usize) { - return unsafe_cast(Memory.allocate(capacity * sizeof())); + return changetype(Heap.allocate(capacity * sizeof())); } - /** Gets the element at the specified index using bracket notation. */ @inline() "[]"(index: usize): T { - return load(unsafe_cast(this) + index * sizeof()); + return load(changetype(this) + index * sizeof()); } - /** Sets the element at the specified index using bracket notation. */ @inline() "[]="(index: usize, value: T): T { - store(unsafe_cast(this) + index * sizeof(), value); + store(changetype(this) + index * sizeof(), value); return value; } - /** Disposes this instance and the memory associated with it. */ dispose(): void { - Memory.dispose(unsafe_cast(this)); + Heap.dispose(changetype(this)); } } diff --git a/std/impl/cstring.ts b/std/impl/cstring.ts index b8d7675a..ec62961a 100644 --- a/std/impl/cstring.ts +++ b/std/impl/cstring.ts @@ -1,17 +1,16 @@ /// -/** A C-compatible string class. */ @global() -class CString extends CArray { +@struct() +class CString { - /** Constructs a new C-String from a String. */ - constructor(text: string) { - super(text.length * 2 + 1); - let idx: usize = unsafe_cast(this); - for (let i: usize = 0, k: usize = (str).length; i < k; ++i) { - let u: i32 = text.charCodeAt(i); + constructor(string: string) { + const ptr: usize = Heap.allocate(string.length * 2 + 1); + let idx: usize = ptr; + for (let i: usize = 0, k: usize = (str).length; i < k; ++i) { + let u: i32 = string.charCodeAt(i); if (u >= 0xD800 && u <= 0xDFFF && i + 1 < k) - u = 0x10000 + ((u & 0x3FF) << 10) | (text.charCodeAt(++i) & 0x3FF); + u = 0x10000 + ((u & 0x3FF) << 10) | (string.charCodeAt(++i) & 0x3FF); if (u <= 0x7F) store(idx++, u as u8); else if (u <= 0x7FF) { @@ -43,5 +42,17 @@ class CString extends CArray { } } store(idx, 0); + return changetype(ptr); + } + + @inline() + "[]"(index: usize): u8 { + return load(changetype(this) + index /* * sizeof() */); + } + + // read-only + + dispose(): void { + Heap.dispose(changetype(this)); } } diff --git a/std/impl/heap.ts b/std/impl/heap.ts new file mode 100644 index 00000000..d6e2bca4 --- /dev/null +++ b/std/impl/heap.ts @@ -0,0 +1,38 @@ +/// + +const ALIGN_LOG2: usize = 3; +const ALIGN_SIZE: usize = 1 << ALIGN_LOG2; +const ALIGN_MASK: usize = ALIGN_SIZE - 1; + +let HEAP_OFFSET: usize = HEAP_START; // HEAP_START is a constant generated by the compiler + +@global() +@struct() +class Heap { + + static allocate(size: usize): usize { + const ptr: usize = HEAP_OFFSET; + assert(ptr + size <= (current_memory() << 16)); + if (((HEAP_OFFSET += size) & ALIGN_MASK) != 0) // align next offset + HEAP_OFFSET = (HEAP_OFFSET | ALIGN_MASK) + 1; + return ptr; + } + + static dispose(ptr: usize): void { + // just a big chunk of non-disposable memory for now + } + + static get used(): usize { + return HEAP_OFFSET - HEAP_START; + } + + static get free(): usize { + return (current_memory() << 16) - HEAP_OFFSET; + } + + static get size(): usize { + return (current_memory() << 16) - HEAP_START; + } + + private constructor() {} +} diff --git a/std/impl/memory.ts b/std/impl/memory.ts deleted file mode 100644 index 7ad2e8d7..00000000 --- a/std/impl/memory.ts +++ /dev/null @@ -1,21 +0,0 @@ -/// - -const MEMORY_ALIGN_LOG2: usize = 3; -const MEMORY_ALIGN_SIZE: usize = 1 << MEMORY_ALIGN_LOG2; -const MEMORY_ALIGN_MASK: usize = MEMORY_ALIGN_SIZE - 1; - -@global() -class Memory { - - static allocate(size: usize): usize { - const ptr: usize = HEAP_OFFSET; - HEAP_OFFSET += size; - if ((HEAP_OFFSET & MEMORY_ALIGN_MASK) != 0) - HEAP_OFFSET = (HEAP_OFFSET | MEMORY_ALIGN_MASK) + 1; - return ptr; - } - - static dispose(ptr: usize): void { - // just a big chunk of non-disposable memory for now - } -} diff --git a/std/impl/tsconfig.json b/std/impl/tsconfig.json index 6241f0cf..b8e74559 100644 --- a/std/impl/tsconfig.json +++ b/std/impl/tsconfig.json @@ -6,6 +6,6 @@ "files": [ "carray.ts", "cstring.ts", - "memory.ts" + "heap.ts" ] } \ No newline at end of file diff --git a/std/memory.d.ts b/std/memory.d.ts deleted file mode 100644 index cdd44d71..00000000 --- a/std/memory.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// - -declare class Memory { - static allocate(size: usize): usize; - static dispose(ptr: usize): void; -} diff --git a/std/tsconfig.json b/std/tsconfig.json index 6963be52..6e2597ab 100644 --- a/std/tsconfig.json +++ b/std/tsconfig.json @@ -6,6 +6,6 @@ "files": [ "carray.d.ts", "cstring.d.ts", - "memory.d.ts" + "heap.d.ts" ] } \ No newline at end of file diff --git a/tests/compiler/assert.optimized.wast b/tests/compiler/assert.optimized.wast index 2b1bd4dc..bfec40ca 100644 --- a/tests/compiler/assert.optimized.wast +++ b/tests/compiler/assert.optimized.wast @@ -1,7 +1,6 @@ (module (type $v (func)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/assert.wast b/tests/compiler/assert.wast index ba061847..010cb566 100644 --- a/tests/compiler/assert.wast +++ b/tests/compiler/assert.wast @@ -1,7 +1,7 @@ (module (type $v (func)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -37,6 +37,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/binary.optimized.wast b/tests/compiler/binary.optimized.wast index c00d003e..3af1279d 100644 --- a/tests/compiler/binary.optimized.wast +++ b/tests/compiler/binary.optimized.wast @@ -6,7 +6,6 @@ (global $binary/f (mut f32) (f32.const 0)) (global $binary/F (mut f64) (f64.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/binary.wast b/tests/compiler/binary.wast index 8fcaf2f0..c492c038 100644 --- a/tests/compiler/binary.wast +++ b/tests/compiler/binary.wast @@ -5,8 +5,8 @@ (global $binary/I (mut i64) (i64.const 0)) (global $binary/f (mut f32) (f32.const 0)) (global $binary/F (mut f64) (f64.const 0)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -840,6 +840,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/builtins.optimized.wast b/tests/compiler/builtins.optimized.wast index 88b5efa0..ed3daf84 100644 --- a/tests/compiler/builtins.optimized.wast +++ b/tests/compiler/builtins.optimized.wast @@ -7,7 +7,6 @@ (global $builtins/F (mut f64) (f64.const 0)) (global $builtins/s (mut i32) (i32.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index 86eeb309..a9a67297 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -6,8 +6,8 @@ (global $builtins/b (mut i32) (i32.const 0)) (global $builtins/F (mut f64) (f64.const 0)) (global $builtins/s (mut i32) (i32.const 0)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -1083,6 +1083,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/declare.wast b/tests/compiler/declare.wast index 39be5905..0699e334 100644 --- a/tests/compiler/declare.wast +++ b/tests/compiler/declare.wast @@ -1,8 +1,8 @@ (module (type $v (func)) (import "env" "external" (func $declare/external)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "external" (func $declare/external)) (export "memory" (memory $0)) ) @@ -30,6 +30,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/do.optimized.wast b/tests/compiler/do.optimized.wast index 2a6b7f33..0962bcc9 100644 --- a/tests/compiler/do.optimized.wast +++ b/tests/compiler/do.optimized.wast @@ -1,7 +1,6 @@ (module (type $iv (func (param i32))) (memory $0 1) - (data (i32.const 4) "\08") (export "loopDo" (func $do/loopDo)) (export "loopDoInDo" (func $do/loopDoInDo)) (export "memory" (memory $0)) diff --git a/tests/compiler/do.wast b/tests/compiler/do.wast index 8611186a..d88e76fc 100644 --- a/tests/compiler/do.wast +++ b/tests/compiler/do.wast @@ -1,7 +1,7 @@ (module (type $iv (func (param i32))) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "loopDo" (func $do/loopDo)) (export "loopDoInDo" (func $do/loopDoInDo)) (export "memory" (memory $0)) @@ -75,6 +75,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/enum.optimized-inlined.wast b/tests/compiler/enum.optimized-inlined.wast index e4738a53..8f245598 100644 --- a/tests/compiler/enum.optimized-inlined.wast +++ b/tests/compiler/enum.optimized-inlined.wast @@ -4,7 +4,6 @@ (global $enum/NonConstant.ZERO (mut i32) (i32.const 0)) (global $enum/NonConstant.ONE (mut i32) (i32.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/enum.optimized.wast b/tests/compiler/enum.optimized.wast index 9505dc59..dd1b5d01 100644 --- a/tests/compiler/enum.optimized.wast +++ b/tests/compiler/enum.optimized.wast @@ -4,7 +4,6 @@ (global $enum/NonConstant.ZERO (mut i32) (i32.const 0)) (global $enum/NonConstant.ONE (mut i32) (i32.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $enum/getZero (; 0 ;) (type $i) (result i32) diff --git a/tests/compiler/enum.wast b/tests/compiler/enum.wast index d15d73fb..aa5ef2f9 100644 --- a/tests/compiler/enum.wast +++ b/tests/compiler/enum.wast @@ -15,8 +15,8 @@ (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)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $enum/getZero (; 0 ;) (type $i) (result i32) @@ -60,6 +60,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/export.optimized.wast b/tests/compiler/export.optimized.wast index 226c555c..d80eec1d 100644 --- a/tests/compiler/export.optimized.wast +++ b/tests/compiler/export.optimized.wast @@ -3,7 +3,6 @@ (global $export/a i32 (i32.const 1)) (global $export/b i32 (i32.const 2)) (memory $0 1) - (data (i32.const 4) "\08") (export "add" (func $export/add)) (export "renamed_sub" (func $export/sub)) (export "a" (global $export/a)) diff --git a/tests/compiler/export.ts b/tests/compiler/export.ts index 73dffc41..ec033cd6 100644 --- a/tests/compiler/export.ts +++ b/tests/compiler/export.ts @@ -13,3 +13,8 @@ export const a: i32 = 1; const b: i32 = 2; export { b as renamed_b }; + +/* export namespace ns { + function one(): void {} + export function two(): void {} +} */ diff --git a/tests/compiler/export.wast b/tests/compiler/export.wast index 24b6af2f..7dfbc78b 100644 --- a/tests/compiler/export.wast +++ b/tests/compiler/export.wast @@ -2,8 +2,8 @@ (type $iii (func (param i32 i32) (result i32))) (global $export/a i32 (i32.const 1)) (global $export/b i32 (i32.const 2)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "add" (func $export/add)) (export "renamed_sub" (func $export/sub)) (export "a" (global $export/a)) @@ -50,6 +50,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/for.optimized.wast b/tests/compiler/for.optimized.wast index 71543b85..41329a3b 100644 --- a/tests/compiler/for.optimized.wast +++ b/tests/compiler/for.optimized.wast @@ -2,7 +2,6 @@ (type $v (func)) (global $for/i (mut i32) (i32.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/for.wast b/tests/compiler/for.wast index 5a2a4d10..24385088 100644 --- a/tests/compiler/for.wast +++ b/tests/compiler/for.wast @@ -1,8 +1,8 @@ (module (type $v (func)) (global $for/i (mut i32) (i32.const 0)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -171,6 +171,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/game-of-life.optimized.wast b/tests/compiler/game-of-life.optimized.wast index d6d2cf72..928b734e 100644 --- a/tests/compiler/game-of-life.optimized.wast +++ b/tests/compiler/game-of-life.optimized.wast @@ -5,7 +5,6 @@ (global $game-of-life/h (mut i32) (i32.const 0)) (global $game-of-life/s (mut i32) (i32.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "init" (func $game-of-life/init)) (export "step" (func $game-of-life/step)) (export "memory" (memory $0)) diff --git a/tests/compiler/game-of-life.wast b/tests/compiler/game-of-life.wast index d847c977..5dfcebc0 100644 --- a/tests/compiler/game-of-life.wast +++ b/tests/compiler/game-of-life.wast @@ -4,8 +4,8 @@ (global $game-of-life/w (mut i32) (i32.const 0)) (global $game-of-life/h (mut i32) (i32.const 0)) (global $game-of-life/s (mut i32) (i32.const 0)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "init" (func $game-of-life/init)) (export "step" (func $game-of-life/step)) (export "memory" (memory $0)) @@ -332,6 +332,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/if.optimized.wast b/tests/compiler/if.optimized.wast index 915c9c7c..6f5d3b2f 100644 --- a/tests/compiler/if.optimized.wast +++ b/tests/compiler/if.optimized.wast @@ -1,7 +1,6 @@ (module (type $ii (func (param i32) (result i32))) (memory $0 1) - (data (i32.const 4) "\08") (export "ifThenElse" (func $if/ifThenElse)) (export "ifThen" (func $if/ifThen)) (export "ifThenElseBlock" (func $if/ifThenElse)) diff --git a/tests/compiler/if.wast b/tests/compiler/if.wast index 230086ce..611ff45f 100644 --- a/tests/compiler/if.wast +++ b/tests/compiler/if.wast @@ -1,7 +1,7 @@ (module (type $ii (func (param i32) (result i32))) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "ifThenElse" (func $if/ifThenElse)) (export "ifThen" (func $if/ifThen)) (export "ifThenElseBlock" (func $if/ifThenElseBlock)) @@ -70,6 +70,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/import.optimized-inlined.wast b/tests/compiler/import.optimized-inlined.wast index fdaf0abe..0fa17f93 100644 --- a/tests/compiler/import.optimized-inlined.wast +++ b/tests/compiler/import.optimized-inlined.wast @@ -2,7 +2,6 @@ (type $iii (func (param i32 i32) (result i32))) (type $v (func)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/import.optimized.wast b/tests/compiler/import.optimized.wast index 91b3f080..f291faf1 100644 --- a/tests/compiler/import.optimized.wast +++ b/tests/compiler/import.optimized.wast @@ -2,7 +2,6 @@ (type $iii (func (param i32 i32) (result i32))) (type $v (func)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) diff --git a/tests/compiler/import.wast b/tests/compiler/import.wast index 80e26efa..74c7a514 100644 --- a/tests/compiler/import.wast +++ b/tests/compiler/import.wast @@ -3,8 +3,8 @@ (type $v (func)) (global $export/a i32 (i32.const 1)) (global $export/b i32 (i32.const 2)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) @@ -62,6 +62,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/literals.optimized.wast b/tests/compiler/literals.optimized.wast index 2b1bd4dc..bfec40ca 100644 --- a/tests/compiler/literals.optimized.wast +++ b/tests/compiler/literals.optimized.wast @@ -1,7 +1,6 @@ (module (type $v (func)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/literals.wast b/tests/compiler/literals.wast index b0c390b8..073ad016 100644 --- a/tests/compiler/literals.wast +++ b/tests/compiler/literals.wast @@ -1,7 +1,7 @@ (module (type $v (func)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -163,6 +163,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/logical.optimized.wast b/tests/compiler/logical.optimized.wast index 18a40836..3c0bd69b 100644 --- a/tests/compiler/logical.optimized.wast +++ b/tests/compiler/logical.optimized.wast @@ -5,7 +5,6 @@ (global $logical/f (mut f32) (f32.const 0)) (global $logical/F (mut f64) (f64.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/logical.wast b/tests/compiler/logical.wast index a7909041..e1c71a3a 100644 --- a/tests/compiler/logical.wast +++ b/tests/compiler/logical.wast @@ -4,8 +4,8 @@ (global $logical/I (mut i64) (i64.const 0)) (global $logical/f (mut f32) (f32.const 0)) (global $logical/F (mut f64) (f64.const 0)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -291,6 +291,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/reexport.optimized.wast b/tests/compiler/reexport.optimized.wast index 7f7d8d26..f3fc9b36 100644 --- a/tests/compiler/reexport.optimized.wast +++ b/tests/compiler/reexport.optimized.wast @@ -4,7 +4,6 @@ (global $export/a i32 (i32.const 1)) (global $export/b i32 (i32.const 2)) (memory $0 1) - (data (i32.const 4) "\08") (export "add" (func $export/add)) (export "renamed_sub" (func $export/sub)) (export "renamed_a" (global $export/a)) diff --git a/tests/compiler/reexport.wast b/tests/compiler/reexport.wast index 53cd96aa..6ad55032 100644 --- a/tests/compiler/reexport.wast +++ b/tests/compiler/reexport.wast @@ -3,8 +3,8 @@ (type $v (func)) (global $export/a i32 (i32.const 1)) (global $export/b i32 (i32.const 2)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "add" (func $export/add)) (export "renamed_sub" (func $export/sub)) (export "renamed_a" (global $export/a)) @@ -68,6 +68,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/switch.optimized.wast b/tests/compiler/switch.optimized.wast index e70dbf01..ab5bd232 100644 --- a/tests/compiler/switch.optimized.wast +++ b/tests/compiler/switch.optimized.wast @@ -1,7 +1,6 @@ (module (type $ii (func (param i32) (result i32))) (memory $0 1) - (data (i32.const 4) "\08") (export "doSwitch" (func $switch/doSwitch)) (export "doSwitchDefaultFirst" (func $switch/doSwitchDefaultFirst)) (export "doSwitchDefaultOmitted" (func $switch/doSwitchDefaultOmitted)) diff --git a/tests/compiler/switch.wast b/tests/compiler/switch.wast index b664df96..4ba127ee 100644 --- a/tests/compiler/switch.wast +++ b/tests/compiler/switch.wast @@ -1,7 +1,7 @@ (module (type $ii (func (param i32) (result i32))) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "doSwitch" (func $switch/doSwitch)) (export "doSwitchDefaultFirst" (func $switch/doSwitchDefaultFirst)) (export "doSwitchDefaultOmitted" (func $switch/doSwitchDefaultOmitted)) @@ -169,6 +169,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/ternary.optimized.wast b/tests/compiler/ternary.optimized.wast index 5e7ebd53..d0d8d5ae 100644 --- a/tests/compiler/ternary.optimized.wast +++ b/tests/compiler/ternary.optimized.wast @@ -2,7 +2,6 @@ (type $v (func)) (global $ternary/a (mut i32) (i32.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/ternary.wast b/tests/compiler/ternary.wast index 96b255b7..231a3500 100644 --- a/tests/compiler/ternary.wast +++ b/tests/compiler/ternary.wast @@ -1,8 +1,8 @@ (module (type $v (func)) (global $ternary/a (mut i32) (i32.const 0)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -57,6 +57,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/tlsf.optimized-inlined.wast b/tests/compiler/tlsf.optimized-inlined.wast index bd557fb2..f2be9451 100644 --- a/tests/compiler/tlsf.optimized-inlined.wast +++ b/tests/compiler/tlsf.optimized-inlined.wast @@ -6,7 +6,6 @@ (type $iv (func (param i32))) (type $v (func)) (memory $0 1) - (data (i32.const 4) "\08") (export "control$construct" (func $tlsf/control$construct)) (export "memory" (memory $0)) (start $start) diff --git a/tests/compiler/tlsf.optimized.wast b/tests/compiler/tlsf.optimized.wast index ebc990a3..39c14039 100644 --- a/tests/compiler/tlsf.optimized.wast +++ b/tests/compiler/tlsf.optimized.wast @@ -6,7 +6,6 @@ (type $iv (func (param i32))) (type $v (func)) (memory $0 1) - (data (i32.const 4) "\08") (export "control$construct" (func $tlsf/control$construct)) (export "memory" (memory $0)) (start $start) diff --git a/tests/compiler/tlsf.wast b/tests/compiler/tlsf.wast index d6cd4eab..7abebab1 100644 --- a/tests/compiler/tlsf.wast +++ b/tests/compiler/tlsf.wast @@ -21,8 +21,8 @@ (global $tlsf/CONTROL$SL_BITMAP_OFFSET i32 (i32.const 20)) (global $tlsf/SL_INDEX_COUNT i32 (i32.const 32)) (global $tlsf/CONTROL$BLOCKS_OFFSET i32 (i32.const 112)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "control$construct" (func $tlsf/control$construct)) (export "memory" (memory $0)) (start $start) @@ -352,6 +352,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/unary.optimized.wast b/tests/compiler/unary.optimized.wast index 772fef91..7c28bb5b 100644 --- a/tests/compiler/unary.optimized.wast +++ b/tests/compiler/unary.optimized.wast @@ -5,7 +5,6 @@ (global $unary/f (mut f32) (f32.const 0)) (global $unary/F (mut f64) (f64.const 0)) (memory $0 1) - (data (i32.const 4) "\08") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/unary.wast b/tests/compiler/unary.wast index 716a93c8..0c89f319 100644 --- a/tests/compiler/unary.wast +++ b/tests/compiler/unary.wast @@ -4,8 +4,8 @@ (global $unary/I (mut i64) (i64.const 0)) (global $unary/f (mut f32) (f32.const 0)) (global $unary/F (mut f64) (f64.const 0)) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) @@ -657,6 +657,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert diff --git a/tests/compiler/while.optimized.wast b/tests/compiler/while.optimized.wast index ccb0d0e6..2827adaa 100644 --- a/tests/compiler/while.optimized.wast +++ b/tests/compiler/while.optimized.wast @@ -1,7 +1,6 @@ (module (type $iv (func (param i32))) (memory $0 1) - (data (i32.const 4) "\08") (export "loopWhile" (func $while/loopWhile)) (export "loopWhileInWhile" (func $while/loopWhileInWhile)) (export "memory" (memory $0)) diff --git a/tests/compiler/while.wast b/tests/compiler/while.wast index 27c27207..ac827a47 100644 --- a/tests/compiler/while.wast +++ b/tests/compiler/while.wast @@ -1,7 +1,7 @@ (module (type $iv (func (param i32))) + (global $HEAP_START i32 (i32.const 4)) (memory $0 1) - (data (i32.const 4) "\08\00\00\00") (export "loopWhile" (func $while/loopWhile)) (export "loopWhileInWhile" (func $while/loopWhileInWhile)) (export "memory" (memory $0)) @@ -84,6 +84,7 @@ reinterpret select sizeof + changetype isNaN isFinite assert