git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: fc3efb6d1a8e5ef2e4cbd0017fa9649b221b8793

Files: fc3efb6d1a8e5ef2e4cbd0017fa9649b221b8793 / wasmContainer.js

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

Built with git-ssb-web