From df637164a6b86a86d8bec16e3c5eb77b06e1a951 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Sun, 26 Nov 2017 04:03:28 +0100 Subject: [PATCH] Cleanup --- src/ast.ts | 140 ---------- src/compiler.ts | 313 +++++++++++++---------- src/constants.ts | 4 +- src/{ => glue}/binaryen.d.ts | 10 +- src/index.ts | 2 +- src/{binaryen.ts => module.ts} | 141 ++++++---- src/program.ts | 4 +- src/tsconfig.json | 4 +- tests/parser/fixtures/precedence.tree.ts | 171 ------------- tests/parser/index.ts | 10 +- 10 files changed, 279 insertions(+), 520 deletions(-) rename src/{ => glue}/binaryen.d.ts (95%) rename src/{binaryen.ts => module.ts} (78%) delete mode 100644 tests/parser/fixtures/precedence.tree.ts diff --git a/src/ast.ts b/src/ast.ts index f3cec461..aebcadc3 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -351,8 +351,6 @@ export abstract class Expression extends Node { (expr.expression = expression).parent = expr; return expr; } - - abstract serializeAsTree(sb: string[], indent: i32): void; } export const enum LiteralKind { @@ -384,20 +382,6 @@ export class ArrayLiteralExpression extends LiteralExpression { } sb.push("]"); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent++); - sb.push("[\n"); - for (let i: i32 = 0, k: i32 = this.elementExpressions.length; i < k; ++i) { - pushIndent(sb, indent); - if (i > 0) - sb.push(",\n"); - if (this.elementExpressions[i]) - (this.elementExpressions[i]).serialize(sb); - } - pushIndent(sb, --indent); - sb.push("]"); - } } export const enum AssertionKind { @@ -424,24 +408,6 @@ export class AssertionExpression extends Expression { this.toType.serialize(sb); } } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - if (this.assertionKind == AssertionKind.PREFIX) { - pushIndent(sb, indent); - sb.push("<"); - this.toType.serialize(sb); - sb.push(">\n"); - this.expression.serializeAsTree(sb, indent + 1); - } else { - this.expression.serializeAsTree(sb, indent + 1); - sb.push("\n"); - pushIndent(sb, indent); - sb.push("as\n"); - pushIndent(sb, indent + 1); - this.toType.serialize(sb); - } - sb.push("\n"); - } } export class BinaryExpression extends Expression { @@ -458,14 +424,6 @@ export class BinaryExpression extends Expression { sb.push(" "); this.right.serialize(sb); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - this.left.serializeAsTree(sb, indent + 1); - pushIndent(sb, indent); - sb.push(operatorTokenToString(this.operator)); - sb.push("\n"); - this.right.serializeAsTree(sb, indent + 1); - } } export class CallExpression extends Expression { @@ -495,12 +453,6 @@ export class CallExpression extends Expression { } sb.push(")"); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); // not interested in a tree here - sb.push("\n"); - } } export class ElementAccessExpression extends Expression { @@ -515,15 +467,6 @@ export class ElementAccessExpression extends Expression { this.elementExpression.serialize(sb); sb.push("]"); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - this.expression.serializeAsTree(sb, indent); - pushIndent(sb, indent); - sb.push("[\n"); - this.elementExpression.serializeAsTree(sb, indent + 1); - pushIndent(sb, indent); - sb.push("]\n"); - } } export class FloatLiteralExpression extends LiteralExpression { @@ -534,12 +477,6 @@ export class FloatLiteralExpression extends LiteralExpression { serialize(sb: string[]): void { sb.push(this.value.toString()); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); - sb.push("\n"); - } } export class IdentifierExpression extends Expression { @@ -550,12 +487,6 @@ export class IdentifierExpression extends Expression { serialize(sb: string[]): void { sb.push(this.name); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); - sb.push("\n"); - } } export class IntegerLiteralExpression extends LiteralExpression { @@ -566,12 +497,6 @@ export class IntegerLiteralExpression extends LiteralExpression { serialize(sb: string[]): void { sb.push(this.value.toString()); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); - sb.push("\n"); - } } export class NewExpression extends CallExpression { @@ -582,12 +507,6 @@ export class NewExpression extends CallExpression { sb.push("new "); super.serialize(sb); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); // not interested in a tree here - sb.push("\n"); - } } export class NullExpression extends IdentifierExpression { @@ -605,14 +524,6 @@ export class ParenthesizedExpression extends Expression { this.expression.serialize(sb); sb.push(")"); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - sb.push("(\n"); - this.expression.serializeAsTree(sb, indent + 1); - pushIndent(sb, indent); - sb.push(")\n"); - } } export class PropertyAccessExpression extends Expression { @@ -626,12 +537,6 @@ export class PropertyAccessExpression extends Expression { sb.push("."); this.property.serialize(sb); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); // not interested in a tree here - sb.push("\n"); - } } export class RegexpLiteralExpression extends LiteralExpression { @@ -642,12 +547,6 @@ export class RegexpLiteralExpression extends LiteralExpression { serialize(sb: string[]): void { sb.push(this.value); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); - sb.push("\n"); - } } export class SelectExpression extends Expression { @@ -664,16 +563,6 @@ export class SelectExpression extends Expression { sb.push(" : "); this.ifElse.serialize(sb); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - this.condition.serializeAsTree(sb, indent + 1); - pushIndent(sb, indent); - sb.push("?\n"); - this.ifThen.serializeAsTree(sb, indent + 1); - pushIndent(sb, indent); - sb.push(":\n"); - this.ifElse.serializeAsTree(sb, indent + 1); - } } export class StringLiteralExpression extends LiteralExpression { @@ -684,12 +573,6 @@ export class StringLiteralExpression extends LiteralExpression { serialize(sb: string[]): void { sb.push(JSON.stringify(this.value)); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - this.serialize(sb); - sb.push("\n"); - } } export class SuperExpression extends IdentifierExpression { @@ -729,17 +612,6 @@ export class UnaryPostfixExpression extends UnaryExpression { default: sb.push("INVALID"); break; } } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - this.expression.serializeAsTree(sb, indent + 1); - pushIndent(sb, indent); - switch (this.operator) { - case Token.PLUS_PLUS: sb.push("++"); break; - case Token.MINUS_MINUS: sb.push("--"); break; - default: sb.push("INVALID"); break; - } - sb.push("\n"); - } } export class UnaryPrefixExpression extends UnaryExpression { @@ -750,13 +622,6 @@ export class UnaryPrefixExpression extends UnaryExpression { sb.push(operatorTokenToString(this.operator)); this.expression.serialize(sb); } - - serializeAsTree(sb: string[], indent: i32 = 0): void { - pushIndent(sb, indent); - sb.push(operatorTokenToString(this.operator)); - sb.push("\n"); - this.expression.serializeAsTree(sb, indent + 1); - } } // statements @@ -1937,8 +1802,3 @@ function builderEndsWith(sb: string[], code: CharCode): bool { } return false; } - -function pushIndent(sb: string[], indent: i32): void { - for (let i: i32 = 0; i < indent; ++i) - sb.push(" "); -} diff --git a/src/compiler.ts b/src/compiler.ts index 982805f8..e419caba 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,6 +1,6 @@ -import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp, Type as BinaryenType, Relooper } from "./binaryen"; import { PATH_DELIMITER } from "./constants"; import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics"; +import { Module, ModuleRef, MemorySegment, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef, FunctionRef, ImportRef, ExportRef, GlobalRef, Relooper } from "./module"; import { Program, ClassPrototype, Class, Element, ElementKind, Enum, FunctionPrototype, Function, Global, Local, Namespace, Parameter } from "./program"; import { CharCode, I64, U64, normalizePath, sb } from "./util"; import { Token, Range } from "./tokenizer"; @@ -77,14 +77,14 @@ import { export enum Target { /** WebAssembly with 32-bit pointers. */ WASM32, - /** WebAssembly with 64-bit pointers. Experimental / not supported by any runtime yet. */ + /** WebAssembly with 64-bit pointers. Experimental and not supported by any runtime yet. */ WASM64 } export class Options { /** WebAssembly target. Defaults to {@link Target.WASM32}. */ target: Target = Target.WASM32; - /** If true, performs compilation as usual but doesn't produce any output (all calls to Binaryen become nops). */ + /** If true, performs compilation as usual but doesn't produce any output (all calls to module become nops). */ noEmit: bool = false; /** If true, compiles everything instead of just reachable code. */ noTreeShaking: bool = false; @@ -92,27 +92,40 @@ export class Options { export class Compiler extends DiagnosticEmitter { + /** Program reference. */ program: Program; + /** Provided options. */ options: Options; + /** Module instance being compiled. */ module: Module; + /** Start function being compiled. */ startFunction: Function; - startFunctionBody: BinaryenExpressionRef[] = new Array(); + /** Start function expressions. */ + startFunctionBody: ExpressionRef[] = new Array(); + /** Current type in compilation. */ currentType: Type = Type.void; + /** Current function in compilation. */ currentFunction: Function; + /** Marker indicating whether continue statements are allowed in the current break context. */ disallowContinue: bool = true; + /** Counting memory offset. */ memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL + /** Memory segments being compiled. */ memorySegments: MemorySegment[] = new Array(); + /** Already processed file names. */ files: Set = new Set(); + /** Compiles a {@link Program} to a {@link Module} using the specified options. */ static compile(program: Program, options: Options | null = null): Module { const compiler: Compiler = new Compiler(program, options); return compiler.compile(); } + /** Constructs a new compiler for a {@link Program} using the specified options. */ constructor(program: Program, options: Options | null = null) { super(program.diagnostics); this.program = program; @@ -124,6 +137,7 @@ export class Compiler extends DiagnosticEmitter { this.memoryOffset = new U64(2 * (this.options.target == Target.WASM64 ? 8 : 4), 0); // leave space for `null` and heapStart (both of usize type) } + /** Performs compilation of the underlying {@link Program} to a {@link Module}. */ compile(): Module { const program: Program = this.program; @@ -141,11 +155,11 @@ export class Compiler extends DiagnosticEmitter { // make start function if not empty if (this.startFunctionBody.length) { - let typeRef: BinaryenFunctionTypeRef = this.module.getFunctionTypeBySignature(BinaryenType.None, []); + let typeRef: FunctionTypeRef = this.module.getFunctionTypeBySignature(NativeType.None, []); if (!typeRef) - typeRef = this.module.addFunctionType("v", BinaryenType.None, []); + typeRef = this.module.addFunctionType("v", NativeType.None, []); this.module.setStart( - this.module.addFunction(this.startFunction.template.internalName, typeRef, typesToBinaryenTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody)) + this.module.addFunction(this.startFunction.template.internalName, typeRef, typesToNativeTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody)) ); } @@ -285,8 +299,8 @@ export class Compiler extends DiagnosticEmitter { return false; element.type = type; } - const binaryenType: BinaryenType = typeToBinaryenType(type); - let initializer: BinaryenExpressionRef; + const nativeType: NativeType = typeToNativeType(type); + let initializer: ExpressionRef; let initializeInStart: bool; if (element.hasConstantValue) { if (type.isLongInteger) @@ -304,23 +318,23 @@ export class Compiler extends DiagnosticEmitter { } else initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() : 0); initializeInStart = false; - this.module.addGlobal(element.internalName, binaryenType, element.isMutable, initializer); + this.module.addGlobal(element.internalName, nativeType, element.isMutable, initializer); } else if (declaration) { if (declaration.initializer) { initializer = this.compileExpression(declaration.initializer, type); initializeInStart = declaration.initializer.kind != NodeKind.LITERAL; // MVP doesn't support complex initializers } else { - initializer = typeToBinaryenZero(this.module, type); + initializer = typeToNativeZero(this.module, type); initializeInStart = false; } } else throw new Error("unexpected missing declaration or constant value"); const internalName: string = element.internalName; if (initializeInStart) { - this.module.addGlobal(internalName, BinaryenType.I32, true, this.module.createI32(-1)); + this.module.addGlobal(internalName, NativeType.I32, true, this.module.createI32(-1)); this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer)); } else - this.module.addGlobal(internalName, BinaryenType.I32, element.isMutable, initializer); + this.module.addGlobal(internalName, NativeType.I32, element.isMutable, initializer); return element.compiled = true; } @@ -340,10 +354,10 @@ export class Compiler extends DiagnosticEmitter { let previousInternalName: string | null = null; for (let [key, val] of element.members) { if (val.hasConstantValue) { - this.module.addGlobal(val.internalName, BinaryenType.I32, false, this.module.createI32(val.constantValue)); + this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue)); } else if (val.declaration) { const declaration: EnumValueDeclaration = val.declaration; - let initializer: BinaryenExpressionRef; + let initializer: ExpressionRef; let initializeInStart: bool = false; if (declaration.value) { initializer = this.compileExpression(declaration.value, Type.i32); @@ -353,16 +367,16 @@ export class Compiler extends DiagnosticEmitter { initializeInStart = false; } else { initializer = this.module.createBinary(BinaryOp.AddI32, - this.module.createGetGlobal(previousInternalName, BinaryenType.I32), + this.module.createGetGlobal(previousInternalName, NativeType.I32), this.module.createI32(1) ); initializeInStart = true; } if (initializeInStart) { - this.module.addGlobal(val.internalName, BinaryenType.I32, true, this.module.createI32(-1)); + this.module.addGlobal(val.internalName, NativeType.I32, true, this.module.createI32(-1)); this.startFunctionBody.push(this.module.createSetGlobal(val.internalName, initializer)); } else - this.module.addGlobal(val.internalName, BinaryenType.I32, false, initializer); + this.module.addGlobal(val.internalName, NativeType.I32, false, initializer); } else throw new Error("unexpected missing declaration or constant value"); previousInternalName = val.internalName; @@ -403,24 +417,24 @@ export class Compiler extends DiagnosticEmitter { // compile statements const previousFunction: Function = this.currentFunction; this.currentFunction = instance; - const stmts: BinaryenExpressionRef[] = this.compileStatements(declaration.statements); + const stmts: ExpressionRef[] = this.compileStatements(declaration.statements); this.currentFunction = previousFunction; // create the function let k: i32 = instance.parameters.length; - const binaryenResultType: BinaryenType = typeToBinaryenType(instance.returnType); - const binaryenParamTypes: BinaryenType[] = new Array(k); + const binaryenResultType: NativeType = typeToNativeType(instance.returnType); + const binaryenParamTypes: NativeType[] = new Array(k); const signatureNameParts: string[] = new Array(k + 1); for (let i: i32 = 0; i < k; ++i) { - binaryenParamTypes[i] = typeToBinaryenType(instance.parameters[i].type); + binaryenParamTypes[i] = typeToNativeType(instance.parameters[i].type); signatureNameParts[i] = typeToSignatureNamePart(instance.parameters[i].type); } signatureNameParts[k] = typeToSignatureNamePart(instance.returnType); - let binaryenTypeRef: BinaryenFunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes); - if (!binaryenTypeRef) - binaryenTypeRef = this.module.addFunctionType(signatureNameParts.join(""), binaryenResultType, binaryenParamTypes); + let typeRef: FunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes); + if (!typeRef) + typeRef = this.module.addFunctionType(signatureNameParts.join(""), binaryenResultType, binaryenParamTypes); const internalName: string = instance.internalName; - this.module.addFunction(internalName, binaryenTypeRef, typesToBinaryenTypes(instance.additionalLocals), this.module.createBlock(null, stmts, BinaryenType.None)); + this.module.addFunction(internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, stmts, NativeType.None)); if (instance.globalExportName != null) this.module.addExport(internalName, instance.globalExportName); } @@ -583,7 +597,7 @@ export class Compiler extends DiagnosticEmitter { // statements - compileStatement(statement: Statement): BinaryenExpressionRef { + compileStatement(statement: Statement): ExpressionRef { switch (statement.kind) { case NodeKind.BLOCK: @@ -631,19 +645,19 @@ export class Compiler extends DiagnosticEmitter { throw new Error("unexpected statement kind"); } - compileStatements(statements: Statement[]): BinaryenExpressionRef[] { + compileStatements(statements: Statement[]): ExpressionRef[] { const k: i32 = statements.length; - const stmts: BinaryenExpressionRef[] = new Array(k); + const stmts: ExpressionRef[] = new Array(k); for (let i: i32 = 0; i < k; ++i) stmts[i] = this.compileStatement(statements[i]); return stmts; } - compileBlockStatement(statement: BlockStatement): BinaryenExpressionRef { - return this.module.createBlock(null, this.compileStatements(statement.statements), BinaryenType.None); + compileBlockStatement(statement: BlockStatement): ExpressionRef { + return this.module.createBlock(null, this.compileStatements(statement.statements), NativeType.None); } - compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef { + compileBreakStatement(statement: BreakStatement): ExpressionRef { if (statement.label) throw new Error("not implemented"); const context: string | null = this.currentFunction.breakContext; @@ -653,7 +667,7 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); } - compileContinueStatement(statement: ContinueStatement): BinaryenExpressionRef { + compileContinueStatement(statement: ContinueStatement): ExpressionRef { if (statement.label) throw new Error("not implemented"); const context: string | null = this.currentFunction.breakContext; @@ -663,10 +677,10 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); } - compileDoStatement(statement: DoStatement): BinaryenExpressionRef { + compileDoStatement(statement: DoStatement): ExpressionRef { const label: string = this.currentFunction.enterBreakContext(); - const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32); - const body: BinaryenExpressionRef = this.compileStatement(statement.statement); + const condition: ExpressionRef = this.compileExpression(statement.condition, Type.i32); + const body: ExpressionRef = this.compileStatement(statement.statement); this.currentFunction.leaveBreakContext(); const breakLabel: string = "break$" + label; const continueLabel: string = "continue$" + label; @@ -675,24 +689,24 @@ export class Compiler extends DiagnosticEmitter { this.module.createBlock(null, [ body, this.module.createBreak(continueLabel, condition) - ], BinaryenType.None)) - ], BinaryenType.None); + ], NativeType.None)) + ], NativeType.None); } - compileEmptyStatement(statement: EmptyStatement): BinaryenExpressionRef { + compileEmptyStatement(statement: EmptyStatement): ExpressionRef { return this.module.createNop(); } - compileExpressionStatement(statement: ExpressionStatement): BinaryenExpressionRef { + compileExpressionStatement(statement: ExpressionStatement): ExpressionRef { return this.compileExpression(statement.expression, Type.void); } - compileForStatement(statement: ForStatement): BinaryenExpressionRef { + compileForStatement(statement: ForStatement): ExpressionRef { const context: string = this.currentFunction.enterBreakContext(); - const initializer: BinaryenExpressionRef = statement.initializer ? this.compileStatement(statement.initializer) : this.module.createNop(); - const condition: BinaryenExpressionRef = statement.condition ? this.compileExpression(statement.condition, Type.i32) : this.module.createI32(1); - const incrementor: BinaryenExpressionRef = statement.incrementor ? this.compileExpression(statement.incrementor, Type.void) : this.module.createNop(); - const body: BinaryenExpressionRef = this.compileStatement(statement.statement); + const initializer: ExpressionRef = statement.initializer ? this.compileStatement(statement.initializer) : this.module.createNop(); + const condition: ExpressionRef = statement.condition ? this.compileExpression(statement.condition, Type.i32) : this.module.createI32(1); + const incrementor: ExpressionRef = statement.incrementor ? this.compileExpression(statement.incrementor, Type.void) : this.module.createNop(); + const body: ExpressionRef = this.compileStatement(statement.statement); this.currentFunction.leaveBreakContext(); const continueLabel: string = "continue$" + context; const breakLabel: string = "break$" + context; @@ -703,27 +717,27 @@ export class Compiler extends DiagnosticEmitter { body, incrementor, this.module.createBreak(continueLabel) - ], BinaryenType.None)) - ], BinaryenType.None)) - ], BinaryenType.None); + ], NativeType.None)) + ], NativeType.None)) + ], NativeType.None); } - compileIfStatement(statement: IfStatement): BinaryenExpressionRef { - const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32); - const ifTrue: BinaryenExpressionRef = this.compileStatement(statement.statement); - const ifFalse: BinaryenExpressionRef = statement.elseStatement ? this.compileStatement(statement.elseStatement) : 0; + compileIfStatement(statement: IfStatement): ExpressionRef { + const condition: ExpressionRef = this.compileExpression(statement.condition, Type.i32); + const ifTrue: ExpressionRef = this.compileStatement(statement.statement); + const ifFalse: ExpressionRef = statement.elseStatement ? this.compileStatement(statement.elseStatement) : 0; return this.module.createIf(condition, ifTrue, ifFalse); } - compileReturnStatement(statement: ReturnStatement): BinaryenExpressionRef { + compileReturnStatement(statement: ReturnStatement): ExpressionRef { if (this.currentFunction) { - const expression: BinaryenExpressionRef = statement.expression ? this.compileExpression(statement.expression, this.currentFunction.returnType) : 0; + const expression: ExpressionRef = statement.expression ? this.compileExpression(statement.expression, this.currentFunction.returnType) : 0; return this.module.createReturn(expression); } return this.module.createUnreachable(); } - compileSwitchStatement(statement: SwitchStatement): BinaryenExpressionRef { + compileSwitchStatement(statement: SwitchStatement): ExpressionRef { const context: string = this.currentFunction.enterBreakContext(); const previousDisallowContinue: bool = this.disallowContinue; this.disallowContinue = true; @@ -733,18 +747,18 @@ export class Compiler extends DiagnosticEmitter { let i: i32, k: i32 = statement.cases.length; // prepend initializer to inner block - const breaks: BinaryenExpressionRef[] = new Array(1 + k); + const breaks: ExpressionRef[] = new Array(1 + k); breaks[0] = this.module.createSetLocal(local.index, this.compileExpression(statement.expression, Type.i32)); // initializer // make one br_if per (possibly dynamic) labeled case - // TODO: take advantage of br_table where labels are known to be (sequential) constant (ideally Binaryen's optimizer would) + // TODO: take advantage of br_table where labels are known to be (sequential) constant (ideally the optimizer would) let breakIndex: i32 = 1; let defaultIndex: i32 = -1; for (i = 0; i < k; ++i) { const case_: SwitchCase = statement.cases[i]; if (case_.label) { breaks[breakIndex++] = this.module.createBreak("case" + i.toString(10) + "$" + context, this.module.createBinary(BinaryOp.EqI32, - this.module.createGetLocal(local.index, BinaryenType.I32), + this.module.createGetLocal(local.index, NativeType.I32), this.compileExpression(case_.label, Type.i32) )); } else @@ -758,18 +772,18 @@ export class Compiler extends DiagnosticEmitter { ) + "$" + context); // nest blocks in order - let currentBlock: BinaryenExpressionRef = this.module.createBlock("case0$" + context, breaks, BinaryenType.None); + let currentBlock: ExpressionRef = this.module.createBlock("case0$" + context, breaks, NativeType.None); for (i = 0; i < k; ++i) { const case_: SwitchCase = statement.cases[i]; const nextLabel: string = i == k - 1 ? "break$" + context : "case" + (i + 1).toString(10) + "$" + context; const l: i32 = case_.statements.length; - const body: BinaryenExpressionRef[] = new Array(1 + l); + const body: ExpressionRef[] = new Array(1 + l); body[0] = currentBlock; for (let j: i32 = 0; j < l; ++j) body[j + 1] = this.compileStatement(case_.statements[j]); - currentBlock = this.module.createBlock(nextLabel, body, BinaryenType.None); + currentBlock = this.module.createBlock(nextLabel, body, NativeType.None); } this.currentFunction.leaveBreakContext(); this.disallowContinue = previousDisallowContinue; @@ -777,17 +791,17 @@ export class Compiler extends DiagnosticEmitter { return currentBlock; } - compileThrowStatement(statement: ThrowStatement): BinaryenExpressionRef { + compileThrowStatement(statement: ThrowStatement): ExpressionRef { return this.module.createUnreachable(); // TODO: waiting for exception-handling spec } - compileTryStatement(statement: TryStatement): BinaryenExpressionRef { + compileTryStatement(statement: TryStatement): ExpressionRef { throw new Error("not implemented"); // can't yet support something like: try { return ... } finally { ... } // worthwhile to investigate lowering returns to block results (here)? } - compileVariableStatement(statement: VariableStatement): BinaryenExpressionRef { + compileVariableStatement(statement: VariableStatement): ExpressionRef { const declarations: VariableDeclaration[] = statement.declarations; // top-level variables become globals @@ -798,7 +812,7 @@ export class Compiler extends DiagnosticEmitter { return this.module.createNop(); } // other variables become locals - const initializers: BinaryenExpressionRef[] = new Array(); + const initializers: ExpressionRef[] = new Array(); for (let i: i32 = 0, k = declarations.length; i < k; ++i) { const declaration: VariableDeclaration = declarations[i]; if (declaration.type) { @@ -814,32 +828,32 @@ export class Compiler extends DiagnosticEmitter { } } } - return initializers.length ? this.module.createBlock(null, initializers, BinaryenType.None) : this.module.createNop(); + return initializers.length ? this.module.createBlock(null, initializers, NativeType.None) : this.module.createNop(); } - compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef { + compileWhileStatement(statement: WhileStatement): ExpressionRef { const label: string = this.currentFunction.enterBreakContext(); - const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32); + const condition: ExpressionRef = this.compileExpression(statement.condition, Type.i32); const breakLabel: string = "break$" + label; const continueLabel: string = "continue$" + label; - const body: BinaryenExpressionRef = this.compileStatement(statement.statement); + const body: ExpressionRef = this.compileStatement(statement.statement); this.currentFunction.leaveBreakContext(); return this.module.createBlock(breakLabel, [ this.module.createLoop(continueLabel, this.module.createIf(condition, this.module.createBlock(null, [ body, this.module.createBreak(continueLabel) - ], BinaryenType.None)) + ], NativeType.None)) ) - ], BinaryenType.None); + ], NativeType.None); } // expressions - compileExpression(expression: Expression, contextualType: Type, convert: bool = true): BinaryenExpressionRef { + compileExpression(expression: Expression, contextualType: Type, convert: bool = true): ExpressionRef { this.currentType = contextualType; - let expr: BinaryenExpressionRef; + let expr: ExpressionRef; switch (expression.kind) { case NodeKind.ASSERTION: @@ -906,7 +920,7 @@ export class Compiler extends DiagnosticEmitter { return expr; } - convertExpression(expr: BinaryenExpressionRef, fromType: Type, toType: Type): BinaryenExpressionRef { + convertExpression(expr: ExpressionRef, fromType: Type, toType: Type): ExpressionRef { // void to any if (fromType.kind == TypeKind.VOID) @@ -1059,19 +1073,19 @@ export class Compiler extends DiagnosticEmitter { return expr; } - compileAssertionExpression(expression: AssertionExpression, contextualType: Type): BinaryenExpressionRef { + 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: BinaryenExpressionRef = this.compileExpression(expression.expression, toType, false); + const expr: ExpressionRef = this.compileExpression(expression.expression, toType, false); return this.convertExpression(expr, this.currentType, toType); } return this.compileExpression(expression.expression, contextualType); } - compileBinaryExpression(expression: BinaryExpression, contextualType: Type): BinaryenExpressionRef { + compileBinaryExpression(expression: BinaryExpression, contextualType: Type): ExpressionRef { let op: BinaryOp; - let left: BinaryenExpressionRef; - let right: BinaryenExpressionRef; + let left: ExpressionRef; + let right: ExpressionRef; let compound: Token = 0; switch (expression.operator) { @@ -1287,12 +1301,12 @@ export class Compiler extends DiagnosticEmitter { return this.module.createBinary(op, left, right); } - compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): BinaryenExpressionRef { + compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef { this.currentType = this.determineExpressionType(expression, contextualType); return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, this.currentType), contextualType != Type.void); } - compileAssignmentWithValue(expression: Expression, valueWithCorrectType: BinaryenExpressionRef, tee: bool = false): BinaryenExpressionRef { + compileAssignmentWithValue(expression: Expression, valueWithCorrectType: ExpressionRef, tee: bool = false): ExpressionRef { const element: Element | null = this.program.resolveElement(expression, this.currentFunction); if (!element) return this.module.createUnreachable(); @@ -1307,12 +1321,12 @@ export class Compiler extends DiagnosticEmitter { if (element.kind == ElementKind.GLOBAL && (element).type) { if (tee) { - const globalBinaryenType: BinaryenType = typeToBinaryenType((element).type); + const globalNativeType: NativeType = typeToNativeType((element).type); this.currentType = (element).type; return this.module.createBlock(null, [ // teeGlobal this.module.createSetGlobal((element).internalName, valueWithCorrectType), - this.module.createGetGlobal((element).internalName, globalBinaryenType) - ], globalBinaryenType); + this.module.createGetGlobal((element).internalName, globalNativeType) + ], globalNativeType); } return this.module.createSetGlobal((element).internalName, valueWithCorrectType); } @@ -1322,7 +1336,17 @@ export class Compiler extends DiagnosticEmitter { throw new Error("not implemented"); } - compileCallExpression(expression: CallExpression, contextualType: Type): BinaryenExpressionRef { + compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef { + // TODO + /* const callee: Expression = expression.expression; + switch (callee.kind) { + case NodeKind.THIS: + // use current class + case NodeKind.PROPERTYACCESS: + // compile, use class in currentType? + case NodeKind.IDENTIFIER: + // lookup identifier? + } */ const element: Element | null = this.program.resolveElement(expression.expression, this.currentFunction); // reports if (!element) return this.module.createUnreachable(); @@ -1336,40 +1360,57 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); } - compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): BinaryenExpressionRef { + /** Compiles a call to a function. If an instance method, `this` is the first element in `argumentExpressions`. */ + compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): ExpressionRef { + // when called, the function is considered reachable -> compile if (!functionInstance.compiled) this.compileFunction(functionInstance); + + // validate and compile arguments const parameters: Parameter[] = functionInstance.parameters; - let k: i32 = parameters.length; - if (argumentExpressions.length > k) { - this.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, k.toString(10), argumentExpressions.length.toString(10)); + const parameterCount: i32 = parameters.length; + const argumentCount: i32 = argumentExpressions.length; + if (argumentExpressions.length > parameterCount) { // too many arguments + this.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, + (functionInstance.isInstance ? parameterCount - 1 : parameterCount).toString(10), + (functionInstance.isInstance ? argumentCount - 1 : argumentCount).toString(10) + ); return this.module.createUnreachable(); } - const operands: BinaryenExpressionRef[] = new Array(k); - for (let i: i32 = 0; i < k; ++i) { + 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); + operands[i] = this.compileExpression(argumentExpressions[i], parameters[i].type, true); } else { const initializer: Expression | null = parameters[i].initializer; - if (initializer) { + if (initializer) { // omitted, uses initializer + // FIXME: here, the initializer is compiled in the caller's scope. + // a solution could be to use a stub for each possible overload, calling the + // full function with optional arguments being part of the stub's body. operands[i] = this.compileExpression(initializer, parameters[i].type); - } else { - this.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, (i + 1).toString(10), argumentExpressions.length.toString(10)); + } else { // too few arguments + this.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, + (functionInstance.isInstance ? i : i + 1).toString(10), + (functionInstance.isInstance ? argumentCount - 1 : argumentCount).toString(10) + ); return this.module.createUnreachable(); } } } - return this.module.createCall(functionInstance.internalName, operands, typeToBinaryenType(functionInstance.returnType)); + + // finally compile the call + this.currentType = functionInstance.returnType; + return this.module.createCall(functionInstance.internalName, operands, typeToNativeType(functionInstance.returnType)); } - compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): BinaryenExpressionRef { + compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): ExpressionRef { const element: Element | null = this.program.resolveElement(expression.expression, this.currentFunction); // reports if (!element) return this.module.createUnreachable(); throw new Error("not implemented"); } - compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): BinaryenExpressionRef { + compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): ExpressionRef { // null if (expression.kind == NodeKind.NULL) { @@ -1397,7 +1438,7 @@ 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, typeToBinaryenType(this.currentType)); + return this.module.createGetLocal(0, typeToNativeType(this.currentType)); } this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); this.currentType = this.options.target == Target.WASM64 ? Type.u64 : Type.u32; @@ -1431,12 +1472,12 @@ export class Compiler extends DiagnosticEmitter { // local if (element.kind == ElementKind.LOCAL) - return this.module.createGetLocal((element).index, typeToBinaryenType(this.currentType = (element).type)); + return this.module.createGetLocal((element).index, typeToNativeType(this.currentType = (element).type)); // global if (element.kind == ElementKind.GLOBAL) return this.compileGlobal(element) // reports - ? this.module.createGetGlobal((element).internalName, typeToBinaryenType(this.currentType = (element).type)) + ? this.module.createGetGlobal((element).internalName, typeToNativeType(this.currentType = (element).type)) : this.module.createUnreachable(); // field @@ -1451,7 +1492,7 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); } - compileLiteralExpression(expression: LiteralExpression, contextualType: Type): BinaryenExpressionRef { + compileLiteralExpression(expression: LiteralExpression, contextualType: Type): ExpressionRef { switch (expression.literalKind) { // case LiteralKind.ARRAY: @@ -1484,62 +1525,62 @@ export class Compiler extends DiagnosticEmitter { throw new Error("not implemented"); } - compileNewExpression(expression: NewExpression, contextualType: Type): BinaryenExpressionRef { + compileNewExpression(expression: NewExpression, contextualType: Type): ExpressionRef { throw new Error("not implemented"); } - compileParenthesizedExpression(expression: ParenthesizedExpression, contextualType: Type): BinaryenExpressionRef { + compileParenthesizedExpression(expression: ParenthesizedExpression, contextualType: Type): ExpressionRef { return this.compileExpression(expression.expression, contextualType); } - compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): BinaryenExpressionRef { + compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): ExpressionRef { throw new Error("not implemented"); } - compileSelectExpression(expression: SelectExpression, contextualType: Type): BinaryenExpressionRef { - const condition: BinaryenExpressionRef = this.compileExpression(expression.condition, Type.i32); - const ifThen: BinaryenExpressionRef = this.compileExpression(expression.ifThen, contextualType); - const ifElse: BinaryenExpressionRef = this.compileExpression(expression.ifElse, contextualType); + compileSelectExpression(expression: SelectExpression, contextualType: Type): ExpressionRef { + const condition: ExpressionRef = this.compileExpression(expression.condition, Type.i32); + const ifThen: ExpressionRef = this.compileExpression(expression.ifThen, contextualType); + const ifElse: ExpressionRef = this.compileExpression(expression.ifElse, contextualType); return this.module.createSelect(condition, ifThen, ifElse); } - compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): BinaryenExpressionRef { + compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): ExpressionRef { const operator: Token = expression.operator; let op: BinaryOp; - let binaryenType: BinaryenType; - let binaryenOne: BinaryenExpressionRef; + let nativeType: NativeType; + let nativeOne: ExpressionRef; if (contextualType == Type.f32) { op = operator == Token.PLUS_PLUS ? BinaryOp.AddF32 : BinaryOp.SubF32; - binaryenType = BinaryenType.F32; - binaryenOne = this.module.createF32(1); + nativeType = NativeType.F32; + nativeOne = this.module.createF32(1); } else if (contextualType == Type.f64) { op = operator == Token.PLUS_PLUS ? BinaryOp.AddF64 : BinaryOp.SubF64; - binaryenType = BinaryenType.F64; - binaryenOne = this.module.createF64(1); + nativeType = NativeType.F64; + nativeOne = this.module.createF64(1); } else if (contextualType.isLongInteger) { op = operator == Token.PLUS_PLUS ? BinaryOp.AddI64 : BinaryOp.SubI64; - binaryenType = BinaryenType.I64; - binaryenOne = this.module.createI64(1, 0); + nativeType = NativeType.I64; + nativeOne = this.module.createI64(1, 0); } else { op = operator == Token.PLUS_PLUS ? BinaryOp.AddI32 : BinaryOp.SubI32; - binaryenType = BinaryenType.I32; - binaryenOne = this.module.createI32(1); + nativeType = NativeType.I32; + nativeOne = this.module.createI32(1); } - const getValue: BinaryenExpressionRef = this.compileExpression(expression.expression, contextualType); - const setValue: BinaryenExpressionRef = this.compileAssignmentWithValue(expression.expression, this.module.createBinary(op, getValue, binaryenOne), false); // reports + const getValue: ExpressionRef = this.compileExpression(expression.expression, contextualType); + const setValue: ExpressionRef = this.compileAssignmentWithValue(expression.expression, this.module.createBinary(op, getValue, nativeOne), false); // reports return this.module.createBlock(null, [ getValue, setValue - ], binaryenType); + ], nativeType); } - compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): BinaryenExpressionRef { + compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): ExpressionRef { const operandExpression: Expression = expression.expression; - let operand: BinaryenExpressionRef; + let operand: ExpressionRef; let op: UnaryOp; switch (expression.operator) { @@ -1610,27 +1651,27 @@ export class Compiler extends DiagnosticEmitter { // helpers -function typeToBinaryenType(type: Type): BinaryenType { +function typeToNativeType(type: Type): NativeType { return type.kind == TypeKind.F32 - ? BinaryenType.F32 + ? NativeType.F32 : type.kind == TypeKind.F64 - ? BinaryenType.F64 + ? NativeType.F64 : type.isLongInteger - ? BinaryenType.I64 + ? NativeType.I64 : type.isAnyInteger || type.kind == TypeKind.BOOL - ? BinaryenType.I32 - : BinaryenType.None; + ? NativeType.I32 + : NativeType.None; } -function typesToBinaryenTypes(types: Type[]): BinaryenType[] { +function typesToNativeTypes(types: Type[]): NativeType[] { const k: i32 = types.length; - const ret: BinaryenType[] = new Array(k); + const ret: NativeType[] = new Array(k); for (let i: i32 = 0; i < k; ++i) - ret[i] = typeToBinaryenType(types[i]); + ret[i] = typeToNativeType(types[i]); return ret; } -function typeToBinaryenZero(module: Module, type: Type): BinaryenExpressionRef { +function typeToNativeZero(module: Module, type: Type): ExpressionRef { return type.kind == TypeKind.F32 ? module.createF32(0) : type.kind == TypeKind.F64 @@ -1640,7 +1681,7 @@ function typeToBinaryenZero(module: Module, type: Type): BinaryenExpressionRef { : module.createI32(0); } -function typeToBinaryenOne(module: Module, type: Type): BinaryenExpressionRef { +function typeToBinaryenOne(module: Module, type: Type): ExpressionRef { return type.kind == TypeKind.F32 ? module.createF32(1) : type.kind == TypeKind.F64 diff --git a/src/constants.ts b/src/constants.ts index 831a3d3e..96226f34 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,7 +2,7 @@ export const PATH_DELIMITER: string = "/"; export const PARENT_SUBST: string = ".."; -export const GETTER_PREFIX: string = "get "; -export const SETTER_PREFIX: string = "set "; +export const GETTER_PREFIX: string = "get_"; +export const SETTER_PREFIX: string = "set_"; export const INSTANCE_DELIMITER: string = "#"; export const STATIC_DELIMITER: string = "."; diff --git a/src/binaryen.d.ts b/src/glue/binaryen.d.ts similarity index 95% rename from src/binaryen.d.ts rename to src/glue/binaryen.d.ts index 9e07d3f7..12555199 100644 --- a/src/binaryen.d.ts +++ b/src/glue/binaryen.d.ts @@ -246,6 +246,8 @@ declare function _BinaryenReturn(module: BinaryenModuleRef, value: BinaryenExpre declare function _BinaryenHost(module: BinaryenModuleRef, op: BinaryenOp, name: CString | 0, operands: CArray, numOperands: BinaryenIndex): BinaryenExpressionRef; declare function _BinaryenNop(module: BinaryenModuleRef): BinaryenExpressionRef; declare function _BinaryenUnreachable(module: BinaryenModuleRef): BinaryenExpressionRef; +declare function _BinaryenAtomicLoad(module: BinaryenModuleRef, bytes: BinaryenIndex, offset: BinaryenIndex, type: BinaryenType, ptr: BinaryenExpressionRef): BinaryenExpressionRef; +declare function _BinaryenAtomicStore(module: BinaryenModuleRef, bytes: BinaryenIndex, offset: BinaryenIndex, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef; declare function _BinaryenAtomicRMW(module: BinaryenModuleRef, op: BinaryenAtomicRMWOp, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef; declare function _BinaryenAtomicCmpxchg(module: BinaryenModuleRef, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, replacement: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef; declare function _BinaryenAtomicWait(module: BinaryenModuleRef, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, timeout: BinaryenExpressionRef, expectedType: BinaryenType): BinaryenExpressionRef; @@ -263,6 +265,12 @@ declare function _BinaryenConstGetValueF64(expr: BinaryenExpressionRef): f64; declare type BinaryenFunctionRef = usize; declare function _BinaryenAddFunction(module: BinaryenModuleRef, name: CString, type: BinaryenFunctionTypeRef, varTypes: CArray, numVarTypes: BinaryenIndex, body: BinaryenExpressionRef): BinaryenFunctionRef; +declare function _BinaryenGetFunction(module: BinaryenModuleRef, name: CString): BinaryenFunctionRef; +declare function _BinaryenRemoveFunction(module: BinaryenModuleRef, name: CString): void; + +declare function _BinaryenFunctionGetBody(func: BinaryenFunctionRef): BinaryenExpressionRef; +declare function _BinaryenFunctionOptimize(func: BinaryenFunctionRef, module: BinaryenModuleRef): void; +declare function _BinaryenFunctionRunPasses(func: BinaryenFunctionRef, module: BinaryenModuleRef, passes: CArray, numPasses: BinaryenIndex): void; declare type BinaryenImportRef = usize; @@ -289,7 +297,7 @@ declare function _BinaryenModulePrint(module: BinaryenModuleRef): void; declare function _BinaryenModulePrintAsmjs(module: BinaryenModuleRef): void; declare function _BinaryenModuleValidate(module: BinaryenModuleRef): i32; declare function _BinaryenModuleOptimize(module: BinaryenModuleRef): void; -declare function _BinaryenModuleRunPasses(module: BinaryenModuleRef, passes: string[]): void; +declare function _BinaryenModuleRunPasses(module: BinaryenModuleRef, passes: CArray, numPasses: BinaryenIndex): void; declare function _BinaryenModuleAutoDrop(module: BinaryenModuleRef): void; declare function _BinaryenModuleWrite(module: BinaryenModuleRef, output: CString, outputSize: usize): usize; declare function _BinaryenModuleRead(input: CString, inputSize: usize): BinaryenModuleRef; diff --git a/src/index.ts b/src/index.ts index 97933ea5..1323f995 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ */ -import { Module } from "./binaryen"; +import { Module } from "./module"; import { Compiler } from "./compiler"; import { DiagnosticMessage, DiagnosticCategory } from "./diagnostics"; import { Parser } from "./parser"; diff --git a/src/binaryen.ts b/src/module.ts similarity index 78% rename from src/binaryen.ts rename to src/module.ts index 25236db9..d1d007a1 100644 --- a/src/binaryen.ts +++ b/src/module.ts @@ -1,7 +1,16 @@ import { I64, U64 } from "./util"; import { Target } from "./compiler"; -export enum Type { +export type ModuleRef = BinaryenModuleRef; +export type FunctionTypeRef = BinaryenFunctionTypeRef; +export type FunctionRef = BinaryenFunctionRef; +export type ExpressionRef = BinaryenExpressionRef; +export type GlobalRef = BinaryenGlobalRef; +export type ImportRef = BinaryenImportRef; +export type ExportRef = BinaryenExportRef; +export type Index = BinaryenIndex; + +export enum NativeType { None = _BinaryenNone(), I32 = _BinaryenInt32(), I64 = _BinaryenInt64(), @@ -201,11 +210,11 @@ export class MemorySegment { export class Module { - ref: BinaryenModuleRef; + ref: ModuleRef; lit: BinaryenLiteral; noEmit: bool; - static MAX_MEMORY_WASM32: BinaryenIndex = 0xffff; + static MAX_MEMORY_WASM32: Index = 0xffff; static create(): Module { const module: Module = new Module(); @@ -240,7 +249,7 @@ export class Module { // types - addFunctionType(name: string, result: Type, paramTypes: Type[]): BinaryenFunctionRef { + addFunctionType(name: string, result: NativeType, paramTypes: NativeType[]): FunctionRef { if (this.noEmit) return 0; const cStr: CString = allocString(name); const cArr: CArray = allocI32Array(paramTypes); @@ -252,7 +261,7 @@ export class Module { } } - getFunctionTypeBySignature(result: Type, paramTypes: Type[]): BinaryenFunctionTypeRef { + getFunctionTypeBySignature(result: NativeType, paramTypes: NativeType[]): FunctionTypeRef { if (this.noEmit) return 0; const cArr: CArray = allocI32Array(paramTypes); try { @@ -264,63 +273,63 @@ export class Module { // expressions - createI32(value: i32): BinaryenExpressionRef { + createI32(value: i32): ExpressionRef { if (this.noEmit) return 0; _BinaryenLiteralInt32(this.lit, value); return _BinaryenConst(this.ref, this.lit); } - createI64(lo: i32, hi: i32): BinaryenExpressionRef { + createI64(lo: i32, hi: i32): ExpressionRef { if (this.noEmit) return 0; _BinaryenLiteralInt64(this.lit, lo, hi); return _BinaryenConst(this.ref, this.lit); } - createF32(value: f32): BinaryenExpressionRef { + createF32(value: f32): ExpressionRef { if (this.noEmit) return 0; _BinaryenLiteralFloat32(this.lit, value); return _BinaryenConst(this.ref, this.lit); } - createF64(value: f64): BinaryenExpressionRef { + createF64(value: f64): ExpressionRef { if (this.noEmit) return 0; _BinaryenLiteralFloat64(this.lit, value); return _BinaryenConst(this.ref, this.lit); } - createUnary(op: UnaryOp, expr: BinaryenExpressionRef): BinaryenExpressionRef { + createUnary(op: UnaryOp, expr: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; return _BinaryenUnary(this.ref, op, expr); } - createBinary(op: BinaryOp, left: BinaryenExpressionRef, right: BinaryenExpressionRef): BinaryenExpressionRef { + createBinary(op: BinaryOp, left: ExpressionRef, right: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; return _BinaryenBinary(this.ref, op, left, right); } - createHost(op: HostOp, name: string | null = null, operands: BinaryenExpressionRef[] | null = null): BinaryenExpressionRef { + createHost(op: HostOp, name: string | null = null, operands: ExpressionRef[] | null = null): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(name); const cArr: CArray = allocI32Array(operands); try { - return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (operands).length : 0); + return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (operands).length : 0); } finally { _free(cArr); _free(cStr); } } - createGetLocal(index: i32, type: Type): BinaryenExpressionRef { + createGetLocal(index: i32, type: NativeType): ExpressionRef { if (this.noEmit) return 0; return _BinaryenGetLocal(this.ref, index, type); } - createTeeLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef { + createTeeLocal(index: i32, value: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; return _BinaryenTeeLocal(this.ref, index, value); } - createGetGlobal(name: string, type: Type): BinaryenExpressionRef { + createGetGlobal(name: string, type: NativeType): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(name); try { @@ -330,34 +339,54 @@ export class Module { } } - createAtomicRMW(op: AtomicRMWOp, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: Type): BinaryenExpressionRef { + createLoad(bytes: Index, signed: bool, ptr: ExpressionRef, type: NativeType, offset: Index = 0): ExpressionRef { + if (this.noEmit) return 0; + return _BinaryenLoad(this.ref, bytes, signed ? 1 : 0, offset, /* always aligned */ bytes, type, ptr); + } + + createStore(bytes: Index, ptr: ExpressionRef, value: ExpressionRef, type: NativeType, offset: Index = 0): ExpressionRef { + if (this.noEmit) return 0; + return _BinaryenStore(this.ref, bytes, offset, /* always aligned */ bytes, ptr, value, type); + } + + createAtomicLoad(bytes: Index, ptr: ExpressionRef, type: NativeType, offset: Index = 0): ExpressionRef { + if (this.noEmit) return 0; + return _BinaryenAtomicLoad(this.ref, bytes, offset, type, ptr); + } + + createAtomicStore(bytes: Index, ptr: ExpressionRef, value: ExpressionRef, type: NativeType, offset: Index = 0): ExpressionRef { + if (this.noEmit) return 0; + return _BinaryenAtomicStore(this.ref, bytes, offset, ptr, value, type); + } + + createAtomicRMW(op: AtomicRMWOp, bytes: Index, offset: Index, ptr: ExpressionRef, value: ExpressionRef, type: NativeType): ExpressionRef { if (this.noEmit) return 0; return _BinaryenAtomicRMW(this.ref, op, bytes, offset, ptr, value, type); } - createAtomicCmpxchg(bytes: i32, offset: i32, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, replacement: BinaryenExpressionRef, type: Type): BinaryenExpressionRef { + createAtomicCmpxchg(bytes: Index, offset: Index, ptr: ExpressionRef, expected: ExpressionRef, replacement: ExpressionRef, type: NativeType): ExpressionRef { if (this.noEmit) return 0; return _BinaryenAtomicCmpxchg(this.ref, bytes, offset, ptr, expected, replacement, type); } - createAtomicWait(ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, timeout: BinaryenExpressionRef, expectedType: BinaryenType): BinaryenExpressionRef { + createAtomicWait(ptr: ExpressionRef, expected: ExpressionRef, timeout: ExpressionRef, expectedType: NativeType): ExpressionRef { if (this.noEmit) return 0; return _BinaryenAtomicWait(this.ref, ptr, expected, timeout, expectedType); } - createAtomicWake(ptr: BinaryenExpressionRef, wakeCount: BinaryenExpressionRef): BinaryenExpressionRef { + createAtomicWake(ptr: ExpressionRef, wakeCount: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; return _BinaryenAtomicWake(this.ref, ptr, wakeCount); } // statements - createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef { + createSetLocal(index: Index, value: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; return _BinaryenSetLocal(this.ref, index, value); } - createSetGlobal(name: string, value: BinaryenExpressionRef): BinaryenExpressionRef { + createSetGlobal(name: string, value: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(name); try { @@ -367,7 +396,7 @@ export class Module { } } - createBlock(label: string | null, children: BinaryenExpressionRef[], type: Type = Type.Undefined): BinaryenExpressionRef { + createBlock(label: string | null, children: ExpressionRef[], type: NativeType = NativeType.Undefined): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(label); const cArr: CArray = allocI32Array(children); @@ -379,7 +408,7 @@ export class Module { } } - createBreak(label: string | null, condition: BinaryenExpressionRef = 0, value: BinaryenExpressionRef = 0): BinaryenExpressionRef { + createBreak(label: string | null, condition: ExpressionRef = 0, value: ExpressionRef = 0): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(label); try { @@ -389,12 +418,12 @@ export class Module { } } - createDrop(expression: BinaryenExpressionRef): BinaryenExpressionRef { + createDrop(expression: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; return _BinaryenDrop(this.ref, expression); } - createLoop(label: string | null, body: BinaryenExpressionRef): BinaryenExpressionRef { + createLoop(label: string | null, body: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(label); try { @@ -404,27 +433,27 @@ export class Module { } } - createIf(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef = 0): BinaryenExpressionRef { + createIf(condition: ExpressionRef, ifTrue: ExpressionRef, ifFalse: ExpressionRef = 0): ExpressionRef { if (this.noEmit) return 0; return _BinaryenIf(this.ref, condition, ifTrue, ifFalse); } - createNop(): BinaryenExpressionRef { + createNop(): ExpressionRef { if (this.noEmit) return 0; return _BinaryenNop(this.ref); } - createReturn(expression: BinaryenExpressionRef = 0): BinaryenExpressionRef { + createReturn(expression: ExpressionRef = 0): ExpressionRef { if (this.noEmit) return 0; return _BinaryenReturn(this.ref, expression); } - createSelect(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef { + createSelect(condition: ExpressionRef, ifTrue: ExpressionRef, ifFalse: ExpressionRef): ExpressionRef { if (this.noEmit) return 0; return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse); } - createSwitch(names: string[], defaultName: string | null, condition: BinaryenExpressionRef, value: BinaryenExpressionRef = 0): BinaryenExpressionRef { + createSwitch(names: string[], defaultName: string | null, condition: ExpressionRef, value: ExpressionRef = 0): ExpressionRef { if (this.noEmit) return 0; const strs: CString[] = new Array(names.length); let i: i32, k: i32 = names.length; @@ -440,7 +469,7 @@ export class Module { } } - createCall(target: string, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef { + createCall(target: string, operands: ExpressionRef[], returnType: NativeType): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(target); const cArr: CArray = allocI32Array(operands); @@ -452,7 +481,7 @@ export class Module { } } - createCallImport(target: string, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef { + createCallImport(target: string, operands: ExpressionRef[], returnType: NativeType): ExpressionRef { if (this.noEmit) return 0; const cStr: CString = allocString(target); const cArr: CArray = allocI32Array(operands); @@ -464,14 +493,14 @@ export class Module { } } - createUnreachable(): BinaryenExpressionRef { + createUnreachable(): ExpressionRef { if (this.noEmit) return 0; return _BinaryenUnreachable(this.ref); } // meta - addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenGlobalRef { + addGlobal(name: string, type: NativeType, mutable: bool, initializer: ExpressionRef): GlobalRef { if (this.noEmit) return 0; const cStr: CString = allocString(name); try { @@ -481,7 +510,7 @@ export class Module { } } - addFunction(name: string, type: BinaryenFunctionTypeRef, varTypes: Type[], body: BinaryenExpressionRef): BinaryenFunctionRef { + addFunction(name: string, type: FunctionTypeRef, varTypes: NativeType[], body: ExpressionRef): FunctionRef { if (this.noEmit) return 0; const cStr: CString = allocString(name); const cArr: CArray = allocI32Array(varTypes); @@ -493,7 +522,7 @@ export class Module { } } - addExport(internalName: string, externalName: string): BinaryenExportRef { + addExport(internalName: string, externalName: string): ExportRef { if (this.noEmit) return 0; const cStr1: CString = allocString(internalName); const cStr2: CString = allocString(externalName); @@ -515,7 +544,7 @@ export class Module { } } - addImport(internalName: string, externalModuleName: string, externalBaseName: string, type: BinaryenFunctionTypeRef): BinaryenImportRef { + addImport(internalName: string, externalModuleName: string, externalBaseName: string, type: FunctionTypeRef): ImportRef { if (this.noEmit) return 0; const cStr1: CString = allocString(internalName); const cStr2: CString = allocString(externalModuleName); @@ -539,13 +568,13 @@ export class Module { } } - setMemory(initial: BinaryenIndex, maximum: BinaryenIndex, segments: MemorySegment[], target: Target, exportName: string | null = null): void { + setMemory(initial: Index, maximum: Index, segments: MemorySegment[], target: Target, exportName: string | null = null): void { if (this.noEmit) return; const cStr: CString = allocString(exportName); let i: i32, k: i32 = segments.length; const segs: CArray[] = new Array(k); - const offs: BinaryenExpressionRef[] = new Array(k); - const sizs: BinaryenIndex[] = new Array(k); + const offs: ExpressionRef[] = new Array(k); + const sizs: Index[] = new Array(k); for (i = 0; i < k; ++i) { const buffer: Uint8Array = segments[i].buffer; const offset: U64 = segments[i].offset; @@ -569,7 +598,7 @@ export class Module { } } - setStart(func: BinaryenFunctionRef): void { + setStart(func: FunctionRef): void { if (this.noEmit) return; _BinaryenSetStart(this.ref, func); } @@ -595,42 +624,42 @@ export class Module { } } -export function getExpressionId(expr: BinaryenExpressionRef): ExpressionId { +export function getExpressionId(expr: ExpressionRef): ExpressionId { return _BinaryenExpressionGetId(expr); } -export function getExpressionType(expr: BinaryenExpressionRef): Type { +export function getExpressionType(expr: ExpressionRef): NativeType { return _BinaryenExpressionGetType(expr); } -export function printExpression(expr: BinaryenExpressionRef): void { +export function printExpression(expr: ExpressionRef): void { return _BinaryenExpressionPrint(expr); } -export function getConstValueI32(expr: BinaryenExpressionRef): i32 { +export function getConstValueI32(expr: ExpressionRef): i32 { return _BinaryenConstGetValueI32(expr); } -export function getConstValueI64Low(expr: BinaryenExpressionRef): i32 { +export function getConstValueI64Low(expr: ExpressionRef): i32 { return _BinaryenConstGetValueI64Low(expr); } -export function getConstValueI64High(expr: BinaryenExpressionRef): i32 { +export function getConstValueI64High(expr: ExpressionRef): i32 { return _BinaryenConstGetValueI64High(expr); } -export function getConstValueI64(expr: BinaryenExpressionRef): I64 { +export function getConstValueI64(expr: ExpressionRef): I64 { return new I64( _BinaryenConstGetValueI64Low(expr), _BinaryenConstGetValueI64High(expr) ); } -export function getConstValueF32(expr: BinaryenExpressionRef): f32 { +export function getConstValueF32(expr: ExpressionRef): f32 { return _BinaryenConstGetValueF32(expr); } -export function getConstValueF64(expr: BinaryenExpressionRef): f64 { +export function getConstValueF64(expr: ExpressionRef): f64 { return _BinaryenConstGetValueF64(expr); } @@ -658,22 +687,22 @@ export class Relooper { private constructor() {} - addBlock(code: BinaryenExpressionRef): RelooperBlockRef { + addBlock(code: ExpressionRef): RelooperBlockRef { if (this.noEmit) return 0; return _RelooperAddBlock(this.ref, code); } - addBranch(from: RelooperBlockRef, to: RelooperBlockRef, condition: BinaryenExpressionRef = 0, code: BinaryenExpressionRef = 0): void { + addBranch(from: RelooperBlockRef, to: RelooperBlockRef, condition: ExpressionRef = 0, code: ExpressionRef = 0): void { if (this.noEmit) return; _RelooperAddBranch(from, to, condition, code); } - addBlockWithSwitch(code: BinaryenExpressionRef, condition: BinaryenExpressionRef): RelooperBlockRef { + addBlockWithSwitch(code: ExpressionRef, condition: ExpressionRef): RelooperBlockRef { if (this.noEmit) return 0; return _RelooperAddBlockWithSwitch(this.ref, code, condition); } - addBranchForSwitch(from: RelooperBlockRef, to: RelooperBlockRef, indexes: i32[], code: BinaryenExpressionRef = 0): void { + addBranchForSwitch(from: RelooperBlockRef, to: RelooperBlockRef, indexes: i32[], code: ExpressionRef = 0): void { if (this.noEmit) return; const cArr: CArray = allocI32Array(indexes); try { @@ -683,7 +712,7 @@ export class Relooper { } } - renderAndDispose(entry: RelooperBlockRef, labelHelper: BinaryenIndex): BinaryenExpressionRef { + renderAndDispose(entry: RelooperBlockRef, labelHelper: Index): ExpressionRef { if (this.noEmit) return 0; return _RelooperRenderAndDispose(this.ref, entry, labelHelper, this.module.ref); } diff --git a/src/program.ts b/src/program.ts index c93e8cf7..367c17ea 100644 --- a/src/program.ts +++ b/src/program.ts @@ -832,7 +832,7 @@ export class Function extends Element { template: FunctionPrototype; /** Concrete type arguments. */ typeArguments: Type[]; - /** Concrete function parameters. */ + /** Concrete function parameters. Excluding `this` if an instance method. */ parameters: Parameter[]; /** Concrete return type. */ returnType: Type; @@ -877,7 +877,7 @@ export class Function extends Element { addLocal(type: Type, name: string | null = null): Local { // if it has a name, check previously as this method will throw otherwise let localIndex = this.parameters.length + this.additionalLocals.length; - if (this.instanceMethodOf) localIndex++; // plus 'this' + if (this.isInstance) localIndex++; // plus 'this' const local: Local = new Local(this.template.program, name ? name : "anonymous$" + localIndex.toString(10), localIndex, type); if (name) { if (this.locals.has(name)) diff --git a/src/tsconfig.json b/src/tsconfig.json index 9ee738f0..1472d176 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -20,15 +20,15 @@ }, "files": [ "ast.ts", - "binaryen.d.ts", - "binaryen.ts", "compiler.ts", "diagnosticMessages.generated.ts", "diagnostics.ts", "evaluator.ts", + "glue/binaryen.d.ts", "glue/js.d.ts", "glue/js.ts", "index.ts", + "moddule.ts", "parser.ts", "program.ts", "tokenizer.ts", diff --git a/tests/parser/fixtures/precedence.tree.ts b/tests/parser/fixtures/precedence.tree.ts deleted file mode 100644 index 805d32d0..00000000 --- a/tests/parser/fixtures/precedence.tree.ts +++ /dev/null @@ -1,171 +0,0 @@ -// incrementing precedence - a -= - b - ? - X - : - c - || - d - && - e - | - f - ^ - g - & - h - == - i - < - j - << - k - + - l - / - ++ - m - ++ -; -// decrementing precedence - ++ - a - ++ - / - b - + - c - << - d - < - e - == - f - & - g - ^ - h - | - i - && - j - || - k -? - X -: - y - = - l -; -// assignment (right-to-left) - a -= - b - += - c - -= - d - **= - e - *= - f - /= - g - %= - h - <<= - i - >>= - j - >>>= - k - &= - l - ^= - m - |= - n -; -// equality (left-to-right) - a - == - b - != - c - === - d -!== - e -; -// relational (left-to-right) - a - < - b - <= - c - > - d ->= - e -; -// bitwise shift (left-to-right) - a - << - b - >> - c ->>> - d -; -// addition (left-to-right) - a - + - b -- - c -; -// exponentiation (right-to-left), multiplication (left-to-right) - a - ** - b - * - c - / - d - % - e - ** - f -* - g -; -// prefix (right-to-left) -delete - void - typeof - -- - ++ - - - + - ~ - ! - a -; -// parenthesized - ( - a - + - ( - b - / - c - ) - - - d - ) -* - e -; diff --git a/tests/parser/index.ts b/tests/parser/index.ts index 80878940..99d9fb90 100644 --- a/tests/parser/index.ts +++ b/tests/parser/index.ts @@ -14,15 +14,7 @@ files.forEach(filename => { const text = fs.readFileSync(__dirname + "/fixtures/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, ""); parser.parseFile(text, filename, true); var sb: string[] = []; - if (isTree) { - const statements = parser.program.sources[0].statements; - statements.forEach(stmt => { - if (stmt.kind != NodeKind.EXPRESSION) return; - (stmt).expression.serializeAsTree(sb, 0); - sb.push(";\n"); - }); - } else - parser.program.sources[0].serialize(sb); + parser.program.sources[0].serialize(sb); const actual = sb.join(""); const expected = isTree ? text : text.replace(/^\s+/mg, ""); const diffs = diff.diffLines(expected, actual);