git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 4193d244fec5109d4e0c0960c30a02fccc9623c6

Files: 4193d244fec5109d4e0c0960c30a02fccc9623c6 / wasmContainer.js

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

Built with git-ssb-web