From b9edfb5185ef58133968cb284d6ab46e3913194e Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Sat, 2 Dec 2017 18:37:59 +0100 Subject: [PATCH] More tests and fixes (unary, binary, globals) --- src/ast.ts | 5 +- src/compiler.ts | 172 +++--- src/diagnosticMessages.generated.ts | 2 + src/diagnosticMessages.json | 1 + src/parser.ts | 6 +- src/program.ts | 2 +- tests/compiler.ts | 9 +- tests/compiler/binary.ts | 161 ++++++ tests/compiler/binary.wast | 847 ++++++++++++++++++++++++++++ tests/compiler/do.ts | 14 + tests/compiler/do.wast | 79 +++ tests/compiler/export.wast | 4 +- tests/compiler/if.ts | 22 + tests/compiler/if.wast | 71 +++ tests/compiler/import.wast | 4 +- tests/compiler/literals.ts | 46 ++ tests/compiler/literals.wast | 168 ++++++ tests/compiler/unary.ts | 88 +++ tests/compiler/unary.wast | 420 ++++++++++++++ tests/compiler/while.ts | 14 + tests/compiler/while.wast | 88 +++ tests/set_global-immutable.js | 16 + 22 files changed, 2159 insertions(+), 80 deletions(-) create mode 100644 tests/compiler/binary.ts create mode 100644 tests/compiler/binary.wast create mode 100644 tests/compiler/do.ts create mode 100644 tests/compiler/do.wast create mode 100644 tests/compiler/if.ts create mode 100644 tests/compiler/if.wast create mode 100644 tests/compiler/literals.ts create mode 100644 tests/compiler/literals.wast create mode 100644 tests/compiler/unary.ts create mode 100644 tests/compiler/unary.wast create mode 100644 tests/compiler/while.ts create mode 100644 tests/compiler/while.wast create mode 100644 tests/set_global-immutable.js diff --git a/src/ast.ts b/src/ast.ts index f84e4975..607c02f6 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -925,12 +925,13 @@ export abstract class Statement extends Node { return stmt; } - static createVariableDeclaration(identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, range: Range): VariableDeclaration { + static createVariableDeclaration(identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, modifiers: Modifier[] | null, range: Range): VariableDeclaration { const elem: VariableDeclaration = new VariableDeclaration(); elem.range = range; (elem.identifier = identifier).parent = elem; if (elem.type = type) (type).parent = elem; if (elem.initializer = initializer) (initializer).parent = elem; + elem.modifiers = modifiers; return elem; } @@ -1681,7 +1682,7 @@ export class TryStatement extends Statement { export class VariableDeclaration extends VariableLikeDeclarationStatement { kind = NodeKind.VARIABLEDECLARATION; - modifiers = null; + modifiers: Modifier[] | null; serialize(sb: string[]): void { this.identifier.serialize(sb); diff --git a/src/compiler.ts b/src/compiler.ts index 49c2c134..9686ff71 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -90,6 +90,15 @@ export class Options { noTreeShaking: bool = false; } +const enum ConversionKind { + /** No conversion. */ + NONE, + /** Implicit conversion. */ + IMPLICIT, + /** Explicit conversion. */ + EXPLICIT +} + export class Compiler extends DiagnosticEmitter { /** Program reference. */ @@ -141,7 +150,7 @@ export class Compiler extends DiagnosticEmitter { compile(): Module { const program: Program = this.program; - // initialize lookup maps + // initialize lookup maps, builtins, imports, exports, etc. program.initialize(this.options.target); // compile entry file (exactly one, usually) @@ -181,7 +190,7 @@ export class Compiler extends DiagnosticEmitter { heapStartBuffer[7] = (initial.hi >>> 24) as u8; } else { if (!initial.fitsInU32) - throw new Error("memory size overflow"); + throw new Error("static memory size overflows 32 bits"); heapStartBuffer = new Uint8Array(4); heapStartOffset = 4; heapStartBuffer[0] = (initial.lo ) as u8; @@ -299,6 +308,8 @@ export class Compiler extends DiagnosticEmitter { return false; element.type = type; } + if (this.module.noEmit) + return true; const nativeType: NativeType = typeToNativeType(type); let initializer: ExpressionRef; let initializeInStart: bool; @@ -331,10 +342,10 @@ export class Compiler extends DiagnosticEmitter { throw new Error("unexpected missing declaration or constant value"); const internalName: string = element.internalName; if (initializeInStart) { - this.module.addGlobal(internalName, NativeType.I32, true, this.module.createI32(-1)); + this.module.addGlobal(internalName, nativeType, true, this.module.createI32(-1)); this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer)); } else - this.module.addGlobal(internalName, NativeType.I32, element.isMutable, initializer); + this.module.addGlobal(internalName, nativeType, element.isMutable, initializer); return element.isCompiled = true; } @@ -350,7 +361,6 @@ export class Compiler extends DiagnosticEmitter { compileEnum(element: Enum): void { if (element.isCompiled) return; - element.isCompiled = true; let previousInternalName: string | null = null; for (let [key, val] of element.members) { if (val.hasConstantValue) { @@ -381,6 +391,7 @@ export class Compiler extends DiagnosticEmitter { throw new Error("unexpected missing declaration or constant value"); previousInternalName = val.internalName; } + element.isCompiled = true; } // functions @@ -405,8 +416,8 @@ export class Compiler extends DiagnosticEmitter { return; const declaration: FunctionDeclaration | null = instance.template.declaration; - if (!declaration) // TODO: compile builtins - throw new Error("not implemented"); + if (!declaration) + throw new Error("unexpected missing declaration"); if (!declaration.statements) { this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, declaration.identifier.range); @@ -588,7 +599,7 @@ export class Compiler extends DiagnosticEmitter { const previousType: Type = this.currentType; const previousNoEmit: bool = this.module.noEmit; this.module.noEmit = true; - this.compileExpression(expression, contextualType, false); // now performs a dry run + this.compileExpression(expression, contextualType, ConversionKind.NONE); // now performs a dry run const type: Type = this.currentType; this.currentType = previousType; this.module.noEmit = previousNoEmit; @@ -698,7 +709,12 @@ export class Compiler extends DiagnosticEmitter { } compileExpressionStatement(statement: ExpressionStatement): ExpressionRef { - return this.compileExpression(statement.expression, Type.void); + let expr: ExpressionRef = this.compileExpression(statement.expression, Type.void, ConversionKind.NONE); + if (this.currentType != Type.void) { + expr = this.module.createDrop(expr); + this.currentType = Type.void; + } + return expr; } compileForStatement(statement: ForStatement): ExpressionRef { @@ -850,7 +866,7 @@ export class Compiler extends DiagnosticEmitter { // expressions - compileExpression(expression: Expression, contextualType: Type, convert: bool = true): ExpressionRef { + compileExpression(expression: Expression, contextualType: Type, conversionKind: ConversionKind = ConversionKind.IMPLICIT): ExpressionRef { this.currentType = contextualType; let expr: ExpressionRef; @@ -912,19 +928,22 @@ export class Compiler extends DiagnosticEmitter { throw new Error("unexpected expression kind"); } - if (convert && this.currentType != contextualType) { - expr = this.convertExpression(expr, this.currentType, contextualType); + if (conversionKind != ConversionKind.NONE) { + expr = this.convertExpression(expr, this.currentType, contextualType, conversionKind, expression); this.currentType = contextualType; } - return expr; } - convertExpression(expr: ExpressionRef, fromType: Type, toType: Type): ExpressionRef { + convertExpression(expr: ExpressionRef, fromType: Type, toType: Type, conversionKind: ConversionKind, reportNode: Node): ExpressionRef { + if (conversionKind == ConversionKind.NONE) + return expr; // void to any - if (fromType.kind == TypeKind.VOID) + if (fromType.kind == TypeKind.VOID) { + this.error(DiagnosticCode.Operation_not_supported, reportNode.range); throw new Error("unexpected conversion from void"); + } // any to void if (toType.kind == TypeKind.VOID) @@ -1070,16 +1089,17 @@ export class Compiler extends DiagnosticEmitter { } } + if (losesInformation && conversionKind == ConversionKind.IMPLICIT) + this.error(DiagnosticCode.Conversion_from_type_0_to_1_requires_an_explicit_cast, reportNode.range, fromType.toString(), toType.toString()); + return expr; } compileAssertionExpression(expression: AssertionExpression, contextualType: Type): ExpressionRef { const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments); // reports - if (toType && toType != contextualType) { - const expr: ExpressionRef = this.compileExpression(expression.expression, toType, false); - return this.convertExpression(expr, this.currentType, toType); - } - return this.compileExpression(expression.expression, contextualType); + return toType && toType != contextualType + ? this.compileExpression(expression.expression, toType, ConversionKind.EXPLICIT) + : this.compileExpression(expression.expression, contextualType); } compileBinaryExpression(expression: BinaryExpression, contextualType: Type): ExpressionRef { @@ -1091,7 +1111,7 @@ export class Compiler extends DiagnosticEmitter { switch (expression.operator) { case Token.LESSTHAN: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.LtF32 @@ -1104,7 +1124,7 @@ export class Compiler extends DiagnosticEmitter { break; case Token.GREATERTHAN: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.GtF32 @@ -1117,7 +1137,7 @@ export class Compiler extends DiagnosticEmitter { break; case Token.LESSTHAN_EQUALS: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.LeF32 @@ -1130,7 +1150,7 @@ export class Compiler extends DiagnosticEmitter { break; case Token.GREATERTHAN_EQUALS: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.GeF32 @@ -1144,7 +1164,7 @@ export class Compiler extends DiagnosticEmitter { case Token.EQUALS_EQUALS: case Token.EQUALS_EQUALS_EQUALS: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.EqF32 @@ -1162,7 +1182,7 @@ export class Compiler extends DiagnosticEmitter { case Token.PLUS_EQUALS: compound = Token.EQUALS; case Token.PLUS: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.AddF32 @@ -1176,7 +1196,7 @@ export class Compiler extends DiagnosticEmitter { case Token.MINUS_EQUALS: compound = Token.EQUALS; case Token.MINUS: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.SubF32 @@ -1190,7 +1210,7 @@ export class Compiler extends DiagnosticEmitter { case Token.ASTERISK_EQUALS: compound = Token.EQUALS; case Token.ASTERISK: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.MulF32 @@ -1204,7 +1224,7 @@ export class Compiler extends DiagnosticEmitter { case Token.SLASH_EQUALS: compound = Token.EQUALS; case Token.SLASH: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType == Type.f32 ? BinaryOp.DivF32 @@ -1218,7 +1238,7 @@ export class Compiler extends DiagnosticEmitter { case Token.PERCENT_EQUALS: compound = Token.EQUALS; case Token.PERCENT: - left = this.compileExpression(expression.left, contextualType, false); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); if (this.currentType.isAnyFloat) throw new Error("not implemented"); // TODO: internal fmod, possibly simply imported from JS @@ -1230,7 +1250,7 @@ export class Compiler extends DiagnosticEmitter { case Token.LESSTHAN_LESSTHAN_EQUALS: compound = Token.EQUALS; case Token.LESSTHAN_LESSTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.ShlI64 @@ -1240,7 +1260,7 @@ export class Compiler extends DiagnosticEmitter { case Token.GREATERTHAN_GREATERTHAN_EQUALS: compound = Token.EQUALS; case Token.GREATERTHAN_GREATERTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isSignedInteger ? this.currentType.isLongInteger @@ -1254,7 +1274,7 @@ export class Compiler extends DiagnosticEmitter { case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: compound = Token.EQUALS; case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.ShrU64 @@ -1264,7 +1284,7 @@ export class Compiler extends DiagnosticEmitter { case Token.AMPERSAND_EQUALS: compound = Token.EQUALS; case Token.AMPERSAND: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.AndI64 @@ -1274,7 +1294,7 @@ export class Compiler extends DiagnosticEmitter { case Token.BAR_EQUALS: compound = Token.EQUALS; case Token.BAR: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.OrI64 @@ -1284,7 +1304,7 @@ export class Compiler extends DiagnosticEmitter { case Token.CARET_EQUALS: compound = Token.EQUALS; case Token.CARET: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.XorI64 @@ -1316,11 +1336,17 @@ export class Compiler extends DiagnosticEmitter { this.currentType = (element).type; return this.module.createTeeLocal((element).index, valueWithCorrectType); } + this.currentType = Type.void; return this.module.createSetLocal((element).index, valueWithCorrectType); } - if (element.kind == ElementKind.GLOBAL && (element).type) { + if (element.kind == ElementKind.GLOBAL) { + this.compileGlobal(element); + if (!(element).isMutable) + this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, element.internalName); if (tee) { + if (!(element).type) + return this.module.createUnreachable(); const globalNativeType: NativeType = typeToNativeType((element).type); this.currentType = (element).type; return this.module.createBlock(null, [ // teeGlobal @@ -1328,6 +1354,7 @@ export class Compiler extends DiagnosticEmitter { this.module.createGetGlobal((element).internalName, globalNativeType) ], globalNativeType); } + this.currentType = Type.void; return this.module.createSetGlobal((element).internalName, valueWithCorrectType); } @@ -1384,7 +1411,7 @@ export class Compiler extends DiagnosticEmitter { const operands: ExpressionRef[] = new Array(parameterCount); for (let i: i32 = 0; i < parameterCount; ++i) { if (argumentExpressions.length > i) { - operands[i] = this.compileExpression(argumentExpressions[i], parameters[i].type, true); + operands[i] = this.compileExpression(argumentExpressions[i], parameters[i].type); } else { const initializer: Expression | null = parameters[i].initializer; if (initializer) { // omitted, uses initializer @@ -1621,47 +1648,52 @@ export class Compiler extends DiagnosticEmitter { } else if (expression.kind == NodeKind.THIS) { if (this.currentFunction.instanceMethodOf) { this.currentType = this.currentFunction.instanceMethodOf.type; - return this.module.createGetLocal(0, typeToNativeType(this.currentType)); + return this.module.createGetLocal(0, this.options.target == Target.WASM64 ? NativeType.I64 : NativeType.I32); } this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); this.currentType = this.options.target == Target.WASM64 ? Type.u64 : Type.u32; return this.module.createUnreachable(); } - const element: Element | null = this.program.resolveElement(expression, this.currentFunction); // reports - if (!element) { - if (expression.kind == NodeKind.IDENTIFIER) { + if (expression.kind == NodeKind.IDENTIFIER) { - // NaN - if ((expression).name == "NaN") - if (this.currentType.kind == TypeKind.F32) - return this.module.createF32(NaN); - else { - this.currentType = Type.f64; - return this.module.createF64(NaN); - } + // NaN + if ((expression).name == "NaN") + if (this.currentType.kind == TypeKind.F32) + return this.module.createF32(NaN); + else { + this.currentType = Type.f64; + return this.module.createF64(NaN); + } - // Infinity - if ((expression).name == "Infinity") - if (this.currentType.kind == TypeKind.F32) - return this.module.createF32(Infinity); - else { - this.currentType = Type.f64; - return this.module.createF64(Infinity); - } - } - return this.module.createUnreachable(); + // Infinity + if ((expression).name == "Infinity") + if (this.currentType.kind == TypeKind.F32) + return this.module.createF32(Infinity); + else { + this.currentType = Type.f64; + return this.module.createF64(Infinity); + } } + const element: Element | null = this.program.resolveElement(expression, this.currentFunction); // reports + if (!element) + return this.module.createUnreachable(); + // local - if (element.kind == ElementKind.LOCAL) + if (element.kind == ElementKind.LOCAL) { + this.currentType = (element).type; return this.module.createGetLocal((element).index, typeToNativeType(this.currentType = (element).type)); + } // global - if (element.kind == ElementKind.GLOBAL) + if (element.kind == ElementKind.GLOBAL) { + if ((element).type) + this.currentType = (element).type; return this.compileGlobal(element) // reports ? this.module.createGetGlobal((element).internalName, typeToNativeType(this.currentType = (element).type)) : this.module.createUnreachable(); + } // field // if (element.kind == ElementKind.FIELD) @@ -1691,6 +1723,10 @@ export class Compiler extends DiagnosticEmitter { const intValue: I64 = (expression).value; if (contextualType == Type.bool && (intValue.isZero || intValue.isOne)) return this.module.createI32(intValue.isZero ? 0 : 1); + if (contextualType == Type.f64) + return this.module.createF64((intValue.lo) + (intValue.hi) * 0xffffffff); + if (contextualType == Type.f32) + return this.module.createF32((intValue.lo) + (intValue.hi) * 0xffffffff); if (contextualType.isLongInteger) return this.module.createI64(intValue.lo, intValue.hi); if (!intValue.fitsInI32) { @@ -1769,10 +1805,10 @@ export class Compiler extends DiagnosticEmitter { switch (expression.operator) { case Token.PLUS: - return this.compileExpression(operandExpression, contextualType); + return this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); case Token.MINUS: - operand = this.compileExpression(operandExpression, contextualType); + operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); if (this.currentType == Type.f32) op = UnaryOp.NegF32; else if (this.currentType == Type.f64) @@ -1784,7 +1820,7 @@ export class Compiler extends DiagnosticEmitter { break; case Token.PLUS_PLUS: - operand = this.compileExpression(operandExpression, contextualType); + operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); return this.currentType == Type.f32 ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddF32, operand, this.module.createF32(1)), contextualType != Type.void) : this.currentType == Type.f64 @@ -1794,7 +1830,7 @@ export class Compiler extends DiagnosticEmitter { : this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)), contextualType != Type.void); case Token.MINUS_MINUS: - operand = this.compileExpression(operandExpression, contextualType); + operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); return this.currentType == Type.f32 ? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubF32, operand, this.module.createF32(1)), contextualType != Type.void) : this.currentType == Type.f64 @@ -1804,7 +1840,7 @@ export class Compiler extends DiagnosticEmitter { : this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)), contextualType != Type.void); case Token.EXCLAMATION: - operand = this.compileExpression(operandExpression, Type.bool, false); + operand = this.compileExpression(operandExpression, Type.bool, ConversionKind.NONE); if (this.currentType == Type.f32) { this.currentType = Type.bool; return this.module.createBinary(BinaryOp.EqF32, operand, this.module.createF32(0)); @@ -1820,7 +1856,7 @@ export class Compiler extends DiagnosticEmitter { break; case Token.TILDE: - operand = this.compileExpression(operandExpression, contextualType.isAnyFloat ? Type.i64 : contextualType); + operand = this.compileExpression(operandExpression, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); return this.currentType.isLongInteger ? this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1)) : this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1)); diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index ecf9a90c..45b58c30 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -60,6 +60,7 @@ export enum DiagnosticCode { Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391, Duplicate_function_implementation = 2393, Export_declaration_conflicts_with_exported_declaration_of_0 = 2484, + Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540, The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541, Expected_0_arguments_but_got_1 = 2554, Expected_at_least_0_arguments_but_got_1 = 2555, @@ -128,6 +129,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 2391: return "Function implementation is missing or not immediately following the declaration."; case 2393: return "Duplicate function implementation."; case 2484: return "Export declaration conflicts with exported declaration of '{0}'."; + case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property."; case 2541: return "The target of an assignment must be a variable or a property access."; case 2554: return "Expected {0} arguments, but got {1}."; case 2555: return "Expected at least {0} arguments, but got {1}."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index ec3a598d..6a19331c 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -60,6 +60,7 @@ "Function implementation is missing or not immediately following the declaration.": 2391, "Duplicate function implementation.": 2393, "Export declaration conflicts with exported declaration of '{0}'.": 2484, + "Cannot assign to '{0}' because it is a constant or a read-only property.": 2540, "The target of an assignment must be a variable or a property access.": 2541, "Expected {0} arguments, but got {1}.": 2554, "Expected at least {0} arguments, but got {1}.": 2555, diff --git a/src/parser.ts b/src/parser.ts index 1b6b3265..df1a796d 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -350,7 +350,7 @@ export class Parser extends DiagnosticEmitter { const members: VariableDeclaration[] = new Array(); const isDeclare = hasModifier(ModifierKind.DECLARE, modifiers); do { - const member: VariableDeclaration | null = this.parseVariableDeclaration(tn, isDeclare); + const member: VariableDeclaration | null = this.parseVariableDeclaration(tn, isDeclare, modifiers); if (!member) return null; members.push(member); @@ -361,7 +361,7 @@ export class Parser extends DiagnosticEmitter { return ret; } - parseVariableDeclaration(tn: Tokenizer, isDeclare: bool = false): VariableDeclaration | null { + parseVariableDeclaration(tn: Tokenizer, isDeclare: bool = false, parentModifiers: Modifier[]): VariableDeclaration | null { // Identifier (':' Type)? ('=' Expression)? if (!tn.skip(Token.IDENTIFIER)) { this.error(DiagnosticCode.Identifier_expected, tn.range()); @@ -383,7 +383,7 @@ export class Parser extends DiagnosticEmitter { if (!initializer) return null; } - return Statement.createVariableDeclaration(identifier, type, initializer, Range.join(identifier.range, tn.range())); + return Statement.createVariableDeclaration(identifier, type, initializer, parentModifiers, Range.join(identifier.range, tn.range())); } parseEnum(tn: Tokenizer, modifiers: Modifier[]): EnumDeclaration | null { diff --git a/src/program.ts b/src/program.ts index 0f75e378..94a833ae 100644 --- a/src/program.ts +++ b/src/program.ts @@ -764,7 +764,7 @@ export class Global extends Element { get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; } get isGlobalExport(): bool { return this.globalExportName != null; } - get isMutable(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; } + get isMutable(): bool { return this.declaration ? !hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; } } /** A function parameter. */ diff --git a/tests/compiler.ts b/tests/compiler.ts index 0a85f333..a960aa88 100644 --- a/tests/compiler.ts +++ b/tests/compiler.ts @@ -49,9 +49,12 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { const actual = module.toText() + "(;\n[program.elements]\n " + iterate(program.elements.keys()).join("\n ") + "\n[program.exports]\n " + iterate(program.exports.keys()).join("\n ") + "\n;)\n"; const fixture = path.basename(filename, ".ts") + ".wast"; + if (module.validate()) + console.log("Validates"); + if (isCreate) { fs.writeFileSync(__dirname + "/compiler/" + fixture, actual, { encoding: "utf8" }); - console.log("Created\n"); + console.log("Created"); } else { const expected = fs.readFileSync(__dirname + "/compiler/" + fixture, { encoding: "utf8" }); const diffs = diff("compiler/" + fixture, expected, actual); @@ -59,9 +62,11 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { process.exitCode = 1; console.log(diffs); } else { - console.log("No changes\n"); + console.log("No changes"); } } + + console.log(); }); function iterate(it: IterableIterator): T[] { diff --git a/tests/compiler/binary.ts b/tests/compiler/binary.ts new file mode 100644 index 00000000..9a5f872c --- /dev/null +++ b/tests/compiler/binary.ts @@ -0,0 +1,161 @@ +let b: bool = false; + +let i: i32 = 0; + +i < 1; +i > 1; +i <= 1; +i >= 1; +i == 1; +i === 1; +i + 1; +i - 1; +i * 1; +i / 1; +i % 1; +i << 1; +i >> 1; +i >>> 1; +i & 1; +i | 1; +i ^ 1; + +b = i < 1; +b = i > 1; +b = i <= 1; +b = i >= 1; +b = i == 1; +b = i === 1; +i = i + 1; +i = i - 1; +i = i * 1; +i = i / 1; +i = i % 1; +i = i << 1; +i = i >> 1; +i = i >>> 1; +i = i & 1; +i = i | 1; +i = i ^ 1; + +i += 1; +i -= 1; +i *= 1; +i %= 1; +i <<= 1; +i >>= 1; +i >>>= 1; +i &= 1; +i |= 1; +i ^= 1; + +let I: i64 = 0; + +I < 1; +I > 1; +I <= 1; +I >= 1; +I == 1; +I === 1; +I + 1; +I - 1; +I * 1; +I / 1; +I % 1; +I << 1; +I >> 1; +I >>> 1; +I & 1; +I | 1; +I ^ 1; + +b = I < 1; +b = I > 1; +b = I <= 1; +b = I >= 1; +b = I == 1; +b = I === 1; +I = I + 1; +I = I - 1; +I = I * 1; +I = I / 1; +I = I % 1; +I = I << 1; +I = I >> 1; +I = I >>> 1; +I = I & 1; +I = I | 1; +I = I ^ 1; + +I += 1; +I -= 1; +I *= 1; +I %= 1; +I <<= 1; +I >>= 1; +I >>>= 1; +I &= 1; +I |= 1; +I ^= 1; + +let f: f32 = 0; + +f < 1; +f > 1; +f <= 1; +f >= 1; +f == 1; +f === 1; +f + 1; +f - 1; +f * 1; +f / 1; +// f % 1; + +b = f < 1; +b = f > 1; +b = f <= 1; +b = f >= 1; +b = f == 1; +b = f === 1; +f = f + 1; +f = f - 1; +f = f * 1; +f = f / 1; +// f = f % 1; + +f += 1; +f -= 1; +f *= 1; +// f %= 1; + +let F: f64 = 0; + +F < 1; +F > 1; +F <= 1; +F >= 1; +F == 1; +F === 1; +F + 1; +F - 1; +F * 1; +F / 1; +// f % 1; + +b = F < 1; +b = F > 1; +b = F <= 1; +b = F >= 1; +b = F == 1; +b = F === 1; +F = F + 1; +F = F - 1; +F = F * 1; +F = F / 1; +// F = F % 1; + +F += 1; +F -= 1; +F *= 1; +// F %= 1; diff --git a/tests/compiler/binary.wast b/tests/compiler/binary.wast new file mode 100644 index 00000000..e3854376 --- /dev/null +++ b/tests/compiler/binary.wast @@ -0,0 +1,847 @@ +(module + (type $v (func)) + (global $binary/i (mut i32) (i32.const 0)) + (global $binary/b (mut i32) (i32.const -1)) + (global $binary/I (mut i64) (i64.const 0)) + (global $binary/f (mut f32) (f32.const 0)) + (global $binary/F (mut f64) (f64.const 0)) + (memory $0 1) + (data (i32.const 4) "\08\00\00\00") + (export "memory" (memory $0)) + (start $start) + (func $start (; 0 ;) (type $v) + (drop + (i32.lt_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.gt_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.le_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.ge_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.eq + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.eq + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.add + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.sub + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.mul + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.div_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.rem_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.shl + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.shr_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.shr_u + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.and + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.or + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i32.xor + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/b + (i32.const 0) + ) + (set_global $binary/b + (i32.lt_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/b + (i32.gt_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/b + (i32.le_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/b + (i32.ge_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/b + (i32.eq + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/b + (i32.eq + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.add + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.sub + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.mul + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.div_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.rem_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.shl + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.shr_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.shr_u + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.and + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.or + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.xor + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.add + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.sub + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.mul + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.rem_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.shl + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.shr_s + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.shr_u + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.and + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.or + (get_global $binary/i) + (i32.const 1) + ) + ) + (set_global $binary/i + (i32.xor + (get_global $binary/i) + (i32.const 1) + ) + ) + (drop + (i64.lt_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.gt_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.le_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.ge_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.eq + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.eq + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.add + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.sub + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.mul + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.div_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.rem_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.shl + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.shr_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.shr_u + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.and + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.or + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (i64.xor + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/b + (i64.lt_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/b + (i64.gt_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/b + (i64.le_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/b + (i64.ge_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/b + (i64.eq + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/b + (i64.eq + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.add + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.sub + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.mul + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.div_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.rem_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.shl + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.shr_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.shr_u + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.and + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.or + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.xor + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.add + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.sub + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.mul + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.rem_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.shl + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.shr_s + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.shr_u + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.and + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.or + (get_global $binary/I) + (i64.const 1) + ) + ) + (set_global $binary/I + (i64.xor + (get_global $binary/I) + (i64.const 1) + ) + ) + (drop + (f32.lt + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.gt + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.le + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.ge + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.eq + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.eq + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.add + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.sub + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.mul + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f32.div + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/b + (f32.lt + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/b + (f32.gt + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/b + (f32.le + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/b + (f32.ge + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/b + (f32.eq + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/b + (f32.eq + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/f + (f32.add + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/f + (f32.sub + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/f + (f32.mul + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/f + (f32.div + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/f + (f32.add + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/f + (f32.sub + (get_global $binary/f) + (f32.const 1) + ) + ) + (set_global $binary/f + (f32.mul + (get_global $binary/f) + (f32.const 1) + ) + ) + (drop + (f64.lt + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.gt + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.le + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.ge + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.eq + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.eq + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.add + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.sub + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.mul + (get_global $binary/F) + (f64.const 1) + ) + ) + (drop + (f64.div + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/b + (f64.lt + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/b + (f64.gt + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/b + (f64.le + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/b + (f64.ge + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/b + (f64.eq + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/b + (f64.eq + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/F + (f64.add + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/F + (f64.sub + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/F + (f64.mul + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/F + (f64.div + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/F + (f64.add + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/F + (f64.sub + (get_global $binary/F) + (f64.const 1) + ) + ) + (set_global $binary/F + (f64.mul + (get_global $binary/F) + (f64.const 1) + ) + ) + ) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + isNaN + isFinite + binary/b + binary/i + binary/I + binary/f + binary/F +[program.exports] + +;) diff --git a/tests/compiler/do.ts b/tests/compiler/do.ts new file mode 100644 index 00000000..38c9425b --- /dev/null +++ b/tests/compiler/do.ts @@ -0,0 +1,14 @@ +export function loopDo(n: i32): void { + do { + n = n - 1; + } while (n); +} + +export function loopDoInDo(n: i32): void { + do { + n = n - 1; + do { + n = n - 1; + } while (n); + } while (n); +} diff --git a/tests/compiler/do.wast b/tests/compiler/do.wast new file mode 100644 index 00000000..e6236a8c --- /dev/null +++ b/tests/compiler/do.wast @@ -0,0 +1,79 @@ +(module + (type $iv (func (param i32))) + (memory $0 1) + (data (i32.const 4) "\08\00\00\00") + (export "memory" (memory $0)) + (func $do/loopDo (; 0 ;) (type $iv) (param $0 i32) + (block $break$1.1 + (loop $continue$1.1 + (block + (set_local $0 + (i32.sub + (get_local $0) + (i32.const 1) + ) + ) + ) + (br_if $continue$1.1 + (get_local $0) + ) + ) + ) + ) + (func $do/loopDoInDo (; 1 ;) (type $iv) (param $0 i32) + (block $break$1.1 + (loop $continue$1.1 + (block + (set_local $0 + (i32.sub + (get_local $0) + (i32.const 1) + ) + ) + (block $break$1.2 + (loop $continue$1.2 + (block + (set_local $0 + (i32.sub + (get_local $0) + (i32.const 1) + ) + ) + ) + (br_if $continue$1.2 + (get_local $0) + ) + ) + ) + ) + (br_if $continue$1.1 + (get_local $0) + ) + ) + ) + ) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + isNaN + isFinite + do/loopDo + do/loopDoInDo +[program.exports] + do/loopDo + do/loopDoInDo +;) diff --git a/tests/compiler/export.wast b/tests/compiler/export.wast index 6ccef16d..6a2707a5 100644 --- a/tests/compiler/export.wast +++ b/tests/compiler/export.wast @@ -1,7 +1,7 @@ (module (type $iii (func (param i32 i32) (result i32))) - (global $export/a i32 (i32.const 1)) - (global $export/b i32 (i32.const 2)) + (global $export/a (mut i32) (i32.const 1)) + (global $export/b (mut i32) (i32.const 2)) (memory $0 1) (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) diff --git a/tests/compiler/if.ts b/tests/compiler/if.ts new file mode 100644 index 00000000..6606acb1 --- /dev/null +++ b/tests/compiler/if.ts @@ -0,0 +1,22 @@ +export function ifThenElse(n: i32): bool { + if (n) + return true; + else + return false; +} + +export function ifThen(n: i32): bool { + if (n) + return true; + return false; +} + +export function ifThenElseBlock(n: i32): bool { + if (n) { + ; // nop + return true; + } else { + ; // nop + return false; + } +} diff --git a/tests/compiler/if.wast b/tests/compiler/if.wast new file mode 100644 index 00000000..908c6c47 --- /dev/null +++ b/tests/compiler/if.wast @@ -0,0 +1,71 @@ +(module + (type $ii (func (param i32) (result i32))) + (memory $0 1) + (data (i32.const 4) "\08\00\00\00") + (export "memory" (memory $0)) + (func $if/ifThenElse (; 0 ;) (type $ii) (param $0 i32) (result i32) + (if + (get_local $0) + (return + (i32.const 1) + ) + (return + (i32.const 0) + ) + ) + ) + (func $if/ifThen (; 1 ;) (type $ii) (param $0 i32) (result i32) + (if + (get_local $0) + (return + (i32.const 1) + ) + ) + (return + (i32.const 0) + ) + ) + (func $if/ifThenElseBlock (; 2 ;) (type $ii) (param $0 i32) (result i32) + (if + (get_local $0) + (block + (nop) + (return + (i32.const 1) + ) + ) + (block + (nop) + (return + (i32.const 0) + ) + ) + ) + ) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + isNaN + isFinite + if/ifThenElse + if/ifThen + if/ifThenElseBlock +[program.exports] + if/ifThenElse + if/ifThen + if/ifThenElseBlock +;) diff --git a/tests/compiler/import.wast b/tests/compiler/import.wast index c7e9693f..f4fc9104 100644 --- a/tests/compiler/import.wast +++ b/tests/compiler/import.wast @@ -1,8 +1,8 @@ (module (type $iii (func (param i32 i32) (result i32))) (type $v (func)) - (global $export/a i32 (i32.const 1)) - (global $export/b i32 (i32.const 2)) + (global $export/a (mut i32) (i32.const 1)) + (global $export/b (mut i32) (i32.const 2)) (memory $0 1) (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) diff --git a/tests/compiler/literals.ts b/tests/compiler/literals.ts new file mode 100644 index 00000000..6c6f629b --- /dev/null +++ b/tests/compiler/literals.ts @@ -0,0 +1,46 @@ +0; +1; +2; +3; +4; +5; +6; +7; +8; +9; +0x0; +0x1; +0x2; +0x3; +0x4; +0x5; +0x6; +0x7; +0x8; +0x9; +0xA; +0xB; +0xC; +0xD; +0xE; +0xF; +0xa; +0xb; +0xc; +0xd; +0xe; +0xf; +0o0; +0o1; +0o2; +0o3; +0o4; +0o5; +0o6; +0o7; +0b0; +0b1; +true; +false; +NaN; +Infinity; diff --git a/tests/compiler/literals.wast b/tests/compiler/literals.wast new file mode 100644 index 00000000..095128d2 --- /dev/null +++ b/tests/compiler/literals.wast @@ -0,0 +1,168 @@ +(module + (type $v (func)) + (memory $0 1) + (data (i32.const 4) "\08\00\00\00") + (export "memory" (memory $0)) + (start $start) + (func $start (; 0 ;) (type $v) + (drop + (i32.const 0) + ) + (drop + (i32.const 1) + ) + (drop + (i32.const 2) + ) + (drop + (i32.const 3) + ) + (drop + (i32.const 4) + ) + (drop + (i32.const 5) + ) + (drop + (i32.const 6) + ) + (drop + (i32.const 7) + ) + (drop + (i32.const 8) + ) + (drop + (i32.const 9) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 1) + ) + (drop + (i32.const 2) + ) + (drop + (i32.const 3) + ) + (drop + (i32.const 4) + ) + (drop + (i32.const 5) + ) + (drop + (i32.const 6) + ) + (drop + (i32.const 7) + ) + (drop + (i32.const 8) + ) + (drop + (i32.const 9) + ) + (drop + (i32.const 10) + ) + (drop + (i32.const 11) + ) + (drop + (i32.const 12) + ) + (drop + (i32.const 13) + ) + (drop + (i32.const 14) + ) + (drop + (i32.const 15) + ) + (drop + (i32.const 10) + ) + (drop + (i32.const 11) + ) + (drop + (i32.const 12) + ) + (drop + (i32.const 13) + ) + (drop + (i32.const 14) + ) + (drop + (i32.const 15) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 1) + ) + (drop + (i32.const 2) + ) + (drop + (i32.const 3) + ) + (drop + (i32.const 4) + ) + (drop + (i32.const 5) + ) + (drop + (i32.const 6) + ) + (drop + (i32.const 7) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 1) + ) + (drop + (i32.const 1) + ) + (drop + (i32.const 0) + ) + (drop + (f64.const nan:0x8000000000000) + ) + (drop + (f64.const inf) + ) + ) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + isNaN + isFinite +[program.exports] + +;) diff --git a/tests/compiler/unary.ts b/tests/compiler/unary.ts new file mode 100644 index 00000000..416c26f0 --- /dev/null +++ b/tests/compiler/unary.ts @@ -0,0 +1,88 @@ ++1; +-1; +!1; +~1; ++1.25; +-1.25; +!1.25; +// ~1.25; + +let i: i32 = 0; + ++i; +-i; +!i; +~i; +++i; +--i; + +i = +1; +i = -1; +i = !1; +i = ~1; +i = +i; +i = -i; +i = !i; +i = ~i; +i = ++i; +i = --i; + +let I: i64 = 0; + ++I; +-I; +!I; +~I; +++I; +--I; + +I = +1; +I = -1; +I = !1; +I = ~1; +I = +I; +I = -I; +I = !I; +I = ~I; +I = ++I; +I = --I; + +let f: f32 = 0; + ++f; +-f; +!f; +// ~f; +++f; +--f; + +f = +1.25; +f = -1.25; +i = !1.25; +// i = ~1.25; +f = +f; +f = -f; +i = !f; +// i = ~f; +f = ++f; +f = --f; + +let F: f64 = 0; + ++F; +-F; +!F; +// ~F; +++F; +--F; + +F = +1.25; +F = -1.25; +I = !1.25; +// I = ~1.25; +F = +F; +F = -F; +I = !F; +// I = ~F; +F = ++F; +F = --F; diff --git a/tests/compiler/unary.wast b/tests/compiler/unary.wast new file mode 100644 index 00000000..42043d2b --- /dev/null +++ b/tests/compiler/unary.wast @@ -0,0 +1,420 @@ +(module + (type $v (func)) + (global $unary/i (mut i32) (i32.const 0)) + (global $unary/I (mut i64) (i64.const 0)) + (global $unary/f (mut f32) (f32.const 0)) + (global $unary/F (mut f64) (f64.const 0)) + (memory $0 1) + (data (i32.const 4) "\08\00\00\00") + (export "memory" (memory $0)) + (start $start) + (func $start (; 0 ;) (type $v) + (drop + (i32.const 1) + ) + (drop + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + (drop + (i32.eqz + (i32.const 1) + ) + ) + (drop + (i32.xor + (i32.const 1) + (i32.const -1) + ) + ) + (drop + (f64.const 1.25) + ) + (drop + (f64.neg + (f64.const 1.25) + ) + ) + (drop + (f64.eq + (f64.const 1.25) + (f64.const 0) + ) + ) + (drop + (get_global $unary/i) + ) + (drop + (i32.sub + (i32.const 0) + (get_global $unary/i) + ) + ) + (drop + (i32.eqz + (get_global $unary/i) + ) + ) + (drop + (i32.xor + (get_global $unary/i) + (i32.const -1) + ) + ) + (set_global $unary/i + (i32.add + (get_global $unary/i) + (i32.const 1) + ) + ) + (set_global $unary/i + (i32.sub + (get_global $unary/i) + (i32.const 1) + ) + ) + (set_global $unary/i + (i32.const 1) + ) + (set_global $unary/i + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + (set_global $unary/i + (i32.eqz + (i32.const 1) + ) + ) + (set_global $unary/i + (i32.xor + (i32.const 1) + (i32.const -1) + ) + ) + (set_global $unary/i + (get_global $unary/i) + ) + (set_global $unary/i + (i32.sub + (i32.const 0) + (get_global $unary/i) + ) + ) + (set_global $unary/i + (i32.eqz + (get_global $unary/i) + ) + ) + (set_global $unary/i + (i32.xor + (get_global $unary/i) + (i32.const -1) + ) + ) + (set_global $unary/i + (block (result i32) + (set_global $unary/i + (i32.add + (get_global $unary/i) + (i32.const 1) + ) + ) + (get_global $unary/i) + ) + ) + (set_global $unary/i + (block (result i32) + (set_global $unary/i + (i32.sub + (get_global $unary/i) + (i32.const 1) + ) + ) + (get_global $unary/i) + ) + ) + (drop + (get_global $unary/I) + ) + (drop + (i64.sub + (i64.const 0) + (get_global $unary/I) + ) + ) + (drop + (i64.eqz + (get_global $unary/I) + ) + ) + (drop + (i64.xor + (get_global $unary/I) + (i64.const -1) + ) + ) + (set_global $unary/I + (i64.add + (get_global $unary/I) + (i64.const 1) + ) + ) + (set_global $unary/I + (i64.sub + (get_global $unary/I) + (i64.const 1) + ) + ) + (set_global $unary/I + (i64.const 1) + ) + (set_global $unary/I + (i64.sub + (i64.const 0) + (i64.const 1) + ) + ) + (set_global $unary/I + (i64.extend_s/i32 + (i32.eqz + (i32.const 1) + ) + ) + ) + (set_global $unary/I + (i64.xor + (i64.const 1) + (i64.const -1) + ) + ) + (set_global $unary/I + (get_global $unary/I) + ) + (set_global $unary/I + (i64.sub + (i64.const 0) + (get_global $unary/I) + ) + ) + (set_global $unary/I + (i64.extend_s/i32 + (i64.eqz + (get_global $unary/I) + ) + ) + ) + (set_global $unary/I + (i64.xor + (get_global $unary/I) + (i64.const -1) + ) + ) + (set_global $unary/I + (block (result i64) + (set_global $unary/I + (i64.add + (get_global $unary/I) + (i64.const 1) + ) + ) + (get_global $unary/I) + ) + ) + (set_global $unary/I + (block (result i64) + (set_global $unary/I + (i64.sub + (get_global $unary/I) + (i64.const 1) + ) + ) + (get_global $unary/I) + ) + ) + (drop + (get_global $unary/f) + ) + (drop + (f32.neg + (get_global $unary/f) + ) + ) + (drop + (f32.eq + (get_global $unary/f) + (f32.const 0) + ) + ) + (set_global $unary/f + (f32.add + (get_global $unary/f) + (f32.const 1) + ) + ) + (set_global $unary/f + (f32.sub + (get_global $unary/f) + (f32.const 1) + ) + ) + (set_global $unary/f + (f32.const 1.25) + ) + (set_global $unary/f + (f32.neg + (f32.const 1.25) + ) + ) + (set_global $unary/i + (f64.eq + (f64.const 1.25) + (f64.const 0) + ) + ) + (set_global $unary/f + (get_global $unary/f) + ) + (set_global $unary/f + (f32.neg + (get_global $unary/f) + ) + ) + (set_global $unary/i + (f32.eq + (get_global $unary/f) + (f32.const 0) + ) + ) + (set_global $unary/f + (block (result f32) + (set_global $unary/f + (f32.add + (get_global $unary/f) + (f32.const 1) + ) + ) + (get_global $unary/f) + ) + ) + (set_global $unary/f + (block (result f32) + (set_global $unary/f + (f32.sub + (get_global $unary/f) + (f32.const 1) + ) + ) + (get_global $unary/f) + ) + ) + (drop + (get_global $unary/F) + ) + (drop + (f64.neg + (get_global $unary/F) + ) + ) + (drop + (f64.eq + (get_global $unary/F) + (f64.const 0) + ) + ) + (set_global $unary/F + (f64.add + (get_global $unary/F) + (f64.const 1) + ) + ) + (set_global $unary/F + (f64.sub + (get_global $unary/F) + (f64.const 1) + ) + ) + (set_global $unary/F + (f64.const 1.25) + ) + (set_global $unary/F + (f64.neg + (f64.const 1.25) + ) + ) + (set_global $unary/I + (i64.extend_s/i32 + (f64.eq + (f64.const 1.25) + (f64.const 0) + ) + ) + ) + (set_global $unary/F + (get_global $unary/F) + ) + (set_global $unary/F + (f64.neg + (get_global $unary/F) + ) + ) + (set_global $unary/I + (i64.extend_s/i32 + (f64.eq + (get_global $unary/F) + (f64.const 0) + ) + ) + ) + (set_global $unary/F + (block (result f64) + (set_global $unary/F + (f64.add + (get_global $unary/F) + (f64.const 1) + ) + ) + (get_global $unary/F) + ) + ) + (set_global $unary/F + (block (result f64) + (set_global $unary/F + (f64.sub + (get_global $unary/F) + (f64.const 1) + ) + ) + (get_global $unary/F) + ) + ) + ) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + isNaN + isFinite + unary/i + unary/I + unary/f + unary/F +[program.exports] + +;) diff --git a/tests/compiler/while.ts b/tests/compiler/while.ts new file mode 100644 index 00000000..95ad44f7 --- /dev/null +++ b/tests/compiler/while.ts @@ -0,0 +1,14 @@ +export function loopWhile(n: i32): void { + while (n) { + n = n - 1; + } +} + +export function loopWhileInWhile(n: i32): void { + while (n) { + n = n - 1; + while (n) { + n = n - 1; + } + } +} diff --git a/tests/compiler/while.wast b/tests/compiler/while.wast new file mode 100644 index 00000000..c99ce578 --- /dev/null +++ b/tests/compiler/while.wast @@ -0,0 +1,88 @@ +(module + (type $iv (func (param i32))) + (memory $0 1) + (data (i32.const 4) "\08\00\00\00") + (export "memory" (memory $0)) + (func $while/loopWhile (; 0 ;) (type $iv) (param $0 i32) + (block $break$1.1 + (loop $continue$1.1 + (if + (get_local $0) + (block + (block + (set_local $0 + (i32.sub + (get_local $0) + (i32.const 1) + ) + ) + ) + (br $continue$1.1) + ) + ) + ) + ) + ) + (func $while/loopWhileInWhile (; 1 ;) (type $iv) (param $0 i32) + (block $break$1.1 + (loop $continue$1.1 + (if + (get_local $0) + (block + (block + (set_local $0 + (i32.sub + (get_local $0) + (i32.const 1) + ) + ) + (block $break$1.2 + (loop $continue$1.2 + (if + (get_local $0) + (block + (block + (set_local $0 + (i32.sub + (get_local $0) + (i32.const 1) + ) + ) + ) + (br $continue$1.2) + ) + ) + ) + ) + ) + (br $continue$1.1) + ) + ) + ) + ) + ) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + isNaN + isFinite + while/loopWhile + while/loopWhileInWhile +[program.exports] + while/loopWhile + while/loopWhileInWhile +;) diff --git a/tests/set_global-immutable.js b/tests/set_global-immutable.js new file mode 100644 index 00000000..3907ccc7 --- /dev/null +++ b/tests/set_global-immutable.js @@ -0,0 +1,16 @@ +var binaryen = require("binaryen"); + +// "It is a validation error for a set_global to index an immutable global variable." + +var mod = new binaryen.Module(); +mod.addGlobal("a", binaryen.i32, false, mod.i32.const(0)); + +var funcType = mod.addFunctionType("v", binaryen.none, []); +var func = mod.addFunction("start", funcType, [], mod.block("", [ + mod.setGlobal("a", mod.i32.const(1)) +])); +mod.setStart(func); + +console.log(mod.emitText()); +if (mod.validate()) + console.log("-> validates");