git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 3f06f72bb36d2b3efb72366b6b5db8a966a3273a

Files: 3f06f72bb36d2b3efb72366b6b5db8a966a3273a / wasmContainer.js

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

Built with git-ssb-web