git ssb

0+

wanderer🌟 / js-primea-wasm-container



Tree: c1222e28c62f73f50a456c4c1fc3379ef69e2a62

Files: c1222e28c62f73f50a456c4c1fc3379ef69e2a62 / index.js

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

Built with git-ssb-web