Skip to content

Commit c22ccf1

Browse files
authored
Merge pull request #4 from mbasso/run_js_function
Run js functions into the worker
2 parents ecfb7ee + 0502935 commit c22ccf1

9 files changed

+148
-32
lines changed

.eslintrc

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
"ACTIONS" : false,
2626
"WebAssembly": false,
2727
"getImportObject": false,
28-
"moduleInstance" : false
28+
"moduleInstance" : false,
29+
"importObject": false,
30+
"wasmModule" : false
2931
}
3032
}

README.md

+31-3
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,45 @@ wasmWorker('add.wasm')
4040
// ex is a string that represents the exception
4141
console.error(ex);
4242
});
43+
44+
// you can also run js functions inside the worker
45+
// to access importObject for example
46+
wasmWorker('add.wasm')
47+
.then(module => {
48+
return module.run(({
49+
// module,
50+
// importObject,
51+
instance,
52+
params
53+
}) => {
54+
// here is sync
55+
const sum = instance.exports.add(...params);
56+
return '1 + 2 = ' + sum;
57+
}, [1, 2]);
58+
})
59+
.then(result => {
60+
console.log(result);
61+
});
4362
```
4463

4564
## API
4665

47-
By default wasm-worker exports a single function:
48-
4966
```js
67+
type JsCallback = (context: {
68+
module: WebAssembly.Module,
69+
instance: WebAssembly.Instance,
70+
importObject: importObject,
71+
params: any,
72+
}) => any;
73+
5074
type WasmWorkerModule = {
5175
exports: {
5276
[export: string]: (...any: Array<any>) => Promise<any>
53-
}
77+
},
78+
// run a js function inside the worker and provides it the given params
79+
// ⚠️ Caveat: the function you pass cannot rely on its surrounding scope, since it is executed in an isolated context.
80+
// Please use the "params" parameter to provide some values to the callback
81+
run: (callback: JsCallback, params?: any) => Promise<any>
5482
};
5583

5684
type Options = {

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "wasm-worker",
3-
"version": "0.3.2",
3+
"version": "0.4.0",
44
"description": "Move a WebAssembly module into its own thread",
55
"main": "lib/index.js",
66
"jsnext:main": "es/index.js",

src/actions.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const ACTIONS = {
22
COMPILE_MODULE: 0,
33
CALL_FUNCTION_EXPORT: 1,
4+
RUN_FUNCTION: 2,
45
};
56

67
export default ACTIONS;

src/index.js

+25-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@ import ACTIONS from './actions';
22
import workerOnMessage from './worker';
33
import { getWasmSource } from './utils';
44

5+
const getTransferableParams = (params = []) =>
6+
params.filter(x => (
7+
(x instanceof ArrayBuffer) ||
8+
(x instanceof MessagePort) ||
9+
(x instanceof ImageBitmap)
10+
));
11+
512
export default function wasmWorker(source, options = {}) {
613
let currentId = 0;
714
const promises = {};
815
const { getImportObject, ...otherOptions } = options;
916

1017
const worker = new Worker(
1118
`data:,ACTIONS=${JSON.stringify(ACTIONS)};getImportObject=${getImportObject};` +
12-
`moduleInstance=null;onmessage=${workerOnMessage}`,
19+
`importObject=undefined;wasmModule=null;moduleInstance=null;onmessage=${workerOnMessage}`,
1320
otherOptions,
1421
);
1522

@@ -33,18 +40,29 @@ export default function wasmWorker(source, options = {}) {
3340
func: exp,
3441
params,
3542
},
36-
}, params.filter(x => (
37-
(x instanceof ArrayBuffer) ||
38-
(x instanceof MessagePort) ||
39-
(x instanceof ImageBitmap)
40-
)));
43+
}, getTransferableParams(params));
4144
}),
4245
}), {}),
46+
run: (func, params) => new Promise((...rest) => {
47+
// eslint-disable-next-line
48+
promises[++currentId] = rest;
49+
worker.postMessage({
50+
id: currentId,
51+
action: ACTIONS.RUN_FUNCTION,
52+
payload: {
53+
func: func.toString(),
54+
params,
55+
},
56+
}, getTransferableParams(params));
57+
}),
4358
});
4459
} else if (result === 1) {
4560
promises[id][1](payload);
4661
}
47-
} else if (action === ACTIONS.CALL_FUNCTION_EXPORT) {
62+
} else if (
63+
action === ACTIONS.CALL_FUNCTION_EXPORT ||
64+
action === ACTIONS.RUN_FUNCTION
65+
) {
4866
promises[id][result](payload);
4967
}
5068

src/worker.js

