git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: bc67c2749ae9d68f54ef3d51c504dd4722172ec7

Files: bc67c2749ae9d68f54ef3d51c504dd4722172ec7 / wasmContainer.js

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

Built with git-ssb-web