git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 0df8aae87c8f14c08596394b83b87af8a4d64614

Files: 0df8aae87c8f14c08596394b83b87af8a4d64614 / wasmContainer.js

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

Built with git-ssb-web