Skip to content

Commit

Permalink
Merge pull request #4 from mbasso/run_js_function
Browse files Browse the repository at this point in the history
Run js functions into the worker
  • Loading branch information
mbasso authored Nov 2, 2018
2 parents ecfb7ee + 0502935 commit c22ccf1
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 32 deletions.
4 changes: 3 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
"ACTIONS" : false,
"WebAssembly": false,
"getImportObject": false,
"moduleInstance" : false
"moduleInstance" : false,
"importObject": false,
"wasmModule" : false
}
}
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,45 @@ wasmWorker('add.wasm')
// ex is a string that represents the exception
console.error(ex);
});

// you can also run js functions inside the worker
// to access importObject for example
wasmWorker('add.wasm')
.then(module => {
return module.run(({
// module,
// importObject,
instance,
params
}) => {
// here is sync
const sum = instance.exports.add(...params);
return '1 + 2 = ' + sum;
}, [1, 2]);
})
.then(result => {
console.log(result);
});
```

## API

By default wasm-worker exports a single function:

```js
type JsCallback = (context: {
module: WebAssembly.Module,
instance: WebAssembly.Instance,
importObject: importObject,
params: any,
}) => any;

type WasmWorkerModule = {
exports: {
[export: string]: (...any: Array<any>) => Promise<any>
}
},
// run a js function inside the worker and provides it the given params
// ⚠️ Caveat: the function you pass cannot rely on its surrounding scope, since it is executed in an isolated context.
// Please use the "params" parameter to provide some values to the callback
run: (callback: JsCallback, params?: any) => Promise<any>
};

type Options = {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wasm-worker",
"version": "0.3.2",
"version": "0.4.0",
"description": "Move a WebAssembly module into its own thread",
"main": "lib/index.js",
"jsnext:main": "es/index.js",
Expand Down
1 change: 1 addition & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const ACTIONS = {
COMPILE_MODULE: 0,
CALL_FUNCTION_EXPORT: 1,
RUN_FUNCTION: 2,
};

export default ACTIONS;
32 changes: 25 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ import ACTIONS from './actions';
import workerOnMessage from './worker';
import { getWasmSource } from './utils';

const getTransferableParams = (params = []) =>
params.filter(x => (
(x instanceof ArrayBuffer) ||
(x instanceof MessagePort) ||
(x instanceof ImageBitmap)
));

export default function wasmWorker(source, options = {}) {
let currentId = 0;
const promises = {};
const { getImportObject, ...otherOptions } = options;

const worker = new Worker(
`data:,ACTIONS=${JSON.stringify(ACTIONS)};getImportObject=${getImportObject};` +
`moduleInstance=null;onmessage=${workerOnMessage}`,
`importObject=undefined;wasmModule=null;moduleInstance=null;onmessage=${workerOnMessage}`,
otherOptions,
);

Expand All @@ -33,18 +40,29 @@ export default function wasmWorker(source, options = {}) {
func: exp,
params,
},
}, params.filter(x => (
(x instanceof ArrayBuffer) ||
(x instanceof MessagePort) ||
(x instanceof ImageBitmap)
)));
}, getTransferableParams(params));
}),
}), {}),
run: (func, params) => new Promise((...rest) => {
// eslint-disable-next-line
promises[++currentId] = rest;
worker.postMessage({
id: currentId,
action: ACTIONS.RUN_FUNCTION,
payload: {
func: func.toString(),
params,
},
}, getTransferableParams(params));
}),
});
} else if (result === 1) {
promises[id][1](payload);
}
} else if (action === ACTIONS.CALL_FUNCTION_EXPORT) {
} else if (
action === ACTIONS.CALL_FUNCTION_EXPORT ||
action === ACTIONS.RUN_FUNCTION
) {
promises[id][result](payload);
}

Expand Down
29 changes: 21 additions & 8 deletions src/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@ export default function worker(e) {
Promise.resolve()
.then(() => {
let res;
const importObject = getImportObject !== undefined
? getImportObject()
: {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({ initial: 256 }),
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' }),
};
if (getImportObject !== undefined) {
// eslint-disable-next-line
importObject = getImportObject();
}

if (typeof payload === 'string') {
res = fetch(payload);
Expand All @@ -46,6 +42,8 @@ export default function worker(e) {
.then(({ module, instance }) => {
// eslint-disable-next-line
moduleInstance = instance;
// eslint-disable-next-line
wasmModule = module;
onSuccess({
exports: WebAssembly.Module
.exports(module)
Expand All @@ -64,5 +62,20 @@ export default function worker(e) {
onSuccess(ctx[func].apply(ctx, params));
})
.catch(onError);
} else if (action === ACTIONS.RUN_FUNCTION) {
const { func, params } = payload;

Promise.resolve()
.then(() => {
// eslint-disable-next-line
const fun = new Function(`return ${func}`)();
onSuccess(fun({
module: wasmModule,
instance: moduleInstance,
importObject,
params,
}));
})
.catch(onError);
}
}
4 changes: 4 additions & 0 deletions test/actions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ describe('actions', () => {
expect(typeof ACTIONS.CALL_FUNCTION_EXPORT).toEqual('number');
});

it('should export a run function action', () => {
expect(typeof ACTIONS.RUN_FUNCTION).toEqual('number');
});

it('should not export duplicated values', () => {
const values = Object.keys(ACTIONS).map(key => ACTIONS[key]);

Expand Down
58 changes: 58 additions & 0 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,62 @@ describe('wasm-worker', () => {
done();
});
});

it('should run a function inside worker', (done) => {
wasmWorker(bytes, {
getImportObject: () => ({
imports: {},
}),
})
.then(wasmModule =>
wasmModule.run(({
module,
instance,
importObject,
params,
}) => {
const err = new Error();
if (params !== undefined) throw err;
if (!(module instanceof WebAssembly.Module)) throw err;
if (!(instance instanceof WebAssembly.Instance)) throw err;
if (importObject.imports === undefined) throw err;

const sum = instance.exports.add(1, 2);
return `1 + 2 = ${sum}`;
}),
)
.then((result) => {
expect(result).toEqual('1 + 2 = 3');
done();
});
});

it('should run a function inside worker with params', (done) => {
wasmWorker(bytes, {
getImportObject: () => ({
imports: {},
}),
})
.then(wasmModule =>
wasmModule.run(({
module,
instance,
importObject,
params,
}) => {
const err = new Error();
if (params === undefined) throw err;
if (!(module instanceof WebAssembly.Module)) throw err;
if (!(instance instanceof WebAssembly.Instance)) throw err;
if (importObject.imports === undefined) throw err;

const sum = instance.exports.add(params[0], params[1]);
return `1 + 2 = ${sum}`;
}, [1, 2]),
)
.then((result) => {
expect(result).toEqual('1 + 2 = 3');
done();
});
});
});
16 changes: 4 additions & 12 deletions test/worker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,10 @@ describe('worker', () => {

/* eslint-disable */
const ACTIONS = ACTIONZ;
let importObject = undefined;
let wasmModule = null;
let moduleInstance = null;
const getImportObject = undefined;
const importObject = {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({ initial: 256 }),
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' }),
};

// helper variables
const id = 0;
Expand Down Expand Up @@ -89,14 +85,10 @@ describe('worker', () => {

/* eslint-disable */
const ACTIONS = ACTIONZ;
let importObject = undefined;
let wasmModule = null;
let moduleInstance = null;
const getImportObject = undefined;
const importObject = {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({ initial: 256 }),
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' }),
};

// helper variables
const id = 0;
Expand Down

0 comments on commit c22ccf1

Please sign in to comment.