+21-8
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,10 @@ export default function worker(e) {
1818
Promise.resolve()
1919
.then(() => {
2020
let res;
21-
const importObject = getImportObject !== undefined
22-
? getImportObject()
23-
: {
24-
memoryBase: 0,
25-
tableBase: 0,
26-
memory: new WebAssembly.Memory({ initial: 256 }),
27-
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' }),
28-
};
21+
if (getImportObject !== undefined) {
22+
// eslint-disable-next-line
23+
importObject = getImportObject();
24+
}
2925

3026
if (typeof payload === 'string') {
3127
res = fetch(payload);
@@ -46,6 +42,8 @@ export default function worker(e) {
4642
.then(({ module, instance }) => {
4743
// eslint-disable-next-line
4844
moduleInstance = instance;
45+
// eslint-disable-next-line
46+
wasmModule = module;
4947
onSuccess({
5048
exports: WebAssembly.Module
5149
.exports(module)
@@ -64,5 +62,20 @@ export default function worker(e) {
6462
onSuccess(ctx[func].apply(ctx, params));
6563
})
6664
.catch(onError);
65+
} else if (action === ACTIONS.RUN_FUNCTION) {
66+
const { func, params } = payload;
67+
68+
Promise.resolve()
69+
.then(() => {
70+
// eslint-disable-next-line
71+
const fun = new Function(`return ${func}`)();
72+
onSuccess(fun({
73+
module: wasmModule,
74+
instance: moduleInstance,
75+
importObject,
76+
params,
77+
}));
78+
})
79+
.catch(onError);
6780
}
6881
}

test/actions.spec.js

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ describe('actions', () => {
1313
expect(typeof ACTIONS.CALL_FUNCTION_EXPORT).toEqual('number');
1414
});
1515

16+
it('should export a run function action', () => {
17+
expect(typeof ACTIONS.RUN_FUNCTION).toEqual('number');
18+
});
19+
1620
it('should not export duplicated values', () => {
1721
const values = Object.keys(ACTIONS).map(key => ACTIONS[key]);
1822

test/index.spec.js

+58
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,62 @@ describe('wasm-worker', () => {
8989
done();
9090
});
9191
});
92+
93+
it('should run a function inside worker', (done) => {
94+
wasmWorker(bytes, {
95+
getImportObject: () => ({
96+
imports: {},
97+
}),
98+
})
99+
.then(wasmModule =>
100+
wasmModule.run(({
101+
module,
102+
instance,
103+
importObject,
104+
params,
105+
}) => {
106+
const err = new Error();
107+
if (params !== undefined) throw err;
108+
if (!(module instanceof WebAssembly.Module)) throw err;
109+
if (!(instance instanceof WebAssembly.Instance)) throw err;
110+
if (importObject.imports === undefined) throw err;
111+
112+
const sum = instance.exports.add(1, 2);
113+
return `1 + 2 = ${sum}`;
114+
}),
115+
)
116+
.then((result) => {
117+
expect(result).toEqual('1 + 2 = 3');
118+
done();
119+
});
120+
});
121+
122+
it('should run a function inside worker with params', (done) => {
123+
wasmWorker(bytes, {
124+
getImportObject: () => ({
125+
imports: {},
126+
}),
127+
})
128+
.then(wasmModule =>
129+
wasmModule.run(({
130+
module,
131+
instance,
132+
importObject,
133+
params,
134+
}) => {
135+
const err = new Error();
136+
if (params === undefined) throw err;
137+
if (!(module instanceof WebAssembly.Module)) throw err;
138+
if (!(instance instanceof WebAssembly.Instance)) throw err;
139+
if (importObject.imports === undefined) throw err;
140+
141+
const sum = instance.exports.add(params[0], params[1]);
142+
return `1 + 2 = ${sum}`;
143+
}, [1, 2]),
144+
)
145+
.then((result) => {
146+
expect(result).toEqual('1 + 2 = 3');
147+
done();
148+
});
149+
});
92150
});

test/worker.spec.js

+4-12
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,10 @@ describe('worker', () => {
2626

2727
/* eslint-disable */
2828
const ACTIONS = ACTIONZ;
29+
let importObject = undefined;
30+
let wasmModule = null;
2931
let moduleInstance = null;
3032
const getImportObject = undefined;
31-
const importObject = {
32-
memoryBase: 0,
33-
tableBase: 0,
34-
memory: new WebAssembly.Memory({ initial: 256 }),
35-
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' }),
36-
};
3733

3834
// helper variables
3935
const id = 0;
@@ -89,14 +85,10 @@ describe('worker', () => {
8985

9086
/* eslint-disable */
9187
const ACTIONS = ACTIONZ;
88+
let importObject = undefined;
89+
let wasmModule = null;
9290
let moduleInstance = null;
9391
const getImportObject = undefined;
94-
const importObject = {
95-
memoryBase: 0,
96-
tableBase: 0,
97-
memory: new WebAssembly.Memory({ initial: 256 }),
98-
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' }),
99-
};
10092

10193
// helper variables
10294
const id = 0;

0 commit comments

Comments
 (0)