git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: e6bb7dfe86f19f0188e093e016c11a70df79bf72

Files: e6bb7dfe86f19f0188e093e016c11a70df79bf72 / wasmContainer.js

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

Built with git-ssb-web