git ssb

0+

wanderer🌟 / js-primea-wasm-container



Tree: 1acaf62122a024e6a953fb715cf4bbda94b804ad

Files: 1acaf62122a024e6a953fb715cf4bbda94b804ad / index.js

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

Built with git-ssb-web