diff --git a/README.md b/README.md index 87ed8d82..ae999eb9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ AssemblyScript NEXT [![Build Status](https://travis-ci.org/AssemblyScript/next.svg?branch=master)](https://travis-ci.org/AssemblyScript/next) -**AssemblyScript** is a new compiler targeting WebAssembly while utilizing [TypeScript](http://www.typescriptlang.org)'s syntax and [node](https://nodejs.org)'s vibrant ecosystem. Instead of requiring complex toolchains to set up, you can simply `npm install` it - or run it in a browser. +**AssemblyScript** is a new compiler targeting [WebAssembly](http://webassembly.org) while utilizing [TypeScript](http://www.typescriptlang.org)'s syntax and [node](https://nodejs.org)'s vibrant ecosystem. Instead of requiring complex toolchains to set up, you can simply `npm install` it - or run it in a browser. By compiling a variant of TypeScript to [Binaryen](https://github.com/WebAssembly/binaryen) IR, the resulting module can be validated, optimized, emitted in WebAssembly text or binary format and converted to [asm.js](http://asmjs.org) as a polyfill. diff --git a/assembly.d.ts b/assembly.d.ts index c658213b..3adb3865 100644 --- a/assembly.d.ts +++ b/assembly.d.ts @@ -90,6 +90,10 @@ declare function isNaN(value: T): bool; declare function isFinite(value: T): bool; /** Traps if the specified value evaluates to `false`. */ declare function assert(isTrue: bool): void; +/** Parses an integer string to a 64-bit float. */ +declare function parseInt(str: string, radix?: i32): f64; +/** Parses a string to a 64-bit float. */ +declare function parseFloat(str: string): f64; // Internal decorators (not yet implemented) diff --git a/portable.d.ts b/portable.d.ts index 843a5709..95ce426c 100644 --- a/portable.d.ts +++ b/portable.d.ts @@ -52,6 +52,10 @@ declare function unreachable(): any; // sic declare function changetype(value: T1): T2; /** Traps if the specified value evaluates to `false`. */ declare function assert(isTrue: bool): void; +/** Parses an integer string to a 64-bit float. */ +declare function parseInt(str: string, radix?: i32): f64; +/** Parses a floating point string to a 64-bit float. */ +declare function parseFloat(str: string): f64; // Portable standard library // Everything marked @deprecated is a temporary filler. Do not use. @@ -143,6 +147,3 @@ declare namespace console { /** @deprecated */ function log(message: string): void; } - -/** @deprecated */ -declare function parseFloat(str: string): f64; diff --git a/src/builtins.ts b/src/builtins.ts index bcf86ab5..ec94a3c2 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -2,7 +2,7 @@ import { Compiler, Target, typeToNativeType, typeToNativeOne } from "./compiler" import { DiagnosticCode } from "./diagnostics"; import { Node, Expression } from "./ast"; import { Type } from "./types"; -import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType } from "./module"; +import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef } from "./module"; import { Program, FunctionPrototype, Local } from "./program"; /** Initializes the specified program with built-in functions. */ @@ -33,23 +33,23 @@ export function initialize(program: Program): void { addFunction(program, "isNaN", true); addFunction(program, "isFinite", true); addFunction(program, "assert"); - // addFunction(program, "fmod", false, true); - // addFunction(program, "pow", true, true); + addFunction(program, "parseInt"); + addFunction(program, "parseFloat"); } /** Adds a built-in function to the specified program. */ -function addFunction(program: Program, name: string, isGeneric: bool = false, isImport: bool = false): void { +function addFunction(program: Program, name: string, isGeneric: bool = false): void { let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null); prototype.isBuiltIn = true; prototype.isGeneric = isGeneric; - prototype.isImport = isImport; program.elements.set(name, prototype); } /** Compiles a call to a built-in function. */ -export function compileCall(compiler: Compiler, internalName: string, typeArguments: Type[], operands: Expression[], reportNode: Node): ExpressionRef { +export function compileCall(compiler: Compiler, prototype: FunctionPrototype, typeArguments: Type[], operands: Expression[], reportNode: Node): ExpressionRef { const module: Module = compiler.module; const usizeType: Type = select(Type.usize64, Type.usize32, compiler.options.target == Target.WASM64); + const nativeUsizeType: NativeType = select(NativeType.I64, NativeType.I32, compiler.options.target == Target.WASM64); let arg0: ExpressionRef, arg1: ExpressionRef, @@ -59,8 +59,9 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume let tempLocal1: Local; let type: Type; + var ftype: FunctionTypeRef; - switch (internalName) { + switch (prototype.internalName) { case "clz": // clz(value: T) -> T if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) @@ -498,8 +499,42 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume module.createUnreachable() ); - // case "fmod": - // case "pow": + case "parseInt": // takes a pointer to the string + compiler.currentType = Type.f64; + if (typeArguments.length != 0) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "0", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + if (operands.length < 1) { + compiler.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + return module.createUnreachable(); + } + if (operands.length > 2) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); + return module.createUnreachable(); + } + if (!prototype.isCompiled) { + if (!(ftype = module.getFunctionTypeBySignature(NativeType.F64, [ nativeUsizeType, NativeType.I32 ]))) + ftype = module.addFunctionType(nativeUsizeType == NativeType.I64 ? "FIi" : "Fii", NativeType.F64, [ nativeUsizeType, NativeType.I32 ]); + module.addFunctionImport("parseInt", "env", "parseInt", ftype); // FIXME: can't call with i64 pointers (WASM64) + prototype.isCompiled = true; + } + arg0 = compiler.compileExpression(operands[0], usizeType); // reports + arg1 = operands.length == 2 ? compiler.compileExpression(operands[1], Type.i32) : module.createI32(-1); // -1 marks omitted + return module.createCallImport("parseInt", [ arg0, arg1 ], NativeType.F64); + + case "parseFloat": // takes a pointer to the string + compiler.currentType = Type.f64; + if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + return module.createUnreachable(); + if (!prototype.isCompiled) { + if (!(ftype = module.getFunctionTypeBySignature(NativeType.F64, [ nativeUsizeType ]))) + ftype = module.addFunctionType(nativeUsizeType == NativeType.I64 ? "FI" : "Fi", NativeType.F64, [ nativeUsizeType ]); + module.addFunctionImport("parseFloat", "env", "parseFloat", ftype); // FIXME: can't call with i64 pointers (WASM64) + prototype.isCompiled = true; + } + arg0 = compiler.compileExpression(operands[0], usizeType); // reports + return module.createCallImport("parseFloat", [ arg0 ], NativeType.F64); } return 0; } diff --git a/src/compiler.ts b/src/compiler.ts index b044b644..9e84eeda 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1673,7 +1673,7 @@ export class Compiler extends DiagnosticEmitter { functionInstance = functionPrototype.instances.get(sb.join(",")); if (!functionInstance) { this.currentType = contextualType; - let expr: ExpressionRef = compileBuiltinCall(this, functionPrototype.internalName, resolvedTypeArguments, expression.arguments, expression); + let expr: ExpressionRef = compileBuiltinCall(this, functionPrototype, resolvedTypeArguments, expression.arguments, expression); if (!expr) { this.error(DiagnosticCode.Operation_not_supported, expression.range); return this.module.createUnreachable(); diff --git a/tests/compiler/assert.wast b/tests/compiler/assert.wast index 010cb566..7920843f 100644 --- a/tests/compiler/assert.wast +++ b/tests/compiler/assert.wast @@ -41,6 +41,8 @@ isNaN isFinite assert + parseInt + parseFloat [program.exports] ;) diff --git a/tests/compiler/binary.wast b/tests/compiler/binary.wast index c492c038..fdc93e6b 100644 --- a/tests/compiler/binary.wast +++ b/tests/compiler/binary.wast @@ -844,6 +844,8 @@ isNaN isFinite assert + parseInt + parseFloat binary/b binary/i binary/I diff --git a/tests/compiler/builtins.ts b/tests/compiler/builtins.ts index 5b27fd12..da7ace9d 100644 --- a/tests/compiler/builtins.ts +++ b/tests/compiler/builtins.ts @@ -1,6 +1,6 @@ let b: bool; -// integer builtins +// integers let i: i32; @@ -40,7 +40,7 @@ I = abs(-42); assert(I == 42); I = max(1, 2); assert(I == 2); I = min(1, 2); assert(i == 1); -// floating point builtins +// floats let f: f32; @@ -104,7 +104,7 @@ F = trunc(1.25); b = isNaN(1.25); b = isFinite(1.25); -// load and store builtins +// load and store i = load(8); store(8, i); store(8, load(8)); @@ -115,7 +115,7 @@ store(8, load(8)); F = load(8); store(8, F); store(8, load(8)); -// reinterpretation builtins +// reinterpretation reinterpret(1.25); reinterpret(25); @@ -127,7 +127,7 @@ f = reinterpret(25); I = reinterpret(1.25); F = reinterpret(25); -// host builtins +// host let s: usize; @@ -137,7 +137,7 @@ grow_memory(1); s = current_memory(); s = grow_memory(1); -// other builtins +// other select(10, 20, true); select(100, 200, false); @@ -151,7 +151,7 @@ F = select(12.5, 25.0, false); if (0) unreachable(); -// AS specific builtins +// AS specific sizeof(); sizeof(); @@ -176,3 +176,11 @@ assert(!isFinite(NaN)); assert(!isFinite(Infinity)); assert(isFinite(0)); assert(isFinite(0)); + +// imported + +// TODO: Can't be interpreted due to 'Fatal: callImport: unknown import: env.parseInt' + +// parseInt(0); +// parseInt(0, 10); +// parseFloat(0); diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index a9a67297..ce16ba57 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -1087,6 +1087,8 @@ isNaN isFinite assert + parseInt + parseFloat builtins/b builtins/i builtins/I diff --git a/tests/compiler/declare.wast b/tests/compiler/declare.wast index 0699e334..a5532316 100644 --- a/tests/compiler/declare.wast +++ b/tests/compiler/declare.wast @@ -34,6 +34,8 @@ isNaN isFinite assert + parseInt + parseFloat declare/external [program.exports] declare/external diff --git a/tests/compiler/do.wast b/tests/compiler/do.wast index 2790cf61..f85f728d 100644 --- a/tests/compiler/do.wast +++ b/tests/compiler/do.wast @@ -79,6 +79,8 @@ isNaN isFinite assert + parseInt + parseFloat do/loopDo do/loopDoInDo [program.exports] diff --git a/tests/compiler/enum.wast b/tests/compiler/enum.wast index aa5ef2f9..9f571813 100644 --- a/tests/compiler/enum.wast +++ b/tests/compiler/enum.wast @@ -64,6 +64,8 @@ isNaN isFinite assert + parseInt + parseFloat enum/Implicit enum/Explicit enum/Mixed diff --git a/tests/compiler/export.wast b/tests/compiler/export.wast index 7dfbc78b..a3cfe3d4 100644 --- a/tests/compiler/export.wast +++ b/tests/compiler/export.wast @@ -54,6 +54,8 @@ isNaN isFinite assert + parseInt + parseFloat export/add export/sub export/a diff --git a/tests/compiler/for.wast b/tests/compiler/for.wast index 199f0f88..bee6a79d 100644 --- a/tests/compiler/for.wast +++ b/tests/compiler/for.wast @@ -175,6 +175,8 @@ isNaN isFinite assert + parseInt + parseFloat for/i [program.exports] diff --git a/tests/compiler/game-of-life.wast b/tests/compiler/game-of-life.wast index 0705d873..74ce6de2 100644 --- a/tests/compiler/game-of-life.wast +++ b/tests/compiler/game-of-life.wast @@ -336,6 +336,8 @@ isNaN isFinite assert + parseInt + parseFloat game-of-life/w game-of-life/h game-of-life/s diff --git a/tests/compiler/i64.optimized.wast b/tests/compiler/i64.optimized.wast index 76f88806..181bd820 100644 --- a/tests/compiler/i64.optimized.wast +++ b/tests/compiler/i64.optimized.wast @@ -22,8 +22,8 @@ (export "or" (func $i64/or)) (export "xor" (func $i64/xor)) (export "shl" (func $i64/shl)) - (export "shr_u" (func $i64/shr_u)) (export "shr_s" (func $i64/shr_s)) + (export "shr_u" (func $i64/shr_u)) (export "rotl" (func $i64/rotl_)) (export "rotr" (func $i64/rotr_)) (export "eq" (func $i64/eq)) @@ -580,12 +580,12 @@ ) ) ) - (func $i64/shr_u (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (func $i64/shr_s (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (local $4 i64) (set_global $i64/lo (i32.wrap/i64 (tee_local $4 - (i64.shr_u + (i64.shr_s (i64.or (i64.extend_u/i32 (get_local $0) @@ -621,12 +621,12 @@ ) ) ) - (func $i64/shr_s (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (func $i64/shr_u (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (local $4 i64) (set_global $i64/lo (i32.wrap/i64 (tee_local $4 - (i64.shr_s + (i64.shr_u (i64.or (i64.extend_u/i32 (get_local $0) diff --git a/tests/compiler/i64.ts b/tests/compiler/i64.ts index c5cc4cc6..540a535c 100644 --- a/tests/compiler/i64.ts +++ b/tests/compiler/i64.ts @@ -102,14 +102,14 @@ export function shl(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void hi = (ret >>> 32); } -export function shr_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) >> (loRight | hiRight << 32); +export function shr_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: u64 = ((loLeft | hiLeft << 32) >> (loRight | hiRight << 32)); lo = ret; hi = (ret >>> 32); } -export function shr_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = ((loLeft | hiLeft << 32) >> (loRight | hiRight << 32)); +export function shr_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: u64 = (loLeft | hiLeft << 32) >> (loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } diff --git a/tests/compiler/i64.wast b/tests/compiler/i64.wast index 5f171edf..617225ad 100644 --- a/tests/compiler/i64.wast +++ b/tests/compiler/i64.wast @@ -23,8 +23,8 @@ (export "or" (func $i64/or)) (export "xor" (func $i64/xor)) (export "shl" (func $i64/shl)) - (export "shr_u" (func $i64/shr_u)) (export "shr_s" (func $i64/shr_s)) + (export "shr_u" (func $i64/shr_u)) (export "rotl" (func $i64/rotl_)) (export "rotr" (func $i64/rotr_)) (export "eq" (func $i64/eq)) @@ -642,11 +642,11 @@ ) ) ) - (func $i64/shr_u (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (func $i64/shr_s (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (local $4 i64) (block (set_local $4 - (i64.shr_u + (i64.shr_s (i64.or (i64.extend_u/i32 (get_local $0) @@ -686,11 +686,11 @@ ) ) ) - (func $i64/shr_s (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (func $i64/shr_u (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (local $4 i64) (block (set_local $4 - (i64.shr_s + (i64.shr_u (i64.or (i64.extend_u/i32 (get_local $0) @@ -1217,6 +1217,8 @@ isNaN isFinite assert + parseInt + parseFloat i64/lo i64/hi i64/getLo @@ -1236,8 +1238,8 @@ i64/or i64/xor i64/shl - i64/shr_u i64/shr_s + i64/shr_u i64/rotl_ i64/rotr_ i64/eq @@ -1268,8 +1270,8 @@ i64/or i64/xor i64/shl - i64/shr_u i64/shr_s + i64/shr_u i64/rotl i64/rotr i64/eq diff --git a/tests/compiler/if.wast b/tests/compiler/if.wast index 611ff45f..01bcb40a 100644 --- a/tests/compiler/if.wast +++ b/tests/compiler/if.wast @@ -74,6 +74,8 @@ isNaN isFinite assert + parseInt + parseFloat if/ifThenElse if/ifThen if/ifThenElseBlock diff --git a/tests/compiler/import.wast b/tests/compiler/import.wast index 74c7a514..166928a5 100644 --- a/tests/compiler/import.wast +++ b/tests/compiler/import.wast @@ -66,6 +66,8 @@ isNaN isFinite assert + parseInt + parseFloat export/add export/sub export/a diff --git a/tests/compiler/literals.wast b/tests/compiler/literals.wast index 073ad016..65aef3c2 100644 --- a/tests/compiler/literals.wast +++ b/tests/compiler/literals.wast @@ -167,6 +167,8 @@ isNaN isFinite assert + parseInt + parseFloat [program.exports] ;) diff --git a/tests/compiler/logical.wast b/tests/compiler/logical.wast index e1c71a3a..813531b4 100644 --- a/tests/compiler/logical.wast +++ b/tests/compiler/logical.wast @@ -295,6 +295,8 @@ isNaN isFinite assert + parseInt + parseFloat logical/i logical/I logical/f diff --git a/tests/compiler/memcpy.wast b/tests/compiler/memcpy.wast index f73c111a..61e17427 100644 --- a/tests/compiler/memcpy.wast +++ b/tests/compiler/memcpy.wast @@ -2161,6 +2161,8 @@ isNaN isFinite assert + parseInt + parseFloat memcpy/memcpy memcpy/base memcpy/dest diff --git a/tests/compiler/reexport.wast b/tests/compiler/reexport.wast index 6ad55032..1d3b1c74 100644 --- a/tests/compiler/reexport.wast +++ b/tests/compiler/reexport.wast @@ -72,6 +72,8 @@ isNaN isFinite assert + parseInt + parseFloat export/add export/sub export/a diff --git a/tests/compiler/switch.wast b/tests/compiler/switch.wast index 474af8cc..955c09f7 100644 --- a/tests/compiler/switch.wast +++ b/tests/compiler/switch.wast @@ -173,6 +173,8 @@ isNaN isFinite assert + parseInt + parseFloat switch/doSwitch switch/doSwitchDefaultFirst switch/doSwitchDefaultOmitted diff --git a/tests/compiler/ternary.wast b/tests/compiler/ternary.wast index 231a3500..20243729 100644 --- a/tests/compiler/ternary.wast +++ b/tests/compiler/ternary.wast @@ -61,6 +61,8 @@ isNaN isFinite assert + parseInt + parseFloat ternary/a [program.exports] diff --git a/tests/compiler/tlsf.wast b/tests/compiler/tlsf.wast index 2bcb0764..4b248c29 100644 --- a/tests/compiler/tlsf.wast +++ b/tests/compiler/tlsf.wast @@ -356,6 +356,8 @@ isNaN isFinite assert + parseInt + parseFloat tlsf/fls tlsf/ffs tlsf/ALIGN_SIZE_LOG2 diff --git a/tests/compiler/unary.wast b/tests/compiler/unary.wast index 0c89f319..6695638a 100644 --- a/tests/compiler/unary.wast +++ b/tests/compiler/unary.wast @@ -661,6 +661,8 @@ isNaN isFinite assert + parseInt + parseFloat unary/i unary/I unary/f diff --git a/tests/compiler/while.wast b/tests/compiler/while.wast index 65d9f503..c9350a90 100644 --- a/tests/compiler/while.wast +++ b/tests/compiler/while.wast @@ -88,6 +88,8 @@ isNaN isFinite assert + parseInt + parseFloat while/loopWhile while/loopWhileInWhile [program.exports]