git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 3c71975dada7fb231be29756db22f3b9f71eab24

Files: 3c71975dada7fb231be29756db22f3b9f71eab24 / wasmContainer.js

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

Built with git-ssb-web