git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: acd65bd28b852ab061aad493bc85901d386b2b9b

Files: acd65bd28b852ab061aad493bc85901d386b2b9b / wasmContainer.js

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

Built with git-ssb-web