git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 1ee5f08dcf25af27f0dce6fdc1eab39b185d202f

Files: 1ee5f08dcf25af27f0dce6fdc1eab39b185d202f / wasmContainer.js

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

Built with git-ssb-web