diff --git a/src/benchmark/index.ts b/src/benchmark/index.ts index 82e9ea9..021561b 100644 --- a/src/benchmark/index.ts +++ b/src/benchmark/index.ts @@ -97,16 +97,16 @@ const mergeSortFunc = { ['mergeSortCopyArray', { parameterTypes: [WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, - WebAssemblyType.INT_32], returnType: WebAssemblyType.INT_32 }], + WebAssemblyType.INT_32], returnType: WebAssemblyType.INT_32_ARRAY }], ['mergeSort', { parameterTypes: [WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, - WebAssemblyType.INT_32], returnType: WebAssemblyType.INT_32 }], + WebAssemblyType.INT_32], returnType: WebAssemblyType.INT_32_ARRAY }], ['mergeSortMerge', { parameterTypes: [WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, WebAssemblyType.INT_32, - WebAssemblyType.INT_32], returnType: WebAssemblyType.INT_32 }], + WebAssemblyType.INT_32], returnType: WebAssemblyType.INT_32_ARRAY }], ['mergeSortIsSorted', { parameterTypes: [WebAssemblyType.INT_32_ARRAY], returnType: WebAssemblyType.BOOLEAN }], ['mergeSortFill', { parameterTypes: [WebAssemblyType.INT_32_ARRAY], returnType: WebAssemblyType.INT_32 }], ['mergeSortWhile', { parameterTypes: [WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY], @@ -126,7 +126,7 @@ const quicksortFunc = { WebAssemblyType.INT_32, WebAssemblyType.INT_32], returnType: WebAssemblyType.INT_32 }], ['quickSort', { parameterTypes: [WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, WebAssemblyType.INT_32], - returnType: WebAssemblyType.INT_32 }], + returnType: WebAssemblyType.INT_32_ARRAY }], ['quickSortFill', { parameterTypes: [WebAssemblyType.INT_32_ARRAY], returnType: WebAssemblyType.INT_32 }], ['quickSortIsSorted', { parameterTypes: [WebAssemblyType.INT_32_ARRAY], returnType: WebAssemblyType.BOOLEAN }], ['quickSortWhile', { parameterTypes: [WebAssemblyType.INT_32_ARRAY], returnType: WebAssemblyType.BOOLEAN }], diff --git a/src/generator/generator.ts b/src/generator/generator.ts index de5f3c2..f77e142 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -68,7 +68,7 @@ class Generator { const totalMapping = Generator.mergeMappings(parameterMapping, variableMapping); - const generatorVisitor = new GeneratorVisitor(module, totalMapping, expressionTypes, signatures); + const generatorVisitor = new GeneratorVisitor(module, totalMapping, expressionTypes, signatures, signature); const body = generatorVisitor.run(tree); const {parameterTypes, returnType} = signature; diff --git a/src/generator/generator_visitor.ts b/src/generator/generator_visitor.ts index e788d1e..a5200f4 100644 --- a/src/generator/generator_visitor.ts +++ b/src/generator/generator_visitor.ts @@ -30,9 +30,9 @@ import {Expression, Module, Statement} from 'binaryen'; import CallWrapper from '../call_wrapper'; import Visitor from '../visitor'; import {VariableMapping} from './declaration_visitor'; +import {FunctionSignature, FunctionSignatures} from './generator'; import {ExpressionTypes} from './type_inference_visitor'; -import {getCommonType, toBinaryenType, WebAssemblyType} from './wasm_type'; -import {FunctionSignatures} from './generator'; +import {getCommonType, isTypeCompatible, toBinaryenType, WebAssemblyType} from './wasm_type'; type BinaryExpressionFunction = (first: Expression, second: Expression) => Expression; @@ -42,6 +42,7 @@ class GeneratorVisitor extends Visitor { private readonly variableMapping: VariableMapping; private readonly expressionTypes: ExpressionTypes; private readonly signatures: FunctionSignatures; + private readonly signature: FunctionSignature; private statements: Statement[] = []; private expressions: Expression[] = []; @@ -51,13 +52,15 @@ class GeneratorVisitor extends Visitor { constructor(module: Module, variableMapping: VariableMapping, expressionType: ExpressionTypes, - signatures: FunctionSignatures) { + signatures: FunctionSignatures, + signature: FunctionSignature) { super(); this.module = module; this.variableMapping = variableMapping; this.expressionTypes = expressionType; this.signatures = signatures; + this.signature = signature; } public run(tree: FunctionDeclaration): Statement { @@ -83,8 +86,14 @@ class GeneratorVisitor extends Visitor { protected visitReturnStatement(node: ReturnStatement) { super.visitReturnStatement(node); - const returnStatement = this.module.return(this.expressions.pop()); - this.statements.push(returnStatement); + if (node.argument !== null) { + if (!isTypeCompatible(this.getExpressionType(node.argument), this.signature.returnType)) { + throw new Error(`Returned value is not compatible with ` + + `returntype ${WebAssemblyType[this.signature.returnType]}`); + } + } + + this.statements.push(this.module.return(this.popExpression())); } protected visitUnaryExpression(node: UnaryExpression) { diff --git a/src/generator/wasm_type.ts b/src/generator/wasm_type.ts index 9d54ff8..a664e0c 100644 --- a/src/generator/wasm_type.ts +++ b/src/generator/wasm_type.ts @@ -48,6 +48,12 @@ function getCommonType(first: WebAssemblyType, second: WebAssemblyType) { } } +function isTypeCompatible(toCheck: WebAssemblyType, type: WebAssemblyType) { + return (toCheck === type) || + (toCheck === WebAssemblyType.INT_32 && type === WebAssemblyType.FLOAT_64) || + (toCheck === WebAssemblyType.INT_32_ARRAY && type === WebAssemblyType.FLOAT_64_ARRAY); +} + function isOfType(toCheck: any, type: WebAssemblyType) { switch (type) { case WebAssemblyType.INT_32: @@ -88,4 +94,4 @@ function isArray(value: any, type: WebAssemblyType) { } } -export {WebAssemblyType, toBinaryenType, getNumberType, getCommonType, isOfType}; +export {WebAssemblyType, toBinaryenType, getNumberType, getCommonType, isTypeCompatible, isOfType}; diff --git a/test/time_limited/transpiler_test.ts b/test/time_limited/transpiler_test.ts index 4223cf8..08af56d 100644 --- a/test/time_limited/transpiler_test.ts +++ b/test/time_limited/transpiler_test.ts @@ -86,7 +86,7 @@ describe('Transpiler', function() { WebAssemblyType.INT_32, WebAssemblyType.INT_32) .setSignature('quickSortPartition', WebAssemblyType.INT_32, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, WebAssemblyType.INT_32) - .setSignature('quickSort', WebAssemblyType.INT_32, WebAssemblyType.INT_32_ARRAY, + .setSignature('quickSort', WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, WebAssemblyType.INT_32) .setSignature('quickSortFill', WebAssemblyType.INT_32, WebAssemblyType.INT_32_ARRAY) .setSignature('quickSortIsSorted', WebAssemblyType.BOOLEAN, WebAssemblyType.INT_32_ARRAY) @@ -101,11 +101,11 @@ describe('Transpiler', function() { const content = mergeSortCopyArray.toString() + mergeSort.toString() + mergeSortMerge.toString() + mergeSortIsSorted.toString() + mergeSortFill.toString() + mergeSortWhile.toString(); const wrapper = transpiler - .setSignature('mergeSortCopyArray', WebAssemblyType.INT_32, WebAssemblyType.INT_32_ARRAY, + .setSignature('mergeSortCopyArray', WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, WebAssemblyType.INT_32) - .setSignature('mergeSort', WebAssemblyType.INT_32, WebAssemblyType.INT_32_ARRAY, + .setSignature('mergeSort', WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, WebAssemblyType.INT_32) - .setSignature('mergeSortMerge', WebAssemblyType.INT_32, WebAssemblyType.INT_32_ARRAY, + .setSignature('mergeSortMerge', WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32_ARRAY, WebAssemblyType.INT_32, WebAssemblyType.INT_32, WebAssemblyType.INT_32) .setSignature('mergeSortIsSorted', WebAssemblyType.BOOLEAN, WebAssemblyType.INT_32_ARRAY) diff --git a/test/transpiler/transpiler_function_test.ts b/test/transpiler/transpiler_function_test.ts index 67e5717..fd4d6d3 100644 --- a/test/transpiler/transpiler_function_test.ts +++ b/test/transpiler/transpiler_function_test.ts @@ -83,17 +83,9 @@ describe('Transpiler', () => { }); it('should handle function call with not matching return type conversion', () => { - const content = 'function func(value) { return value; }'; - - const wrapper = transpiler + expect(() => transpiler .setSignature('func', WebAssemblyType.BOOLEAN, WebAssemblyType.INT_32) - .transpile(content); - wrapper.setFunctionName('func'); - - expect(wrapper.call(1)).to.equal(true); - expect(wrapper.call(0)).to.equal(false); - expect(() => wrapper.call(2)).to.throw(); - expect(() => wrapper.call(-1)).to.throw(); + .transpile('function func(value) { return value; }')).to.throw(); }); it('should handle different types for one variable', () => { diff --git a/test/transpiler/transpiler_literal_test.ts b/test/transpiler/transpiler_literal_test.ts index 794583e..d02cf4c 100644 --- a/test/transpiler/transpiler_literal_test.ts +++ b/test/transpiler/transpiler_literal_test.ts @@ -39,5 +39,23 @@ describe('Transpiler', () => { .transpile('function alwaysFalse() { return false; }'); expect(wrapper2.setFunctionName('alwaysFalse').call()).to.equal(false); }); + + it('should handle integer as boolean values', () => { + expect(() => transpiler + .setSignature('integer', WebAssemblyType.BOOLEAN) + .transpile('function integer() { return 0; }')).to.throw(); + + expect(() => new Transpiler() + .setSignature('integer', WebAssemblyType.BOOLEAN) + .transpile('function integer() { return 1; }')).to.throw(); + + expect(() => new Transpiler() + .setSignature('integer', WebAssemblyType.BOOLEAN) + .transpile('function integer() { return 2; }')).to.throw(); + + expect(() => new Transpiler() + .setSignature('integer', WebAssemblyType.BOOLEAN) + .transpile('function integer() { return -2; }')).to.throw(); + }); }); }); diff --git a/test/transpiler/transpiler_variable_test.ts b/test/transpiler/transpiler_variable_test.ts index 79aeb06..e3632c1 100644 --- a/test/transpiler/transpiler_variable_test.ts +++ b/test/transpiler/transpiler_variable_test.ts @@ -77,7 +77,26 @@ describe('Transpiler', () => { expect(wrapper.setFunctionName('variables').call(100)).to.equal(10); }); + it('should handle boolean parameters', () => { + const content = 'function variables(x) { return x; }'; + const wrapper = transpiler + .setSignature('variables', WebAssemblyType.BOOLEAN, WebAssemblyType.BOOLEAN) + .transpile(content); + + expect(wrapper.setFunctionName('variables').call(true)).to.equal(true); + expect(wrapper.setFunctionName('variables').call(false)).to.equal(false); + }); + it('should handle boolean variable', () => { + const content = 'function variables() { var x = true; return x; }'; + const wrapper = transpiler + .setSignature('variables', WebAssemblyType.BOOLEAN) + .transpile(content); + + expect(wrapper.setFunctionName('variables').call()).to.equal(true); + }); + + it('should handle boolean variables in expression', () => { const content = 'function variables() { var x = true; var y; y = false; return x || y; }'; const wrapper = transpiler .setSignature('variables', WebAssemblyType.BOOLEAN)