wasmContainer.jsView |
---|
2 | 2 | const wasmMetering = require('wasm-metering') |
3 | 3 | const customTypes = require('./customTypes.js') |
4 | 4 | const typeCheckWrapper = require('./typeCheckWrapper.js') |
5 | 5 | const ReferanceMap = require('reference-map') |
| 6 | +const leb128 = require('leb128') |
6 | 7 | |
7 | 8 | const nativeTypes = new Set(['i32', 'i64', 'f32', 'f64']) |
8 | 9 | const LANGUAGE_TYPES = { |
9 | 10 | 'actor': 0x0, |
10 | 11 | 'buf': 0x1, |
| 12 | + 'elem': 0x2, |
11 | 13 | 'i32': 0x7f, |
12 | 14 | 'i64': 0x7e, |
13 | 15 | 'f32': 0x7d, |
14 | 16 | 'f64': 0x7c, |
26 | 28 | 0x60: 'func', |
27 | 29 | 0x40: 'block_type' |
28 | 30 | } |
29 | 31 | |
| 32 | +class ElementBuffer { |
| 33 | + constructor (size) { |
| 34 | + this._array = new Array(size) |
| 35 | + } |
| 36 | + |
| 37 | + serialize () { |
| 38 | + const serialized = this._array.map(ref => ref.serailize()) |
| 39 | + return Buffer.concat(Buffer.from([LANGUAGE_TYPES['elem']]), leb128.encode(serialized.length), serialized) |
| 40 | + } |
| 41 | + |
| 42 | + static deserialize (serialized) {} |
| 43 | +} |
| 44 | + |
| 45 | +class DataBuffer { |
| 46 | + constructor (memory, offset, length) { |
| 47 | + this._data = new Uint8Array(this.instance.exports.memory.buffer, offset, length) |
| 48 | + } |
| 49 | + serialize () { |
| 50 | + return Buffer.concat(Buffer.from([LANGUAGE_TYPES['elem']]), leb128.encode(this._data.length), this._data) |
| 51 | + } |
| 52 | + static deserialize (serialized) {} |
| 53 | +} |
| 54 | + |
| 55 | +class LinkRef { |
| 56 | + serialize () { |
| 57 | + return Buffer.concat(Buffer.from([LANGUAGE_TYPES['link'], this])) |
| 58 | + } |
| 59 | + static deserialize (serialized) {} |
| 60 | +} |
| 61 | + |
30 | 62 | class FunctionRef { |
31 | 63 | constructor (name, json, id) { |
32 | 64 | this.name = name |
33 | 65 | this.destId = id |
45 | 77 | while (args.length) { |
46 | 78 | const type = LANGUAGE_TYPES[args.shift()] |
47 | 79 | let arg = args.shift() |
48 | 80 | if (!nativeTypes.has(type)) { |
49 | | - arg = self._container.refs.get(arg) |
50 | | - if (arg.type !== type) { |
51 | | - throw new Error('invalid type') |
52 | | - } |
| 81 | + arg = self._container.refs.get(arg, type) |
53 | 82 | } |
54 | 83 | self.args.push({ |
55 | 84 | arg, |
56 | 85 | type |
72 | 101 | this.actor = actor |
73 | 102 | this.refs = new ReferanceMap() |
74 | 103 | } |
75 | 104 | |
76 | | - static onCreation (wasm, id, cachedb) { |
| 105 | + static async onCreation (wasm, id, cachedb) { |
77 | 106 | WebAssembly.validate(wasm) |
78 | 107 | let moduleJSON = wasm2json(wasm) |
79 | 108 | const json = mergeTypeSections(moduleJSON) |
80 | 109 | moduleJSON = wasmMetering.meterJSON(moduleJSON, { |
81 | 110 | meterType: 'i32' |
82 | 111 | }) |
83 | 112 | wasm = json2wasm(moduleJSON) |
84 | | - cachedb.put(id.toString() + 'meta', json) |
85 | | - cachedb.put(id.toString() + 'code', wasm.toString('hex')) |
| 113 | + await Promise.all([ |
| 114 | + new Promise((resolve, reject) => { |
| 115 | + cachedb.put(id.toString() + 'meta', json, resolve) |
| 116 | + }), |
| 117 | + new Promise((resolve, reject) => { |
| 118 | + cachedb.put(id.toString() + 'code', wasm.toString('hex'), resolve) |
| 119 | + }) |
| 120 | + ]) |
86 | 121 | const refs = {} |
87 | 122 | Object.keys(json.typeMap).forEach(key => { |
88 | 123 | refs[key] = new FunctionRef(key, json, id) |
89 | 124 | }) |
111 | 146 | const {funcRef} = self.refs.get(ref, FunctionRef) |
112 | 147 | const {funcRef: catchFunc} = self.refs.get(ref, FunctionRef) |
113 | 148 | funcRef.catch = catchFunc |
114 | 149 | }, |
115 | | - getGasAmount: () => {}, |
116 | | - setGasAmount: () => {} |
| 150 | + getGasAmount: (funcRef) => {}, |
| 151 | + setGasAmount: (funcRef) => {} |
117 | 152 | }, |
118 | | - storage: { |
119 | | - load: () => {}, |
120 | | - store: () => {}, |
121 | | - delete: () => {} |
122 | | - }, |
123 | 153 | link: { |
124 | 154 | wrap: (ref) => { |
125 | 155 | const obj = this.refs.get(ref) |
126 | | - obj.seriarlize() |
| 156 | + const link = new LinkRef(obj.serialize()) |
| 157 | + return this.refs.add(link, 'link') |
127 | 158 | }, |
128 | | - unwrap: () => {} |
| 159 | + unwrap: async (ref, cb) => { |
| 160 | + const obj = this.refs.get(ref, 'link') |
| 161 | + const promise = this.actor.tree.dataStore.get(obj) |
| 162 | + await this._opsQueue.push(promise) |
| 163 | + |
| 164 | + } |
129 | 165 | }, |
130 | | - databuf: { |
131 | | - create: () => {}, |
132 | | - load8: () => {}, |
133 | | - load16: () => {}, |
134 | | - load32: () => {}, |
135 | | - load64: () => {}, |
136 | | - store8: () => {}, |
137 | | - store16: () => {}, |
138 | | - store32: () => {}, |
139 | | - store64: () => {}, |
140 | | - copy: () => {} |
| 166 | + module: { |
| 167 | + new: code => {}, |
| 168 | + exports: (mod, name) => {} |
141 | 169 | }, |
142 | | - elembuf: { |
143 | | - create: () => {}, |
144 | | - load: () => {}, |
145 | | - store: () => {}, |
146 | | - delete: () => {} |
| 170 | + memory: { |
| 171 | + externalize: (index, length) => { |
| 172 | + const buf = this.getMemory(index, length) |
| 173 | + return this.refs.add(buf, 'buf') |
| 174 | + }, |
| 175 | + internalize: (dataRef, writeOffset, readOffset, length) => { |
| 176 | + let buf = this.refs.get(dataRef, 'buf') |
| 177 | + buf = buf.subarray(readOffset, length) |
| 178 | + const mem = this.getMemory(writeOffset, buf.length) |
| 179 | + mem.set(buf) |
| 180 | + } |
147 | 181 | }, |
| 182 | + table: { |
| 183 | + externalize: (index, length) => { |
| 184 | + const mem = this.getMemory(index, length * 4) |
| 185 | + const objects = [] |
| 186 | + while (length--) { |
| 187 | + const ref = mem[index + length] |
| 188 | + if (this.refs.has(ref)) { |
| 189 | + objects.push(ref) |
| 190 | + } else { |
| 191 | + throw new Error('invalid ref') |
| 192 | + } |
| 193 | + } |
| 194 | + const eleBuf = new ElementBuffer(objects) |
| 195 | + return this.refs.add(eleBuf, 'ele') |
| 196 | + }, |
| 197 | + internalize: (dataRef, writeOffset, readOffset, length) => { |
| 198 | + let buf = this.refs.get(dataRef, 'ele') |
| 199 | + buf = buf.subarray(readOffset, length) |
| 200 | + const mem = this.getMemory(writeOffset, buf.length) |
| 201 | + mem.set(buf) |
| 202 | + } |
| 203 | + }, |
148 | 204 | metering: { |
149 | 205 | usegas: (amount) => { |
150 | 206 | funcRef.gas -= amount |
151 | 207 | if (funcRef.gas < 0) { |
155 | 211 | } |
156 | 212 | } |
157 | 213 | } |
158 | 214 | |
159 | | - onMessage (message) { |
| 215 | + async onMessage (message) { |
160 | 216 | const funcRef = message.funcRef |
161 | 217 | const intef = this.getInteface(funcRef) |
162 | 218 | this.instance = WebAssembly.Instance(this.mod, intef) |
| 219 | + if (this.instance.exports.table) { |
| 220 | + this._orginalTable = this.instance.exports.table.slice() |
| 221 | + } |
163 | 222 | const args = message.funcArguments.map(arg => { |
164 | 223 | if (typeof arg === 'number') { |
165 | 224 | return arg |
166 | 225 | } else { |
167 | 226 | return this.refs.add(arg) |
168 | 227 | } |
169 | 228 | }) |
170 | 229 | this.instance.exports[funcRef.name](...args) |
| 230 | + await this.onDone() |
| 231 | + this.referanceMap.clear() |
171 | 232 | } |
172 | 233 | |
| 234 | + |
| 235 | + * returns a promise that resolves when the wasm instance is done running |
| 236 | + * @returns {Promise} |
| 237 | + */ |
| 238 | + async onDone () { |
| 239 | + let prevOps |
| 240 | + while (prevOps !== this._opsQueue) { |
| 241 | + prevOps = this._opsQueue |
| 242 | + await prevOps |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + |
| 247 | + * Pushed an async operation to the a promise queue that |
| 248 | + * @returns {Promise} the returned promise resolves in the order the intail |
| 249 | + * operation was pushed to the queue |
| 250 | + */ |
| 251 | + pushOpsQueue (promise) { |
| 252 | + this._opsQueue = Promise.all([this._opsQueue, promise]) |
| 253 | + return this._opsQueue |
| 254 | + } |
| 255 | + |
173 | 256 | getFuncRef (name, send) { |
174 | 257 | const funcRef = new FunctionRef(this.json, name, send) |
175 | 258 | return funcRef |
176 | 259 | } |
200 | 283 | this.mod = WebAssembly.Module(wasm) |
201 | 284 | this.json = json |
202 | 285 | } |
203 | 286 | |
| 287 | + getMemory (offset, length) { |
| 288 | + return new DataBuffer(this.instance.exports.memory.buffer, offset, length) |
| 289 | + } |
| 290 | + |
204 | 291 | static get typeId () { |
205 | 292 | return 9 |
206 | 293 | } |
207 | 294 | } |