git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 8aa474dcde21c44a2049cae1deeb33baa977dad8

Files: 8aa474dcde21c44a2049cae1deeb33baa977dad8 / wasmContainer.js

9109 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 async onCreation (unverifiedWasm, id, tree) {
92 let {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
147 },
148 export: (modRef, bufRef) => {
149 const mod = this.refs.get(modRef, 'mod')
150 let name = this.refs.get(bufRef, 'buf')
151 name = Buffer.from(name).toString()
152 const funcRef = mod.getFuncRef(name)
153 return this.refs.add(funcRef, 'func')
154 },
155 self: () => {
156 return this.refs.add(this.modSelf, 'mod')
157 }
158 },
159 memory: {
160 externalize: (index, length) => {
161 const buf = Buffer.from(this.get8Memory(index, length))
162 return this.refs.add(buf, 'buf')
163 },
164 internalize: (dataRef, srcOffset, sinkOffset, length) => {
165 let buf = this.refs.get(dataRef, 'buf')
166 buf = buf.subarray(srcOffset, length)
167 const mem = this.get8Memory(sinkOffset, buf.length)
168 mem.set(buf)
169 },
170 length (dataRef) {
171 let buf = this.refs.get(dataRef, 'buf')
172 return buf.length
173 }
174 },
175 table: {
176 externalize: (index, length) => {
177 const mem = Buffer.from(this.get8Memory(index, length * 4))
178 const objects = []
179 while (length--) {
180 const ref = mem.readUInt32LE(length * 4)
181 const obj = this.refs.get(ref)
182 objects.unshift(obj)
183 }
184 return this.refs.add(objects, 'elem')
185 },
186 internalize: (elemRef, srcOffset, sinkOffset, length) => {
187 let table = this.refs.get(elemRef, 'elem')
188 const buf = table.slice(srcOffset, srcOffset + length).map(obj => this.refs.add(obj))
189 const mem = this.get32Memory(sinkOffset, length)
190 mem.set(buf)
191 },
192 length (elemRef) {
193 let elem = this.refs.get(elemRef, 'elem')
194 return elem.length
195 }
196 },
197 metering: {
198 usegas: amount => {
199 this.actor.incrementTicks(amount)
200 // funcRef.gas -= amount
201 if (funcRef.gas < 0) {
202 throw new Error('out of gas! :(')
203 }
204 }
205 }
206 }
207 }
208
209 async onMessage (message) {
210 const funcRef = message.funcRef
211 const intef = this.getInterface(funcRef)
212 this.instance = WebAssembly.Instance(this.mod, intef)
213 // map table indexes
214 const table = this.instance.exports.table
215 if (table) {
216 let length = table.length
217 while (length--) {
218 const func = table.get(length)
219 if (func) {
220 func.tableIndex = length
221 }
222 }
223 }
224 // import references
225 const args = message.funcArguments.map((arg, index) => {
226 const type = funcRef.params[index]
227 if (nativeTypes.has(type)) {
228 return arg
229 } else {
230 return this.refs.add(arg, type)
231 }
232 })
233
234 // setup globals
235 let numOfGlobals = this.json.globals.length
236 if (numOfGlobals) {
237 const refs = []
238 while (numOfGlobals--) {
239 const obj = this.actor.storage[numOfGlobals] || DEFAULTS[this.json.globals[numOfGlobals].type]
240 refs.push(this.refs.add(obj, this.json.globals[numOfGlobals].type))
241 }
242 this.instance.exports.setter_globals(...refs)
243 }
244
245 // call entrypoint function
246 if (funcRef.private) {
247 this.instance.exports.table.get(funcRef.identifier)(...args)
248 } else {
249 this.instance.exports[funcRef.identifier](...args)
250 }
251 await this.onDone()
252
253 // store globals
254 numOfGlobals = this.json.globals.length
255 if (numOfGlobals) {
256 this.actor.storage = []
257 this.instance.exports.getter_globals()
258 const mem = this.get32Memory(0, numOfGlobals)
259 while (numOfGlobals--) {
260 const ref = mem[numOfGlobals]
261 this.actor.storage.push(this.refs.get(ref, this.json.globals[numOfGlobals].type))
262 }
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 const code = this.actor.code
292 const {json, wasm, modRef} = WasmContainer.createModule(code, this.actor.id)
293 this.mod = WebAssembly.Module(wasm)
294 this.json = json
295 this.modSelf = modRef
296 }
297
298 get8Memory (offset, length) {
299 return new Uint8Array(this.instance.exports.memory.buffer, offset, length)
300 }
301
302 get32Memory (offset, length) {
303 return new Uint32Array(this.instance.exports.memory.buffer, offset, length)
304 }
305
306 static get typeId () {
307 return 9
308 }
309}
310

Built with git-ssb-web