git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: cd5644a350c66679340731cc00c9fcf16834bdb9

Files: cd5644a350c66679340731cc00c9fcf16834bdb9 / wasmContainer.js

9903 bytesRaw
1const {wasm2json, json2wasm} = require('wasm-json-toolkit')
2const wasmMetering = require('wasm-metering')
3const ReferanceMap = require('reference-map')
4const leb128 = require('leb128')
5const Message = require('./message.js')
6const customTypes = require('./customTypes.js')
7const injectGlobals = require('./injectGlobals.js')
8const typeCheckWrapper = require('./typeCheckWrapper.js')
9
10const nativeTypes = new Set(['i32', 'i64', 'f32', 'f64'])
11const LANGUAGE_TYPES = {
12 'actor': 0x0,
13 'buf': 0x1,
14 'elem': 0x2,
15 'i32': 0x7f,
16 'i64': 0x7e,
17 'f32': 0x7d,
18 'f64': 0x7c,
19 'anyFunc': 0x70,
20 'func': 0x60,
21 'block_type': 0x40,
22
23 0x0: 'actor',
24 0x1: 'buf',
25 0x7f: 'i32',
26 0x7e: 'i64',
27 0x7d: 'f32',
28 0x7c: 'f64',
29 0x70: 'anyFunc',
30 0x60: 'func',
31 0x40: 'block_type'
32}
33
34class ElementBuffer {
35 static get type () {
36 return 'elem'
37 }
38 constructor (size) {
39 this._array = new Array(size)
40 }
41
42 serialize () {
43 const serialized = this._array.map(ref => ref.serailize())
44 return Buffer.concat(Buffer.from([LANGUAGE_TYPES['elem']]), leb128.encode(serialized.length), serialized)
45 }
46
47 static deserialize (serialized) {}
48}
49
50class DataBuffer {
51 static get type () {
52 return 'data'
53 }
54 constructor (memory, offset, length) {
55 this._data = new Uint8Array(this.instance.exports.memory.buffer, offset, length)
56 }
57 serialize () {
58 return Buffer.concat(Buffer.from([LANGUAGE_TYPES['elem']]), leb128.encode(this._data.length), this._data)
59 }
60 static deserialize (serialized) {}
61}
62
63class LinkRef {
64 static get type () {
65 return 'link'
66 }
67 serialize () {
68 return Buffer.concat(Buffer.from([LANGUAGE_TYPES['link'], this]))
69 }
70 static deserialize (serialized) {}
71}
72
73class FunctionRef {
74 static get type () {
75 return 'func'
76 }
77
78 constructor (type, identifier, json, id) {
79 this.type = type
80 this.destId = id
81 let funcIndex
82 if (type === 'export') {
83 this.indentifier = identifier
84 funcIndex = json.exports[identifier]
85 } else {
86 this.indentifier = identifier.tableIndex
87 funcIndex = Number(identifier.name) - 1
88 }
89 const typeIndex = json.indexes[funcIndex]
90 const funcType = json.types[typeIndex]
91
92 const wrapper = typeCheckWrapper(funcType)
93 const wasm = json2wasm(wrapper)
94 const mod = WebAssembly.Module(wasm)
95 const self = this
96 this.wrapper = WebAssembly.Instance(mod, {
97 'env': {
98 'checkTypes': function () {
99 const args = [...arguments]
100 const checkedArgs = []
101 while (args.length) {
102 const type = LANGUAGE_TYPES[args.shift()]
103 let arg = args.shift()
104 if (!nativeTypes.has(type)) {
105 arg = self._container.refs.get(arg, type)
106 }
107 checkedArgs.push(arg)
108 }
109 const message = new Message({
110 funcRef: self,
111 funcArguments: checkedArgs
112 })
113 self._container.actor.send(message)
114 }
115 }
116 })
117 this.wrapper.exports.check.object = this
118 }
119 set container (container) {
120 this._container = container
121 }
122}
123
124class ModuleRef {
125 static get type () {
126 return 'mod'
127 }
128
129 constructor (json, id) {
130 this._json = json
131 this.id = id
132 }
133
134 getFuncRef (name) {
135 return new FunctionRef('export', name, this._json, this.id)
136 }
137
138 serialize () {
139 return this._json
140 }
141
142 static deserialize (serialized) {}
143}
144
145module.exports = class WasmContainer {
146 constructor (actor) {
147 this.actor = actor
148 this.refs = new ReferanceMap()
149 }
150
151 static async onCreation (wasm, id, cachedb) {
152 if (!WebAssembly.validate(wasm)) {
153 throw new Error('invalid wasm binary')
154 }
155 let moduleJSON = wasm2json(wasm)
156 const json = customTypes.mergeTypeSections(moduleJSON)
157 moduleJSON = wasmMetering.meterJSON(moduleJSON, {
158 meterType: 'i32'
159 })
160 if (json.globals.length) {
161 moduleJSON = injectGlobals(moduleJSON, json.globals)
162 }
163 wasm = json2wasm(moduleJSON)
164 await Promise.all([
165 new Promise((resolve, reject) => {
166 cachedb.put(id.toString() + 'meta', JSON.stringify(json), resolve)
167 }),
168 new Promise((resolve, reject) => {
169 cachedb.put(id.toString() + 'code', wasm.toString('hex'), resolve)
170 })
171 ])
172 return new ModuleRef(json, id)
173 }
174
175 getInterface (funcRef) {
176 const self = this
177 return {
178 func: {
179 externalize: index => {
180 const func = this.instance.exports.table.get(index)
181 const object = func.object
182 if (object) {
183 return self.refs.add(object)
184 } else {
185 const ref = new FunctionRef('table', object.tableIndex, self.json, self.actor.id)
186 return self.refs.add(ref)
187 }
188 },
189 internalize: (ref, index) => {
190 const funcRef = self.refs.get(ref)
191 funcRef.container = self
192 this.instance.exports.table.set(index, funcRef.wrapper.exports.check)
193 },
194 catch: (ref, catchRef) => {
195 const {funcRef} = self.refs.get(ref, FunctionRef)
196 const {funcRef: catchFunc} = self.refs.get(ref, FunctionRef)
197 funcRef.catch = catchFunc
198 },
199 getGasAmount: (funcRef) => {},
200 setGasAmount: (funcRef) => {}
201 },
202 link: {
203 wrap: ref => {
204 const obj = this.refs.get(ref)
205 const link = new LinkRef(obj.serialize())
206 return this.refs.add(link, 'link')
207 },
208 unwrap: async (ref, cb) => {
209 const obj = this.refs.get(ref, 'link')
210 const promise = this.actor.tree.dataStore.get(obj)
211 await this._opsQueue.push(promise)
212 // todo
213 }
214 },
215 module: {
216 new: code => {},
217 exports: (modRef, offset, length) => {
218 const mod = this.refs.get(modRef, 'mod')
219 let name = this.getMemory(offset, length)
220 name = Buffer.from(name).toString()
221 const funcRef = mod.getFuncRef(name)
222 return this.refs.add(funcRef, 'func')
223 },
224 self: () => {
225 return this.refs.add(this.moduleObj, 'mod')
226 }
227 },
228 memory: {
229 externalize: (index, length) => {
230 const buf = this.getMemory(index, length)
231 return this.refs.add(buf, 'buf')
232 },
233 internalize: (dataRef, writeOffset, readOffset, length) => {
234 let buf = this.refs.get(dataRef, 'buf')
235 buf = buf.subarray(readOffset, length)
236 const mem = this.getMemory(writeOffset, buf.length)
237 mem.set(buf)
238 }
239 },
240 table: {
241 externalize: (index, length) => {
242 const mem = this.getMemory(index, length * 4)
243 const objects = []
244 while (length--) {
245 const ref = mem[index + length]
246 if (this.refs.has(ref)) {
247 objects.push(ref)
248 } else {
249 throw new Error('invalid ref')
250 }
251 }
252 const eleBuf = new ElementBuffer(objects)
253 return this.refs.add(eleBuf, 'elem')
254 },
255 internalize: (dataRef, writeOffset, readOffset, length) => {
256 let buf = this.refs.get(dataRef, 'elem')
257 buf = buf.subarray(readOffset, length)
258 const mem = this.getMemory(writeOffset, buf.length)
259 mem.set(buf)
260 }
261 },
262 metering: {
263 usegas: amount => {
264 funcRef.gas -= amount
265 if (funcRef.gas < 0) {
266 throw new Error('out of gas! :(')
267 }
268 }
269 }
270 }
271 }
272
273 async onMessage (message) {
274 const funcRef = message.funcRef
275 const intef = this.getInterface(funcRef)
276 this.instance = WebAssembly.Instance(this.mod, intef)
277 const table = this.instance.exports.table
278 if (table) {
279 let length = table.length
280 while (length--) {
281 const func = table.get(length)
282 if (func) {
283 func.tableIndex = length
284 }
285 }
286 }
287 const args = message.funcArguments.map(arg => {
288 if (typeof arg === 'number') {
289 return arg
290 } else {
291 return this.refs.add(arg, arg.constructor.type)
292 }
293 })
294 if (funcRef.type === 'export') {
295 this.instance.exports[funcRef.indentifier](...args)
296 } else {
297 this.instance.exports.table.get(funcRef.indentifier)(...args)
298 }
299 await this.onDone()
300 this.refs.clear()
301 }
302
303 /**
304 * returns a promise that resolves when the wasm instance is done running
305 * @returns {Promise}
306 */
307 async onDone () {
308 let prevOps
309 while (prevOps !== this._opsQueue) {
310 prevOps = this._opsQueue
311 await prevOps
312 }
313 }
314
315 /**
316 * Pushed an async operation to the a promise queue that
317 * @returns {Promise} the returned promise resolves in the order the intail
318 * operation was pushed to the queue
319 */
320 pushOpsQueue (promise) {
321 this._opsQueue = Promise.all([this._opsQueue, promise])
322 return this._opsQueue
323 }
324
325 getFuncRef (name, send) {
326 const funcRef = new FunctionRef(this.json, name, send)
327 return funcRef
328 }
329
330 async onStartup () {
331 let [json, wasm] = await Promise.all([
332 new Promise((resolve, reject) => {
333 this.actor.cachedb.get(this.actor.id.toString() + 'meta', (err, json) => {
334 if (err) {
335 reject(err)
336 } else {
337 resolve(json)
338 }
339 })
340 }),
341 new Promise((resolve, reject) => {
342 this.actor.cachedb.get(this.actor.id.toString() + 'code', (err, wasm) => {
343 if (err) {
344 reject(err)
345 } else {
346 resolve(wasm)
347 }
348 })
349 })
350 ])
351 wasm = Buffer.from(wasm, 'hex')
352 json = JSON.parse(json)
353 this.mod = WebAssembly.Module(wasm)
354 this.json = json
355 this.moduleObj = new ModuleRef(json, this.actor.id)
356 }
357
358 getMemory (offset, length) {
359 return new Uint8Array(this.instance.exports.memory.buffer, offset, length)
360 }
361
362 static get typeId () {
363 return 9
364 }
365}
366

Built with git-ssb-web