git ssb

0+

wanderer🌟 / js-primea-wasm-container



Tree: 0246a38679005239a423179e25f48e91341a390f

Files: 0246a38679005239a423179e25f48e91341a390f / index.js

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

Built with git-ssb-web