git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 5013b5ed5b183c9bd021b9b9b20f7df558b0a664

Files: 5013b5ed5b183c9bd021b9b9b20f7df558b0a664 / wasmContainer.js

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

Built with git-ssb-web