git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 891456b4ce75b2fdc83622388dc0ebf2837835cc

Files: 891456b4ce75b2fdc83622388dc0ebf2837835cc / wasmContainer.js

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

Built with git-ssb-web