git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: e729c860b2eaa16901cefd395558a96c683a5cad

Files: e729c860b2eaa16901cefd395558a96c683a5cad / wasmContainer.js

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

Built with git-ssb-web