git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 53fa2cb6dbb5833d412e128716f96fe637b40738

Files: 53fa2cb6dbb5833d412e128716f96fe637b40738 / wasmContainer.js

9015 bytesRaw
1const {wasm2json, json2wasm} = require('wasm-json-toolkit')
2const annotations = require('primea-annotations')
3const wasmMetering = require('wasm-metering')
4const ReferanceMap = require('reference-map')
5const Message = require('./message.js')
6const injectGlobals = require('./injectGlobals.js')
7const typeCheckWrapper = require('./typeCheckWrapper.js')
8const {FunctionRef, ModuleRef, DEFAULTS} = require('./systemObjects.js')
9
10const nativeTypes = new Set(['i32', 'i64', 'f32', 'f64'])
11const FUNC_INDEX_OFFSET = 1
12
13function generateWrapper (funcRef, container) {
14 let wrapper = typeCheckWrapper(funcRef.params)
15 const wasm = json2wasm(wrapper)
16 const mod = WebAssembly.Module(wasm)
17 const self = funcRef
18 wrapper = WebAssembly.Instance(mod, {
19 'env': {
20 'checkTypes': function () {
21 const args = [...arguments]
22 const checkedArgs = []
23 while (args.length) {
24 const type = annotations.LANGUAGE_TYPES_BIN[args.shift()]
25 let arg = args.shift()
26 if (!nativeTypes.has(type)) {
27 arg = container.refs.get(arg, type)
28 }
29 checkedArgs.push(arg)
30 }
31 const message = new Message({
32 funcRef: self,
33 funcArguments: checkedArgs
34 }).on('execution:error', e => {
35 console.log(e)
36 })
37 container.actor.send(message)
38 }
39 }
40 })
41 wrapper.exports.check.object = funcRef
42 return wrapper
43}
44
45module.exports = class WasmContainer {
46 constructor (actor) {
47 this.actor = actor
48 this.refs = new ReferanceMap()
49 }
50
51 static createModule (wasm, id) {
52 if (!WebAssembly.validate(wasm)) {
53 throw new Error('invalid wasm binary')
54 }
55
56 let moduleJSON = wasm2json(wasm)
57 const json = annotations.mergeTypeSections(moduleJSON)
58 moduleJSON = wasmMetering.meterJSON(moduleJSON, {
59 meterType: 'i32'
60 })
61
62 // initialize the globals
63 let numOfGlobals = json.persist.length
64 if (numOfGlobals) {
65 moduleJSON = injectGlobals(moduleJSON, json.persist)
66 }
67 // recompile the wasm
68 wasm = json2wasm(moduleJSON)
69 const modRef = ModuleRef.fromMetaJSON(json, id)
70 return {
71 wasm,
72 json,
73 modRef
74 }
75 }
76
77 static onCreation (unverifiedWasm, id, tree) {
78 const {modRef} = this.createModule(unverifiedWasm, id)
79 return modRef
80 }
81
82 getInterface (funcRef) {
83 const self = this
84 return {
85 func: {
86 externalize: index => {
87 const func = this.instance.exports.table.get(index)
88 const object = func.object
89 if (object) {
90 // externalize a pervously internalized function
91 return self.refs.add(object)
92 } else {
93 const params = self.json.types[self.json.indexes[func.name - FUNC_INDEX_OFFSET]].params
94 const ref = new FunctionRef(true, func.tableIndex, params, self.actor.id)
95 return self.refs.add(ref, 'func')
96 }
97 },
98 internalize: (index, ref) => {
99 const funcRef = self.refs.get(ref, 'func')
100 const wrapper = generateWrapper(funcRef, self)
101 this.instance.exports.table.set(index, wrapper.exports.check)
102 },
103 catch: (ref, catchRef) => {
104 const {funcRef} = self.refs.get(ref, FunctionRef)
105 const {funcRef: catchFunc} = self.refs.get(ref, FunctionRef)
106 funcRef.catch = catchFunc
107 },
108 get_gas_budget: (funcRef) => {
109 const func = self.refs.get(funcRef, 'func')
110 return func.gas
111 },
112 set_gas_budget: (funcRef, amount) => {
113 const func = self.refs.get(funcRef, 'func')
114 func.gas = amount
115 }
116 },
117 link: {
118 wrap: ref => {
119 const obj = this.refs.get(ref)
120 const link = {'/': obj}
121 return this.refs.add(link, 'link')
122 },
123 unwrap: async (ref, cb) => {
124 const obj = this.refs.get(ref, 'link')
125 const promise = this.actor.tree.dataStore.get(obj)
126 await this._opsQueue.push(promise)
127 // todo
128 }
129 },
130 module: {
131 new: dataRef => {
132 const mod = this.actor.createActor(dataRef)
133 return this.refs.add(mod, 'mod')
134 },
135 export: (modRef, dataRef) => {
136 const mod = this.refs.get(modRef, 'mod')
137 let name = this.refs.get(dataRef, 'data')
138 name = Buffer.from(name).toString()
139 const funcRef = mod.getFuncRef(name)
140 return this.refs.add(funcRef, 'func')
141 },
142 self: () => {
143 return this.refs.add(this.modSelf, 'mod')
144 }
145 },
146 memory: {
147 externalize: (index, length) => {
148 const data = Buffer.from(this.get8Memory(index, length))
149 return this.refs.add(data, 'data')
150 },
151 internalize: (dataRef, srcOffset, sinkOffset, length) => {
152 let data = this.refs.get(dataRef, 'data')
153 data = data.subarray(srcOffset, length)
154 const mem = this.get8Memory(sinkOffset, data.length)
155 mem.set(data)
156 },
157 length (dataRef) {
158 let data = this.refs.get(dataRef, 'data')
159 return data.length
160 }
161 },
162 table: {
163 externalize: (index, length) => {
164 const mem = Buffer.from(this.get8Memory(index, length * 4))
165 const objects = []
166 while (length--) {
167 const ref = mem.readUInt32LE(length * 4)
168 const obj = this.refs.get(ref)
169 objects.unshift(obj)
170 }
171 return this.refs.add(objects, 'elem')
172 },
173 internalize: (elemRef, srcOffset, sinkOffset, length) => {
174 let table = this.refs.get(elemRef, 'elem')
175 const buf = table.slice(srcOffset, srcOffset + length).map(obj => this.refs.add(obj))
176 const mem = this.get32Memory(sinkOffset, length)
177 mem.set(buf)
178 },
179 length (elemRef) {
180 let elem = this.refs.get(elemRef, 'elem')
181 return elem.length
182 }
183 },
184 metering: {
185 usegas: amount => {
186 this.actor.incrementTicks(amount)
187 // funcRef.gas -= amount
188 if (funcRef.gas < 0) {
189 throw new Error('out of gas! :(')
190 }
191 }
192 }
193 }
194 }
195
196 async onMessage (message) {
197 const funcRef = message.funcRef
198 const intef = this.getInterface(funcRef)
199 this.instance = WebAssembly.Instance(this.mod, intef)
200 // map table indexes
201 const table = this.instance.exports.table
202 if (table) {
203 let length = table.length
204 while (length--) {
205 const func = table.get(length)
206 if (func) {
207 func.tableIndex = length
208 }
209 }
210 }
211 // import references
212 const args = message.funcArguments.map((arg, index) => {
213 const type = funcRef.params[index]
214 if (nativeTypes.has(type)) {
215 return arg
216 } else {
217 return this.refs.add(arg, type)
218 }
219 })
220
221 // setup globals
222 let numOfGlobals = this.json.persist.length
223 if (numOfGlobals) {
224 const refs = []
225 while (numOfGlobals--) {
226 const obj = this.actor.storage[numOfGlobals] || DEFAULTS[this.json.persist[numOfGlobals].type]
227 refs.push(this.refs.add(obj, this.json.persist[numOfGlobals].type))
228 }
229 this.instance.exports.setter_globals(...refs)
230 }
231
232 // call entrypoint function
233 if (funcRef.private) {
234 this.instance.exports.table.get(funcRef.identifier)(...args)
235 } else {
236 this.instance.exports[funcRef.identifier](...args)
237 }
238 await this.onDone()
239
240 // store globals
241 numOfGlobals = this.json.persist.length
242 if (numOfGlobals) {
243 this.actor.storage = []
244 this.instance.exports.getter_globals()
245 const mem = this.get32Memory(0, numOfGlobals)
246 while (numOfGlobals--) {
247 const ref = mem[numOfGlobals]
248 this.actor.storage.push(this.refs.get(ref, this.json.persist[numOfGlobals].type))
249 }
250 }
251
252 this.refs.clear()
253 }
254
255 /**
256 * returns a promise that resolves when the wasm instance is done running
257 * @returns {Promise}
258 */
259 async onDone () {
260 let prevOps
261 while (prevOps !== this._opsQueue) {
262 prevOps = this._opsQueue
263 await prevOps
264 }
265 }
266
267 /**
268 * Pushed an async operation to the a promise queue that
269 * @returns {Promise} the returned promise resolves in the order the intail
270 * operation was pushed to the queue
271 */
272 pushOpsQueue (promise) {
273 this._opsQueue = Promise.all([this._opsQueue, promise])
274 return this._opsQueue
275 }
276
277 async onStartup () {
278 const code = this.actor.code
279 const {json, wasm, modRef} = WasmContainer.createModule(code, this.actor.id)
280 this.mod = WebAssembly.Module(wasm)
281 this.json = json
282 this.modSelf = modRef
283 }
284
285 get8Memory (offset, length) {
286 return new Uint8Array(this.instance.exports.memory.buffer, offset, length)
287 }
288
289 get32Memory (offset, length) {
290 return new Uint32Array(this.instance.exports.memory.buffer, offset, length)
291 }
292
293 static get typeId () {
294 return 9
295 }
296}
297

Built with git-ssb-web