git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: c1baf026f5fba05cb5619603b350445d6e40a0d5

Files: c1baf026f5fba05cb5619603b350445d6e40a0d5 / wasmContainer.js

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

Built with git-ssb-web