Files: a9b5a00dafae8aebd3540837fb35491346ec2528 / index.js
3007 bytesRaw
1 | const ReferanceMap = require('./referanceMap.js') |
2 | |
3 | module.exports = class WasmContainer { |
4 | /** |
5 | * The wasm container runs wasm code and provides a basic API for wasm |
6 | * interfaces for interacting with the kernel |
7 | * @param {object} kernel - the kernel instance |
8 | * @param {object} interfaces - a map of interfaces to expose to the wasm binary |
9 | */ |
10 | constructor (kernel, interfaces) { |
11 | this.kernel = kernel |
12 | this.imports = interfaces |
13 | this.referanceMap = new ReferanceMap() |
14 | } |
15 | |
16 | async initialize (message) { |
17 | let code = message.data |
18 | if (!WebAssembly.validate(code)) { |
19 | throw new Error('invalid wasm binary') |
20 | } else { |
21 | for (const name in this.imports) { |
22 | const interf = this.imports[name] |
23 | if (interf.initialize) { |
24 | code = await interf.initialize(code) |
25 | } |
26 | } |
27 | this.kernel.state.code = code |
28 | } |
29 | return this._run(message, 'init') |
30 | } |
31 | |
32 | /** |
33 | * Runs the wasm VM given a message |
34 | * @param {object} message |
35 | * @returns {Promise} a promise that resolves once the compuation is finished |
36 | */ |
37 | run (message) { |
38 | return this._run(message, 'main') |
39 | } |
40 | |
41 | async _run (message, method) { |
42 | // Builds a import map with an array of given interfaces |
43 | const importMap = {} |
44 | for (const name in this.imports) { |
45 | importMap[name] = {} |
46 | const Import = this.imports[name] |
47 | const newInterface = new Import(this) |
48 | const props = Object.getOwnPropertyNames(Import.prototype) |
49 | |
50 | // bind the methods to the correct 'this' |
51 | for (const prop of props) { |
52 | if (prop !== 'constructor') { |
53 | importMap[name][prop] = newInterface[prop].bind(newInterface) |
54 | } |
55 | } |
56 | } |
57 | |
58 | const result = await WebAssembly.instantiate(this.kernel.state.code, importMap) |
59 | this.instance = result.instance |
60 | // runs the wasm code |
61 | this.instance.exports[method]() |
62 | return this.onDone() |
63 | } |
64 | |
65 | /** |
66 | * returns a promise that resolves when the wasm instance is done running |
67 | * @returns {Promise} |
68 | */ |
69 | async onDone () { |
70 | let prevOps |
71 | while (prevOps !== this._opsQueue) { |
72 | prevOps = this._opsQueue |
73 | await prevOps |
74 | } |
75 | this.referanceMap.clear() |
76 | } |
77 | |
78 | /** |
79 | * Pushed an async operation to the a promise queue that |
80 | * @returns {Promise} the returned promise resolves in the order the intail |
81 | * operation was pushed to the queue |
82 | */ |
83 | pushOpsQueue (promise) { |
84 | this._opsQueue = Promise.all([this._opsQueue, promise]) |
85 | return this._opsQueue |
86 | } |
87 | |
88 | /** |
89 | * executes a callback given an index in the exported callback container |
90 | * @param {integer} cb |
91 | * @param {*} val - a value to return to the callback function |
92 | */ |
93 | execute (cb, val) { |
94 | this.instance.exports.table.get(cb)(val) |
95 | } |
96 | |
97 | /** |
98 | * returns a section of memory from the wasm instance |
99 | * @param {integer} offset |
100 | * @param {integer} length |
101 | * @returns {Uint8Array} |
102 | */ |
103 | getMemory (offset, length) { |
104 | return new Uint8Array(this.instance.exports.memory.buffer, offset, length) |
105 | } |
106 | } |
107 |
Built with git-ssb-web