From 675afb0c3126dfc785bcd2ac23205ebcc0cf009c Mon Sep 17 00:00:00 2001 From: Surma Date: Mon, 22 Mar 2021 23:41:12 +0000 Subject: [PATCH 1/3] Add a promise test --- lib/asbind-instance/asbind-instance.js | 6 + lib/asbind-instance/bind-function.js | 178 ++++++++++++++++++++----- test/test-runner.js | 11 +- test/tests/string-promise/asc.ts | 5 + test/tests/string-promise/config.js | 5 + test/tests/string-promise/test.js | 12 ++ 6 files changed, 179 insertions(+), 38 deletions(-) create mode 100644 test/tests/string-promise/asc.ts create mode 100644 test/tests/string-promise/config.js create mode 100644 test/tests/string-promise/test.js diff --git a/lib/asbind-instance/asbind-instance.js b/lib/asbind-instance/asbind-instance.js index d09316c..1597498 100644 --- a/lib/asbind-instance/asbind-instance.js +++ b/lib/asbind-instance/asbind-instance.js @@ -45,6 +45,7 @@ export default class AsbindInstance { this.unboundExports = {}; this.exports = {}; this.importObject = {}; + this.asyncifyStorageSize = 8 * 1024; } getTypeId(typeName) { @@ -62,6 +63,11 @@ export default class AsbindInstance { } _validate() { + this.isAsyncifyModule = Boolean( + WebAssembly.Module.exports(this.module).find( + ({ name }) => name === "asyncify_start_unwind" + ) + ); if ( !WebAssembly.Module.exports(this.module).find(exp => exp.name === "__new") ) { diff --git a/lib/asbind-instance/bind-function.js b/lib/asbind-instance/bind-function.js index e96b347..e3f08ae 100644 --- a/lib/asbind-instance/bind-function.js +++ b/lib/asbind-instance/bind-function.js @@ -26,26 +26,92 @@ export function bindImportFunction( // Create a wrapper function that applies the correct converter function to arguments and // return value respectively. - return function(...args) { - if (args.length != argumentConverterFunctions.length) { - throw Error( - `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` + if (!asbindInstance.isAsyncifyModule) { + return function(...args) { + if (args.length != argumentConverterFunctions.length) { + throw Error( + `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` + ); + } + const newArgs = args.map((v, i) => + argumentConverterFunctions[i]( + asbindInstance, + v, + importedFunctionDescriptor.parameters[i] + ) ); - } - const newArgs = args.map((v, i) => - argumentConverterFunctions[i]( + const result = importedFunction(...newArgs); + return returnValueConverterFunction( asbindInstance, - v, - importedFunctionDescriptor.parameters[i] - ) - ); - const result = importedFunction(...newArgs); - return returnValueConverterFunction( - asbindInstance, - result, - importedFunctionDescriptor.returnType - ); - }; + result, + importedFunctionDescriptor.returnType + ); + }; + } else { + return function(...args) { + if ( + asbindInstance.loadedModule.exports.asyncify_get_state() === + 2 /* Rewinding */ + ) { + asbindInstance.loadedModule.exports.asyncify_stop_rewind(); + asbindInstance.loadedModule.exports.__unpin( + asbindInstance.asyncifyState.ptr + ); + return returnValueConverterFunction( + asbindInstance, + asbindInstance.asyncifyState.value, + importedFunctionDescriptor.returnType + ); + } + + if (args.length != argumentConverterFunctions.length) { + throw Error( + `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` + ); + } + const newArgs = args.map((v, i) => + argumentConverterFunctions[i]( + asbindInstance, + v, + importedFunctionDescriptor.parameters[i] + ) + ); + const result = importedFunction(...newArgs); + if (!(result instanceof Promise)) { + return returnValueConverterFunction( + asbindInstance, + result, + importedFunctionDescriptor.returnType + ); + } + asbindInstance.asyncifyState = { + ptr: asbindInstance.loadedModule.exports.__new( + asbindInstance.asyncifyStorageSize, + 0 + ) + }; + asbindInstance.loadedModule.exports.__pin( + asbindInstance.asyncifyState.ptr + ); + const dv = new DataView( + asbindInstance.loadedModule.exports.memory.buffer + ); + dv.setUint32( + asbindInstance.asyncifyState.ptr, + asbindInstance.asyncifyState.ptr + 8, + true + ); + dv.setUint32( + asbindInstance.asyncifyState.ptr + 4, + asbindInstance.asyncifyState.ptr + 8 * 1024, + true + ); + asbindInstance.loadedModule.exports.asyncify_start_unwind( + asbindInstance.asyncifyState.ptr + ); + asbindInstance.asyncifyState.promise = result; + }; + } } // Function that takes in an asbind instance, and the key to the export function on the @@ -65,24 +131,64 @@ export function bindExportFunction( // Create a wrapper function that applies the correct converter function to arguments and // return value respectively. - return (...args) => { - if (args.length != argumentConverterFunctions.length) { - throw Error( - `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` + if (!asbindInstance.isAsyncifyModule) { + return (...args) => { + if (args.length != argumentConverterFunctions.length) { + throw Error( + `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` + ); + } + const newArgs = args.map((v, i) => + argumentConverterFunctions[i]( + asbindInstance, + v, + exportedFunctionDescriptor.parameters[i] + ) ); - } - const newArgs = args.map((v, i) => - argumentConverterFunctions[i]( + const result = exportedFunction(...newArgs); + return returnValueConverterFunction( asbindInstance, - v, - exportedFunctionDescriptor.parameters[i] - ) - ); - const result = exportedFunction(...newArgs); - return returnValueConverterFunction( - asbindInstance, - result, - exportedFunctionDescriptor.returnType - ); - }; + result, + exportedFunctionDescriptor.returnType + ); + }; + } else { + return (...args) => { + if (args.length != argumentConverterFunctions.length) { + throw Error( + `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` + ); + } + const newArgs = args.map((v, i) => + argumentConverterFunctions[i]( + asbindInstance, + v, + exportedFunctionDescriptor.parameters[i] + ) + ); + return function f(...args) { + const result = exportedFunction(...args); + if ( + asbindInstance.loadedModule.exports.asyncify_get_state() === + 0 /* Normal */ + ) { + return returnValueConverterFunction( + asbindInstance, + result, + exportedFunctionDescriptor.returnType + ); + } + asbindInstance.loadedModule.exports.asyncify_stop_unwind(); + let localAsyncifyState = asbindInstance.asyncifyState; + return localAsyncifyState.promise.then(value => { + localAsyncifyState.value = value; + asbindInstance.asyncifyState = localAsyncifyState; + asbindInstance.loadedModule.exports.asyncify_start_rewind( + asbindInstance.asyncifyState.ptr + ); + return f(...args); + }); + }.bind(this)(...newArgs); + }; + } } diff --git a/test/test-runner.js b/test/test-runner.js index 7456d41..095d806 100644 --- a/test/test-runner.js +++ b/test/test-runner.js @@ -1,5 +1,6 @@ const { promisify } = require("util"); const fs = require("fs/promises"); +const path = require("path"); const Express = require("express"); const Mocha = require("mocha"); @@ -32,7 +33,7 @@ async function compileAllAsc() { const transformFile = require.resolve("../dist/transform.cjs.js"); for (const ascFile of ascFiles) { console.log(`Compiling ${ascFile}...`); - await asc.main([ + const params = [ "--runtime", "stub", "--exportRuntime", @@ -41,7 +42,13 @@ async function compileAllAsc() { "--binaryFile", ascFile.replace(/\.ts$/, ".wasm"), ascFile - ]); + ]; + try { + const configFile = "./" + path.join(path.dirname(ascFile), "config.js"); + const config = require(configFile); + await config?.params?.(params); + } catch (e) {} + await asc.main(params); } } diff --git a/test/tests/string-promise/asc.ts b/test/tests/string-promise/asc.ts new file mode 100644 index 0000000..92a577a --- /dev/null +++ b/test/tests/string-promise/asc.ts @@ -0,0 +1,5 @@ +export function swapAndPad(a: string, b: string): string { + return "!" + swappedConcat(a, b) + "!"; +} + +declare function swappedConcat(a: string, b: string): string; diff --git a/test/tests/string-promise/config.js b/test/tests/string-promise/config.js new file mode 100644 index 0000000..7c95bef --- /dev/null +++ b/test/tests/string-promise/config.js @@ -0,0 +1,5 @@ +module.exports = { + params(param) { + param.push("--runPasses", "asyncify"); + } +}; diff --git a/test/tests/string-promise/test.js b/test/tests/string-promise/test.js new file mode 100644 index 0000000..4af38cc --- /dev/null +++ b/test/tests/string-promise/test.js @@ -0,0 +1,12 @@ +describe("as-bind", function() { + it("should handle strings", async function() { + const instance = await AsBind.instantiate(this.rawModule, { + asc: { + async swappedConcat(a, b) { + return b + a; + } + } + }); + assert((await instance.exports.swapAndPad("a", "b")) === "!ba!"); + }); +}); From 1f51c356c98b0cb04eabf40468f679aa8623ad0f Mon Sep 17 00:00:00 2001 From: Surma Date: Mon, 22 Mar 2021 23:48:37 +0000 Subject: [PATCH 2/3] Remove duplicated code --- lib/asbind-instance/asbind-instance.js | 6 + lib/asbind-instance/bind-function.js | 210 +++++++++---------------- 2 files changed, 83 insertions(+), 133 deletions(-) diff --git a/lib/asbind-instance/asbind-instance.js b/lib/asbind-instance/asbind-instance.js index 1597498..4422792 100644 --- a/lib/asbind-instance/asbind-instance.js +++ b/lib/asbind-instance/asbind-instance.js @@ -142,5 +142,11 @@ export default class AsbindInstance { descriptor ); } + + if (!this.isAsyncifyModule) { + // If this module wasn’t built with Ayncify, we mock + // the asyncify state function to return that we are in “normal” mode. + this.exports.asyncify_get_state = () => 0; + } } } diff --git a/lib/asbind-instance/bind-function.js b/lib/asbind-instance/bind-function.js index e3f08ae..2536ee1 100644 --- a/lib/asbind-instance/bind-function.js +++ b/lib/asbind-instance/bind-function.js @@ -26,92 +26,62 @@ export function bindImportFunction( // Create a wrapper function that applies the correct converter function to arguments and // return value respectively. - if (!asbindInstance.isAsyncifyModule) { - return function(...args) { - if (args.length != argumentConverterFunctions.length) { - throw Error( - `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` - ); - } - const newArgs = args.map((v, i) => - argumentConverterFunctions[i]( - asbindInstance, - v, - importedFunctionDescriptor.parameters[i] - ) + return function(...args) { + if (asbindInstance.exports.asyncify_get_state() === 2 /* Rewinding */) { + asbindInstance.loadedModule.exports.asyncify_stop_rewind(); + asbindInstance.loadedModule.exports.__unpin( + asbindInstance.asyncifyState.ptr ); - const result = importedFunction(...newArgs); return returnValueConverterFunction( asbindInstance, - result, + asbindInstance.asyncifyState.value, importedFunctionDescriptor.returnType ); - }; - } else { - return function(...args) { - if ( - asbindInstance.loadedModule.exports.asyncify_get_state() === - 2 /* Rewinding */ - ) { - asbindInstance.loadedModule.exports.asyncify_stop_rewind(); - asbindInstance.loadedModule.exports.__unpin( - asbindInstance.asyncifyState.ptr - ); - return returnValueConverterFunction( - asbindInstance, - asbindInstance.asyncifyState.value, - importedFunctionDescriptor.returnType - ); - } + } - if (args.length != argumentConverterFunctions.length) { - throw Error( - `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` - ); - } - const newArgs = args.map((v, i) => - argumentConverterFunctions[i]( - asbindInstance, - v, - importedFunctionDescriptor.parameters[i] - ) - ); - const result = importedFunction(...newArgs); - if (!(result instanceof Promise)) { - return returnValueConverterFunction( - asbindInstance, - result, - importedFunctionDescriptor.returnType - ); - } - asbindInstance.asyncifyState = { - ptr: asbindInstance.loadedModule.exports.__new( - asbindInstance.asyncifyStorageSize, - 0 - ) - }; - asbindInstance.loadedModule.exports.__pin( - asbindInstance.asyncifyState.ptr - ); - const dv = new DataView( - asbindInstance.loadedModule.exports.memory.buffer - ); - dv.setUint32( - asbindInstance.asyncifyState.ptr, - asbindInstance.asyncifyState.ptr + 8, - true + if (args.length != argumentConverterFunctions.length) { + throw Error( + `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` ); - dv.setUint32( - asbindInstance.asyncifyState.ptr + 4, - asbindInstance.asyncifyState.ptr + 8 * 1024, - true - ); - asbindInstance.loadedModule.exports.asyncify_start_unwind( - asbindInstance.asyncifyState.ptr + } + const newArgs = args.map((v, i) => + argumentConverterFunctions[i]( + asbindInstance, + v, + importedFunctionDescriptor.parameters[i] + ) + ); + const result = importedFunction(...newArgs); + if (!asbindInstance.isAsyncifyModule || !(result instanceof Promise)) { + return returnValueConverterFunction( + asbindInstance, + result, + importedFunctionDescriptor.returnType ); - asbindInstance.asyncifyState.promise = result; + } + asbindInstance.asyncifyState = { + ptr: asbindInstance.loadedModule.exports.__new( + asbindInstance.asyncifyStorageSize, + 0 + ) }; - } + asbindInstance.loadedModule.exports.__pin(asbindInstance.asyncifyState.ptr); + const dv = new DataView(asbindInstance.loadedModule.exports.memory.buffer); + dv.setUint32( + asbindInstance.asyncifyState.ptr, + asbindInstance.asyncifyState.ptr + 8, + true + ); + dv.setUint32( + asbindInstance.asyncifyState.ptr + 4, + asbindInstance.asyncifyState.ptr + 8 * 1024, + true + ); + asbindInstance.loadedModule.exports.asyncify_start_unwind( + asbindInstance.asyncifyState.ptr + ); + asbindInstance.asyncifyState.promise = result; + }; } // Function that takes in an asbind instance, and the key to the export function on the @@ -131,64 +101,38 @@ export function bindExportFunction( // Create a wrapper function that applies the correct converter function to arguments and // return value respectively. - if (!asbindInstance.isAsyncifyModule) { - return (...args) => { - if (args.length != argumentConverterFunctions.length) { - throw Error( - `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` - ); - } - const newArgs = args.map((v, i) => - argumentConverterFunctions[i]( - asbindInstance, - v, - exportedFunctionDescriptor.parameters[i] - ) + return (...args) => { + if (args.length != argumentConverterFunctions.length) { + throw Error( + `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` ); - const result = exportedFunction(...newArgs); - return returnValueConverterFunction( + } + const newArgs = args.map((v, i) => + argumentConverterFunctions[i]( asbindInstance, - result, - exportedFunctionDescriptor.returnType - ); - }; - } else { - return (...args) => { - if (args.length != argumentConverterFunctions.length) { - throw Error( - `Expected ${argumentConverterFunctions.length} arguments, got ${args.length}` + v, + exportedFunctionDescriptor.parameters[i] + ) + ); + return function f(...args) { + const result = exportedFunction(...args); + if (asbindInstance.exports.asyncify_get_state() === 0 /* Normal */) { + return returnValueConverterFunction( + asbindInstance, + result, + exportedFunctionDescriptor.returnType ); } - const newArgs = args.map((v, i) => - argumentConverterFunctions[i]( - asbindInstance, - v, - exportedFunctionDescriptor.parameters[i] - ) - ); - return function f(...args) { - const result = exportedFunction(...args); - if ( - asbindInstance.loadedModule.exports.asyncify_get_state() === - 0 /* Normal */ - ) { - return returnValueConverterFunction( - asbindInstance, - result, - exportedFunctionDescriptor.returnType - ); - } - asbindInstance.loadedModule.exports.asyncify_stop_unwind(); - let localAsyncifyState = asbindInstance.asyncifyState; - return localAsyncifyState.promise.then(value => { - localAsyncifyState.value = value; - asbindInstance.asyncifyState = localAsyncifyState; - asbindInstance.loadedModule.exports.asyncify_start_rewind( - asbindInstance.asyncifyState.ptr - ); - return f(...args); - }); - }.bind(this)(...newArgs); - }; - } + asbindInstance.loadedModule.exports.asyncify_stop_unwind(); + let localAsyncifyState = asbindInstance.asyncifyState; + return localAsyncifyState.promise.then(value => { + localAsyncifyState.value = value; + asbindInstance.asyncifyState = localAsyncifyState; + asbindInstance.loadedModule.exports.asyncify_start_rewind( + asbindInstance.asyncifyState.ptr + ); + return f(...args); + }); + }.bind(this)(...newArgs); + }; } From 1584919f05c57e906e5268668626d66d298d6279 Mon Sep 17 00:00:00 2001 From: Surma Date: Tue, 23 Mar 2021 00:00:17 +0000 Subject: [PATCH 3/3] Forgot that one constant --- lib/asbind-instance/bind-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/asbind-instance/bind-function.js b/lib/asbind-instance/bind-function.js index 2536ee1..a5e7208 100644 --- a/lib/asbind-instance/bind-function.js +++ b/lib/asbind-instance/bind-function.js @@ -74,7 +74,7 @@ export function bindImportFunction( ); dv.setUint32( asbindInstance.asyncifyState.ptr + 4, - asbindInstance.asyncifyState.ptr + 8 * 1024, + asbindInstance.asyncifyState.ptr + asbindInstance.asyncifyStorageSize, true ); asbindInstance.loadedModule.exports.asyncify_start_unwind(