diff --git a/bin/asc.js b/bin/asc.js index 405a3e48..0041ae5f 100644 --- a/bin/asc.js +++ b/bin/asc.js @@ -48,7 +48,7 @@ function main(argv, options, callback) { if (!callback) callback = function defaultCallback(err) { var code = 0; if (err) { - stderr.write(err + os.EOL); + stderr.write(err.stack + os.EOL); code = 1; } return code; @@ -195,7 +195,11 @@ function main(argv, options, callback) { var module; stats.compileCount++; - stats.compileTime += measure(() => module = assemblyscript.compile(parser, compilerOptions)); + try { + stats.compileTime += measure(() => module = assemblyscript.compile(parser, compilerOptions)); + } catch (e) { + return callback(e); + } if (checkDiagnostics(parser, stderr)) { if (module) module.dispose(); return callback(Error("Compile error")); diff --git a/examples/tlsf/assembly/tlsf.ts b/examples/tlsf/assembly/tlsf.ts index 125f012f..74900310 100644 --- a/examples/tlsf/assembly/tlsf.ts +++ b/examples/tlsf/assembly/tlsf.ts @@ -383,8 +383,7 @@ class Root { // merge with current tail if adjacent if (start - Block.INFO == tailRef) { start -= Block.INFO; - let tail = changetype(tailRef); - tailInfo = tail.info; + tailInfo = changetype(tailRef).info; } } else @@ -407,7 +406,7 @@ class Root { tail.info = 0 | LEFT_FREE; this.tailRef = changetype(tail); - this.insert(left); // also sets jump + this.insert(left); // also merges with free left before tail / sets jump return true; } diff --git a/examples/tlsf/index.js b/examples/tlsf/index.js new file mode 100644 index 00000000..a56677dc --- /dev/null +++ b/examples/tlsf/index.js @@ -0,0 +1,5 @@ +const fs = require("fs"); +module.exports = new WebAssembly.Instance( + new WebAssembly.Module(fs.readFileSync(__dirname + "/tlsf.optimized.wasm")), + { env: { abort: function() { throw Error("abort called"); } } } +).exports; diff --git a/examples/tlsf/package.json b/examples/tlsf/package.json index 1e4a2e42..339824ec 100644 --- a/examples/tlsf/package.json +++ b/examples/tlsf/package.json @@ -5,7 +5,7 @@ "scripts": { "build": "npm run build:untouched && npm run build:optimized", "build:untouched": "asc assembly/tlsf.ts -t tlsf.untouched.wast -b tlsf.untouched.wasm --validate --sourceMap --measure", - "build:optimized": "asc -O3 assembly/tlsf.ts -b tlsf.optimized.wasm -t tlsf.optimized.wast --validate --noDebug --noAssert --sourceMap --measure", + "build:optimized": "asc assembly/tlsf.ts -t tlsf.optimized.wast -b tlsf.optimized.wasm --validate --sourceMap --measure --noDebug --noAssert --optimize", "test": "node tests", "test:forever": "node tests/forever" } diff --git a/examples/tlsf/tests/index.js b/examples/tlsf/tests/index.js index 83b56350..509eeb81 100644 --- a/examples/tlsf/tests/index.js +++ b/examples/tlsf/tests/index.js @@ -20,7 +20,7 @@ function test(file) { return String.fromCharCode.apply(String, str); } - runner(exports, 5, 20000); // picked so I/O isn't the bottleneck + runner(exports, 20, 20000); // picked so I/O isn't the bottleneck console.log("mem final: " + exports.memory.buffer.byteLength); console.log(); } diff --git a/src/ast.ts b/src/ast.ts index 62211c40..261b8de0 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -560,7 +560,7 @@ export abstract class Node { return elem; } - static createParameter(identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, isRest: bool, range: Range): Parameter { + static createParameter(identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, kind: ParameterKind, range: Range): Parameter { var elem = new Parameter(); elem.range = range; (elem.name = identifier).parent = elem; @@ -568,7 +568,7 @@ export abstract class Node { (type).parent = elem; if (elem.initializer = initializer) (initializer).parent = elem; - elem.isRest = isRest; + elem.parameterKind = kind; return elem; } @@ -1050,7 +1050,7 @@ export class Source extends Node { /** Tests if this source is an entry file. */ get isEntry(): bool { return this.sourceKind == SourceKind.ENTRY; } /** Tests if this source is a stdlib file. */ - get isStdlib(): bool { return this.sourceKind == SourceKind.LIBRARY; } + get isLibrary(): bool { return this.sourceKind == SourceKind.LIBRARY; } } /** Base class of all declaration statements. */ @@ -1352,18 +1352,28 @@ export class NamespaceDeclaration extends DeclarationStatement { members: Statement[]; } +/** Represents the kind of a parameter. */ +export enum ParameterKind { + /** No specific flags. */ + DEFAULT, + /** Is an optional parameter. */ + OPTIONAL, + /** Is a rest parameter. */ + REST +} + /** Represents a function parameter. */ export class Parameter extends Node { kind = NodeKind.PARAMETER; /** Parameter name. */ - name: IdentifierExpression | null; + name: IdentifierExpression; /** Parameter type. */ type: TypeNode | null; + /** Parameter kind. */ + parameterKind: ParameterKind; /** Initializer expression, if present. */ initializer: Expression | null; - /** Whether a rest parameter or not. */ - isRest: bool; } /** Represents a single modifier. */ diff --git a/src/builtins.ts b/src/builtins.ts index 6b0c8077..9c93b4b8 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -46,147 +46,6 @@ import { ElementKind } from "./program"; -/** Initializes the specified program with built-in constants and functions. */ -export function initialize(program: Program): void { - - // math - addConstant(program, "NaN", Type.f64); - addConstant(program, "Infinity", Type.f64); - - addFunction(program, "isNaN", true); - addFunction(program, "isFinite", true); - addFunction(program, "clz", true); - addFunction(program, "ctz", true); - addFunction(program, "popcnt", true); - addFunction(program, "rotl", true); - addFunction(program, "rotr", true); - addFunction(program, "abs", true); - addFunction(program, "max", true); - addFunction(program, "min", true); - addFunction(program, "ceil", true); - addFunction(program, "floor", true); - addFunction(program, "copysign", true); - addFunction(program, "nearest", true); - addFunction(program, "reinterpret", true); - addFunction(program, "sqrt", true); - addFunction(program, "trunc", true); - - // memory access - addFunction(program, "load", true); - addFunction(program, "store", true); - addFunction(program, "sizeof", true); - - // control flow - addFunction(program, "select", true); - addFunction(program, "unreachable"); - - // host operations - addFunction(program, "current_memory"); - addFunction(program, "grow_memory"); - // addFunction(program, "move_memory"); - // addFunction(program, "set_memory"); - - // other - addFunction(program, "changetype", true); - addFunction(program, "assert"); - - // abort is special in that it is imported conditionally. for example, when - // compiling with noAssert=true, it isn't necessary that it is present, that - // is if a user doesn't call it manually. - var abortPrototype = addFunction(program, "abort"); - abortPrototype.set(ElementFlags.DECLARED); - abortPrototype.instances.set("", new Function(abortPrototype, "abort", null, [ - new Parameter(null, program.options.usizeType), // message (string) - new Parameter(null, program.options.usizeType), // file name (string) - new Parameter(null, Type.u32), // line number - new Parameter(null, Type.u32) // column number - ], Type.void, null)); - - // conversions and limits - var i32Func: FunctionPrototype, - u32Func: FunctionPrototype, - i64Func: FunctionPrototype, - u64Func: FunctionPrototype; - addFunction(program, "i8").members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "i8.MIN_VALUE", null, Type.i8).withConstantIntegerValue(-128, -1) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "i8.MAX_VALUE", null, Type.i8).withConstantIntegerValue(127, 0) ] - ]); - addFunction(program, "i16").members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "i16.MIN_VALUE", null, Type.i16).withConstantIntegerValue(-32768, -1) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "i16.MAX_VALUE", null, Type.i16).withConstantIntegerValue(32767, 0) ] - ]); - (i32Func = addFunction(program, "i32")).members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "i32.MIN_VALUE", null, Type.i32).withConstantIntegerValue(-2147483648, -1) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "i32.MAX_VALUE", null, Type.i32).withConstantIntegerValue(2147483647, 0) ] - ]); - (i64Func = addFunction(program, "i64")).members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "i64.MIN_VALUE", null, Type.i64).withConstantIntegerValue(0, -2147483648) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "i64.MAX_VALUE", null, Type.i64).withConstantIntegerValue(-1, 2147483647) ] - ]); - addFunction(program, "u8").members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "u8.MIN_VALUE", null, Type.u8).withConstantIntegerValue(0, 0) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "u8.MAX_VALUE", null, Type.u8).withConstantIntegerValue(255, 0) ] - ]); - addFunction(program, "u16").members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "u16.MIN_VALUE", null, Type.u16).withConstantIntegerValue(0, 0) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "u16.MAX_VALUE", null, Type.u16).withConstantIntegerValue(65535, 0) ] - ]); - (u32Func = addFunction(program, "u32")).members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "u32.MIN_VALUE", null, Type.u32).withConstantIntegerValue(0, 0) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "u32.MAX_VALUE", null, Type.u32).withConstantIntegerValue(-1, 0) ] - ]); - (u64Func = addFunction(program, "u64")).members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "u64.MIN_VALUE", null, Type.u64).withConstantIntegerValue(0, 0) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "u64.MAX_VALUE", null, Type.i64).withConstantIntegerValue(-1, -1) ] - ]); - addFunction(program, "bool").members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "bool.MIN_VALUE", null, Type.bool).withConstantIntegerValue(0, 0) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "bool.MAX_VALUE", null, Type.bool).withConstantIntegerValue(1, 0) ] - ]); - addFunction(program, "f32").members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "f32.MIN_VALUE", null, Type.f32).withConstantFloatValue(-3.40282347e+38) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "f32.MAX_VALUE", null, Type.f32).withConstantFloatValue(3.40282347e+38) ], - [ "MIN_SAFE_INTEGER", new Global(program, "MIN_SAFE_INTEGER", "f32.MIN_SAFE_INTEGER", null, Type.f32).withConstantFloatValue(-16777215) ], - [ "MAX_SAFE_INTEGER", new Global(program, "MAX_SAFE_INTEGER", "f32.MAX_SAFE_INTEGER", null, Type.f32).withConstantFloatValue(16777215) ], - [ "EPSILON", new Global(program, "EPSILON", "f32.EPSILON", null, Type.f32).withConstantFloatValue(1.19209290e-07) ] - ]); - addFunction(program, "f64").members = new Map([ - [ "MIN_VALUE", new Global(program, "MIN_VALUE", "f64.MIN_VALUE", null, Type.f64).withConstantFloatValue(-1.7976931348623157e+308) ], - [ "MAX_VALUE", new Global(program, "MAX_VALUE", "f64.MAX_VALUE", null, Type.f64).withConstantFloatValue(1.7976931348623157e+308) ], - [ "MIN_SAFE_INTEGER", new Global(program, "MIN_SAFE_INTEGER", "f64.MIN_SAFE_INTEGER", null, Type.f64).withConstantFloatValue(-9007199254740991) ], - [ "MAX_SAFE_INTEGER", new Global(program, "MAX_SAFE_INTEGER", "f64.MAX_SAFE_INTEGER", null, Type.f64).withConstantFloatValue(9007199254740991) ], - [ "EPSILON", new Global(program, "EPSILON", "f64.EPSILON", null, Type.f64).withConstantFloatValue(2.2204460492503131e-16) ] - ]); - if (program.options.isWasm64) { - program.elements.set("isize", i64Func); - program.elements.set("usize", u64Func); - addConstant(program, "HEAP_BASE", Type.usize64); - } else { - program.elements.set("isize", i32Func); - program.elements.set("usize", u32Func); - addConstant(program, "HEAP_BASE", Type.usize32); - } -} - -/** Adds a built-in constant to the specified program. */ -function addConstant(program: Program, name: string, type: Type): Global { - var global = new Global(program, name, name, null, type); - global.set(ElementFlags.BUILTIN); - global.set(ElementFlags.CONSTANT); - program.elements.set(name, global); - return global; -} - -/** Adds a built-in function to the specified program. */ -function addFunction(program: Program, name: string, isGeneric: bool = false): FunctionPrototype { - var prototype = new FunctionPrototype(program, name, name, null, null); - prototype.set(ElementFlags.BUILTIN); - if (isGeneric) - prototype.set(ElementFlags.GENERIC); - program.elements.set(name, prototype); - return prototype; -} - /** Compiles a get of a built-in global. */ export function compileGetConstant(compiler: Compiler, global: Global, reportNode: Node): ExpressionRef { switch (global.internalName) { // switching on strings should become a compiler optimization eventually @@ -204,7 +63,8 @@ export function compileGetConstant(compiler: Compiler, global: Global, reportNod return compiler.module.createF64(Infinity); case "HEAP_BASE": // constant, but never inlined - return compiler.module.createGetGlobal("HEAP_BASE", (compiler.currentType = global.type).toNativeType()); + compiler.currentType = compiler.options.usizeType; + return compiler.module.createGetGlobal("HEAP_BASE", compiler.options.nativeSizeType); } compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); return compiler.module.createUnreachable(); diff --git a/src/compiler.ts b/src/compiler.ts index b501a4bf..02f22380 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -219,12 +219,6 @@ export class Compiler extends DiagnosticEmitter { this.options = options ? options : new Options(); this.memoryOffset = new U64(this.options.usizeType.byteSize); // leave space for `null` this.module = Module.create(); - - // set up the start function wrapping top-level statements, of all files. - var startFunctionTemplate = new FunctionPrototype(program, "start", "start", null); - var startFunctionInstance = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null); - startFunctionInstance.set(ElementFlags.START); - this.currentFunction = this.startFunction = startFunctionInstance; } /** Performs compilation of the underlying {@link Program} to a {@link Module}. */ @@ -233,6 +227,13 @@ export class Compiler extends DiagnosticEmitter { // initialize lookup maps, built-ins, imports, exports, etc. this.program.initialize(this.options); + // set up the start function wrapping top-level statements, of all files. + var startFunctionPrototype = assert(this.program.elements.get("start")); + assert(startFunctionPrototype.kind == ElementKind.FUNCTION_PROTOTYPE); + var startFunctionInstance = new Function(startFunctionPrototype, startFunctionPrototype.internalName, [], [], Type.void, null); + startFunctionInstance.set(ElementFlags.START); + this.currentFunction = this.startFunction = startFunctionInstance; + var sources = this.program.sources; // compile entry file(s) while traversing to reachable elements @@ -389,31 +390,27 @@ export class Compiler extends DiagnosticEmitter { var declaration = global.declaration; var initExpr: ExpressionRef = 0; - if (global.type == Type.void) { // infer type - if (declaration) { - if (declaration.type) { - var resolvedType = this.program.resolveType(declaration.type); // reports - if (!resolvedType) - return false; - if (resolvedType == Type.void) { - this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.type.range, "*", resolvedType.toString()); - return false; - } - global.type = resolvedType; - } else if (declaration.initializer) { // infer type using void/NONE for proper literal inference - initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports - if (this.currentType == Type.void) { - this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.initializer.range, this.currentType.toString(), ""); - return false; - } - global.type = this.currentType; - } else { - this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd); + if (declaration.type) { + var resolvedType = this.program.resolveType(declaration.type); // reports + if (!resolvedType) + return false; + if (resolvedType == Type.void) { + this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.type.range, "*", resolvedType.toString()); return false; } - } else - throw new Error("declaration expected"); + global.type = resolvedType; + } else if (declaration.initializer) { // infer type using void/NONE for proper literal inference + initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports + if (this.currentType == Type.void) { + this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.initializer.range, this.currentType.toString(), ""); + return false; + } + global.type = this.currentType; + } else { + this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd); + return false; + } } var nativeType = global.type.toNativeType(); @@ -423,9 +420,8 @@ export class Compiler extends DiagnosticEmitter { this.module.addGlobalImport(global.internalName, global.namespace ? global.namespace.simpleName : "env", global.simpleName, nativeType); global.set(ElementFlags.COMPILED); return true; - } else if (declaration) { + } else this.error(DiagnosticCode.Operation_not_supported, declaration.range); - } return false; } @@ -433,7 +429,7 @@ export class Compiler extends DiagnosticEmitter { if (global.is(ElementFlags.INLINED)) { initExpr = this.compileInlineConstant(global, global.type); - } else if (declaration) { + } else { if (declaration.initializer) { if (!initExpr) initExpr = this.compileExpression(declaration.initializer, global.type); @@ -449,8 +445,7 @@ export class Compiler extends DiagnosticEmitter { } } else initExpr = global.type.toNativeZero(this.module); - } else - throw new Error("declaration expected"); + } var internalName = global.internalName; if (initializeInStart) { @@ -482,10 +477,9 @@ export class Compiler extends DiagnosticEmitter { throw new Error("concrete type expected"); } global.set(ElementFlags.INLINED); - if (!declaration || declaration.isTopLevel) { // might be re-exported + if (declaration.isTopLevel) // might be re-exported this.module.addGlobal(internalName, nativeType, !global.is(ElementFlags.CONSTANT), initExpr); - } - if (declaration && declaration.range.source.isEntry && declaration.isTopLevelExport) + if (declaration.range.source.isEntry && declaration.isTopLevelExport) this.module.addGlobalExport(global.internalName, declaration.programLevelInternalName); } else this.module.addGlobal(internalName, nativeType, !global.is(ElementFlags.CONSTANT), initExpr); @@ -521,9 +515,9 @@ export class Compiler extends DiagnosticEmitter { var valueDeclaration = val.declaration; val.set(ElementFlags.COMPILED); if (val.is(ElementFlags.INLINED)) { - if (!element.declaration || element.declaration.isTopLevelExport) + if (element.declaration.isTopLevelExport) this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue)); - } else if (valueDeclaration) { + } else { var initExpr: ExpressionRef; if (valueDeclaration.value) { initExpr = this.compileExpression(valueDeclaration.value, Type.i32); @@ -561,12 +555,11 @@ export class Compiler extends DiagnosticEmitter { } else throw new Error("i32 expected"); } - } else - throw new Error("declaration expected"); + } previousValue = val; // export values if the enum is exported - if (element.declaration && element.declaration.range.source.isEntry && element.declaration.isTopLevelExport) { + if (element.declaration.range.source.isEntry && element.declaration.isTopLevelExport) { if (member.is(ElementFlags.INLINED)) this.module.addGlobalExport(member.internalName, member.internalName); else if (valueDeclaration) @@ -579,15 +572,15 @@ export class Compiler extends DiagnosticEmitter { // functions - compileFunctionDeclaration(declaration: FunctionDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map | null = null, alternativeReportNode: Node | null = null): Function | null { + compileFunctionDeclaration(declaration: FunctionDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map | null = null): Function | null { var element = this.program.elements.get(declaration.fileLevelInternalName); if (!element || element.kind != ElementKind.FUNCTION_PROTOTYPE) throw new Error("function expected"); - return this.compileFunctionUsingTypeArguments(element, typeArguments, contextualTypeArguments, alternativeReportNode); // reports + return this.compileFunctionUsingTypeArguments(element, typeArguments, contextualTypeArguments, (element).declaration.name); // reports } - compileFunctionUsingTypeArguments(prototype: FunctionPrototype, typeArguments: TypeNode[], contextualTypeArguments: Map | null = null, alternativeReportNode: Node | null = null): Function | null { - var instance = prototype.resolveInclTypeArguments(typeArguments, contextualTypeArguments, alternativeReportNode); // reports + compileFunctionUsingTypeArguments(prototype: FunctionPrototype, typeArguments: TypeNode[], contextualTypeArguments: Map | null, reportNode: Node): Function | null { + var instance = prototype.resolveInclTypeArguments(typeArguments, contextualTypeArguments, reportNode); // reports if (!instance) return null; return this.compileFunction(instance) ? instance : null; @@ -597,20 +590,17 @@ export class Compiler extends DiagnosticEmitter { if (instance.is(ElementFlags.COMPILED)) return true; - var declaration = instance.prototype.declaration; + assert(!instance.is(ElementFlags.BUILTIN) || instance.simpleName == "abort"); + var declaration = instance.prototype.declaration; if (instance.is(ElementFlags.DECLARED)) { - if (declaration && declaration.statements) { + if (declaration.statements) { this.error(DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts, declaration.name.range); return false; } - } else { - if (!declaration) - throw new Error("declaration expected"); // built-ins are not compiled here - if (!declaration.statements) { - this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, declaration.name.range); - return false; - } + } else if (!declaration.statements) { + this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, declaration.name.range); + return false; } // might trigger compilation of other functions referring to this one @@ -619,7 +609,6 @@ export class Compiler extends DiagnosticEmitter { // compile statements var stmts: ExpressionRef[] | null = null; if (!instance.is(ElementFlags.DECLARED)) { - declaration = assert(declaration, "declaration expected"); var previousFunction = this.currentFunction; this.currentFunction = instance; var statements = assert(declaration.statements, "implementation expected"); @@ -661,7 +650,7 @@ export class Compiler extends DiagnosticEmitter { ref = this.module.addFunction(instance.internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, stmts, NativeType.None)); // check module export - if (declaration && declaration.range.source.isEntry && declaration.isTopLevelExport) + if (declaration.range.source.isEntry && declaration.isTopLevelExport) this.module.addFunctionExport(instance.internalName, declaration.name.name); instance.finalize(this.module, ref); @@ -735,7 +724,7 @@ export class Compiler extends DiagnosticEmitter { case ElementKind.FUNCTION_PROTOTYPE: if ((noTreeShaking || (element).is(ElementFlags.EXPORTED)) && !(element).is(ElementFlags.GENERIC)) - this.compileFunctionUsingTypeArguments(element, []); + this.compileFunctionUsingTypeArguments(element, [], null, (element).declaration.name); break; case ElementKind.GLOBAL: @@ -772,7 +761,7 @@ export class Compiler extends DiagnosticEmitter { case ElementKind.FUNCTION_PROTOTYPE: if (!(element).is(ElementFlags.GENERIC) && statement.range.source.isEntry) { - var functionInstance = this.compileFunctionUsingTypeArguments(element, []); + var functionInstance = this.compileFunctionUsingTypeArguments(element, [], null, (element).declaration.name); if (functionInstance) { var functionDeclaration = functionInstance.prototype.declaration; if (functionDeclaration && functionDeclaration.needsExplicitExport(member)) @@ -1451,14 +1440,19 @@ export class Compiler extends DiagnosticEmitter { precomputeExpressionRef(expr: ExpressionRef): ExpressionRef { var nativeType = this.currentType.toNativeType(); var typeRef = this.module.getFunctionTypeBySignature(nativeType, []); - if (!typeRef) + var typeRefAdded = false; + if (!typeRef) { typeRef = this.module.addFunctionType(this.currentType.toSignatureString(), nativeType, []); + typeRefAdded = true; + } var funcRef = this.module.addFunction("__precompute", typeRef, [], expr); this.module.runPasses([ "precompute" ], funcRef); var ret = _BinaryenFunctionGetBody(funcRef); this.module.removeFunction("__precompute"); - // TODO: also remove the function type somehow if no longer used or make the C-API accept - // a `null` typeRef, using an implicit type. + if (typeRefAdded) { + // TODO: also remove the function type somehow if no longer used or make the C-API accept + // a `null` typeRef, using an implicit type. + } return ret; } @@ -2656,56 +2650,62 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); var element = resolved.element; - if (element.kind == ElementKind.FUNCTION_PROTOTYPE) { - var functionPrototype = element; - var functionInstance: Function | null = null; - if (functionPrototype.is(ElementFlags.BUILTIN)) { - 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(""); - - if (!functionInstance) { - var expr = compileBuiltinCall(this, functionPrototype, resolvedTypeArguments, expression.arguments, contextualType, expression); - if (!expr) { - this.error(DiagnosticCode.Operation_not_supported, expression.range); - return this.module.createUnreachable(); - } - return expr; - } - } else { - // TODO: infer type arguments from parameter types if omitted - functionInstance = (element).resolveInclTypeArguments(expression.typeArguments, this.currentFunction.contextualTypeArguments, expression); // reports - } - if (!functionInstance) - return this.module.createUnreachable(); - - var numArguments = expression.arguments.length; - var numArgumentsInclThis = functionInstance.instanceMethodOf != null ? numArguments + 1 : numArguments; - var argumentIndex = 0; - - var args = new Array(numArgumentsInclThis); - if (functionInstance.instanceMethodOf) { - assert(resolved.targetExpression != null); - args[argumentIndex++] = resolved.targetExpression; - } - for (i = 0; i < numArguments; ++i) - args[argumentIndex++] = expression.arguments[i]; - return this.compileCall(functionInstance, args, expression); + if (element.kind != ElementKind.FUNCTION_PROTOTYPE) { + 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(); } - 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(); + + var functionPrototype = element; + var functionInstance: Function | null = null; + + // TODO: generalize? + if (functionPrototype.is(ElementFlags.BUILTIN)) { + var resolvedTypeArguments: Type[] | null = null; + if (expression.typeArguments) { + var k = expression.typeArguments.length; + resolvedTypeArguments = 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; + } + } + var expr = compileBuiltinCall(this, functionPrototype, resolvedTypeArguments, expression.arguments, contextualType, expression); + if (!expr) { + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); + } + return expr; + } + + // TODO: infer type arguments from parameter types if omitted + var functionInstance = functionPrototype.resolveInclTypeArguments(expression.typeArguments, this.currentFunction.contextualTypeArguments, expression); // reports + if (!functionInstance) + return this.module.createUnreachable(); + + // TODO: generalize? (see above) + /* if (functionInstance.is(ElementFlags.BUILTIN)) { + var expr = compileBuiltinCall(this, functionPrototype, functionInstance.typeArguments, expression.arguments, contextualType, expression); + if (!expr) { + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); + } + return expr; + } */ + + var numArguments = expression.arguments.length; + var numArgumentsInclThis = functionInstance.instanceMethodOf != null ? numArguments + 1 : numArguments; + var argumentIndex = 0; + + var args = new Array(numArgumentsInclThis); + if (functionInstance.instanceMethodOf) { + assert(resolved.targetExpression != null); + args[argumentIndex++] = resolved.targetExpression; + } + for (i = 0; i < numArguments; ++i) + args[argumentIndex++] = expression.arguments[i]; + return this.compileCall(functionInstance, args, expression); } /** @@ -2968,16 +2968,75 @@ export class Compiler extends DiagnosticEmitter { compileStaticArray(elementType: Type, expressions: (Expression | null)[]): ExpressionRef { // compile as static if all element expressions are precomputable, otherwise // initialize in place. - var exprs = new Array(expressions.length); var isStatic = true; - var expr: BinaryenExpressionRef; - for (var i = 0; i < expressions.length; ++i) { - exprs[i] = expressions[i] ? this.compileExpression(expressions[i], elementType) : elementType.toNativeZero(this.module); - if (isStatic && _BinaryenExpressionGetId(expr = this.precomputeExpressionRef(exprs[i])) == ExpressionId.Const) { - // TODO: use multiple arrays of possible native types? - } else - isStatic = false; + var size = expressions.length; + + var nativeType = elementType.toNativeType(); + var values: usize; + switch (nativeType) { + + case NativeType.I32: + values = changetype(new Int32Array(size)); + break; + + case NativeType.I64: + values = changetype(new Array(size)); + break; + + case NativeType.F32: + values = changetype(new Float32Array(size)); + break; + + case NativeType.F64: + values = changetype(new Float64Array(size)); + break; + + default: + throw new Error("concrete type expected"); } + + var exprs = new Array(size); + var expr: BinaryenExpressionRef; + for (var i = 0; i < size; ++i) { + exprs[i] = expressions[i] ? this.compileExpression(expressions[i], elementType) : elementType.toNativeZero(this.module); + if (isStatic) { + if (_BinaryenExpressionGetId(expr = this.precomputeExpressionRef(exprs[i])) == ExpressionId.Const) { + assert(_BinaryenExpressionGetType(expr) == nativeType); + switch (nativeType) { + + case NativeType.I32: + changetype(values)[i] = _BinaryenConstGetValueI32(expr); + break; + + case NativeType.I64: + changetype(values)[i] = new I64(_BinaryenConstGetValueI64Low(expr), _BinaryenConstGetValueI64High(expr)); + break; + + case NativeType.F32: + changetype(values)[i] = _BinaryenConstGetValueF32(expr); + break; + + case NativeType.F64: + changetype(values)[i] = _BinaryenConstGetValueF64(expr); + break; + + default: + assert(false); // checked above + } + } else { + // TODO: emit a warning if declared 'const' + isStatic = false; + } + } + } + + if (isStatic) { + // TODO: convert to Uint8Array and create the segment + } else { + // TODO: initialize in place + } + // TODO: alternatively, static elements could go into data segments while + // dynamic ones are initialized on top? any benefits? (doesn't seem so) throw new Error("not implemented"); } diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index a4134a3b..ec97f9b3 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -21,11 +21,14 @@ export enum DiagnosticCode { Trailing_comma_not_allowed = 1009, Unexpected_token = 1012, A_rest_parameter_must_be_last_in_a_parameter_list = 1014, + Parameter_cannot_have_question_mark_and_initializer = 1015, A_required_parameter_cannot_follow_an_optional_parameter = 1016, Enum_member_must_have_initializer = 1061, Statements_are_not_allowed_in_ambient_contexts = 1036, Initializers_are_not_allowed_in_ambient_contexts = 1039, _0_modifier_cannot_be_used_here = 1042, + A_rest_parameter_cannot_be_optional = 1047, + A_rest_parameter_cannot_have_an_initializer = 1048, A_set_accessor_must_have_exactly_one_parameter = 1049, A_set_accessor_parameter_cannot_have_an_initializer = 1052, A_get_accessor_cannot_have_parameters = 1054, @@ -115,11 +118,14 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 1009: return "Trailing comma not allowed."; case 1012: return "Unexpected token."; case 1014: return "A rest parameter must be last in a parameter list."; + case 1015: return "Parameter cannot have question mark and initializer."; case 1016: return "A required parameter cannot follow an optional parameter."; case 1061: return "Enum member must have initializer."; case 1036: return "Statements are not allowed in ambient contexts."; case 1039: return "Initializers are not allowed in ambient contexts."; case 1042: return "'{0}' modifier cannot be used here."; + case 1047: return "A rest parameter cannot be optional."; + case 1048: return "A rest parameter cannot have an initializer."; case 1049: return "A 'set' accessor must have exactly one parameter."; case 1052: return "A 'set' accessor parameter cannot have an initializer."; case 1054: return "A 'get' accessor cannot have parameters."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 28e6cfab..e8b2c864 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -20,11 +20,14 @@ "Trailing comma not allowed.": 1009, "Unexpected token.": 1012, "A rest parameter must be last in a parameter list.": 1014, + "Parameter cannot have question mark and initializer.": 1015, "A required parameter cannot follow an optional parameter.": 1016, "Enum member must have initializer.": 1061, "Statements are not allowed in ambient contexts.": 1036, "Initializers are not allowed in ambient contexts.": 1039, "'{0}' modifier cannot be used here.": 1042, + "A rest parameter cannot be optional.": 1047, + "A rest parameter cannot have an initializer.": 1048, "A 'set' accessor must have exactly one parameter.": 1049, "A 'set' accessor parameter cannot have an initializer.": 1052, "A 'get' accessor cannot have parameters.": 1054, diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 21143ca8..a05a94d5 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -170,10 +170,8 @@ export abstract class DiagnosticEmitter { emitDiagnostic(code: DiagnosticCode, category: DiagnosticCategory, range: Range, arg0: string | null = null, arg1: string | null = null) { var message = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range); this.diagnostics.push(message); - /* if (!this.silentDiagnostics) { - console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary - console.log(new Error("stack").stack); - } */ + // console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary + // console.log(new Error("stack").stack); } error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void { diff --git a/src/extra/ast.ts b/src/extra/ast.ts index 4cf3c4fb..33faaf15 100644 --- a/src/extra/ast.ts +++ b/src/extra/ast.ts @@ -69,6 +69,7 @@ import { Modifier, ModifierKind, Parameter, + ParameterKind, ExportMember, SwitchCase, @@ -1189,11 +1190,14 @@ export function serializeModifier(node: Modifier, sb: string[]): void { } export function serializeParameter(node: Parameter, sb: string[]): void { - if (node.isRest) + if (node.parameterKind == ParameterKind.REST) sb.push("..."); serializeIdentifierExpression(node.name, sb); if (node.type) { - sb.push(": "); + if (node.parameterKind == ParameterKind.OPTIONAL && !node.initializer) + sb.push("?: "); + else + sb.push(": "); serializeTypeNode(node.type, sb); } if (node.initializer) { diff --git a/src/parser.ts b/src/parser.ts index 976786d7..4537994b 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -23,6 +23,10 @@ import { DiagnosticEmitter } from "./diagnostics"; +import { + I64 +} from "./util/i64"; + import { normalize as normalizePath } from "./util/path"; @@ -66,6 +70,7 @@ import { ModifierKind, NamespaceDeclaration, Parameter, + ParameterKind, ReturnStatement, SwitchCase, SwitchStatement, @@ -533,12 +538,34 @@ export class Parser extends DiagnosticEmitter { parseParameters(tn: Tokenizer): Parameter[] | null { // at '(': (Parameter (',' Parameter)*)? ')' var parameters = new Array(); + var seenRest: Parameter | null = null; + var seenOptional = false; + var reportedRest = false; if (tn.peek() != Token.CLOSEPAREN) { do { var param = this.parseParameter(tn); if (!param) return null; - parameters.push(param); + if (seenRest && !reportedRest) { + this.error(DiagnosticCode.A_rest_parameter_must_be_last_in_a_parameter_list, seenRest.name.range); + reportedRest = true; + } + switch (param.parameterKind) { + + default: + if (seenOptional) + this.error(DiagnosticCode.A_required_parameter_cannot_follow_an_optional_parameter, param.name.range); + break; + + case ParameterKind.OPTIONAL: + seenOptional = true; + break; + + case ParameterKind.REST: + seenRest = param; + break; + } + parameters.push(param); } while (tn.skip(Token.COMMA)); } if (tn.skip(Token.CLOSEPAREN)) @@ -549,8 +576,11 @@ export class Parser extends DiagnosticEmitter { } parseParameter(tn: Tokenizer): Parameter | null { - // '...'? Identifier (':' Type)? ('=' Expression)? + // '...'? Identifier '?'? (':' Type)? ('=' Expression)? var isRest = false; + var seenRest = false; + var isOptional = false; + var seenOptional = false; var startRange: Range | null = null; if (tn.skip(Token.DOT_DOT_DOT)) { isRest = true; @@ -561,6 +591,10 @@ export class Parser extends DiagnosticEmitter { startRange = tn.range(); var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); var type: TypeNode | null = null; + if (isOptional = tn.skip(Token.QUESTION)) { + if (isRest) + this.error(DiagnosticCode.A_rest_parameter_cannot_be_optional, identifier.range); + } if (tn.skip(Token.COLON)) { type = this.parseType(tn); if (!type) @@ -568,11 +602,17 @@ export class Parser extends DiagnosticEmitter { } var initializer: Expression | null = null; if (tn.skip(Token.EQUALS)) { + if (isRest) + this.error(DiagnosticCode.A_rest_parameter_cannot_have_an_initializer, identifier.range); + if (isOptional) + this.error(DiagnosticCode.Parameter_cannot_have_question_mark_and_initializer, identifier.range); + else + isOptional = true; initializer = this.parseExpression(tn, Precedence.COMMA + 1); if (!initializer) return null; } - return Node.createParameter(identifier, type, initializer, isRest, Range.join(startRange, tn.range())); + return Node.createParameter(identifier, type, initializer, isRest ? ParameterKind.REST : isOptional ? ParameterKind.OPTIONAL : ParameterKind.DEFAULT, Range.join(startRange, tn.range())); } else this.error(DiagnosticCode.Identifier_expected, tn.range()); return null; diff --git a/src/program.ts b/src/program.ts index c50bb051..e4d52f3a 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1,7 +1,3 @@ -import { - initialize as initializeBuiltins -} from "./builtins"; - import { Options } from "./compiler"; @@ -157,8 +153,6 @@ export class Program extends DiagnosticEmitter { ["boolean", Type.bool] ]); - initializeBuiltins(this); - var queuedExports = new Map(); var queuedImports = new Array(); var queuedDerivedClasses = new Array(); @@ -230,9 +224,8 @@ export class Program extends DiagnosticEmitter { var currentExport: QueuedExport | null = queuedExport; // nullable below do { if (currentExport.isReExport) { - element = this.exports.get(currentExport.referencedName); - if (element) { - this.exports.set(exportName, element); + if (element = this.exports.get(currentExport.referencedName)) { + this.setExportAndCheckLibrary(exportName, element, currentExport.member.externalIdentifier); break; } currentExport = queuedExports.get(currentExport.referencedName); @@ -241,9 +234,9 @@ export class Program extends DiagnosticEmitter { } else { if ( (element = this.elements.get(currentExport.referencedName)) || // normal export - (element = this.elements.get(currentExport.member.identifier.name)) // stdlib re-export + (element = this.elements.get(currentExport.member.identifier.name)) // library re-export ) - this.exports.set(exportName, element); + this.setExportAndCheckLibrary(exportName, element, currentExport.member.externalIdentifier); else this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.range, queuedExport.member.identifier.name); break; @@ -272,8 +265,7 @@ export class Program extends DiagnosticEmitter { private tryResolveImport(referencedName: string, queuedExports: Map): Element | null { var element: Element | null; do { - element = this.exports.get(referencedName); - if (element) + if (element = this.exports.get(referencedName)) return element; var queuedExport = queuedExports.get(referencedName); if (!queuedExport) @@ -286,17 +278,33 @@ export class Program extends DiagnosticEmitter { } while (true); } - private checkGlobalAlias(element: Element, declaration: DeclarationStatement): bool { - if (hasDecorator("global", declaration.decorators) || (declaration.range.source.isStdlib && assert(declaration.parent).kind == NodeKind.SOURCE && element.is(ElementFlags.EXPORTED))) { + private checkInternalDecorators(element: Element, declaration: DeclarationStatement): void { + var isBuiltin: bool = hasDecorator("builtin", declaration.decorators); + if (isBuiltin) + element.set(ElementFlags.BUILTIN); + if ( + hasDecorator("global", declaration.decorators) || + ( + declaration.range.source.isLibrary && + element.is(ElementFlags.EXPORTED) && + ( + assert(declaration.parent).kind == NodeKind.SOURCE || + ( + declaration.parent).kind == NodeKind.VARIABLE && + assert((declaration.parent).parent).kind == NodeKind.SOURCE + ) + ) + ) { + element.set(ElementFlags.GLOBAL); if (this.elements.has(declaration.name.name)) this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, element.internalName); else { this.elements.set(declaration.name.name, element); this.exports.set(declaration.name.name, element); - return true; + if (isBuiltin) + element.internalName = declaration.name.name; } } - return false; } private initializeClass(declaration: ClassDeclaration, queuedDerivedClasses: ClassPrototype[], namespace: Element | null = null): void { @@ -309,6 +317,8 @@ export class Program extends DiagnosticEmitter { prototype.namespace = namespace; this.elements.set(internalName, prototype); + this.checkInternalDecorators(prototype, declaration); + if (hasDecorator("unmanaged", declaration.decorators)) { prototype.isUnmanaged = true; if (declaration.implementsTypes && declaration.implementsTypes.length) @@ -363,14 +373,11 @@ export class Program extends DiagnosticEmitter { } } - if (this.checkGlobalAlias(prototype, declaration)) { - if (declaration.name.name === "String") { - var instance = prototype.resolve(null, null); - if (instance) { - assert(!this.types.has("string")); - this.types.set("string", instance.type); - } - } + // check and possibly register string type + if (prototype.is(ElementFlags.GLOBAL) && declaration.name.name === "String" && !this.types.has("string")) { + var instance = prototype.resolve(null); + if (instance) + this.types.set("string", instance.type); } } @@ -569,7 +576,7 @@ export class Program extends DiagnosticEmitter { enm.namespace = namespace; this.elements.set(internalName, enm); - this.checkGlobalAlias(enm, declaration); + this.checkInternalDecorators(enm, declaration); if (namespace) { if (namespace.members) { @@ -613,6 +620,18 @@ export class Program extends DiagnosticEmitter { this.initializeExport(members[i], statement.internalPath, queuedExports); } + private setExportAndCheckLibrary(name: string, element: Element, identifier: IdentifierExpression): void { + this.exports.set(name, element); + if (identifier.range.source.isLibrary) { // add global alias + if (this.elements.has(identifier.name)) + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, identifier.range, identifier.name); + else { + element.internalName = identifier.name; + this.elements.set(identifier.name, element); + } + } + } + private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map): void { var externalName = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name; if (this.exports.has(externalName)) { @@ -620,6 +639,7 @@ export class Program extends DiagnosticEmitter { return; } var referencedName: string; + var referencedElement: Element | null; var queuedExport: QueuedExport | null; // export local element @@ -627,8 +647,8 @@ export class Program extends DiagnosticEmitter { referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name; // resolve right away if the element exists - if (this.elements.has(referencedName)) { - this.exports.set(externalName, this.elements.get(referencedName)); + if (referencedElement = this.elements.get(referencedName)) { + this.setExportAndCheckLibrary(externalName, referencedElement, member.externalIdentifier); return; } @@ -648,8 +668,8 @@ export class Program extends DiagnosticEmitter { referencedName = (internalPath) + PATH_DELIMITER + member.identifier.name; // resolve right away if the export exists - if (this.exports.has(referencedName)) { - this.exports.set(externalName, this.exports.get(referencedName)); + if (referencedElement = this.elements.get(referencedName)) { + this.setExportAndCheckLibrary(externalName, referencedElement, member.externalIdentifier); return; } @@ -657,8 +677,8 @@ export class Program extends DiagnosticEmitter { var seen = new Set(); while (queuedExport = queuedExports.get(referencedName)) { if (queuedExport.isReExport) { - if (this.exports.has(queuedExport.referencedName)) { - this.exports.set(externalName, this.exports.get(referencedName)); + if (referencedElement = this.exports.get(queuedExport.referencedName)) { + this.setExportAndCheckLibrary(externalName, referencedElement, member.externalIdentifier); return; } referencedName = queuedExport.referencedName; @@ -666,8 +686,8 @@ export class Program extends DiagnosticEmitter { break; seen.add(queuedExport); } else { - if (this.elements.has(queuedExport.referencedName)) { - this.exports.set(externalName, this.elements.get(referencedName)); + if (referencedElement = this.elements.get(queuedExport.referencedName)) { + this.setExportAndCheckLibrary(externalName, referencedElement, member.externalIdentifier); return; } break; @@ -697,7 +717,7 @@ export class Program extends DiagnosticEmitter { prototype.namespace = namespace; this.elements.set(internalName, prototype); - this.checkGlobalAlias(prototype, declaration); + this.checkInternalDecorators(prototype, declaration); if (namespace) { if (namespace.members) { @@ -787,7 +807,7 @@ export class Program extends DiagnosticEmitter { prototype.namespace = namespace; this.elements.set(internalName, prototype); - this.checkGlobalAlias(prototype, declaration); + this.checkInternalDecorators(prototype, declaration); if (namespace) { if (namespace.members) { @@ -837,7 +857,7 @@ export class Program extends DiagnosticEmitter { namespace = new Namespace(this, declaration.name.name, internalName, declaration); namespace.namespace = parentNamespace; this.elements.set(internalName, namespace); - this.checkGlobalAlias(namespace, declaration); + this.checkInternalDecorators(namespace, declaration); } if (parentNamespace) { @@ -922,13 +942,7 @@ export class Program extends DiagnosticEmitter { global.namespace = namespace; this.elements.set(internalName, global); - // differs a bit from this.checkGlobalAlias in that it checks the statement's parent - if (hasDecorator("global", declaration.decorators) || (declaration.range.source.isStdlib && assert(statement.parent).kind == NodeKind.SOURCE && global.is(ElementFlags.EXPORTED))) { - if (this.elements.has(declaration.name.name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName); - else - this.elements.set(declaration.name.name, global); - } + this.checkInternalDecorators(global, declaration); if (namespace) { if (namespace.members) { @@ -1202,6 +1216,18 @@ export class Program extends DiagnosticEmitter { case NodeKind.ELEMENTACCESS: return this.resolveElementAccess(expression, contextualFunction); + + case NodeKind.CALL: + var resolved = this.resolveExpression((expression).expression, contextualFunction); + if (resolved) { + var element = resolved.element; + if (element && element.kind == ElementKind.FUNCTION_PROTOTYPE) { + var instance = (element).resolveInclTypeArguments((expression).typeArguments, null, expression); + if (instance && instance.returnType.classType) + return (resolvedElement || (resolvedElement = new ResolvedElement())).set(instance.returnType.classType); + } + } + break; } this.error(DiagnosticCode.Operation_not_supported, expression.range); return null; @@ -1356,12 +1382,13 @@ export class Namespace extends Element { kind = ElementKind.NAMESPACE; /** Declaration reference. */ - declaration: NamespaceDeclaration | null; // more specific + declaration: NamespaceDeclaration; // more specific /** Constructs a new namespace. */ - constructor(program: Program, simpleName: string, internalName: string, declaration: NamespaceDeclaration | null = null) { + constructor(program: Program, simpleName: string, internalName: string, declaration: NamespaceDeclaration) { super(program, simpleName, internalName); - if ((this.declaration = declaration) && this.declaration.modifiers) { + this.declaration = declaration; + if (this.declaration.modifiers) { for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { switch (this.declaration.modifiers[i].modifierKind) { case ModifierKind.IMPORT: this.set(ElementFlags.IMPORTED); break; @@ -1380,12 +1407,13 @@ export class Enum extends Element { kind = ElementKind.ENUM; /** Declaration reference. */ - declaration: EnumDeclaration | null; + declaration: EnumDeclaration; /** Constructs a new enum. */ - constructor(program: Program, simpleName: string, internalName: string, declaration: EnumDeclaration | null = null) { + constructor(program: Program, simpleName: string, internalName: string, declaration: EnumDeclaration) { super(program, simpleName, internalName); - if ((this.declaration = declaration) && this.declaration.modifiers) { + this.declaration = declaration; + if (this.declaration.modifiers) { for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { switch (this.declaration.modifiers[i].modifierKind) { case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; @@ -1405,13 +1433,13 @@ export class EnumValue extends Element { kind = ElementKind.ENUMVALUE; /** Declaration reference. */ - declaration: EnumValueDeclaration | null; + declaration: EnumValueDeclaration; /** Parent enum. */ enum: Enum; /** Constant value, if applicable. */ constantValue: i32 = 0; - constructor(enm: Enum, program: Program, simpleName: string, internalName: string, declaration: EnumValueDeclaration | null = null) { + constructor(enm: Enum, program: Program, simpleName: string, internalName: string, declaration: EnumValueDeclaration) { super(program, simpleName, internalName); this.enum = enm; this.declaration = declaration; @@ -1423,7 +1451,7 @@ export class VariableLikeElement extends Element { // kind varies /** Declaration reference. */ - declaration: VariableLikeDeclarationStatement | null; + declaration: VariableLikeDeclarationStatement; /** Variable type. Is {@link Type.void} for type-inferred {@link Global}s before compilation. */ type: Type; /** Constant integer value, if applicable. */ @@ -1449,28 +1477,25 @@ export class Global extends VariableLikeElement { kind = ElementKind.GLOBAL; - constructor(program: Program, simpleName: string, internalName: string, declaration: VariableLikeDeclarationStatement | null = null, type: Type) { + constructor(program: Program, simpleName: string, internalName: string, declaration: VariableLikeDeclarationStatement, type: Type) { super(program, simpleName, internalName); - if (this.declaration = declaration) { - if (this.declaration.modifiers) { - for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { - switch (this.declaration.modifiers[i].modifierKind) { - case ModifierKind.IMPORT: this.set(ElementFlags.IMPORTED); break; - case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; - case ModifierKind.CONST: this.set(ElementFlags.CONSTANT); break; - case ModifierKind.LET: this.set(ElementFlags.SCOPED); break; - case ModifierKind.DECLARE: this.set(ElementFlags.DECLARED); break; - case ModifierKind.READONLY: this.set(this.declaration.initializer ? ElementFlags.CONSTANT | ElementFlags.READONLY : ElementFlags.READONLY); break; - case ModifierKind.PUBLIC: - case ModifierKind.PRIVATE: - case ModifierKind.PROTECTED: - case ModifierKind.STATIC: break; // static fields become globals - default: throw new Error("unexpected modifier"); - } + this.declaration = declaration; + if (this.declaration.modifiers) { + for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { + switch (this.declaration.modifiers[i].modifierKind) { + case ModifierKind.IMPORT: this.set(ElementFlags.IMPORTED); break; + case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; + case ModifierKind.CONST: this.set(ElementFlags.CONSTANT); break; + case ModifierKind.LET: this.set(ElementFlags.SCOPED); break; + case ModifierKind.DECLARE: this.set(ElementFlags.DECLARED); break; + case ModifierKind.READONLY: this.set(this.declaration.initializer ? ElementFlags.CONSTANT | ElementFlags.READONLY : ElementFlags.READONLY); break; + case ModifierKind.PUBLIC: + case ModifierKind.PRIVATE: + case ModifierKind.PROTECTED: + case ModifierKind.STATIC: break; // static fields become globals + default: throw new Error("unexpected modifier"); } } - } else { - this.set(ElementFlags.CONSTANT | ElementFlags.INLINED); // built-ins have constant values } this.type = type; // resolved later if `void` } @@ -1482,14 +1507,14 @@ export class Parameter { // not an Element on its own /** Parameter name. */ - name: string | null; + name: string; /** Parameter type. */ type: Type; /** Parameter initializer. */ initializer: Expression | null; /** Constructs a new function parameter. */ - constructor(name: string | null, type: Type, initializer: Expression | null = null) { + constructor(name: string, type: Type, initializer: Expression | null = null) { this.name = name; this.type = type; this.initializer = initializer; @@ -1517,7 +1542,7 @@ export class FunctionPrototype extends Element { kind = ElementKind.FUNCTION_PROTOTYPE; /** Declaration reference. */ - declaration: FunctionDeclaration | null; + declaration: FunctionDeclaration; /** If an instance method, the class prototype reference. */ classPrototype: ClassPrototype | null; /** Resolved instances. */ @@ -1526,28 +1551,27 @@ export class FunctionPrototype extends Element { classTypeArguments: Type[] | null = null; /** Constructs a new function prototype. */ - constructor(program: Program, simpleName: string, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) { + constructor(program: Program, simpleName: string, internalName: string, declaration: FunctionDeclaration, classPrototype: ClassPrototype | null = null) { super(program, simpleName, internalName); - if (this.declaration = declaration) { - if (this.declaration.modifiers) - for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { - switch (this.declaration.modifiers[i].modifierKind) { - case ModifierKind.IMPORT: this.set(ElementFlags.IMPORTED); break; - case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; - case ModifierKind.DECLARE: this.set(ElementFlags.DECLARED); break; - case ModifierKind.GET: this.set(ElementFlags.GETTER); break; - case ModifierKind.SET: this.set(ElementFlags.SETTER); break; - case ModifierKind.STATIC: - case ModifierKind.ABSTRACT: - case ModifierKind.PRIVATE: - case ModifierKind.PROTECTED: - case ModifierKind.PUBLIC: break; // already handled - default: throw new Error("unexpected modifier"); - } + this.declaration = declaration; + if (this.declaration.modifiers) + for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { + switch (this.declaration.modifiers[i].modifierKind) { + case ModifierKind.IMPORT: this.set(ElementFlags.IMPORTED); break; + case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; + case ModifierKind.DECLARE: this.set(ElementFlags.DECLARED); break; + case ModifierKind.GET: this.set(ElementFlags.GETTER); break; + case ModifierKind.SET: this.set(ElementFlags.SETTER); break; + case ModifierKind.STATIC: + case ModifierKind.ABSTRACT: + case ModifierKind.PRIVATE: + case ModifierKind.PROTECTED: + case ModifierKind.PUBLIC: break; // already handled + default: throw new Error("unexpected modifier"); } - if (this.declaration.typeParameters.length) - this.set(ElementFlags.GENERIC); - } + } + if (this.declaration.typeParameters.length) + this.set(ElementFlags.GENERIC); if (this.classPrototype = classPrototype) this.set(ElementFlags.INSTANCE); } @@ -1559,8 +1583,6 @@ export class FunctionPrototype extends Element { return instance; var declaration = this.declaration; - if (!declaration) - throw new Error("cannot resolve built-ins"); // inherit contextual type arguments var inheritedTypeArguments = contextualTypeArguments; @@ -1576,8 +1598,6 @@ export class FunctionPrototype extends Element { if (!this.classPrototype) throw new Error("partially resolved instance method must reference its class prototype"); var classDeclaration = (this.classPrototype).declaration; - if (!classDeclaration) - throw new Error("cannot resolve built-ins"); var classTypeParameters = classDeclaration.typeParameters; if ((k = this.classTypeArguments.length) != classTypeParameters.length) throw new Error("type argument count mismatch"); @@ -1605,7 +1625,7 @@ export class FunctionPrototype extends Element { typeNode = assert(parameterDeclaration.type); var parameterType = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports if (parameterType) { - parameters[i] = new Parameter(parameterDeclaration.name ? parameterDeclaration.name.name : null, parameterType, parameterDeclaration.initializer); + parameters[i] = new Parameter(parameterDeclaration.name.name, parameterType, parameterDeclaration.initializer); parameterTypes[i] = parameterType; } else return null; @@ -1641,13 +1661,11 @@ export class FunctionPrototype extends Element { return instance; } - resolveInclTypeArguments(typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map | null, alternativeReportNode: Node | null): Function | null { + resolveInclTypeArguments(typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map | null, reportNode: Node): Function | null { var resolvedTypeArguments: Type[] | null = null; if (this.is(ElementFlags.GENERIC)) { assert(typeArgumentNodes != null && typeArgumentNodes.length != 0); - if (!this.declaration) - throw new Error("cannot resolve built-ins"); - resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode); + resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, reportNode); if (!resolvedTypeArguments) return null; } @@ -1709,7 +1727,7 @@ export class Function extends Element { this.returnType = returnType; this.instanceMethodOf = instanceMethodOf; this.flags = prototype.flags; - if (!prototype.is(ElementFlags.BUILTIN | ElementFlags.DECLARED)) { + if (!(prototype.is(ElementFlags.BUILTIN) || prototype.is(ElementFlags.DECLARED))) { var localIndex = 0; if (instanceMethodOf) { assert(this.is(ElementFlags.INSTANCE)); // internal error @@ -1851,15 +1869,16 @@ export class FieldPrototype extends Element { kind = ElementKind.FIELD_PROTOTYPE; /** Declaration reference. */ - declaration: FieldDeclaration | null; + declaration: FieldDeclaration; /** Parent class prototype. */ classPrototype: ClassPrototype; /** Constructs a new field prototype. */ - constructor(classPrototype: ClassPrototype, simpleName: string, internalName: string, declaration: FieldDeclaration | null = null) { + constructor(classPrototype: ClassPrototype, simpleName: string, internalName: string, declaration: FieldDeclaration) { super(classPrototype.program, simpleName, internalName); this.classPrototype = classPrototype; - if ((this.declaration = declaration) && this.declaration.modifiers) { + this.declaration = declaration; + if (this.declaration.modifiers) { for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { switch (this.declaration.modifiers[i].modifierKind) { case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; @@ -1923,7 +1942,7 @@ export class ClassPrototype extends Element { kind = ElementKind.CLASS_PROTOTYPE; /** Declaration reference. */ - declaration: ClassDeclaration | null; + declaration: ClassDeclaration; /** Resolved instances. */ instances: Map = new Map(); /** Instance member prototypes. */ @@ -1942,22 +1961,21 @@ export class ClassPrototype extends Element { /** Overloaded equality comparison method, if any. */ fnEquals: string | null = null; - constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration | null = null) { + constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration) { super(program, simpleName, internalName); - if (this.declaration = declaration) { - if (this.declaration.modifiers) { - for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { - switch (this.declaration.modifiers[i].modifierKind) { - case ModifierKind.IMPORT: this.set(ElementFlags.IMPORTED); break; - case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; - case ModifierKind.DECLARE: this.set(ElementFlags.DECLARED); break; - default: throw new Error("unexpected modifier"); - } + this.declaration = declaration; + if (this.declaration.modifiers) { + for (var i = 0, k = this.declaration.modifiers.length; i < k; ++i) { + switch (this.declaration.modifiers[i].modifierKind) { + case ModifierKind.IMPORT: this.set(ElementFlags.IMPORTED); break; + case ModifierKind.EXPORT: this.set(ElementFlags.EXPORTED); break; + case ModifierKind.DECLARE: this.set(ElementFlags.DECLARED); break; + default: throw new Error("unexpected modifier"); } } - if (this.declaration.typeParameters.length) - this.set(ElementFlags.GENERIC); } + if (this.declaration.typeParameters.length) + this.set(ElementFlags.GENERIC); } /** Whether an unamanaged class or not. */ @@ -1970,10 +1988,6 @@ export class ClassPrototype extends Element { if (instance) return instance; - var declaration = this.declaration; - if (!declaration) - throw new Error("cannot resolve built-ins"); - // inherit contextual type arguments var inheritedTypeArguments = contextualTypeArguments; contextualTypeArguments = new Map(); @@ -1981,6 +1995,7 @@ export class ClassPrototype extends Element { for (var [inheritedName, inheritedType] of inheritedTypeArguments) contextualTypeArguments.set(inheritedName, inheritedType); + var declaration = this.declaration; var baseClass: Class | null = null; if (declaration.extendsType) { var baseClassType = this.program.resolveType(declaration.extendsType, null); // reports @@ -2039,8 +2054,6 @@ export class ClassPrototype extends Element { if (!instance.members) instance.members = new Map(); var fieldDeclaration = (member).declaration; - if (!fieldDeclaration) - throw new Error("cannot resolve built-ins"); 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 @@ -2091,8 +2104,6 @@ export class ClassPrototype extends Element { var resolvedTypeArguments: Type[] | null = null; if (this.is(ElementFlags.GENERIC)) { assert(typeArgumentNodes != null && typeArgumentNodes.length != 0); - if (!this.declaration) - throw new Error("cannot resolve built-ins"); resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode); if (!resolvedTypeArguments) return null; @@ -2171,10 +2182,10 @@ export class InterfacePrototype extends ClassPrototype { kind = ElementKind.INTERFACE_PROTOTYPE; /** Declaration reference. */ - declaration: InterfaceDeclaration | null; // more specific + declaration: InterfaceDeclaration; // more specific /** Constructs a new interface prototype. */ - constructor(program: Program, simpleName: string, internalName: string, declaration: InterfaceDeclaration | null = null) { + constructor(program: Program, simpleName: string, internalName: string, declaration: InterfaceDeclaration) { super(program, simpleName, internalName, declaration); } } diff --git a/std/assembly.d.ts b/std/assembly.d.ts index 63900de6..f1ca8b01 100644 --- a/std/assembly.d.ts +++ b/std/assembly.d.ts @@ -297,3 +297,6 @@ declare function unmanaged(target: Function): any; /** Annotates a class field with an explicit offset. */ declare function offset(offset: usize): any; + +/** Annotates an element as begin built-in. */ +declare function builtin(target: Function): any; diff --git a/std/assembly/builtins.ts b/std/assembly/builtins.ts new file mode 100644 index 00000000..1df888c9 --- /dev/null +++ b/std/assembly/builtins.ts @@ -0,0 +1,208 @@ +@builtin +export declare const NaN: f64; // | f32 + +@builtin +export declare const Infinity: f64; // | f32 + +@builtin +export declare function isNaN(value: T): bool; + +@builtin +export declare function isFinite(value: T): bool; + +@builtin +export declare function clz(value: T): T; + +@builtin +export declare function ctz(value: T): T; + +@builtin +export declare function popcnt(value: T): T; + +@builtin +export declare function rotl(value: T, shift: T): T; + +@builtin +export declare function rotr(value: T, shift: T): T; + +@builtin +export declare function abs(value: T): T; + +@builtin +export declare function max(left: T, right: T): T; + +@builtin +export declare function min(left: T, right: T): T; + +@builtin +export declare function ceil(value: T): T; + +@builtin +export declare function floor(value: T): T; + +@builtin +export declare function copysign(left: T, right: T): T; + +@builtin +export declare function nearest(left: T, right: T): T; + +@builtin +export declare function reinterpret(value: void): T; + +@builtin +export declare function sqrt(value: T): T; + +@builtin +export declare function trunc(value: T): T; + +@builtin +export declare function load(offset: usize, constantOffset?: usize): T; + +@builtin +export declare function store(offset: usize, value: void, constantOffset?: usize): T; + +@builtin +export declare function sizeof(): usize; + +@builtin +export declare function select(ifTrue: T, ifFalse: T, condition: bool): T; + +@builtin +export declare function unreachable(): void; + +@builtin +export declare function current_memory(): i32; + +@builtin +export declare function grow_memory(pages: i32): i32; + +// @builtin +// export declare function move_memory(dest: usize, src: usize: n: usize): void; + +// @builtin +// export declare function set_memory(dest: usize, value: u32, n: usize): void; + +@builtin +export declare function changetype(value: void): T; + +@builtin +export declare function assert(isTrueish: T, message?: string): T; + +@builtin +export declare function abort(message?: string | null, fileName?: string | null, lineNumber?: u32, columnNumber?: u32): void; + +@builtin +declare function i8(value: void): i8; +namespace i8 { + export const MIN_VALUE: i8 = -128; + export const MAX_VALUE: i8 = 127; +} +export { i8 }; + +@builtin +declare function i16(value: void): i16; +namespace i16 { + export const MIN_VALUE: i16 = -32768; + export const MAX_VALUE: i16 = 32767; +} +export { i16 }; + +@builtin +declare function i32(value: void): i32; +namespace i32 { + export const MIN_VALUE: i32 = -2147483648; + export const MAX_VALUE: i32 = 2147483647; +} +export { i32 }; + +@builtin +declare function i64(value: void): i64; +namespace i64 { + export const MIN_VALUE: i64 = -9223372036854775808; + export const MAX_VALUE: i64 = 9223372036854775807; +} +export { i64 }; + +@builtin +declare function isize(value: void): isize; +namespace isize { + export const MIN_VALUE: isize = sizeof() == sizeof() ? -2147483648 : -9223372036854775808; + export const MAX_VALUE: isize = sizeof() == sizeof() ? 2147483647 : 9223372036854775807; +} +export { isize }; + +@builtin +declare function u8(value: void): u8; +namespace u8 { + export const MIN_VALUE: u8 = 0; + export const MAX_VALUE: u8 = 255; +} +export { u8 }; + +@builtin +declare function u16(value: void): u16; +namespace u16 { + export const MIN_VALUE: u16 = 0; + export const MAX_VALUE: u16 = 65535; +} +export { u16 }; + +@builtin +declare function u32(value: void): u32; +namespace u32 { + export const MIN_VALUE: u32 = 0; + export const MAX_VALUE: u32 = 4294967295; +} +export { u32 }; + +@builtin +declare function u64(value: void): u64; +namespace u64 { + export const MIN_VALUE: u64 = 0; + export const MAX_VALUE: u64 = 18446744073709551615; +} +export { u64 }; + +@builtin +declare function usize(value: void): usize; +namespace usize { + export const MIN_VALUE: usize = 0; + export const MAX_VALUE: usize = sizeof() == sizeof() ? 4294967295 : 18446744073709551615; +} +export { usize }; + +@builtin +declare function bool(value: void): bool; +namespace bool { + export const MIN_VALUE: bool = 0; + export const MAX_VALUE: bool = 1; +} +export { bool }; + +@builtin +declare function f32(value: void): f32; +namespace f32 { + export const MIN_VALUE: f32 = -3.40282347e+38; + export const MAX_VALUE: f32 = 3.40282347e+38; + export const MIN_SAFE_INTEGER: f32 = -16777215; + export const MAX_SAFE_INTEGER: f32 = 16777215; + export const EPSILON: f32 = 1.19209290e-07; +} +export { f32 }; + +@builtin +declare function f64(value: void): f64; +namespace f64 { + export const MIN_VALUE: f64 = -1.7976931348623157e+308; + export const MAX_VALUE: f64 = 1.7976931348623157e+308; + export const MIN_SAFE_INTEGER: f64 = -9007199254740991; + export const MAX_SAFE_INTEGER: f64 = 9007199254740991; + export const EPSILON: f64 = 2.2204460492503131e-16; +} +export{ f64 }; + +@builtin +export declare const HEAP_BASE: usize; + +@builtin +export declare function start(): void; diff --git a/std/portable.d.ts b/std/portable.d.ts index 3fe2a859..5b97eb40 100644 --- a/std/portable.d.ts +++ b/std/portable.d.ts @@ -169,6 +169,8 @@ declare class Uint32Array extends Array {} declare class Int8Array extends Array {} declare class Int16Array extends Array {} declare class Int32Array extends Array {} +declare class Float32Array extends Array {} +declare class Float64Array extends Array {} declare class String { static fromCharCode(ls: i32, hs?: i32): string; diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index 1b6095c1..b0deb9ee 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -2,6 +2,9 @@ (type $iiiiv (func (param i32 i32 i32 i32))) (type $i (func (result i32))) (type $v (func)) + (type $I (func (result i64))) + (type $f (func (result f32))) + (type $F (func (result f64))) (import "env" "abort" (func $abort (param i32 i32 i32 i32))) (global $builtins/b (mut i32) (i32.const 0)) (global $builtins/i (mut i32) (i32.const 0)) @@ -12,34 +15,6 @@ (global $builtins/u (mut i32) (i32.const 0)) (global $builtins/U (mut i64) (i64.const 0)) (global $builtins/s (mut i32) (i32.const 0)) - (global $i8.MIN_VALUE i32 (i32.const -128)) - (global $i8.MAX_VALUE i32 (i32.const 127)) - (global $i16.MIN_VALUE i32 (i32.const -32768)) - (global $i16.MAX_VALUE i32 (i32.const 32767)) - (global $i32.MIN_VALUE i32 (i32.const -2147483648)) - (global $i32.MAX_VALUE i32 (i32.const 2147483647)) - (global $i64.MIN_VALUE i64 (i64.const -9223372036854775808)) - (global $i64.MAX_VALUE i64 (i64.const 9223372036854775807)) - (global $u8.MIN_VALUE i32 (i32.const 0)) - (global $u8.MAX_VALUE i32 (i32.const 255)) - (global $u16.MIN_VALUE i32 (i32.const 0)) - (global $u16.MAX_VALUE i32 (i32.const 65535)) - (global $u32.MIN_VALUE i32 (i32.const 0)) - (global $u32.MAX_VALUE i32 (i32.const -1)) - (global $u64.MIN_VALUE i64 (i64.const 0)) - (global $u64.MAX_VALUE i64 (i64.const -1)) - (global $bool.MIN_VALUE i32 (i32.const 0)) - (global $bool.MAX_VALUE i32 (i32.const 1)) - (global $f32.MIN_VALUE f32 (f32.const -3402823466385288598117041e14)) - (global $f32.MAX_VALUE f32 (f32.const 3402823466385288598117041e14)) - (global $f32.MIN_SAFE_INTEGER f32 (f32.const -16777215)) - (global $f32.MAX_SAFE_INTEGER f32 (f32.const 16777215)) - (global $f32.EPSILON f32 (f32.const 1.1920928955078125e-07)) - (global $f64.MIN_VALUE f64 (f64.const -1797693134862315708145274e284)) - (global $f64.MAX_VALUE f64 (f64.const 1797693134862315708145274e284)) - (global $f64.MIN_SAFE_INTEGER f64 (f64.const -9007199254740991)) - (global $f64.MAX_SAFE_INTEGER f64 (f64.const 9007199254740991)) - (global $f64.EPSILON f64 (f64.const 2.220446049250313e-16)) (global $HEAP_BASE i32 (i32.const 36)) (memory $0 1) (data (i32.const 8) "\0b\00\00\00b\00u\00i\00l\00t\00i\00n\00s\00.\00t\00s\00") diff --git a/tests/compiler/fmod.wast b/tests/compiler/fmod.wast index e9bcedeb..bcefce5b 100644 --- a/tests/compiler/fmod.wast +++ b/tests/compiler/fmod.wast @@ -4,8 +4,6 @@ (type $fff (func (param f32 f32) (result f32))) (type $v (func)) (import "env" "abort" (func $abort (param i32 i32 i32 i32))) - (global $f64.EPSILON f64 (f64.const 2.220446049250313e-16)) - (global $f32.EPSILON f32 (f32.const 1.1920928955078125e-07)) (global $HEAP_BASE i32 (i32.const 28)) (memory $0 1) (data (i32.const 8) "\07\00\00\00f\00m\00o\00d\00.\00t\00s\00") diff --git a/tests/compiler/limits.wast b/tests/compiler/limits.wast index bfbc96e8..b581bc23 100644 --- a/tests/compiler/limits.wast +++ b/tests/compiler/limits.wast @@ -1,27 +1,9 @@ (module + (type $i (func (result i32))) + (type $I (func (result i64))) + (type $f (func (result f32))) + (type $F (func (result f64))) (type $v (func)) - (global $i8.MIN_VALUE i32 (i32.const -128)) - (global $i8.MAX_VALUE i32 (i32.const 127)) - (global $i16.MIN_VALUE i32 (i32.const -32768)) - (global $i16.MAX_VALUE i32 (i32.const 32767)) - (global $i32.MIN_VALUE i32 (i32.const -2147483648)) - (global $i32.MAX_VALUE i32 (i32.const 2147483647)) - (global $i64.MIN_VALUE i64 (i64.const -9223372036854775808)) - (global $i64.MAX_VALUE i64 (i64.const 9223372036854775807)) - (global $u8.MIN_VALUE i32 (i32.const 0)) - (global $u8.MAX_VALUE i32 (i32.const 255)) - (global $u16.MIN_VALUE i32 (i32.const 0)) - (global $u16.MAX_VALUE i32 (i32.const 65535)) - (global $u32.MIN_VALUE i32 (i32.const 0)) - (global $u32.MAX_VALUE i32 (i32.const -1)) - (global $u64.MIN_VALUE i64 (i64.const 0)) - (global $u64.MAX_VALUE i64 (i64.const -1)) - (global $bool.MIN_VALUE i32 (i32.const 0)) - (global $bool.MAX_VALUE i32 (i32.const 1)) - (global $f32.MIN_SAFE_INTEGER f32 (f32.const -16777215)) - (global $f32.MAX_SAFE_INTEGER f32 (f32.const 16777215)) - (global $f64.MIN_SAFE_INTEGER f64 (f64.const -9007199254740991)) - (global $f64.MAX_SAFE_INTEGER f64 (f64.const 9007199254740991)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) (export "memory" (memory $0)) diff --git a/tests/compiler/retain-i32.wast b/tests/compiler/retain-i32.wast index 47f64a1e..c3a2b20b 100644 --- a/tests/compiler/retain-i32.wast +++ b/tests/compiler/retain-i32.wast @@ -1,17 +1,9 @@ (module (type $iiiiv (func (param i32 i32 i32 i32))) (type $iiv (func (param i32 i32))) + (type $i (func (result i32))) (type $v (func)) (import "env" "abort" (func $abort (param i32 i32 i32 i32))) - (global $i8.MAX_VALUE i32 (i32.const 127)) - (global $i8.MIN_VALUE i32 (i32.const -128)) - (global $u8.MAX_VALUE i32 (i32.const 255)) - (global $i16.MIN_VALUE i32 (i32.const -32768)) - (global $i16.MAX_VALUE i32 (i32.const 32767)) - (global $u16.MAX_VALUE i32 (i32.const 65535)) - (global $i32.MAX_VALUE i32 (i32.const 2147483647)) - (global $i32.MIN_VALUE i32 (i32.const -2147483648)) - (global $u32.MAX_VALUE i32 (i32.const -1)) (global $retain-i32/si (mut i32) (i32.const 0)) (global $retain-i32/ui (mut i32) (i32.const 0)) (global $HEAP_BASE i32 (i32.const 40)) diff --git a/tests/compiler/showcase.wast b/tests/compiler/showcase.wast index e4b36361..c71e57d8 100644 --- a/tests/compiler/showcase.wast +++ b/tests/compiler/showcase.wast @@ -1,6 +1,9 @@ (module (type $iiiiv (func (param i32 i32 i32 i32))) (type $i (func (result i32))) + (type $I (func (result i64))) + (type $f (func (result f32))) + (type $F (func (result f64))) (type $ii (func (param i32) (result i32))) (type $iii (func (param i32 i32) (result i32))) (type $fff (func (param f32 f32) (result f32))) @@ -39,34 +42,6 @@ (global $builtins/u (mut i32) (i32.const 0)) (global $builtins/U (mut i64) (i64.const 0)) (global $builtins/s (mut i32) (i32.const 0)) - (global $i8.MIN_VALUE i32 (i32.const -128)) - (global $i8.MAX_VALUE i32 (i32.const 127)) - (global $i16.MIN_VALUE i32 (i32.const -32768)) - (global $i16.MAX_VALUE i32 (i32.const 32767)) - (global $i32.MIN_VALUE i32 (i32.const -2147483648)) - (global $i32.MAX_VALUE i32 (i32.const 2147483647)) - (global $i64.MIN_VALUE i64 (i64.const -9223372036854775808)) - (global $i64.MAX_VALUE i64 (i64.const 9223372036854775807)) - (global $u8.MIN_VALUE i32 (i32.const 0)) - (global $u8.MAX_VALUE i32 (i32.const 255)) - (global $u16.MIN_VALUE i32 (i32.const 0)) - (global $u16.MAX_VALUE i32 (i32.const 65535)) - (global $u32.MIN_VALUE i32 (i32.const 0)) - (global $u32.MAX_VALUE i32 (i32.const -1)) - (global $u64.MIN_VALUE i64 (i64.const 0)) - (global $u64.MAX_VALUE i64 (i64.const -1)) - (global $bool.MIN_VALUE i32 (i32.const 0)) - (global $bool.MAX_VALUE i32 (i32.const 1)) - (global $f32.MIN_VALUE f32 (f32.const -3402823466385288598117041e14)) - (global $f32.MAX_VALUE f32 (f32.const 3402823466385288598117041e14)) - (global $f32.MIN_SAFE_INTEGER f32 (f32.const -16777215)) - (global $f32.MAX_SAFE_INTEGER f32 (f32.const 16777215)) - (global $f32.EPSILON f32 (f32.const 1.1920928955078125e-07)) - (global $f64.MIN_VALUE f64 (f64.const -1797693134862315708145274e284)) - (global $f64.MAX_VALUE f64 (f64.const 1797693134862315708145274e284)) - (global $f64.MIN_SAFE_INTEGER f64 (f64.const -9007199254740991)) - (global $f64.MAX_SAFE_INTEGER f64 (f64.const 9007199254740991)) - (global $f64.EPSILON f64 (f64.const 2.220446049250313e-16)) (global $showcase/ANamespace.aNamespacedGlobal (mut i32) (i32.const 42)) (global $showcase/AnEnum.ONE i32 (i32.const 1)) (global $showcase/AnEnum.TWO i32 (i32.const 2)) diff --git a/tests/parser/parameter-order.ts b/tests/parser/parameter-order.ts new file mode 100644 index 00000000..e5ec7571 --- /dev/null +++ b/tests/parser/parameter-order.ts @@ -0,0 +1,6 @@ +function restValid(a: i32, ...b: i32[]): void {} +function restParameterMustBeLast(...a: i32[], b: i32): void {} + +function optionalValid(a: i32, b?: i32): void {} +function optionalCannotPrecedeRequired(a?: i32, b: i32): void {} +function optionalWithInitializerCannotPrecedeRequired(a: i32 = 1, b: i32): void {} diff --git a/tests/parser/parameter-order.ts.fixture.ts b/tests/parser/parameter-order.ts.fixture.ts new file mode 100644 index 00000000..0dd2a73f --- /dev/null +++ b/tests/parser/parameter-order.ts.fixture.ts @@ -0,0 +1,13 @@ +function restValid(a: i32, ...b: Array): void { +} +function restParameterMustBeLast(...a: Array, b: i32): void { +} +function optionalValid(a: i32, b?: i32): void { +} +function optionalCannotPrecedeRequired(a?: i32, b: i32): void { +} +function optionalWithInitializerCannotPrecedeRequired(a: i32 = 1, b: i32): void { +} +// ERROR 1014: "A rest parameter must be last in a parameter list." in parameter-order.ts @ 85,86 +// ERROR 1016: "A required parameter cannot follow an optional parameter." in parameter-order.ts @ 210,211 +// ERROR 1016: "A required parameter cannot follow an optional parameter." in parameter-order.ts @ 293,294