diff --git a/examples/pson/README.md b/examples/pson/README.md index 776ec5aa..8dd77777 100644 --- a/examples/pson/README.md +++ b/examples/pson/README.md @@ -12,8 +12,9 @@ To build [assembly/pson.ts](./assembly/pson.ts) to an untouched and an optimized $> npm run build ``` -Afterwards, to run the included test, do: +Afterwards, to run the included [test](./tests/index.js): ``` +$> npm install $> npm test ``` diff --git a/src/ast.ts b/src/ast.ts index 59e9865f..0502b9bb 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -152,12 +152,13 @@ export abstract class Node { return expr; } - static createCall(expression: Expression, typeArguments: TypeNode[], args: Expression[], range: Range): CallExpression { + static createCall(expression: Expression, typeArguments: TypeNode[] | null, args: Expression[], range: Range): CallExpression { var expr = new CallExpression(); expr.range = range; (expr.expression = expression).parent = expr; - for (var i = 0, k = (expr.typeArguments = typeArguments).length; i < k; ++i) - typeArguments[i].parent = expr; + if (expr.typeArguments = typeArguments) + for (var i = 0, k = (typeArguments).length; i < k; ++i) + (typeArguments)[i].parent = expr; for (i = 0, k = (expr.arguments = args).length; i < k; ++i) args[i].parent = expr; return expr; @@ -197,12 +198,13 @@ export abstract class Node { return expr; } - static createNew(expression: Expression, typeArguments: TypeNode[], args: Expression[], range: Range): NewExpression { + static createNew(expression: Expression, typeArguments: TypeNode[] | null, args: Expression[], range: Range): NewExpression { var expr = new NewExpression(); expr.range = range; (expr.expression = expression).parent = expr; - for (var i = 0, k = (expr.typeArguments = typeArguments).length; i < k; ++i) - typeArguments[i].parent = expr; + if (expr.typeArguments = typeArguments) + for (var i = 0, k = (typeArguments).length; i < k; ++i) + (typeArguments)[i].parent = expr; for (i = 0, k = (expr.arguments = args).length; i < k; ++i) args[i].parent = expr; return expr; @@ -333,12 +335,13 @@ export abstract class Node { return stmt; } - static createDecorator(expression: Expression, args: Expression[], range: Range): Decorator { + static createDecorator(expression: Expression, args: Expression[] | null, range: Range): Decorator { var stmt = new Decorator(); stmt.range = range; (stmt.name = expression).parent = stmt; - for (var i: i32 = 0, k: i32 = (stmt.arguments = args).length; i < k; ++i) - args[i].parent = stmt; + if (stmt.arguments = args) + for (var i: i32 = 0, k: i32 = (args).length; i < k; ++i) + (args)[i].parent = stmt; return stmt; } @@ -857,14 +860,14 @@ export class CallExpression extends Expression { /** Called expression. Usually an identifier or property access expression. */ expression: Expression; /** Provided type arguments. */ - typeArguments: TypeNode[]; + typeArguments: TypeNode[] | null; /** Provided arguments. */ arguments: Expression[]; serialize(sb: string[]): void { this.expression.serialize(sb); - var k = this.typeArguments.length; - if (k) { + var k: i32; + if (this.typeArguments && (k = this.typeArguments.length)) { sb.push("<"); for (var i = 0; i < k; ++i) { if (i > 0) @@ -1311,18 +1314,20 @@ export class Decorator extends Statement { /** Name expression. */ name: Expression; /** Argument expressions. */ - arguments: Expression[]; + arguments: Expression[] | null; serialize(sb: string[]): void { sb.push("@"); this.name.serialize(sb); - sb.push("("); - for (var i = 0, k = this.arguments.length; i < k; ++i) { - if (i > 0) - sb.push(", "); - this.arguments[i].serialize(sb); + if (this.arguments) { + sb.push("("); + for (var i = 0, k = this.arguments.length; i < k; ++i) { + if (i > 0) + sb.push(", "); + this.arguments[i].serialize(sb); + } + sb.push(")"); } - sb.push(")"); } } diff --git a/src/builtins.ts b/src/builtins.ts index 8985aac5..d99357a2 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -10,7 +10,8 @@ import { import { Node, - Expression + Expression, + BinaryExpression } from "./ast"; import { @@ -187,7 +188,7 @@ export function compileGetConstant(compiler: Compiler, global: Global): Expressi } /** Compiles a call to a built-in function. */ -export function compileCall(compiler: Compiler, prototype: FunctionPrototype, typeArguments: Type[], operands: Expression[], reportNode: Node): ExpressionRef { +export function compileCall(compiler: Compiler, prototype: FunctionPrototype, typeArguments: Type[] | null, operands: Expression[], reportNode: Node): ExpressionRef { var module = compiler.module; var usizeType = select(Type.usize64, Type.usize32, compiler.options.target == Target.WASM64); var nativeUsizeType = select(NativeType.I64, NativeType.I32, compiler.options.target == Target.WASM64); @@ -209,12 +210,12 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty compiler.currentType = Type.bool; if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if (typeArguments[0].isAnyInteger) + if ((typeArguments)[0].isAnyInteger) return module.createI32(0); - if (typeArguments[0].isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports + if ((typeArguments)[0].isAnyFloat) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); // reports compiler.currentType = Type.bool; - if (typeArguments[0] == Type.f32) { + if ((typeArguments)[0] == Type.f32) { tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f32); return module.createBinary(BinaryOp.NeF32, module.createTeeLocal(tempLocal0.index, arg0), @@ -234,12 +235,12 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty compiler.currentType = Type.bool; if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if (typeArguments[0].isAnyInteger) + if ((typeArguments)[0].isAnyInteger) return module.createI32(1); - if (typeArguments[0].isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports + if ((typeArguments)[0].isAnyFloat) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); // reports compiler.currentType = Type.bool; - if (typeArguments[0] == Type.f32) { + if ((typeArguments)[0] == Type.f32) { tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f32); return module.createSelect( module.createBinary(BinaryOp.NeF32, @@ -276,14 +277,14 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "clz": // clz(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]).isLongInteger // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic ? module.createUnary(UnaryOp.ClzI64, arg0) - : typeArguments[0].isSmallInteger + : (typeArguments)[0].isSmallInteger ? module.createBinary(BinaryOp.AndI32, module.createUnary(UnaryOp.ClzI32, arg0), - module.createI32(typeArguments[0].smallIntegerMask) + module.createI32((typeArguments)[0].smallIntegerMask) ) : module.createUnary(UnaryOp.ClzI32, arg0); } @@ -292,14 +293,14 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "ctz": // ctz(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]).isLongInteger // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic ? module.createUnary(UnaryOp.CtzI64, arg0) - : typeArguments[0].isSmallInteger + : (typeArguments)[0].isSmallInteger ? module.createBinary(BinaryOp.AndI32, module.createUnary(UnaryOp.CtzI32, arg0), - module.createI32(typeArguments[0].smallIntegerMask) + module.createI32((typeArguments)[0].smallIntegerMask) ) : module.createUnary(UnaryOp.CtzI32, arg0); } @@ -308,14 +309,14 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "popcnt": // popcnt(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]).isLongInteger // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic ? module.createUnary(UnaryOp.PopcntI64, arg0) - : typeArguments[0].isSmallInteger + : (typeArguments)[0].isSmallInteger ? module.createBinary(BinaryOp.AndI32, module.createUnary(UnaryOp.PopcntI32, arg0), - module.createI32(typeArguments[0].smallIntegerMask) + module.createI32((typeArguments)[0].smallIntegerMask) ) : module.createUnary(UnaryOp.PopcntI32, arg0); } @@ -324,15 +325,15 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "rotl": // rotl(value: T, shift: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) 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 + 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 ? module.createBinary(BinaryOp.RotlI64, arg0, arg1) - : typeArguments[0].isSmallInteger + : (typeArguments)[0].isSmallInteger ? module.createBinary(BinaryOp.AndI32, module.createBinary(BinaryOp.RotlI32, arg0, arg1), - module.createI32(typeArguments[0].smallIntegerMask) + module.createI32((typeArguments)[0].smallIntegerMask) ) : module.createBinary(BinaryOp.RotlI32, arg0, arg1); } @@ -341,15 +342,15 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "rotr": // rotr(value: T, shift: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) 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 + 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 ? module.createBinary(BinaryOp.RotrI64, arg0, arg1) - : typeArguments[0].isSmallInteger + : (typeArguments)[0].isSmallInteger ? module.createBinary(BinaryOp.AndI32, module.createBinary(BinaryOp.RotrI32, arg0, arg1), - module.createI32(typeArguments[0].smallIntegerMask) + module.createI32((typeArguments)[0].smallIntegerMask) ) : module.createBinary(BinaryOp.RotrI32, arg0, arg1); } @@ -358,16 +359,16 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "abs": // abs(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) 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 + 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 ? module.createUnary(UnaryOp.AbsF32, arg0) : module.createUnary(UnaryOp.AbsF64, arg0); - if (typeArguments[0].isAnyInteger) { - if (typeArguments[0].isSignedInteger) { - tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(typeArguments[0]); - if (typeArguments[0].isLongInteger) + if ((typeArguments)[0].isAnyInteger) { + if ((typeArguments)[0].isSignedInteger) { + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal((typeArguments)[0]); + if ((typeArguments)[0].isLongInteger) return module.createSelect( module.createBinary(BinaryOp.SubI64, module.createI64(0, 0), @@ -400,22 +401,22 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "max": // max(left: T, right: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) 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 + 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 ? module.createBinary(BinaryOp.MaxF32, arg0, arg1) : module.createBinary(BinaryOp.MaxF64, arg0, arg1); - if (typeArguments[0].isAnyInteger) { - tempLocal0 = compiler.currentFunction.getTempLocal(typeArguments[0]); - tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(typeArguments[0]); + if ((typeArguments)[0].isAnyInteger) { + tempLocal0 = compiler.currentFunction.getTempLocal((typeArguments)[0]); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal((typeArguments)[0]); compiler.currentFunction.freeTempLocal(tempLocal0); - if (typeArguments[0].isLongInteger) + if ((typeArguments)[0].isLongInteger) return module.createSelect( module.createTeeLocal(tempLocal0.index, arg0), module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.GtI64 : BinaryOp.GtU64, + module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.GtI64 : BinaryOp.GtU64, module.createGetLocal(tempLocal0.index, NativeType.I64), module.createGetLocal(tempLocal1.index, NativeType.I64) ) @@ -424,7 +425,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty return module.createSelect( module.createTeeLocal(tempLocal0.index, arg0), module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.GtI32 : BinaryOp.GtU32, + module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.GtI32 : BinaryOp.GtU32, module.createGetLocal(tempLocal0.index, NativeType.I32), module.createGetLocal(tempLocal1.index, NativeType.I32) ) @@ -436,22 +437,22 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "min": // min(left: T, right: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) 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 + 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 ? module.createBinary(BinaryOp.MinF32, arg0, arg1) : module.createBinary(BinaryOp.MinF64, arg0, arg1); - if (typeArguments[0].isAnyInteger) { - tempLocal0 = compiler.currentFunction.getTempLocal(typeArguments[0]); - tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(typeArguments[0]); + if ((typeArguments)[0].isAnyInteger) { + tempLocal0 = compiler.currentFunction.getTempLocal((typeArguments)[0]); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal((typeArguments)[0]); compiler.currentFunction.freeTempLocal(tempLocal0); - if (typeArguments[0].isLongInteger) + if ((typeArguments)[0].isLongInteger) return module.createSelect( module.createTeeLocal(tempLocal0.index, arg0), module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.LtI64 : BinaryOp.LtU64, + module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.LtI64 : BinaryOp.LtU64, module.createGetLocal(tempLocal0.index, NativeType.I64), module.createGetLocal(tempLocal1.index, NativeType.I64) ) @@ -460,7 +461,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty return module.createSelect( module.createTeeLocal(tempLocal0.index, arg0), module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary(typeArguments[0].isSignedInteger ? BinaryOp.LtI32 : BinaryOp.LtU32, + module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.LtI32 : BinaryOp.LtU32, module.createGetLocal(tempLocal0.index, NativeType.I32), module.createGetLocal(tempLocal1.index, NativeType.I32) ) @@ -472,9 +473,9 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "ceil": // ceil(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]) == Type.f32 // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic ? module.createUnary(UnaryOp.CeilF32, arg0) : module.createUnary(UnaryOp.CeilF64, arg0); } @@ -483,9 +484,9 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "floor": // floor(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]) == Type.f32 // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic ? module.createUnary(UnaryOp.FloorF32, arg0) : module.createUnary(UnaryOp.FloorF64, arg0); } @@ -494,10 +495,10 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "copysign": // copysign(left: T, right: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) 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 + 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 ? module.createBinary(BinaryOp.CopysignF32, arg0, arg1) : module.createBinary(BinaryOp.CopysignF64, arg0, arg1); } @@ -506,9 +507,9 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "nearest": // nearest(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]) == Type.f32 // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic ? module.createUnary(UnaryOp.NearestF32, arg0) : module.createUnary(UnaryOp.NearestF64, arg0); } @@ -517,25 +518,25 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "reinterpret": // reinterpret(value: T1) -> T2 if (!validateCall(compiler, typeArguments, 2, operands, 1, reportNode)) return module.createUnreachable(); - compiler.currentType = typeArguments[1]; - if (typeArguments[0].isLongInteger && typeArguments[1] == Type.f64) { + 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 module.createUnary(UnaryOp.ReinterpretI64, arg0); } - if (typeArguments[0].isAnyInteger && typeArguments[0].byteSize == 4 && typeArguments[1] == Type.f32) { + 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 module.createUnary(UnaryOp.ReinterpretI32, arg0); } - if (typeArguments[0] == Type.f64 && typeArguments[1].isLongInteger) { + if ((typeArguments)[0] == Type.f64 && (typeArguments)[1].isLongInteger) { arg0 = compiler.compileExpression(operands[0], Type.f64); // reports - compiler.currentType = typeArguments[1]; + compiler.currentType = (typeArguments)[1]; return module.createUnary(UnaryOp.ReinterpretF64, arg0); } - if (typeArguments[0] == Type.f32 && typeArguments[1].isAnyInteger && typeArguments[1].byteSize == 4) { + if ((typeArguments)[0] == Type.f32 && (typeArguments)[1].isAnyInteger && (typeArguments)[1].byteSize == 4) { arg0 = compiler.compileExpression(operands[0], Type.f32); // reports - compiler.currentType = typeArguments[1]; + compiler.currentType = (typeArguments)[1]; return module.createUnary(UnaryOp.ReinterpretF32, arg0); } break; @@ -543,9 +544,9 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "sqrt": // sqrt(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]) == Type.f32 // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic ? module.createUnary(UnaryOp.SqrtF32, arg0) : module.createUnary(UnaryOp.SqrtF64, arg0); } @@ -554,9 +555,9 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "trunc": // trunc(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); - if ((compiler.currentType = typeArguments[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); - return (compiler.currentType = typeArguments[0]) == Type.f32 // sic + if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { + arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); + return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic ? module.createUnary(UnaryOp.TruncF32, arg0) : module.createUnary(UnaryOp.TruncF64, arg0); } @@ -568,8 +569,8 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); arg0 = compiler.compileExpression(operands[0], usizeType); // reports - if ((compiler.currentType = typeArguments[0]) != Type.void) - return module.createLoad(typeArguments[0].byteSize, typeArguments[0].isSignedInteger, arg0, typeArguments[0].toNativeType()); + if ((compiler.currentType = (typeArguments)[0]) != Type.void) + return module.createLoad((typeArguments)[0].byteSize, (typeArguments)[0].isSignedInteger, arg0, (typeArguments)[0].toNativeType()); break; case "store": // store(offset: usize, value: T) -> void @@ -577,10 +578,10 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) return module.createUnreachable(); arg0 = compiler.compileExpression(operands[0], usizeType); // reports - arg1 = compiler.compileExpression(operands[1], typeArguments[0]); // reports + arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); // reports compiler.currentType = Type.void; - if (typeArguments[0] != Type.void) - return module.createStore(typeArguments[0].byteSize, arg0, arg1, typeArguments[0].toNativeType()); + if ((typeArguments)[0] != Type.void) + return module.createStore((typeArguments)[0].byteSize, arg0, arg1, (typeArguments)[0].toNativeType()); break; case "sizeof": // sizeof() -> usize @@ -588,19 +589,19 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty 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); + ? module.createI64((typeArguments)[0].byteSize, 0) + : module.createI32((typeArguments)[0].byteSize); // control flow case "select": // select(ifTrue: T, ifFalse: T, condition: bool) -> T if (!validateCall(compiler, typeArguments, 1, operands, 3, reportNode)) return module.createUnreachable(); - if (typeArguments[0] != Type.void) { - arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports - arg1 = compiler.compileExpression(operands[1], typeArguments[0]); // reports + 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]; + compiler.currentType = (typeArguments)[0]; return module.createSelect(arg0, arg1, arg2); } break; @@ -652,7 +653,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "parseInt": // takes a pointer to the string compiler.currentType = Type.f64; - if (typeArguments.length != 0) { + if (typeArguments && typeArguments.length) { compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "0", typeArguments.length.toString(10)); return module.createUnreachable(); } @@ -693,16 +694,16 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) return module.createUnreachable(); arg0 = compiler.compileExpression(operands[0], Type.void, ConversionKind.NONE); - if ((compiler.currentType == usizeType && typeArguments[1].classType) || (compiler.currentType.classType && typeArguments[1] == usizeType)) { - compiler.currentType = typeArguments[1]; + if ((compiler.currentType == usizeType && (typeArguments)[1].classType) || (compiler.currentType.classType && (typeArguments)[1] == usizeType)) { + 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()); + compiler.error(DiagnosticCode.Type_0_cannot_be_changed_to_type_1, reportNode.range, (typeArguments)[0].toString(), (typeArguments)[1].toString()); return module.createUnreachable(); case "assert": // assert(isTrue: bool) -> void compiler.currentType = Type.void; - if (typeArguments.length != 0) { + if (typeArguments && typeArguments.length) { compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "0", typeArguments.length.toString(10)); return module.createUnreachable(); } @@ -795,9 +796,10 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty } /** Pre-validates a call to a built-in function. */ -function validateCall(compiler: Compiler, typeArguments: Type[], expectedTypeArguments: i32, operands: Expression[], expectedOperands: i32, reportNode: Node): bool { - if (typeArguments.length != expectedTypeArguments) { - compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, expectedTypeArguments.toString(10), typeArguments.length.toString(10)); +function validateCall(compiler: Compiler, typeArguments: Type[] | null, expectedTypeArguments: i32, operands: Expression[], expectedOperands: i32, reportNode: Node): bool { + var numTypeArguments = typeArguments && typeArguments.length || 0; + if (numTypeArguments != expectedTypeArguments) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, expectedTypeArguments.toString(10), numTypeArguments.toString(10)); return false; } if (operands.length != expectedOperands) { diff --git a/src/compiler.ts b/src/compiler.ts index fe84e48c..7af38e72 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -31,6 +31,8 @@ import { Element, ElementKind, Enum, + FieldPrototype, + Field, FunctionPrototype, Function, Global, @@ -553,15 +555,23 @@ export class Compiler extends DiagnosticEmitter { } // create the function type - var k = instance.parameters.length; + var numParameters = instance.parameters.length; + var numParametersInclThis = instance.instanceMethodOf ? numParameters + 1 : numParameters; + var paramIndex = 0; + var nativeResultType = instance.returnType.toNativeType(); - var nativeParamTypes = new Array(k); - var signatureNameParts = new Array(k + 1); - for (var i = 0; i < k; ++i) { - nativeParamTypes[i] = instance.parameters[i].type.toNativeType(); - signatureNameParts[i] = instance.parameters[i].type.toSignatureName(); + var nativeParamTypes = new Array(numParametersInclThis); + var signatureNameParts = new Array(numParametersInclThis + 1); + + if (instance.instanceMethodOf) { + nativeParamTypes[paramIndex] = select(NativeType.I64, NativeType.I32, this.options.target == Target.WASM64); + signatureNameParts[paramIndex++] = instance.instanceMethodOf.type.toSignatureString(); } - signatureNameParts[k] = instance.returnType.toSignatureName(); + for (var i = 0; i < numParameters; ++i) { + nativeParamTypes[paramIndex] = instance.parameters[i].type.toNativeType(); + signatureNameParts[paramIndex++] = instance.parameters[i].type.toSignatureString(); + } + signatureNameParts[paramIndex] = instance.returnType.toSignatureString(); var typeRef = this.module.getFunctionTypeBySignature(nativeResultType, nativeParamTypes); if (!typeRef) typeRef = this.module.addFunctionType(signatureNameParts.join(""), nativeResultType, nativeParamTypes); @@ -716,8 +726,11 @@ export class Compiler extends DiagnosticEmitter { this.compileClass(instance); } - compileClass(cls: Class) { - throw new Error("not implemented"); + compileClass(instance: Class): bool { + if (instance.isCompiled) + return true; + + return instance.isCompiled = true; } compileInterfaceDeclaration(declaration: InterfaceDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map | null = null, alternativeReportNode: Node | null = null): void { @@ -788,7 +801,7 @@ export class Compiler extends DiagnosticEmitter { case NodeKind.TYPEDECLARATION: if (this.currentFunction == this.startFunction) return this.module.createNop(); - // fall-through: must be top-level; function bodies are not initialized + // fall-through: must be top-level; function bodies are not guaranteed to be evaluated default: throw new Error("statement expected"); @@ -819,7 +832,7 @@ export class Compiler extends DiagnosticEmitter { } var context = this.currentFunction.breakContext; if (context != null) - return this.module.createBreak("break|" + (context)); + return this.module.createBreak("break|" + context); this.error(DiagnosticCode.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement, statement.range); return this.module.createUnreachable(); @@ -831,8 +844,8 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); } var context = this.currentFunction.breakContext; - if (context != null && !this.disallowContinue) - return this.module.createBreak("continue|" + (context)); + if (context && !this.disallowContinue) + return this.module.createBreak("continue|" + context); this.error(DiagnosticCode.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement, statement.range); return this.module.createUnreachable(); @@ -914,7 +927,7 @@ export class Compiler extends DiagnosticEmitter { // introduce a local for evaluating the condition (exactly once) var tempLocal = this.currentFunction.getTempLocal(Type.i32); - var i: i32, k = statement.cases.length; + var k = statement.cases.length; // prepend initializer to inner block var breaks = new Array(1 + k); @@ -923,7 +936,7 @@ export class Compiler extends DiagnosticEmitter { // make one br_if per (possibly dynamic) labeled case (binaryen optimizes to br_table where possible) var breakIndex = 1; var defaultIndex = -1; - for (i = 0; i < k; ++i) { + for (var i = 0; i < k; ++i) { var case_ = statement.cases[i]; if (case_.label) { breaks[breakIndex++] = this.module.createBreak("case" + i.toString(10) + "|" + context, @@ -1139,7 +1152,7 @@ export class Compiler extends DiagnosticEmitter { var nativeType = this.currentType.toNativeType(); var typeRef = this.module.getFunctionTypeBySignature(nativeType, []); if (!typeRef) - typeRef = this.module.addFunctionType(this.currentType.toSignatureName(), nativeType, []); + typeRef = this.module.addFunctionType(this.currentType.toSignatureString(), nativeType, []); var funcRef = this.module.addFunction("__precompute", typeRef, [], expr); this.module.runPasses([ "precompute" ], funcRef); var ret = _BinaryenFunctionGetBody(funcRef); @@ -1307,7 +1320,7 @@ export class Compiler extends DiagnosticEmitter { } if (losesInformation && conversionKind == ConversionKind.IMPLICIT) - this.error(DiagnosticCode.Conversion_from_type_0_to_1_requires_an_explicit_cast, reportNode.range, fromType.toString(), toType.toString()); + this.error(DiagnosticCode.Conversion_from_type_0_to_1_possibly_loses_information_and_thus_requires_an_explicit_cast, reportNode.range, fromType.toString(), toType.toString()); return expr; } @@ -1333,68 +1346,200 @@ export class Compiler extends DiagnosticEmitter { case Token.LESSTHAN: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.LtF32 - : this.currentType == Type.f64 - ? BinaryOp.LtF64 - : this.currentType.isSignedInteger - ? this.currentType.isLongInteger - ? BinaryOp.LtI64 - : BinaryOp.LtI32 - : this.currentType.isLongInteger - ? BinaryOp.LtU64 - : BinaryOp.LtU32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + op = BinaryOp.LtI32; + break; + + case TypeKind.ISIZE: + op = select(BinaryOp.LtI64, BinaryOp.LtI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + op = BinaryOp.LtI64; + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.LtU32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + op = select(BinaryOp.LtU64, BinaryOp.LtU32, this.options.target == Target.WASM64); + break; + + case TypeKind.U64: + op = BinaryOp.LtU64; + break; + + case TypeKind.F32: + op = BinaryOp.LtF32; + break; + + case TypeKind.F64: + op = BinaryOp.LtF64; + break; + + default: + throw new Error("concrete type expected"); + } this.currentType = Type.bool; break; case Token.GREATERTHAN: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.GtF32 - : this.currentType == Type.f64 - ? BinaryOp.GtF64 - : this.currentType.isSignedInteger - ? this.currentType.isLongInteger - ? BinaryOp.GtI64 - : BinaryOp.GtI32 - : this.currentType.isLongInteger - ? BinaryOp.GtU64 - : BinaryOp.GtU32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + op = BinaryOp.GtI32; + break; + + case TypeKind.ISIZE: + op = select(BinaryOp.GtI64, BinaryOp.GtI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + op = BinaryOp.GtI64; + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.GtU32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + op = select(BinaryOp.GtU64, BinaryOp.GtU32, this.options.target == Target.WASM64); + break; + + case TypeKind.U64: + op = BinaryOp.GtU64; + break; + + case TypeKind.F32: + op = BinaryOp.GtF32; + break; + + case TypeKind.F64: + op = BinaryOp.GtF64; + break; + + default: + throw new Error("concrete type expected"); + } this.currentType = Type.bool; break; case Token.LESSTHAN_EQUALS: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.LeF32 - : this.currentType == Type.f64 - ? BinaryOp.LeF64 - : this.currentType.isSignedInteger - ? this.currentType.isLongInteger - ? BinaryOp.LeI64 - : BinaryOp.LeI32 - : this.currentType.isLongInteger - ? BinaryOp.LeU64 - : BinaryOp.LeU32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + op = BinaryOp.LeI32; + break; + + case TypeKind.ISIZE: + op = select(BinaryOp.LeI64, BinaryOp.LeI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + op = BinaryOp.LeI64; + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.LeU32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + op = select(BinaryOp.LeU64, BinaryOp.LeU32, this.options.target == Target.WASM64); + break; + + case TypeKind.U64: + op = BinaryOp.LeU64; + break; + + case TypeKind.F32: + op = BinaryOp.LeF32; + break; + + case TypeKind.F64: + op = BinaryOp.LeF64; + break; + + default: + throw new Error("concrete type expected"); + } this.currentType = Type.bool; break; case Token.GREATERTHAN_EQUALS: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.GeF32 - : this.currentType == Type.f64 - ? BinaryOp.GeF64 - : this.currentType.isSignedInteger - ? this.currentType.isLongInteger - ? BinaryOp.GeI64 - : BinaryOp.GeI32 - : this.currentType.isLongInteger - ? BinaryOp.GeU64 - : BinaryOp.GeU32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + op = BinaryOp.GeI32; + break; + + case TypeKind.ISIZE: + op = select(BinaryOp.GeI64, BinaryOp.GeI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + op = BinaryOp.GeI64; + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.GeU32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + op = select(BinaryOp.GeU64, BinaryOp.GeU32, this.options.target == Target.WASM64); + break; + + case TypeKind.U64: + op = BinaryOp.GeU64; + break; + + case TypeKind.F32: + op = BinaryOp.GeF32; + break; + + case TypeKind.F64: + op = BinaryOp.GeF64; + break; + + default: + throw new Error("concrete type expected"); + } this.currentType = Type.bool; break; @@ -1402,13 +1547,41 @@ export class Compiler extends DiagnosticEmitter { case Token.EQUALS_EQUALS_EQUALS: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.EqF32 - : this.currentType == Type.f64 - ? BinaryOp.EqF64 - : this.currentType.isLongInteger - ? BinaryOp.EqI64 - : BinaryOp.EqI32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.EqI32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.EqI64, BinaryOp.EqI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.EqI64; + break; + + case TypeKind.F32: + op = BinaryOp.EqF32; + break; + + case TypeKind.F64: + op = BinaryOp.EqF64; + break; + + default: + throw new Error("concrete type expected"); + } this.currentType = Type.bool; break; @@ -1416,13 +1589,41 @@ export class Compiler extends DiagnosticEmitter { case Token.EXCLAMATION_EQUALS_EQUALS: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.NeF32 - : this.currentType == Type.f64 - ? BinaryOp.NeF64 - : this.currentType.isLongInteger - ? BinaryOp.NeI64 - : BinaryOp.NeI32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.NeI32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.NeI64, BinaryOp.NeI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.NeI64; + break; + + case TypeKind.F32: + op = BinaryOp.NeF32; + break; + + case TypeKind.F64: + op = BinaryOp.NeF64; + break; + + default: + throw new Error("concrete type expected"); + } this.currentType = Type.bool; break; @@ -1434,13 +1635,41 @@ export class Compiler extends DiagnosticEmitter { case Token.PLUS: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.AddF32 - : this.currentType == Type.f64 - ? BinaryOp.AddF64 - : this.currentType.isLongInteger - ? BinaryOp.AddI64 - : BinaryOp.AddI32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.AddI32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.AddI64, BinaryOp.AddI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.AddI64; + break; + + case TypeKind.F32: + op = BinaryOp.AddF32; + break; + + case TypeKind.F64: + op = BinaryOp.AddF64; + break; + + default: + throw new Error("concrete type expected"); + } break; case Token.MINUS_EQUALS: @@ -1448,13 +1677,41 @@ export class Compiler extends DiagnosticEmitter { case Token.MINUS: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.SubF32 - : this.currentType == Type.f64 - ? BinaryOp.SubF64 - : this.currentType.isLongInteger - ? BinaryOp.SubI64 - : BinaryOp.SubI32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.SubI32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.SubI64; + break; + + case TypeKind.F32: + op = BinaryOp.SubF32; + break; + + case TypeKind.F64: + op = BinaryOp.SubF64; + break; + + default: + throw new Error("concrete type expected"); + } break; case Token.ASTERISK_EQUALS: @@ -1462,13 +1719,41 @@ export class Compiler extends DiagnosticEmitter { case Token.ASTERISK: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.MulF32 - : this.currentType == Type.f64 - ? BinaryOp.MulF64 - : this.currentType.isLongInteger - ? BinaryOp.MulI64 - : BinaryOp.MulI32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.MulI32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.MulI64, BinaryOp.MulI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.MulI64; + break; + + case TypeKind.F32: + op = BinaryOp.MulF32; + break; + + case TypeKind.F64: + op = BinaryOp.MulF64; + break; + + default: + throw new Error("concrete type expected"); + } break; case Token.SLASH_EQUALS: @@ -1476,17 +1761,50 @@ export class Compiler extends DiagnosticEmitter { case Token.SLASH: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType == Type.f32 - ? BinaryOp.DivF32 - : this.currentType == Type.f64 - ? BinaryOp.DivF64 - : this.currentType.isSignedInteger - ? this.currentType.isLongInteger - ? BinaryOp.DivI64 - : BinaryOp.DivI32 - : this.currentType.isLongInteger - ? BinaryOp.DivU64 - : BinaryOp.DivU32; + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + op = BinaryOp.DivI32; + break; + + case TypeKind.ISIZE: + op = select(BinaryOp.DivI64, BinaryOp.DivI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + op = BinaryOp.DivI64; + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.DivU32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + op = select(BinaryOp.DivU64, BinaryOp.DivU32, this.options.target == Target.WASM64); + break; + + case TypeKind.U64: + op = BinaryOp.DivU64; + break; + + case TypeKind.F32: + op = BinaryOp.DivF32; + break; + + case TypeKind.F64: + op = BinaryOp.DivF64; + break; + + default: + throw new Error("concrete type expected"); + } break; case Token.PERCENT_EQUALS: @@ -1494,18 +1812,48 @@ export class Compiler extends DiagnosticEmitter { case Token.PERCENT: left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - if (this.currentType.isAnyFloat) { - // TODO: internal fmod, possibly simply imported from JS - this.error(DiagnosticCode.Operation_not_supported, expression.range); - return this.module.createUnreachable(); + + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + op = BinaryOp.RemI32; + break; + + case TypeKind.ISIZE: + op = select(BinaryOp.RemI64, BinaryOp.RemI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + op = BinaryOp.RemI64; + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.RemU32; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + op = select(BinaryOp.RemU64, BinaryOp.RemU32, this.options.target == Target.WASM64); + break; + + case TypeKind.U64: + op = BinaryOp.RemU64; + break; + + case TypeKind.F32: + case TypeKind.F64: + // TODO: internal fmod, possibly simply imported from JS + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); + + default: + throw new Error("concrete type expected"); } - op = this.currentType.isSignedInteger - ? this.currentType.isLongInteger - ? BinaryOp.RemI64 - : BinaryOp.RemI32 - : this.currentType.isLongInteger - ? BinaryOp.RemU64 - : BinaryOp.RemU32; break; case Token.LESSTHAN_LESSTHAN_EQUALS: @@ -1513,9 +1861,24 @@ export class Compiler extends DiagnosticEmitter { case Token.LESSTHAN_LESSTHAN: left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType.isLongInteger - ? BinaryOp.ShlI64 - : BinaryOp.ShlI32; + + switch (this.currentType.kind) { + + default: + op = BinaryOp.ShlI32; + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.ShlI64; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.ShlI64, BinaryOp.ShlI32, this.options.target == Target.WASM64); + break; + } break; case Token.GREATERTHAN_GREATERTHAN_EQUALS: @@ -1523,13 +1886,37 @@ export class Compiler extends DiagnosticEmitter { case Token.GREATERTHAN_GREATERTHAN: left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType.isSignedInteger - ? this.currentType.isLongInteger - ? BinaryOp.ShrI64 - : BinaryOp.ShrI32 - : this.currentType.isLongInteger - ? BinaryOp.ShrU64 - : BinaryOp.ShrU32; + + switch (this.currentType.kind) { + + default: + op = BinaryOp.ShrI32; + break; + + case TypeKind.I64: + op = BinaryOp.ShrI64; + break; + + case TypeKind.ISIZE: + op = select(BinaryOp.ShrI64, BinaryOp.ShrI32, this.options.target == Target.WASM64); + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + op = BinaryOp.ShrU32; + break; + + case TypeKind.U64: + op = BinaryOp.ShrU64; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + op = select(BinaryOp.ShrU64, BinaryOp.ShrU32, this.options.target == Target.WASM64); + break; + } break; case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: @@ -1537,9 +1924,24 @@ export class Compiler extends DiagnosticEmitter { case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType.isLongInteger - ? BinaryOp.ShrU64 - : BinaryOp.ShrU32; + + switch (this.currentType.kind) { + + default: + op = BinaryOp.ShrU32; + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.ShrU64; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.ShrU64, BinaryOp.ShrU32, this.options.target == Target.WASM64); + break; + } break; case Token.AMPERSAND_EQUALS: @@ -1547,9 +1949,24 @@ export class Compiler extends DiagnosticEmitter { case Token.AMPERSAND: left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType.isLongInteger - ? BinaryOp.AndI64 - : BinaryOp.AndI32; + + switch (this.currentType.kind) { + + default: + op = BinaryOp.AndI32; + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.AndI64; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.AndI64, BinaryOp.AndI32, this.options.target == Target.WASM64); + break; + } break; case Token.BAR_EQUALS: @@ -1557,9 +1974,24 @@ export class Compiler extends DiagnosticEmitter { case Token.BAR: left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType.isLongInteger - ? BinaryOp.OrI64 - : BinaryOp.OrI32; + + switch (this.currentType.kind) { + + default: + op = BinaryOp.OrI32; + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.OrI64; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.OrI64, BinaryOp.OrI32, this.options.target == Target.WASM64); + break; + } break; case Token.CARET_EQUALS: @@ -1567,11 +1999,28 @@ export class Compiler extends DiagnosticEmitter { case Token.CARET: left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); - op = this.currentType.isLongInteger - ? BinaryOp.XorI64 - : BinaryOp.XorI32; + + switch (this.currentType.kind) { + + default: + op = BinaryOp.XorI32; + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.XorI64; + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.XorI64, BinaryOp.XorI32, this.options.target == Target.WASM64); + break; + } break; + // logical (no overloading) + case Token.AMPERSAND_AMPERSAND: // left && right left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); @@ -1767,15 +2216,18 @@ export class Compiler extends DiagnosticEmitter { compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef { var element: Element | null = null; + var thisExpression: Expression; switch (expression.expression.kind) { + // case NodeKind.THIS: // case NodeKind.SUPER: case NodeKind.IDENTIFIER: - element = this.program.resolveIdentifier(expression.expression, this.currentFunction); + element = this.program.resolveIdentifier(thisExpression = expression.expression, this.currentFunction); break; case NodeKind.PROPERTYACCESS: element = this.program.resolvePropertyAccess(expression.expression, this.currentFunction); + thisExpression = (expression.expression).expression; break; default: @@ -1788,18 +2240,22 @@ export class Compiler extends DiagnosticEmitter { var functionPrototype = element; var functionInstance: Function | null = null; if (functionPrototype.isBuiltIn) { - var k = expression.typeArguments.length; - var resolvedTypeArguments = new Array(k); - sb.length = 0; - for (var i = 0; i < k; ++i) { - var resolvedType = this.program.resolveType(expression.typeArguments[i], this.currentFunction.contextualTypeArguments, true); // reports - if (!resolvedType) - return this.module.createUnreachable(); - resolvedTypeArguments[i] = resolvedType; - sb.push(resolvedType.toString()); - } + var resolvedTypeArguments: Type[] | null = null; + if (expression.typeArguments) { + var k = expression.typeArguments.length; + resolvedTypeArguments = new Array(k); + var sb = new Array(k); + for (var i = 0; i < k; ++i) { + var resolvedType = this.program.resolveType(expression.typeArguments[i], this.currentFunction.contextualTypeArguments, true); // reports + if (!resolvedType) + return this.module.createUnreachable(); + resolvedTypeArguments[i] = resolvedType; + sb[i] = resolvedType.toString(); + } + functionInstance = functionPrototype.instances.get(sb.join(",")); + } else + functionInstance = functionPrototype.instances.get(""); - functionInstance = functionPrototype.instances.get(sb.join(",")); if (!functionInstance) { this.currentType = contextualType; var expr = compileBuiltinCall(this, functionPrototype, resolvedTypeArguments, expression.arguments, expression); @@ -1815,7 +2271,17 @@ export class Compiler extends DiagnosticEmitter { } if (!functionInstance) return this.module.createUnreachable(); - return this.compileCall(functionInstance, expression.arguments, expression); + + var numArguments = expression.arguments.length; + var numArgumentsInclThis = select(numArguments + 1, numArguments, functionInstance.instanceMethodOf != null); + var argumentIndex = 0; + + var args = new Array(numArgumentsInclThis); + if (functionInstance.instanceMethodOf) + args[argumentIndex++] = thisExpression; + for (i = 0; i < numArguments; ++i) + args[argumentIndex++] = expression.arguments[i]; + return this.compileCall(functionInstance, args, expression); } this.error(DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, expression.range, element.internalName); return this.module.createUnreachable(); @@ -1826,30 +2292,42 @@ export class Compiler extends DiagnosticEmitter { // validate and compile arguments var parameters = functionInstance.parameters; - var parameterCount = parameters.length; - var argumentCount = argumentExpressions.length; - if (argumentExpressions.length > parameterCount) { // too many arguments + + var numParameters = parameters.length; + var numParametersInclThis = select(numParameters + 1, numParameters, functionInstance.instanceMethodOf != null); + var numArgumentsInclThis = argumentExpressions.length; + var numArguments = select(numArgumentsInclThis - 1, numArgumentsInclThis, functionInstance.instanceMethodOf != null); + + if (numArgumentsInclThis > numParametersInclThis) { // too many arguments this.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, - (functionInstance.isInstance ? parameterCount - 1 : parameterCount).toString(10), - (functionInstance.isInstance ? argumentCount - 1 : argumentCount).toString(10) + numParameters.toString(10), + numArguments.toString(10) ); return this.module.createUnreachable(); } - var operands = new Array(parameterCount); - for (var i = 0; i < parameterCount; ++i) { - if (argumentExpressions.length > i) { - operands[i] = this.compileExpression(argumentExpressions[i], parameters[i].type); + var operands = new Array(numParametersInclThis); + var operandIndex = 0; + if (functionInstance.instanceMethodOf) + operands[operandIndex++] = this.compileExpression(argumentExpressions[0], functionInstance.instanceMethodOf.type); + for (; operandIndex < numParametersInclThis; ++operandIndex) { + + // argument has been provided + if (numArgumentsInclThis > operandIndex) { + operands[operandIndex] = this.compileExpression(argumentExpressions[operandIndex], parameters[operandIndex + numParameters - numParametersInclThis].type); + + // argument has been omitted } else { - var initializer = parameters[i].initializer; - if (initializer) { // omitted, uses initializer + var initializer = parameters[operandIndex + numParameters - numParametersInclThis].initializer; + if (initializer) { // fall back to provided initializer + operands[operandIndex] = this.compileExpression(initializer, parameters[operandIndex + numParameters - numParametersInclThis].type); // FIXME: here, the initializer is compiled in the caller's scope. // a solution could be to use a stub for each possible overload, calling the // full function with optional arguments being part of the stub's body. - operands[i] = this.compileExpression(initializer, parameters[i].type); + } else { // too few arguments this.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, - (functionInstance.isInstance ? i : i + 1).toString(10), - (functionInstance.isInstance ? argumentCount - 1 : argumentCount).toString(10) + (operandIndex + numParametersInclThis - numParameters).toString(10), + numArguments.toString(10) ); return this.module.createUnreachable(); } @@ -1870,8 +2348,24 @@ export class Compiler extends DiagnosticEmitter { } compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): ExpressionRef { - var element = this.program.resolveElement(expression.expression, this.currentFunction); // reports - if (!element) + var targetExpression = expression.expression; + var target: Element | null = null; + switch (targetExpression.kind) { + + // case NodeKind.THIS: + + case NodeKind.IDENTIFIER: + target = this.program.resolveIdentifier(targetExpression, this.currentFunction); + break; + + case NodeKind.PROPERTYACCESS: + target = this.program.resolvePropertyAccess(targetExpression, this.currentFunction); + break; + + default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + } + if (!target) return this.module.createUnreachable(); throw new Error("not implemented"); } @@ -2135,28 +2629,47 @@ export class Compiler extends DiagnosticEmitter { var nativeType: NativeType; var nativeOne: ExpressionRef; - if (tempLocal.type == Type.f32) { - op = operator == Token.PLUS_PLUS ? BinaryOp.AddF32 : BinaryOp.SubF32; - nativeType = NativeType.F32; - nativeOne = this.module.createF32(1); + switch ((tempLocal.type).kind) { - } else if (tempLocal.type == Type.f64) { - op = operator == Token.PLUS_PLUS ? BinaryOp.AddF64 : BinaryOp.SubF64; - nativeType = NativeType.F64; - nativeOne = this.module.createF64(1); + default: + op = select(BinaryOp.AddI32, BinaryOp.SubI32, operator == Token.PLUS_PLUS); + nativeType = NativeType.I32; + nativeOne = this.module.createI32(1); + break; - } else if ((tempLocal.type).isLongInteger) { - op = operator == Token.PLUS_PLUS ? BinaryOp.AddI64 : BinaryOp.SubI64; - nativeType = NativeType.I64; - nativeOne = this.module.createI64(1, 0); + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + if (this.options.target != Target.WASM64) { + op = select(BinaryOp.AddI32, BinaryOp.SubI32, operator == Token.PLUS_PLUS); + nativeType = NativeType.I32; + nativeOne = this.module.createI32(1); + break; + } + // fall-through - } else { - op = operator == Token.PLUS_PLUS ? BinaryOp.AddI32 : BinaryOp.SubI32; - nativeType = NativeType.I32; - nativeOne = this.module.createI32(1); + case TypeKind.I64: + case TypeKind.U64: + op = select(BinaryOp.AddI64, BinaryOp.SubI64, operator == Token.PLUS_PLUS); + nativeType = NativeType.I64; + nativeOne = this.module.createI64(1, 0); + break; + + case TypeKind.F32: + op = select(BinaryOp.AddF32, BinaryOp.SubF32, operator == Token.PLUS_PLUS); + nativeType = NativeType.F32; + nativeOne = this.module.createF32(1); + break; + + case TypeKind.F64: + op = select(BinaryOp.AddF64, BinaryOp.SubF64, operator == Token.PLUS_PLUS); + nativeType = NativeType.F64; + nativeOne = this.module.createF64(1); + break; } // make a setter that sets the new value (temp value +/- 1) + // note that this is not inlined below because it modifies currentType var setValue = this.compileAssignmentWithValue(expression.operand, this.module.createBinary(op, this.module.createGetLocal(tempLocal.index, nativeType), @@ -2188,57 +2701,139 @@ export class Compiler extends DiagnosticEmitter { case Token.MINUS: operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); - if (this.currentType == Type.f32) - op = UnaryOp.NegF32; - else if (this.currentType == Type.f64) - op = UnaryOp.NegF64; - else - return this.currentType.isLongInteger - ? this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0, 0), operand) - : this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand); + switch (this.currentType.kind) { + + default: + return this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand); + + case TypeKind.ISIZE: + case TypeKind.USIZE: + if (this.options.target != Target.WASM64) + return this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand); + // fall-through + + case TypeKind.I64: + case TypeKind.U64: + return this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0, 0), operand); + + case TypeKind.F32: + op = UnaryOp.NegF32; + break; + + case TypeKind.F64: + op = UnaryOp.NegF64; + break; + } break; case Token.PLUS_PLUS: operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); - return this.currentType == Type.f32 - ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddF32, operand, this.module.createF32(1)), contextualType != Type.void) - : this.currentType == Type.f64 - ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddF64, operand, this.module.createF64(1)), contextualType != Type.void) - : this.currentType.isLongInteger - ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddI64, operand, this.module.createI64(1, 0)), contextualType != Type.void) - : this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)), contextualType != Type.void); + switch (this.currentType.kind) { + + default: + operand = this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)); + break; + + case TypeKind.ISIZE: + case TypeKind.USIZE: + if (this.options.target != Target.WASM64) { + operand = this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)); + break; + } + // fall-through + + case TypeKind.I64: + case TypeKind.U64: + operand = this.module.createBinary(BinaryOp.AddI64, operand, this.module.createI64(1, 0)); + break; + + case TypeKind.F32: + operand = this.module.createBinary(BinaryOp.AddF32, operand, this.module.createF32(1)); + break; + + case TypeKind.F64: + operand = this.module.createBinary(BinaryOp.AddF64, operand, this.module.createF64(1)); + break; + } + return this.compileAssignmentWithValue(operandExpression, operand, contextualType != Type.void); case Token.MINUS_MINUS: operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); - return this.currentType == Type.f32 - ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubF32, operand, this.module.createF32(1)), contextualType != Type.void) - : this.currentType == Type.f64 - ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubF64, operand, this.module.createF64(1)), contextualType != Type.void) - : this.currentType.isLongInteger - ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubI64, operand, this.module.createI64(1, 0)), contextualType != Type.void) - : this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)), contextualType != Type.void); + switch (this.currentType.kind) { + + default: + operand = this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)); + break; + + case TypeKind.ISIZE: + case TypeKind.USIZE: + if (this.options.target != Target.WASM64) { + operand = this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)); + break; + } + // fall-through + + case TypeKind.I64: + case TypeKind.U64: + operand = this.module.createBinary(BinaryOp.SubI64, operand, this.module.createI64(1, 0)); + break; + + case TypeKind.F32: + operand = this.module.createBinary(BinaryOp.SubF32, operand, this.module.createF32(1)); + break; + + case TypeKind.F64: + operand = this.module.createBinary(BinaryOp.SubF64, operand, this.module.createF64(1)); + break; + } + return this.compileAssignmentWithValue(operandExpression, operand, contextualType != Type.void); case Token.EXCLAMATION: operand = this.compileExpression(operandExpression, Type.bool, ConversionKind.NONE); - if (this.currentType == Type.f32) { - this.currentType = Type.bool; - return this.module.createBinary(BinaryOp.EqF32, operand, this.module.createF32(0)); + switch (this.currentType.kind) { + + default: + op = UnaryOp.EqzI32; + break; + + case TypeKind.ISIZE: + case TypeKind.USIZE: + op = select(UnaryOp.EqzI64, UnaryOp.EqzI32, this.options.target == Target.WASM64); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = UnaryOp.EqzI64; + break; + + case TypeKind.F32: + this.currentType = Type.bool; + return this.module.createBinary(BinaryOp.EqF32, operand, this.module.createF32(0)); + + case TypeKind.F64: + this.currentType = Type.bool; + return this.module.createBinary(BinaryOp.EqF64, operand, this.module.createF64(0)); } - if (this.currentType == Type.f64) { - this.currentType = Type.bool; - return this.module.createBinary(BinaryOp.EqF64, operand, this.module.createF64(0)); - } - op = this.currentType.isLongInteger - ? UnaryOp.EqzI64 - : UnaryOp.EqzI32; this.currentType = Type.bool; break; case Token.TILDE: operand = this.compileExpression(operandExpression, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); - return this.currentType.isLongInteger - ? this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1)) - : this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1)); + switch (this.currentType.kind) { + + default: + return this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1)); + + case TypeKind.ISIZE: + case TypeKind.USIZE: + if (this.options.target != Target.WASM64) + return this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1)); + // fall-through + + case TypeKind.I64: + case TypeKind.U64: + return this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1)); + } default: throw new Error("unary operator expected"); @@ -2273,23 +2868,39 @@ function isModuleExport(element: Element, declaration: DeclarationStatement): bo function makeInlineConstant(element: VariableLikeElement, module: Module): ExpressionRef { assert(element.hasConstantValue); assert(element.type != null); - if (element.type == Type.f32) - return module.createF32((element).constantFloatValue); - else if (element.type == Type.f64) - return module.createF64((element).constantFloatValue); - else if ((element.type).isLongInteger) - return element.constantIntegerValue - ? module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi) - : module.createI64(0, 0); - else if ((element.type).isSmallInteger) { - if ((element.type).isSignedInteger) { + switch ((element.type).kind) { + + case TypeKind.I8: + case TypeKind.I16: var shift = (element.type).smallIntegerShift; return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0); - } else + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & (element.type).smallIntegerMask: 0); - } else if ((element.type).isAnyInteger) - return element.constantIntegerValue - ? module.createI32(element.constantIntegerValue.lo) - : module.createI32(0); + + case TypeKind.I32: + case TypeKind.U32: + return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.lo : 0) + + case TypeKind.ISIZE: + case TypeKind.USIZE: + if (element.program.target != Target.WASM64) + return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.lo : 0) + // fall-through + + case TypeKind.I64: + case TypeKind.U64: + return element.constantIntegerValue + ? module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi) + : module.createI64(0, 0); + + case TypeKind.F32: + return module.createF32((element).constantFloatValue); + + case TypeKind.F64: + return module.createF64((element).constantFloatValue); + } throw new Error("concrete type expected"); } diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 3d1e7118..beaa340a 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -1,7 +1,7 @@ // code below is generated from diagnosticsMessages.json by scripts/build-diagnostics export enum DiagnosticCode { - Conversion_from_type_0_to_1_requires_an_explicit_cast = 100, + Conversion_from_type_0_to_1_possibly_loses_information_and_thus_requires_an_explicit_cast = 100, Basic_type_0_cannot_be_nullable = 101, Operation_not_supported = 102, Operation_is_unsafe = 103, @@ -82,7 +82,7 @@ export enum DiagnosticCode { export function diagnosticCodeToString(code: DiagnosticCode): string { switch (code) { - case 100: return "Conversion from type '{0}' to '{1}' requires an explicit cast."; + case 100: return "Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast."; case 101: return "Basic type '{0}' cannot be nullable."; case 102: return "Operation not supported."; case 103: return "Operation is unsafe."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 2a4444f8..339825e4 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -1,5 +1,5 @@ { - "Conversion from type '{0}' to '{1}' requires an explicit cast.": 100, + "Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast.": 100, "Basic type '{0}' cannot be nullable.": 101, "Operation not supported.": 102, "Operation is unsafe.": 103, diff --git a/src/index.ts b/src/index.ts index 54c4c638..9b0d64dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,9 +68,9 @@ export function nextFile(parser: Parser): string | null { /** Obtains the next diagnostic message. Returns `null` once there are no more messages. */ export function nextDiagnostic(parser: Parser): DiagnosticMessage | null { var program = parser.program; - if (program.diagnosticsOffset < program.diagnostics.length) - return program.diagnostics[program.diagnosticsOffset++]; - return null; + return program.diagnosticsOffset < program.diagnostics.length + ? program.diagnostics[program.diagnosticsOffset++] + : null; } /** Formats a diagnostic message to a string. */ diff --git a/src/parser.ts b/src/parser.ts index ac2d5534..1f664c88 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -372,12 +372,13 @@ export class Parser extends DiagnosticEmitter { return null; } } + var args: Expression[] | null; if (tn.skip(Token.OPENPAREN)) { - var args = this.parseArguments(tn); + args = this.parseArguments(tn); if (args) - return Node.createDecorator(expression, args, tn.range(startPos, tn.pos)); + return Node.createDecorator(expression, args, tn.range(startPos, tn.pos)); } else - this.error(DiagnosticCode._0_expected, tn.range(), "("); + return Node.createDecorator(expression, null, tn.range(startPos, tn.pos)); } else this.error(DiagnosticCode.Identifier_expected, tn.range()); return null; @@ -1515,7 +1516,7 @@ export class Parser extends DiagnosticEmitter { } tryParseTypeArgumentsBeforeArguments(tn: Tokenizer): TypeNode[] | null { - // at '<': Identifier (',' Identifier)* '>' '(' + // at '<': Type (',' Type)* '>' '(' tn.mark(); if (!tn.skip(Token.LESSTHAN)) return null; @@ -1529,11 +1530,12 @@ export class Parser extends DiagnosticEmitter { } typeArguments.push(type); } while (tn.skip(Token.COMMA)); - if (!(tn.skip(Token.GREATERTHAN) && tn.skip(Token.OPENPAREN))) { - tn.reset(); - return null; - } - return typeArguments; + + if (tn.skip(Token.GREATERTHAN) && tn.skip(Token.OPENPAREN)) + return typeArguments; + + tn.reset(); + return null; } parseArguments(tn: Tokenizer): Expression[] | null { @@ -1562,13 +1564,13 @@ export class Parser extends DiagnosticEmitter { var startPos = expr.range.start; // CallExpression - var typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn); + var typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn); // skips '(' on success // there might be better ways to distinguish a LESSTHAN from a CALL with type arguments if (typeArguments || tn.skip(Token.OPENPAREN)) { var args = this.parseArguments(tn); if (!args) return null; - expr = Node.createCall(expr, (typeArguments ? typeArguments : []), args, tn.range(startPos, tn.pos)); + expr = Node.createCall(expr, typeArguments, args, tn.range(startPos, tn.pos)); } var token: Token; diff --git a/src/program.ts b/src/program.ts index 0c865a3a..72c9ed8d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -44,6 +44,7 @@ import { PropertyAccessExpression, StringLiteralExpression, CallExpression, + NewExpression, Statement, ClassDeclaration, @@ -64,6 +65,7 @@ import { VariableDeclaration, VariableStatement, + hasDecorator, hasModifier, mangleInternalName } from "./ast"; @@ -175,7 +177,7 @@ export class Program extends DiagnosticEmitter { break; case NodeKind.TYPEDECLARATION: - this.initializeType(statement); + this.initializeTypeAlias(statement); break; case NodeKind.VARIABLE: @@ -746,7 +748,7 @@ export class Program extends DiagnosticEmitter { break; case NodeKind.TYPEDECLARATION: - this.initializeType(members[i], namespace); + this.initializeTypeAlias(members[i], namespace); break; case NodeKind.VARIABLE: @@ -759,7 +761,7 @@ export class Program extends DiagnosticEmitter { } } - private initializeType(declaration: TypeDeclaration, namespace: Element | null = null): void { + private initializeTypeAlias(declaration: TypeDeclaration, namespace: Element | null = null): void { // type aliases are program globals // TODO: what about namespaced types? var name = declaration.name.name; @@ -811,6 +813,19 @@ export class Program extends DiagnosticEmitter { /** Resolves a {@link TypeNode} to a concrete {@link Type}. */ resolveType(node: TypeNode, contextualTypeArguments: Map | null = null, reportNotFound: bool = true): Type | null { + var globalName = node.identifier.name; + var localName = node.range.source.internalPath + PATH_DELIMITER + node.identifier.name; + + var element: Element | null; + + // check file-global / program-global element + if ((element = this.elements.get(localName)) || (element = this.elements.get(globalName))) { + switch (element.kind) { + case ElementKind.CLASS_PROTOTYPE: + var instance = (element).resolveInclTypeArguments(node.typeArguments, contextualTypeArguments, null); // reports + return instance ? instance.type : null; + } + } // resolve parameters var k = node.typeArguments.length; @@ -822,10 +837,13 @@ export class Program extends DiagnosticEmitter { paramTypes[i] = paramType; } - var globalName = node.identifier.name; - if (k) // can't be a placeholder if it has parameters - globalName += typesToString(paramTypes); - else if (contextualTypeArguments) { + if (k) { // can't be a placeholder if it has parameters + var instanceKey = typesToString(paramTypes); + if (instanceKey.length) { + localName += "<" + instanceKey + ">"; + globalName += "<" + instanceKey + ">"; + } + } else if (contextualTypeArguments) { var placeholderType = contextualTypeArguments.get(globalName); if (placeholderType) return placeholderType; @@ -833,12 +851,8 @@ export class Program extends DiagnosticEmitter { var type: Type | null; - // check file-global type - if (type = this.types.get(node.range.source.internalPath + PATH_DELIMITER + globalName)) - return type; - - // check program-global type - if (type = this.types.get(globalName)) + // check file-global / program-global type + if ((type = this.types.get(localName)) || (type = this.types.get(globalName))) return type; // check type alias @@ -904,7 +918,7 @@ export class Program extends DiagnosticEmitter { return null; } - /** Resolves a property access the element it refers to. */ + /** Resolves a property access to the element it refers to. */ resolvePropertyAccess(propertyAccess: PropertyAccessExpression, contextualFunction: Function): Element | null { var expression = propertyAccess.expression; var target: Element | null = null; @@ -917,12 +931,27 @@ export class Program extends DiagnosticEmitter { if (!target) return null; var propertyName = propertyAccess.property.name; - if (target.members) { - var member = target.members.get(propertyName); - if (member) - return member; + switch (target.kind) { + + case ElementKind.GLOBAL: + case ElementKind.LOCAL: + var type = (target).type; + assert(type != null); + if ((type).classType) { + target = (type).classType; + // fall-through + } else + break; + + default: + if (target.members) { + var member = target.members.get(propertyName); + if (member) + return member; + } + break; } - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (expression).property.name, target.internalName); + this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyAccess.property.name, target.internalName); return null; } @@ -946,24 +975,13 @@ export class Program extends DiagnosticEmitter { // instantiation } else if (expression.kind == NodeKind.NEW) { - return this.resolveElement((expression).expression, contextualFunction); + return this.resolveElement((expression).expression, contextualFunction); } throw new Error("not implemented"); } } -function hasDecorator(name: string, decorators: Decorator[] | null): bool { - if (decorators) - for (var i = 0, k = decorators.length; i < k; ++i) { - var decorator = decorators[i]; - var expression = decorator.name; - if (expression.kind == NodeKind.IDENTIFIER && decorator.arguments.length <= 1 && (expression).name == name) - return true; - } - return false; -} - /** Indicates the specific kind of an {@link Element}. */ export enum ElementKind { /** A {@link Global}. */ @@ -1274,6 +1292,8 @@ export class FunctionPrototype extends Element { classPrototype: ClassPrototype | null; /** Resolved instances. */ instances: Map = new Map(); + /** Class type arguments, if a partially resolved method of a generic class. */ + classTypeArguments: Type[] | null = null; /** Constructs a new function prototype. */ constructor(program: Program, simpleName: string, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) { @@ -1298,9 +1318,8 @@ export class FunctionPrototype extends Element { if (this.declaration.typeParameters.length) this.isGeneric = true; } - if (this.classPrototype = classPrototype) { + if (this.classPrototype = classPrototype) this.isInstance = true; - } } /** Whether a getter function or not. */ @@ -1314,8 +1333,8 @@ export class FunctionPrototype extends Element { // Whether a getter/setter function or not. get isAccessor(): bool { return (this.flags & (ElementFlags.GETTER | ElementFlags.SETTER)) != 0; } - resolve(typeArguments: Type[] | null = null, contextualTypeArguments: Map | null = null): Function | null { - var instanceKey = typeArguments ? typesToString(typeArguments, "", "") : ""; + resolve(functionTypeArguments: Type[] | null = null, contextualTypeArguments: Map | null = null): Function | null { + var instanceKey = functionTypeArguments ? typesToString(functionTypeArguments) : ""; var instance = this.instances.get(instanceKey); if (instance) return instance; @@ -1323,16 +1342,34 @@ export class FunctionPrototype extends Element { if (!declaration) throw new Error("declaration expected"); // cannot resolve built-ins - // override call specific contextual type arguments + // inherit contextual type arguments + var inheritedTypeArguments = contextualTypeArguments; + contextualTypeArguments = new Map(); + if (inheritedTypeArguments) + for (var [inheritedName, inheritedType] of inheritedTypeArguments) + contextualTypeArguments.set(inheritedName, inheritedType); + var i: i32, k: i32; - if (typeArguments && (k = typeArguments.length)) { - var inheritedTypeArguments = contextualTypeArguments; - contextualTypeArguments = new Map(); - if (inheritedTypeArguments) - for (var [inheritedName, inheritedType] of inheritedTypeArguments) - contextualTypeArguments.set(inheritedName, inheritedType); + + // inherit class type arguments if a partially resolved instance method + if (this.classPrototype && this.classTypeArguments) { + var classDeclaration = this.classPrototype.declaration; + if (!classDeclaration) + throw new Error("declaration expected"); // cannot resolve built-ins + var classTypeParameters = classDeclaration.typeParameters; + if ((k = this.classTypeArguments.length) != classTypeParameters.length) + throw new Error("unexpected type argument count mismatch"); for (i = 0; i < k; ++i) - contextualTypeArguments.set(declaration.typeParameters[i].identifier.name, typeArguments[i]); + contextualTypeArguments.set(classTypeParameters[i].identifier.name, this.classTypeArguments[i]); + } + + // override call specific contextual type arguments + var functionTypeParameters = declaration.typeParameters; + if (functionTypeArguments && (k = functionTypeArguments.length)) { + if (k != functionTypeParameters.length) + throw new Error("unexpected type argument count mismatch"); + for (i = 0; i < k; ++i) + contextualTypeArguments.set(functionTypeParameters[i].identifier.name, functionTypeArguments[i]); } // resolve parameters @@ -1370,13 +1407,20 @@ export class FunctionPrototype extends Element { var internalName = this.internalName; if (instanceKey.length) internalName += "<" + instanceKey + ">"; - instance = new Function(this, internalName, typeArguments, parameters, returnType, null); // TODO: class + var classInstance: Class | null = null; + if (this.classPrototype) { + classInstance = this.classPrototype.resolve(this.classTypeArguments, contextualTypeArguments); // reports + if (!classInstance) + return null; + } + instance = new Function(this, internalName, functionTypeArguments, parameters, returnType, classInstance); + instance.contextualTypeArguments = contextualTypeArguments; this.instances.set(instanceKey, instance); return instance; } resolveInclTypeArguments(typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map | null, alternativeReportNode: Node | null): Function | null { - var resolvedTypeArguments: Type[] | null; + var resolvedTypeArguments: Type[] | null = null; if (this.isGeneric) { assert(typeArgumentNodes != null && typeArgumentNodes.length != 0); if (!this.declaration) @@ -1384,13 +1428,21 @@ export class FunctionPrototype extends Element { resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode); if (!resolvedTypeArguments) return null; - } else { - assert(typeArgumentNodes == null || typeArgumentNodes.length == 0); - resolvedTypeArguments = []; } return this.resolve(resolvedTypeArguments, contextualTypeArguments); } + resolvePartial(classTypeArguments: Type[] | null): FunctionPrototype | null { + assert(this.classPrototype != null); + if (classTypeArguments && classTypeArguments.length) { + var partialPrototype = new FunctionPrototype(this.program, this.simpleName, this.internalName, this.declaration, this.classPrototype); + partialPrototype.flags = this.flags; + partialPrototype.classTypeArguments = classTypeArguments; + return partialPrototype; + } + return this; // no need to clone + } + toString(): string { return this.simpleName; } } @@ -1435,9 +1487,10 @@ export class Function extends Element { assert(this.isInstance); this.locals.set("this", new Local(prototype.program, "this", localIndex++, instanceMethodOf.type)); if (instanceMethodOf.contextualTypeArguments) { - if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map(); - for (var [name, type] of instanceMethodOf.contextualTypeArguments) - this.contextualTypeArguments.set(name, type); + if (!this.contextualTypeArguments) + this.contextualTypeArguments = new Map(); + for (var [inheritedName, inheritedType] of instanceMethodOf.contextualTypeArguments) + this.contextualTypeArguments.set(inheritedName, inheritedType); } } else assert(!this.isInstance); @@ -1477,9 +1530,9 @@ export class Function extends Element { case NativeType.F64: temps = this.tempF64s; break; default: throw new Error("unexpected type"); } - if (temps && temps.length > 0) - return temps.pop(); - return this.addLocal(type); + return temps && temps.length > 0 + ? temps.pop() + : this.addLocal(type); } /** Frees the temporary local for reuse. */ @@ -1584,6 +1637,12 @@ export class FieldPrototype extends Element { /** Whether the field is read-only or not. */ get isReadonly(): bool { return (this.flags & ElementFlags.READONLY) != 0; } set isReadonly(is: bool) { if (is) this.flags |= ElementFlags.READONLY; else this.flags &= ~ElementFlags.READONLY; } + + // resolve(contextualTypeArguments: Map | null = null): Field { + // if (!this.declaration) + // throw new Error("declaration expected"); + // this.declaration.type + // } } /** A resolved instance field. */ @@ -1595,10 +1654,12 @@ export class Field extends Element { prototype: FieldPrototype; /** Resolved type. */ type: Type; - /** Constant integer value, if applicable. */ + /** Constant integer value, if a constant static integer. */ constantIntegerValue: I64 | null = null; - /** Constant float value, if applicable. */ + /** Constant float value, if a constant static float. */ constantFloatValue: f64 = 0; + /** Field memory offset, if an instance field. */ + memoryOffset: i32 = -1; /** Constructs a new field. */ constructor(prototype: FieldPrototype, internalName: string, type: Type) { @@ -1657,36 +1718,31 @@ export class ClassPrototype extends Element { } } - resolve(typeArguments: Type[], contextualTypeArguments: Map | null): Class { - var instanceKey = typesToString(typeArguments, "", ""); + resolve(typeArguments: Type[] | null, contextualTypeArguments: Map | null = null): Class { + var instanceKey = typeArguments ? typesToString(typeArguments) : ""; var instance = this.instances.get(instanceKey); if (instance) return instance; + var declaration = this.declaration; if (!declaration) throw new Error("declaration expected"); // cannot resolve built-ins - // override call specific contextual type arguments - var i: i32, k = typeArguments.length; - if (k) { - var inheritedTypeArguments = contextualTypeArguments; - contextualTypeArguments = new Map(); - if (inheritedTypeArguments) - for (var [inheritedName, inheritedType] of inheritedTypeArguments) - contextualTypeArguments.set(inheritedName, inheritedType); - for (i = 0; i < k; ++i) - contextualTypeArguments.set(declaration.typeParameters[i].identifier.name, typeArguments[i]); - } + // inherit contextual type arguments + var inheritedTypeArguments = contextualTypeArguments; + contextualTypeArguments = new Map(); + if (inheritedTypeArguments) + for (var [inheritedName, inheritedType] of inheritedTypeArguments) + contextualTypeArguments.set(inheritedName, inheritedType); - // TODO: set up instance fields and methods - if (this.instanceMembers) - for (var member of this.instanceMembers.values()) { - switch (member.kind) { - case ElementKind.FIELD_PROTOTYPE: break; - case ElementKind.FUNCTION_PROTOTYPE: break; - default: throw new Error("unexpected instance member"); - } - } + if (declaration.extendsType) // TODO: base class + throw new Error("not implemented"); + + // override call specific contextual type arguments if provided + var i: i32, k: i32; + if (typeArguments) + for (var i = 0, k = typeArguments.length; i < k; ++i) + contextualTypeArguments.set(declaration.typeParameters[i].identifier.name, typeArguments[i]); var internalName = this.internalName; if (instanceKey.length) @@ -1694,6 +1750,51 @@ export class ClassPrototype extends Element { instance = new Class(this, internalName, typeArguments, null); // TODO: base class instance.contextualTypeArguments = contextualTypeArguments; this.instances.set(instanceKey, instance); + + var memoryOffset: i32 = 0; + + if (this.instanceMembers) + for (var member of this.instanceMembers.values()) { + switch (member.kind) { + + case ElementKind.FIELD_PROTOTYPE: // fields are layed out in advance + if (!instance.members) + instance.members = new Map(); + var fieldDeclaration = (member).declaration; + if (!fieldDeclaration) + throw new Error("declaration expected"); + if (!fieldDeclaration.type) + throw new Error("type expected"); // TODO: check if parent class defines a type for it already + var fieldType = this.program.resolveType(fieldDeclaration.type, instance.contextualTypeArguments); // reports + if (fieldType) { + var fieldInstance = new Field(member, (member).internalName, fieldType); + switch (fieldType.byteSize) { // align + case 1: break; + case 2: if (memoryOffset & 1) ++memoryOffset; break; + case 4: if (memoryOffset & 3) memoryOffset = (memoryOffset | 3) + 1; break; + case 8: if (memoryOffset & 7) memoryOffset = (memoryOffset | 7) + 1; break; + default: assert(false); + } + fieldInstance.memoryOffset = memoryOffset; + memoryOffset += fieldType.byteSize; + instance.members.set(member.simpleName, fieldInstance); + } + break; + + case ElementKind.FUNCTION_PROTOTYPE: // instance methods remain partially resolved prototypes until compiled + if (!instance.members) + instance.members = new Map(); + var methodPrototype = (member).resolvePartial(typeArguments); // reports + if (methodPrototype) + instance.members.set(member.simpleName, methodPrototype); + break; + + default: + throw new Error("instance member expected"); + } + } + + instance.type.byteSize = memoryOffset; return instance; } @@ -1724,7 +1825,7 @@ export class Class extends Element { /** Prototype reference. */ prototype: ClassPrototype; /** Resolved type arguments. */ - typeArguments: Type[]; + typeArguments: Type[] | null; /** Resolved class type. */ type: Type; /** Base class, if applicable. */ @@ -1733,7 +1834,7 @@ export class Class extends Element { contextualTypeArguments: Map | null = null; /** Constructs a new class. */ - constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] = [], base: Class | null = null) { + constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] | null = null, base: Class | null = null) { super(prototype.program, prototype.simpleName, internalName); this.prototype = prototype; this.flags = prototype.flags; @@ -1750,17 +1851,20 @@ export class Class extends Element { // apply instance-specific contextual type arguments var declaration = this.prototype.declaration; + var i: i32, k: i32; if (declaration) { // irrelevant for built-ins var typeParameters = declaration.typeParameters; - if (typeParameters.length != typeArguments.length) + if (typeArguments) { + if (typeParameters.length != typeArguments.length) + throw new Error("unexpected type argument count mismatch"); + if (k = typeArguments.length) { + if (!this.contextualTypeArguments) + this.contextualTypeArguments = new Map(); + for (i = 0; i < k; ++i) + this.contextualTypeArguments.set(typeParameters[i].identifier.name, typeArguments[i]); + } + } else throw new Error("unexpected type argument count mismatch"); - var k = typeArguments.length; - if (k) { - if (!this.contextualTypeArguments) - this.contextualTypeArguments = new Map(); - for (var i = 0; i < k; ++i) - this.contextualTypeArguments.set(typeParameters[i].identifier.name, typeArguments[i]); - } } } diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 9e0c3389..d2d487c6 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -380,13 +380,13 @@ export class Tokenizer extends DiagnosticEmitter { // skip bom if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.BYTEORDERMARK) - this.pos++; + ++this.pos; // skip shebang if (this.pos + 1 < this.end && text.charCodeAt(this.pos) == CharCode.HASH && text.charCodeAt(this.pos + 1) == CharCode.EXCLAMATION) { this.pos += 2; while (this.pos < this.end && text.charCodeAt(this.pos) != CharCode.LINEFEED) - this.pos++; + ++this.pos; // 'next' now starts at lf or eof } } @@ -396,7 +396,7 @@ export class Tokenizer extends DiagnosticEmitter { return this.token = this.unsafeNext(preferIdentifier); } - private unsafeNext(preferIdentifier: bool = false): Token { + private unsafeNext(preferIdentifier: bool = false, maxCompoundLength: i32 = i32.MAX_VALUE): Token { var text = this.source.text; while (true) { if (this.pos >= this.end) @@ -408,7 +408,7 @@ export class Tokenizer extends DiagnosticEmitter { case CharCode.CARRIAGERETURN: if (++this.pos < this.end && text.charCodeAt(this.pos) == CharCode.LINEFEED) - this.pos++; + ++this.pos; break; case CharCode.LINEFEED: @@ -416,13 +416,15 @@ export class Tokenizer extends DiagnosticEmitter { case CharCode.VERTICALTAB: case CharCode.FORMFEED: case CharCode.SPACE: - this.pos++; + ++this.pos; break; case CharCode.EXCLAMATION: - if (++this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - if (++this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; + if (maxCompoundLength > 2 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; return Token.EXCLAMATION_EQUALS_EQUALS; } return Token.EXCLAMATION_EQUALS; @@ -435,42 +437,46 @@ export class Tokenizer extends DiagnosticEmitter { return Token.STRINGLITERAL; // expects a call to readString case CharCode.PERCENT: - if (++this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; return Token.PERCENT_EQUALS; } return Token.PERCENT; case CharCode.AMPERSAND: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.AMPERSAND) { - this.pos++; + ++this.pos; return Token.AMPERSAND_AMPERSAND; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.AMPERSAND_EQUALS; } } return Token.AMPERSAND; case CharCode.OPENPAREN: - this.pos++; + ++this.pos; return Token.OPENPAREN; case CharCode.CLOSEPAREN: - this.pos++; + ++this.pos; return Token.CLOSEPAREN; case CharCode.ASTERISK: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.ASTERISK_EQUALS; } if (text.charCodeAt(this.pos) == CharCode.ASTERISK) { - if (++this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; + if (maxCompoundLength > 2 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; return Token.ASTERISK_ASTERISK_EQUALS; } return Token.ASTERISK_ASTERISK; @@ -479,42 +485,45 @@ export class Tokenizer extends DiagnosticEmitter { return Token.ASTERISK; case CharCode.PLUS: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.PLUS) { - this.pos++; + ++this.pos; return Token.PLUS_PLUS; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.PLUS_EQUALS; } } return Token.PLUS; case CharCode.COMMA: - this.pos++; + ++this.pos; return Token.COMMA; case CharCode.MINUS: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.MINUS) { - this.pos++; + ++this.pos; return Token.MINUS_MINUS; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.MINUS_EQUALS; } } return Token.MINUS; case CharCode.DOT: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (isDecimalDigit(text.charCodeAt(this.pos))) { - this.pos--; + --this.pos; return Token.FLOATLITERAL; // expects a call to readFloat } - if (text.charCodeAt(this.pos) == CharCode.DOT && this.pos + 1 < this.end && text.charCodeAt(this.pos + 1) == CharCode.DOT) { + if (maxCompoundLength > 2 && text.charCodeAt(this.pos) == CharCode.DOT && this.pos + 1 < this.end && text.charCodeAt(this.pos + 1) == CharCode.DOT) { this.pos += 2; return Token.DOT_DOT_DOT; } @@ -522,7 +531,8 @@ export class Tokenizer extends DiagnosticEmitter { return Token.DOT; case CharCode.SLASH: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.SLASH) { // single-line comment if (this.pos + 1 < this.end && text.charCodeAt(this.pos + 1) == CharCode.SLASH) { // TODO: triple-slash directives, i.e. '/// ' @@ -548,7 +558,7 @@ export class Tokenizer extends DiagnosticEmitter { continue; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.SLASH_EQUALS; } } @@ -571,120 +581,125 @@ export class Tokenizer extends DiagnosticEmitter { : Token.FLOATLITERAL; // expects a call to readFloat case CharCode.COLON: - this.pos++; + ++this.pos; return Token.COLON; case CharCode.SEMICOLON: - this.pos++; + ++this.pos; return Token.SEMICOLON; case CharCode.LESSTHAN: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.LESSTHAN) { - this.pos++; - if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; + if (maxCompoundLength > 2 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; return Token.LESSTHAN_LESSTHAN_EQUALS; } return Token.LESSTHAN_LESSTHAN; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.LESSTHAN_EQUALS; } } return Token.LESSTHAN; case CharCode.EQUALS: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; - if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; + if (maxCompoundLength > 2 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; return Token.EQUALS_EQUALS_EQUALS; } return Token.EQUALS_EQUALS; } if (text.charCodeAt(this.pos) == CharCode.GREATERTHAN) { - this.pos++; + ++this.pos; return Token.EQUALS_GREATERTHAN; } } return Token.EQUALS; case CharCode.GREATERTHAN: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.GREATERTHAN) { - this.pos++; - if (this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 2 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.GREATERTHAN) { - this.pos++; - if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; + if (maxCompoundLength > 3 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; return Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS; } return Token.GREATERTHAN_GREATERTHAN_GREATERTHAN; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.GREATERTHAN_GREATERTHAN_EQUALS; } } return Token.GREATERTHAN_GREATERTHAN; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.GREATERTHAN_EQUALS; } } return Token.GREATERTHAN; case CharCode.QUESTION: - this.pos++; + ++this.pos; return Token.QUESTION; case CharCode.OPENBRACKET: - this.pos++; + ++this.pos; return Token.OPENBRACKET; case CharCode.CLOSEBRACKET: - this.pos++; + ++this.pos; return Token.CLOSEBRACKET; case CharCode.CARET: - if (++this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end && text.charCodeAt(this.pos) == CharCode.EQUALS) { + ++this.pos; return Token.CARET_EQUALS; } return Token.CARET; case CharCode.OPENBRACE: - this.pos++; + ++this.pos; return Token.OPENBRACE; case CharCode.BAR: - if (++this.pos < this.end) { + ++this.pos; + if (maxCompoundLength > 1 && this.pos < this.end) { if (text.charCodeAt(this.pos) == CharCode.BAR) { - this.pos++; + ++this.pos; return Token.BAR_BAR; } if (text.charCodeAt(this.pos) == CharCode.EQUALS) { - this.pos++; + ++this.pos; return Token.BAR_EQUALS; } } return Token.BAR; case CharCode.CLOSEBRACE: - this.pos++; + ++this.pos; return Token.CLOSEBRACE; case CharCode.TILDE: - this.pos++; + ++this.pos; return Token.TILDE; case CharCode.AT: - this.pos++; + ++this.pos; return Token.AT; default: @@ -705,11 +720,11 @@ export class Tokenizer extends DiagnosticEmitter { } return Token.IDENTIFIER; // expects a call to readIdentifier } else if (isWhiteSpace(c)) { - this.pos++; + ++this.pos; break; } this.error(DiagnosticCode.Invalid_character, this.range(this.pos, this.pos + 1)); - this.pos++; + ++this.pos; return Token.INVALID; } } @@ -742,7 +757,13 @@ export class Tokenizer extends DiagnosticEmitter { var posBefore = this.pos; var tokenBefore = this.token; var tokenPosBefore = this.tokenPos; - if ((this.token = this.unsafeNext(token == Token.IDENTIFIER)) == token) { + var maxCompoundLength = i32.MAX_VALUE; + switch (token) { + case Token.GREATERTHAN: // where parsing type arguments + maxCompoundLength = 1; + break; + } + if ((this.token = this.unsafeNext(token == Token.IDENTIFIER, maxCompoundLength)) == token) { this.nextToken = -1; return true; } else { @@ -820,7 +841,7 @@ export class Tokenizer extends DiagnosticEmitter { this.error(DiagnosticCode.Unterminated_string_literal, this.range(start - 1, this.pos)); break; } - this.pos++; + ++this.pos; } return result; } @@ -864,7 +885,7 @@ export class Tokenizer extends DiagnosticEmitter { case CharCode.u: { if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.OPENBRACE) { - this.pos++; + ++this.pos; return this.readExtendedUnicodeEscape(); // \u{DDDDDDDD} } return this.readUnicodeEscape(); // \uDDDD @@ -872,7 +893,7 @@ export class Tokenizer extends DiagnosticEmitter { case CharCode.CARRIAGERETURN: if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.LINEFEED) - this.pos++; + ++this.pos; // fall through case CharCode.LINEFEED: @@ -901,14 +922,14 @@ export class Tokenizer extends DiagnosticEmitter { break; } if (text.charCodeAt(this.pos) == CharCode.BACKSLASH) { - this.pos++; + ++this.pos; escaped = true; continue; } var c = text.charCodeAt(this.pos); if (c == CharCode.SLASH) { result += text.substring(start, this.pos); - this.pos++; + ++this.pos; break; } if (isLineBreak(c)) { @@ -916,7 +937,7 @@ export class Tokenizer extends DiagnosticEmitter { this.error(DiagnosticCode.Unterminated_regular_expression_literal, this.range(start, this.pos)); break; } - this.pos++; + ++this.pos; } return result; } @@ -965,7 +986,7 @@ export class Tokenizer extends DiagnosticEmitter { } if (isOctalDigit(text.charCodeAt(this.pos + 1))) { var start = this.pos; - this.pos++; + ++this.pos; var value = this.readOctalInteger(); this.error(DiagnosticCode.Octal_literals_are_not_allowed_in_strict_mode, this.range(start, this.pos)); return value; @@ -994,7 +1015,7 @@ export class Tokenizer extends DiagnosticEmitter { value.add32(10 + c - CharCode.a); } else break; - this.pos++; + ++this.pos; } if (this.pos == start) this.error(DiagnosticCode.Hexadecimal_digit_expected, this.range(start)); @@ -1013,7 +1034,7 @@ export class Tokenizer extends DiagnosticEmitter { value.add32(c - CharCode._0); } else break; - this.pos++; + ++this.pos; } if (this.pos == start) this.error(DiagnosticCode.Digit_expected, this.range(start)); @@ -1032,7 +1053,7 @@ export class Tokenizer extends DiagnosticEmitter { value.add32(c - CharCode._0); } else break; - this.pos++; + ++this.pos; } if (this.pos == start) this.error(DiagnosticCode.Octal_digit_expected, this.range(start)); @@ -1055,7 +1076,7 @@ export class Tokenizer extends DiagnosticEmitter { } else break; - this.pos++; + ++this.pos; } if (this.pos == start) this.error(DiagnosticCode.Binary_digit_expected, this.range(start)); @@ -1066,19 +1087,19 @@ export class Tokenizer extends DiagnosticEmitter { var start = this.pos; var text = this.source.text; while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) - this.pos++; + ++this.pos; if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.DOT) { - this.pos++; + ++this.pos; while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) - this.pos++; + ++this.pos; } if (this.pos < this.end) { var c = text.charCodeAt(this.pos); if (c == CharCode.E || c == CharCode.e) { if (++this.pos < this.end && text.charCodeAt(this.pos) == CharCode.MINUS) - this.pos++; + ++this.pos; while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) - this.pos++; + ++this.pos; } } return parseFloat(text.substring(start, this.pos)); @@ -1126,7 +1147,7 @@ export class Tokenizer extends DiagnosticEmitter { this.error(DiagnosticCode.Unexpected_end_of_text, this.range(start, this.end)); invalid = true; } else if (text.charCodeAt(this.pos) == CharCode.CLOSEBRACE) { - this.pos++; + ++this.pos; } else { this.error(DiagnosticCode.Unterminated_Unicode_escape_sequence, this.range(start, this.pos)); invalid = true; diff --git a/src/types.ts b/src/types.ts index cdead5dd..ca8fd5f0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,7 +12,9 @@ import { } from "./util/sb"; import { - NativeType, ExpressionRef, Module + NativeType, + ExpressionRef, + Module } from "./module"; /** Indicates the kind of a type. */ @@ -39,7 +41,6 @@ export const enum TypeKind { // other VOID - // SYMBOL ? } /** Represents a resolved type. */ @@ -87,6 +88,10 @@ export class Type { get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; } /** Tests if this type is of any float kind, i.e., `f32` or `f64`. */ get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; } + /** Tests if this type is a class type. */ + get isClass(): bool { return this.classType != null; } + /** Tests if this type is a function type. */ + get isFunction(): bool { return this.functionType != null; } /** Composes a class type from this type and a class. */ asClass(classType: Class): Type { @@ -164,13 +169,30 @@ export class Type { : module.createI32(1); } - /** Converts this type to its signature name. */ - toSignatureName(): string { - return this.kind == TypeKind.VOID ? "v" - : this.kind == TypeKind.F32 ? "f" - : this.kind == TypeKind.F64 ? "F" - : this.isLongInteger ? "I" - : "i"; + /** Converts this type to its signature string. */ + toSignatureString(): string { + switch (this.kind) { + + default: + return "i"; + + case TypeKind.I64: + case TypeKind.U64: + return "I"; + + case TypeKind.ISIZE: + case TypeKind.USIZE: + return select("I", "i", this.size == 64); + + case TypeKind.F32: + return "f"; + + case TypeKind.F64: + return "F"; + + case TypeKind.VOID: + return "v"; + } } // Types @@ -184,9 +206,9 @@ export class Type { /** A 64-bit signed integer. */ static readonly i64: Type = new Type(TypeKind.I64, 64); /** A 32-bit signed size. WASM32 only. */ - static readonly isize32: Type = new Type(TypeKind.I32, 32); + static readonly isize32: Type = new Type(TypeKind.ISIZE, 32); /** A 64-bit signed size. WASM64 only. */ - static readonly isize64: Type = new Type(TypeKind.I64, 64); + static readonly isize64: Type = new Type(TypeKind.ISIZE, 64); /** An 8-bit unsigned integer. */ static readonly u8: Type = new Type(TypeKind.U8, 8); /** A 16-bit unsigned integer. */ @@ -196,9 +218,9 @@ export class Type { /** A 64-bit unsigned integer. */ static readonly u64: Type = new Type(TypeKind.U64, 64); /** A 32-bit unsigned size. WASM32 only. */ - static readonly usize32: Type = new Type(TypeKind.U32, 32); + static readonly usize32: Type = new Type(TypeKind.USIZE, 32); /** A 64-bit unsigned size. WASM64 only. */ - static readonly usize64: Type = new Type(TypeKind.U64, 64); + static readonly usize64: Type = new Type(TypeKind.USIZE, 64); /** A 1-bit unsigned integer. */ static readonly bool: Type = new Type(TypeKind.BOOL, 1); /** A 32-bit float. */ @@ -209,19 +231,6 @@ export class Type { static readonly void: Type = new Type(TypeKind.VOID, 0); } -// export class ClassType extends Type { -// constructor(cls: Class) { -// super(TypeKind.USIZE, /* clz.size */ 4); // TODO -// } -// } - -// // TODO: what about 'type fun = (a: T) => T;' ? -// export class FunctionType extends Type { -// constructor(fun: Function) { -// super(TypeKind.USIZE, fun.program.target == Target.WASM64 ? 8 : 4); -// } -// } - /** Converts an array of types to an array of native types. */ export function typesToNativeTypes(types: Type[]): NativeType[] { var k = types.length; @@ -232,12 +241,12 @@ export function typesToNativeTypes(types: Type[]): NativeType[] { } /** Converts an array of types to its combined string representation. Usually type arguments. */ -export function typesToString(types: Type[], prefix: string = "<", postfix: string = ">"): string { +export function typesToString(types: Type[]): string { var k = types.length; if (!k) return ""; - sb.length = 0; + var sb = new Array(k); for (var i = 0; i < k; ++i) sb[i] = types[i].toString(); - return prefix + sb.join(", ") + postfix; + return sb.join(", "); } diff --git a/std/assembly.d.ts b/std/assembly.d.ts index a475268c..af7d3c1b 100644 --- a/std/assembly.d.ts +++ b/std/assembly.d.ts @@ -272,10 +272,10 @@ interface RegExp {} // Internal decorators (not yet implemented) /** Annotates an element being part of the global namespace. */ -declare function global(): any; +declare function global(target: Function): any; /** Annotates a method being an operator overload. */ declare function operator(token: string): any; -declare function struct(): any; +declare function struct(target: Function): any; declare function size(size: usize): any; diff --git a/std/assembly/array.ts b/std/assembly/array.ts index 498fa8fb..e91e07ee 100644 --- a/std/assembly/array.ts +++ b/std/assembly/array.ts @@ -1,4 +1,4 @@ -@global() +@global export class Array { private ptr: usize; diff --git a/std/assembly/error.ts b/std/assembly/error.ts index 097a6944..05e99b6d 100644 --- a/std/assembly/error.ts +++ b/std/assembly/error.ts @@ -1,4 +1,4 @@ -@global() +@global export class Error { name: string = "Error"; @@ -10,7 +10,7 @@ export class Error { } } -@global() +@global export class RangeError extends Error { name: string = "RangeError"; } diff --git a/std/assembly/heap.ts b/std/assembly/heap.ts index a33e4bd9..deffef55 100644 --- a/std/assembly/heap.ts +++ b/std/assembly/heap.ts @@ -6,7 +6,7 @@ var HEAP_OFFSET: usize = HEAP_BASE; // HEAP_BASE is a constant generated by the // TODO: maybe tlsf -@global() +@global export class Heap { static get used(): usize { return HEAP_OFFSET - HEAP_BASE; } diff --git a/std/assembly/map.ts b/std/assembly/map.ts index 926a7bcb..3281d192 100644 --- a/std/assembly/map.ts +++ b/std/assembly/map.ts @@ -1,4 +1,4 @@ -@global() +@global export class Map { // TODO } diff --git a/std/assembly/set.ts b/std/assembly/set.ts index 691ad76c..22c0a47b 100644 --- a/std/assembly/set.ts +++ b/std/assembly/set.ts @@ -1,4 +1,4 @@ -@global() +@global export class Set { // TODO } diff --git a/std/assembly/string.ts b/std/assembly/string.ts index ec546071..8c03211f 100644 --- a/std/assembly/string.ts +++ b/std/assembly/string.ts @@ -1,6 +1,6 @@ const EMPTY: String = changetype(""); -@global() +@global export class String { // [key: number]: string; diff --git a/std/portable.d.ts b/std/portable.d.ts index 3f03257c..ea276b23 100644 --- a/std/portable.d.ts +++ b/std/portable.d.ts @@ -136,6 +136,8 @@ declare class Array { constructor(capacity?: i32); push(value: T): void; pop(): T; + unshift(value: T): void; + shift(): T; join(delim: string): string; slice(from: i32, to?: i32): T[]; splice(index: i32, count: i32): T[]; diff --git a/tests/binaryen/get_global-missing.js b/tests/binaryen/get_global-missing.js index b732e04d..58230c6e 100644 --- a/tests/binaryen/get_global-missing.js +++ b/tests/binaryen/get_global-missing.js @@ -9,8 +9,9 @@ var func = mod.addFunction("test", funcType, [], ) ]) ); -mod.addExport("test", func); +mod.addFunctionExport("test", "test", func); +console.log(mod.emitText()); if (mod.validate()) console.log("-> validates"); mod.emitBinary(); // -> Assertion failed: mappedGlobals.count(name), at: binaryen/src/wasm/wasm-binary.cpp,355,getGlobalIndex at Error diff --git a/tests/binaryen/get_local-missing.js b/tests/binaryen/get_local-missing.js new file mode 100644 index 00000000..6abae3a4 --- /dev/null +++ b/tests/binaryen/get_local-missing.js @@ -0,0 +1,19 @@ +var binaryen = require("binaryen"); + +var mod = new binaryen.Module(); +var funcType = mod.addFunctionType("v", binaryen.void, []); +var func = mod.addFunction("test", funcType, [], + mod.block("", [ + mod.drop( + mod.getLocal(0, binaryen.i32) + ) + ]) +); +mod.addFunctionExport("test", "test", func); + +console.log(mod.emitText()); +if (mod.validate()) { + console.log("-> validates"); + var binary = mod.emitBinary(); + new WebAssembly.Module(binary); // CompileError: WebAssembly.Module(): Compiling wasm function #0:test failed: invalid local index: 0 @+34 +} diff --git a/tests/compiler/class.optimized-inlined.wast b/tests/compiler/class.optimized-inlined.wast index 5272998e..57d5223d 100644 --- a/tests/compiler/class.optimized-inlined.wast +++ b/tests/compiler/class.optimized-inlined.wast @@ -1,11 +1,70 @@ (module (type $iii (func (param i32 i32) (result i32))) (type $fff (func (param f32 f32) (result f32))) + (type $iiii (func (param i32 i32 i32) (result i32))) + (type $ifff (func (param i32 f32 f32) (result f32))) + (type $iv (func (param i32))) (type $v (func)) + (global $class/Animal.ONE (mut i32) (i32.const 1)) (memory $0 1) + (export "test" (func $class/test)) (export "memory" (memory $0)) (start $start) - (func $start (; 0 ;) (type $v) + (func $class/test (; 0 ;) (type $iv) (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 f32) + (local $6 f32) + (drop + (block (result i32) + (block $__inlined_func$class/Animal#instanceAdd (result i32) + (set_local $1 + (get_local $0) + ) + (set_local $2 + (i32.const 1) + ) + (set_local $3 + (i32.const 2) + ) + (i32.add + (i32.add + (get_local $2) + (get_local $3) + ) + (get_global $class/Animal.ONE) + ) + ) + ) + ) + (drop + (block (result f32) + (block $__inlined_func$class/Animal#instanceSub (result f32) + (set_local $4 + (get_local $0) + ) + (set_local $5 + (f32.const 1) + ) + (set_local $6 + (f32.const 2) + ) + (f32.add + (f32.sub + (get_local $5) + (get_local $6) + ) + (f32.convert_s/i32 + (get_global $class/Animal.ONE) + ) + ) + ) + ) + ) + ) + (func $start (; 1 ;) (type $v) (local $0 i32) (local $1 i32) (local $2 f32) @@ -20,8 +79,11 @@ (i32.const 2) ) (i32.add - (get_local $0) - (get_local $1) + (i32.add + (get_local $0) + (get_local $1) + ) + (get_global $class/Animal.ONE) ) ) ) @@ -35,9 +97,14 @@ (set_local $3 (f32.const 2) ) - (f32.sub - (get_local $2) - (get_local $3) + (f32.add + (f32.sub + (get_local $2) + (get_local $3) + ) + (f32.convert_s/i32 + (get_global $class/Animal.ONE) + ) ) ) ) diff --git a/tests/compiler/class.optimized.wast b/tests/compiler/class.optimized.wast index 40b48b38..86d6a2f9 100644 --- a/tests/compiler/class.optimized.wast +++ b/tests/compiler/class.optimized.wast @@ -1,23 +1,72 @@ (module (type $iii (func (param i32 i32) (result i32))) (type $fff (func (param f32 f32) (result f32))) + (type $iiii (func (param i32 i32 i32) (result i32))) + (type $ifff (func (param i32 f32 f32) (result f32))) + (type $iv (func (param i32))) (type $v (func)) + (global $class/Animal.ONE (mut i32) (i32.const 1)) (memory $0 1) + (export "test" (func $class/test)) (export "memory" (memory $0)) (start $start) (func $class/Animal.add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (i32.add - (get_local $0) - (get_local $1) + (i32.add + (get_local $0) + (get_local $1) + ) + (get_global $class/Animal.ONE) ) ) (func $class/Animal.sub (; 1 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) - (f32.sub - (get_local $0) - (get_local $1) + (f32.add + (f32.sub + (get_local $0) + (get_local $1) + ) + (f32.convert_s/i32 + (get_global $class/Animal.ONE) + ) ) ) - (func $start (; 2 ;) (type $v) + (func $class/Animal#instanceAdd (; 2 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (i32.add + (i32.add + (get_local $1) + (get_local $2) + ) + (get_global $class/Animal.ONE) + ) + ) + (func $class/Animal#instanceSub (; 3 ;) (type $ifff) (param $0 i32) (param $1 f32) (param $2 f32) (result f32) + (f32.add + (f32.sub + (get_local $1) + (get_local $2) + ) + (f32.convert_s/i32 + (get_global $class/Animal.ONE) + ) + ) + ) + (func $class/test (; 4 ;) (type $iv) (param $0 i32) + (drop + (call $class/Animal#instanceAdd + (get_local $0) + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (call $class/Animal#instanceSub + (get_local $0) + (f32.const 1) + (f32.const 2) + ) + ) + ) + (func $start (; 5 ;) (type $v) (drop (call $class/Animal.add (i32.const 1) diff --git a/tests/compiler/class.ts b/tests/compiler/class.ts index 7952408e..2966c3c9 100644 --- a/tests/compiler/class.ts +++ b/tests/compiler/class.ts @@ -1,9 +1,22 @@ -class Animal { - static MAX: i32 = 1; - static add(a: i32, b: i32): i32 { return a + b; } - static sub(a: T, b: T): T { return a - b; } // tsc does not allow this +class Animal { + static ONE: i32 = 1; + static add(a: i32, b: i32): i32 { return a + b + Animal.ONE; } + static sub(a: T, b: T): T { return a - b + Animal.ONE; } // tsc does not allow this + + one: i32 = 1; // 4 + two: i16 = 2; // 6 + three: i8 = 3; // 7 + instanceAdd(a: i32, b: i32): i32 { return a + b + Animal.ONE; } + instanceSub(a: T, b: T): T { return a - b + Animal.ONE; } // tsc does not allow this } -Animal.MAX; +assert(sizeof>() == 7); + +Animal.ONE; Animal.add(1,2); -Animal.sub(1.0, 2.0); +Animal.sub(1, 2); + +export function test(animal: Animal): void { + animal.instanceAdd(1, 2); + animal.instanceSub(1, 2); +} diff --git a/tests/compiler/class.wast b/tests/compiler/class.wast index ec597c0f..866040c8 100644 --- a/tests/compiler/class.wast +++ b/tests/compiler/class.wast @@ -1,31 +1,92 @@ (module (type $iii (func (param i32 i32) (result i32))) (type $fff (func (param f32 f32) (result f32))) + (type $iiii (func (param i32 i32 i32) (result i32))) + (type $ifff (func (param i32 f32 f32) (result f32))) + (type $iv (func (param i32))) (type $v (func)) - (global $class/Animal.MAX (mut i32) (i32.const 1)) + (global $class/Animal.ONE (mut i32) (i32.const 1)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) + (export "test" (func $class/test)) (export "memory" (memory $0)) (start $start) (func $class/Animal.add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (return (i32.add - (get_local $0) - (get_local $1) + (i32.add + (get_local $0) + (get_local $1) + ) + (get_global $class/Animal.ONE) ) ) ) (func $class/Animal.sub (; 1 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) (return - (f32.sub - (get_local $0) - (get_local $1) + (f32.add + (f32.sub + (get_local $0) + (get_local $1) + ) + (f32.convert_s/i32 + (get_global $class/Animal.ONE) + ) ) ) ) - (func $start (; 2 ;) (type $v) + (func $class/Animal#instanceAdd (; 2 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (return + (i32.add + (i32.add + (get_local $1) + (get_local $2) + ) + (get_global $class/Animal.ONE) + ) + ) + ) + (func $class/Animal#instanceSub (; 3 ;) (type $ifff) (param $0 i32) (param $1 f32) (param $2 f32) (result f32) + (return + (f32.add + (f32.sub + (get_local $1) + (get_local $2) + ) + (f32.convert_s/i32 + (get_global $class/Animal.ONE) + ) + ) + ) + ) + (func $class/test (; 4 ;) (type $iv) (param $0 i32) (drop - (get_global $class/Animal.MAX) + (call $class/Animal#instanceAdd + (get_local $0) + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (call $class/Animal#instanceSub + (get_local $0) + (f32.const 1) + (f32.const 2) + ) + ) + ) + (func $start (; 5 ;) (type $v) + (if + (i32.eqz + (i32.eq + (i32.const 7) + (i32.const 7) + ) + ) + (unreachable) + ) + (drop + (get_global $class/Animal.ONE) ) (drop (call $class/Animal.add @@ -88,9 +149,10 @@ FUNCTION_PROTOTYPE: usize GLOBAL: HEAP_BASE CLASS_PROTOTYPE: class/Animal - GLOBAL: class/Animal.MAX + GLOBAL: class/Animal.ONE FUNCTION_PROTOTYPE: class/Animal.add FUNCTION_PROTOTYPE: class/Animal.sub + FUNCTION_PROTOTYPE: class/test [program.exports] - + FUNCTION_PROTOTYPE: class/test ;) diff --git a/tests/compiler/function.optimized-inlined.wast b/tests/compiler/function.optimized-inlined.wast new file mode 100644 index 00000000..e776dcfd --- /dev/null +++ b/tests/compiler/function.optimized-inlined.wast @@ -0,0 +1,195 @@ +(module + (type $v (func)) + (type $i (func (result i32))) + (type $I (func (result i64))) + (type $f (func (result f32))) + (type $F (func (result f64))) + (type $iv (func (param i32))) + (type $ii (func (param i32) (result i32))) + (type $II (func (param i64) (result i64))) + (type $ff (func (param f32) (result f32))) + (type $FF (func (param f64) (result f64))) + (type $iiv (func (param i32 i32))) + (type $iii (func (param i32 i32) (result i32))) + (type $IiI (func (param i64 i32) (result i64))) + (type $fff (func (param f32 f32) (result f32))) + (type $FFF (func (param f64 f64) (result f64))) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $start (; 0 ;) (type $v) + (local $0 i32) + (local $1 i32) + (local $2 i64) + (local $3 f32) + (local $4 f64) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i64) + (local $10 i32) + (local $11 f32) + (local $12 f32) + (local $13 f64) + (local $14 f64) + (block + (block $__inlined_func$function/v + (nop) + ) + ) + (drop + (block (result i32) + (block $__inlined_func$function/i (result i32) + (i32.const 0) + ) + ) + ) + (drop + (block (result i64) + (block $__inlined_func$function/I (result i64) + (i64.const 0) + ) + ) + ) + (drop + (block (result f32) + (block $__inlined_func$function/f (result f32) + (f32.const 0) + ) + ) + ) + (drop + (block (result f64) + (block $__inlined_func$function/F (result f64) + (f64.const 0) + ) + ) + ) + (block + (block $__inlined_func$function/iv + (set_local $0 + (i32.const 0) + ) + (nop) + ) + ) + (drop + (block (result i32) + (block $__inlined_func$function/ii (result i32) + (set_local $1 + (i32.const 0) + ) + (get_local $1) + ) + ) + ) + (drop + (block (result i64) + (block $__inlined_func$function/II (result i64) + (set_local $2 + (i64.const 0) + ) + (get_local $2) + ) + ) + ) + (drop + (block (result f32) + (block $__inlined_func$function/ff (result f32) + (set_local $3 + (f32.const 0) + ) + (get_local $3) + ) + ) + ) + (drop + (block (result f64) + (block $__inlined_func$function/FF (result f64) + (set_local $4 + (f64.const 0) + ) + (get_local $4) + ) + ) + ) + (block + (block $__inlined_func$function/iiv + (set_local $5 + (i32.const 1) + ) + (set_local $6 + (i32.const 2) + ) + (nop) + ) + ) + (drop + (block (result i32) + (block $__inlined_func$function/iii (result i32) + (set_local $7 + (i32.const 1) + ) + (set_local $8 + (i32.const 2) + ) + (i32.add + (get_local $7) + (get_local $8) + ) + ) + ) + ) + (drop + (block (result i64) + (block $__inlined_func$function/III (result i64) + (set_local $9 + (i64.const 1) + ) + (set_local $10 + (i32.const 2) + ) + (i64.add + (get_local $9) + (i64.extend_s/i32 + (get_local $10) + ) + ) + ) + ) + ) + (drop + (block (result f32) + (block $__inlined_func$function/fff (result f32) + (set_local $11 + (f32.const 1) + ) + (set_local $12 + (f32.const 2) + ) + (f32.add + (get_local $11) + (get_local $12) + ) + ) + ) + ) + (drop + (block (result f64) + (block $__inlined_func$function/FFF (result f64) + (set_local $13 + (f64.const 1) + ) + (set_local $14 + (f64.const 2) + ) + (f64.add + (get_local $13) + (get_local $14) + ) + ) + ) + ) + ) +) diff --git a/tests/compiler/function.optimized.wast b/tests/compiler/function.optimized.wast new file mode 100644 index 00000000..c710d8a5 --- /dev/null +++ b/tests/compiler/function.optimized.wast @@ -0,0 +1,145 @@ +(module + (type $v (func)) + (type $i (func (result i32))) + (type $I (func (result i64))) + (type $f (func (result f32))) + (type $F (func (result f64))) + (type $iv (func (param i32))) + (type $ii (func (param i32) (result i32))) + (type $II (func (param i64) (result i64))) + (type $ff (func (param f32) (result f32))) + (type $FF (func (param f64) (result f64))) + (type $iiv (func (param i32 i32))) + (type $iii (func (param i32 i32) (result i32))) + (type $IiI (func (param i64 i32) (result i64))) + (type $fff (func (param f32 f32) (result f32))) + (type $FFF (func (param f64 f64) (result f64))) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $function/v (; 0 ;) (type $v) + (nop) + ) + (func $function/i (; 1 ;) (type $i) (result i32) + (i32.const 0) + ) + (func $function/I (; 2 ;) (type $I) (result i64) + (i64.const 0) + ) + (func $function/f (; 3 ;) (type $f) (result f32) + (f32.const 0) + ) + (func $function/F (; 4 ;) (type $F) (result f64) + (f64.const 0) + ) + (func $function/iv (; 5 ;) (type $iv) (param $0 i32) + (nop) + ) + (func $function/ii (; 6 ;) (type $ii) (param $0 i32) (result i32) + (get_local $0) + ) + (func $function/II (; 7 ;) (type $II) (param $0 i64) (result i64) + (get_local $0) + ) + (func $function/ff (; 8 ;) (type $ff) (param $0 f32) (result f32) + (get_local $0) + ) + (func $function/FF (; 9 ;) (type $FF) (param $0 f64) (result f64) + (get_local $0) + ) + (func $function/iiv (; 10 ;) (type $iiv) (param $0 i32) (param $1 i32) + (nop) + ) + (func $function/iii (; 11 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (i32.add + (get_local $0) + (get_local $1) + ) + ) + (func $function/III (; 12 ;) (type $IiI) (param $0 i64) (param $1 i32) (result i64) + (i64.add + (get_local $0) + (i64.extend_s/i32 + (get_local $1) + ) + ) + ) + (func $function/fff (; 13 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) + (f32.add + (get_local $0) + (get_local $1) + ) + ) + (func $function/FFF (; 14 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64) + (f64.add + (get_local $0) + (get_local $1) + ) + ) + (func $start (; 15 ;) (type $v) + (call $function/v) + (drop + (call $function/i) + ) + (drop + (call $function/I) + ) + (drop + (call $function/f) + ) + (drop + (call $function/F) + ) + (call $function/iv + (i32.const 0) + ) + (drop + (call $function/ii + (i32.const 0) + ) + ) + (drop + (call $function/II + (i64.const 0) + ) + ) + (drop + (call $function/ff + (f32.const 0) + ) + ) + (drop + (call $function/FF + (f64.const 0) + ) + ) + (call $function/iiv + (i32.const 1) + (i32.const 2) + ) + (drop + (call $function/iii + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (call $function/III + (i64.const 1) + (i32.const 2) + ) + ) + (drop + (call $function/fff + (f32.const 1) + (f32.const 2) + ) + ) + (drop + (call $function/FFF + (f64.const 1) + (f64.const 2) + ) + ) + ) +) diff --git a/tests/compiler/function.ts b/tests/compiler/function.ts new file mode 100644 index 00000000..4c974e60 --- /dev/null +++ b/tests/compiler/function.ts @@ -0,0 +1,35 @@ +function v(): void {} +function i(): i32 { return 0; } +function I(): i64 { return 0; } +function f(): f32 { return 0; } +function F(): f64 { return 0; } + +v(); +i(); +I(); +f(); +F(); + +function iv(a: i32): void {} +function ii(a: i32): i32 { return a; } +function II(a: i64): i64 { return a; } +function ff(a: f32): f32 { return a; } +function FF(a: f64): f64 { return a; } + +iv(0); +ii(0); +II(0); +ff(0); +FF(0); + +function iiv(a: i32, b: i32): void {} +function iii(a: i32, b: i32): i32 { return a + b; } +function III(a: i64, b: i32): i64 { return a + b; } +function fff(a: f32, b: f32): f32 { return a + b; } +function FFF(a: f64, b: f64): f64 { return a + b; } + +iiv(1, 2); +iii(1, 2); +III(1, 2); +fff(1, 2); +FFF(1, 2); diff --git a/tests/compiler/function.wast b/tests/compiler/function.wast new file mode 100644 index 00000000..6be7a0b5 --- /dev/null +++ b/tests/compiler/function.wast @@ -0,0 +1,231 @@ +(module + (type $v (func)) + (type $i (func (result i32))) + (type $I (func (result i64))) + (type $f (func (result f32))) + (type $F (func (result f64))) + (type $iv (func (param i32))) + (type $ii (func (param i32) (result i32))) + (type $II (func (param i64) (result i64))) + (type $ff (func (param f32) (result f32))) + (type $FF (func (param f64) (result f64))) + (type $iiv (func (param i32 i32))) + (type $iii (func (param i32 i32) (result i32))) + (type $IiI (func (param i64 i32) (result i64))) + (type $fff (func (param f32 f32) (result f32))) + (type $FFF (func (param f64 f64) (result f64))) + (global $HEAP_BASE i32 (i32.const 4)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $function/v (; 0 ;) (type $v) + ) + (func $function/i (; 1 ;) (type $i) (result i32) + (return + (i32.const 0) + ) + ) + (func $function/I (; 2 ;) (type $I) (result i64) + (return + (i64.const 0) + ) + ) + (func $function/f (; 3 ;) (type $f) (result f32) + (return + (f32.const 0) + ) + ) + (func $function/F (; 4 ;) (type $F) (result f64) + (return + (f64.const 0) + ) + ) + (func $function/iv (; 5 ;) (type $iv) (param $0 i32) + ) + (func $function/ii (; 6 ;) (type $ii) (param $0 i32) (result i32) + (return + (get_local $0) + ) + ) + (func $function/II (; 7 ;) (type $II) (param $0 i64) (result i64) + (return + (get_local $0) + ) + ) + (func $function/ff (; 8 ;) (type $ff) (param $0 f32) (result f32) + (return + (get_local $0) + ) + ) + (func $function/FF (; 9 ;) (type $FF) (param $0 f64) (result f64) + (return + (get_local $0) + ) + ) + (func $function/iiv (; 10 ;) (type $iiv) (param $0 i32) (param $1 i32) + ) + (func $function/iii (; 11 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (return + (i32.add + (get_local $0) + (get_local $1) + ) + ) + ) + (func $function/III (; 12 ;) (type $IiI) (param $0 i64) (param $1 i32) (result i64) + (return + (i64.add + (get_local $0) + (i64.extend_s/i32 + (get_local $1) + ) + ) + ) + ) + (func $function/fff (; 13 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) + (return + (f32.add + (get_local $0) + (get_local $1) + ) + ) + ) + (func $function/FFF (; 14 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64) + (return + (f64.add + (get_local $0) + (get_local $1) + ) + ) + ) + (func $start (; 15 ;) (type $v) + (call $function/v) + (drop + (call $function/i) + ) + (drop + (call $function/I) + ) + (drop + (call $function/f) + ) + (drop + (call $function/F) + ) + (call $function/iv + (i32.const 0) + ) + (drop + (call $function/ii + (i32.const 0) + ) + ) + (drop + (call $function/II + (i64.const 0) + ) + ) + (drop + (call $function/ff + (f32.const 0) + ) + ) + (drop + (call $function/FF + (f64.const 0) + ) + ) + (call $function/iiv + (i32.const 1) + (i32.const 2) + ) + (drop + (call $function/iii + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (call $function/III + (i64.const 1) + (i32.const 2) + ) + ) + (drop + (call $function/fff + (f32.const 1) + (f32.const 2) + ) + ) + (drop + (call $function/FFF + (f64.const 1) + (f64.const 2) + ) + ) + ) +) +(; +[program.elements] + GLOBAL: NaN + GLOBAL: Infinity + FUNCTION_PROTOTYPE: isNaN + FUNCTION_PROTOTYPE: isFinite + FUNCTION_PROTOTYPE: clz + FUNCTION_PROTOTYPE: ctz + FUNCTION_PROTOTYPE: popcnt + FUNCTION_PROTOTYPE: rotl + FUNCTION_PROTOTYPE: rotr + FUNCTION_PROTOTYPE: abs + FUNCTION_PROTOTYPE: max + FUNCTION_PROTOTYPE: min + FUNCTION_PROTOTYPE: ceil + FUNCTION_PROTOTYPE: floor + FUNCTION_PROTOTYPE: copysign + FUNCTION_PROTOTYPE: nearest + FUNCTION_PROTOTYPE: reinterpret + FUNCTION_PROTOTYPE: sqrt + FUNCTION_PROTOTYPE: trunc + FUNCTION_PROTOTYPE: load + FUNCTION_PROTOTYPE: store + FUNCTION_PROTOTYPE: sizeof + FUNCTION_PROTOTYPE: select + FUNCTION_PROTOTYPE: unreachable + FUNCTION_PROTOTYPE: current_memory + FUNCTION_PROTOTYPE: grow_memory + FUNCTION_PROTOTYPE: parseInt + FUNCTION_PROTOTYPE: parseFloat + FUNCTION_PROTOTYPE: changetype + FUNCTION_PROTOTYPE: assert + FUNCTION_PROTOTYPE: i8 + FUNCTION_PROTOTYPE: i16 + FUNCTION_PROTOTYPE: i32 + FUNCTION_PROTOTYPE: i64 + FUNCTION_PROTOTYPE: u8 + FUNCTION_PROTOTYPE: u16 + FUNCTION_PROTOTYPE: u32 + FUNCTION_PROTOTYPE: u64 + FUNCTION_PROTOTYPE: bool + FUNCTION_PROTOTYPE: f32 + FUNCTION_PROTOTYPE: f64 + FUNCTION_PROTOTYPE: isize + FUNCTION_PROTOTYPE: usize + GLOBAL: HEAP_BASE + FUNCTION_PROTOTYPE: function/v + FUNCTION_PROTOTYPE: function/i + FUNCTION_PROTOTYPE: function/I + FUNCTION_PROTOTYPE: function/f + FUNCTION_PROTOTYPE: function/F + FUNCTION_PROTOTYPE: function/iv + FUNCTION_PROTOTYPE: function/ii + FUNCTION_PROTOTYPE: function/II + FUNCTION_PROTOTYPE: function/ff + FUNCTION_PROTOTYPE: function/FF + FUNCTION_PROTOTYPE: function/iiv + FUNCTION_PROTOTYPE: function/iii + FUNCTION_PROTOTYPE: function/III + FUNCTION_PROTOTYPE: function/fff + FUNCTION_PROTOTYPE: function/FFF +[program.exports] + +;)