Commit 172b9d65cd4d2fbc210b0cee09b451dfdccabf34
Merge branch 'master' into greenkeeper/reference-map-1.2.4
wanderer authored on 4/3/2018, 7:33:44 PMGitHub committed on 4/3/2018, 7:33:44 PM
Parent: 92fb09c87c224982aa17e3fa025810620e571b26
Parent: d29552f49ecfa1893262e16349cb6b9878377cef
Files changed
index.js | changed |
package-lock.json | changed |
package.json | changed |
tests/index.js | changed |
tests/wasm/memory.wasm | changed |
tests/wasm/link.wasm | added |
tests/wasm/storage.wasm | added |
tests/wast/funcRef_reciever.wast | changed |
tests/wast/memory.wast | changed |
tests/wast/link.json | added |
tests/wast/link.wast | added |
tests/wast/storage.json | added |
tests/wast/storage.wast | added |
typeCheckWrapper.js | changed |
injectGlobals.js | deleted |
index.js | ||
---|---|---|
@@ -1,10 +1,9 @@ | ||
1 | -const {Message, FunctionRef, ModuleRef, DEFAULTS} = require('primea-objects') | |
1 | +const {Message, FunctionRef, ModuleRef, getType} = require('primea-objects') | |
2 | 2 | const {wasm2json, json2wasm} = require('wasm-json-toolkit') |
3 | 3 | const annotations = require('primea-annotations') |
4 | 4 | const wasmMetering = require('wasm-metering') |
5 | 5 | const ReferanceMap = require('reference-map') |
6 | -const injectGlobals = require('./injectGlobals.js') | |
7 | 6 | const typeCheckWrapper = require('./typeCheckWrapper.js') |
8 | 7 | |
9 | 8 | const nativeTypes = new Set(['i32', 'i64', 'f32', 'f64']) |
10 | 9 | const FUNC_INDEX_OFFSET = 1 |
@@ -62,8 +61,9 @@ | ||
62 | 61 | module.exports = class WasmContainer { |
63 | 62 | constructor (actor) { |
64 | 63 | this.actor = actor |
65 | 64 | this.refs = new ReferanceMap() |
65 | + this._opsQueue = Promise.resolve() | |
66 | 66 | } |
67 | 67 | |
68 | 68 | static createModule (wasm, id) { |
69 | 69 | if (!WebAssembly.validate(wasm)) { |
@@ -75,12 +75,8 @@ | ||
75 | 75 | moduleJSON = wasmMetering.meterJSON(moduleJSON, { |
76 | 76 | meterType: 'i32' |
77 | 77 | }) |
78 | 78 | |
79 | - // initialize the globals | |
80 | - if (json.persist.length) { | |
81 | - moduleJSON = injectGlobals(moduleJSON, json.persist) | |
82 | - } | |
83 | 79 | // recompile the wasm |
84 | 80 | wasm = json2wasm(moduleJSON) |
85 | 81 | const modRef = fromMetaJSON(json, id) |
86 | 82 | return { |
@@ -119,9 +115,9 @@ | ||
119 | 115 | const funcRef = self.refs.get(ref, 'func') |
120 | 116 | const wrapper = generateWrapper(funcRef, self) |
121 | 117 | self.instance.exports.table.set(index, wrapper.exports.check) |
122 | 118 | }, |
123 | - get_gas_budget: (funcRef) => { | |
119 | + get_gas_budget: funcRef => { | |
124 | 120 | const func = self.refs.get(funcRef, 'func') |
125 | 121 | return func.gas |
126 | 122 | }, |
127 | 123 | set_gas_budget: (funcRef, amount) => { |
@@ -136,11 +132,14 @@ | ||
136 | 132 | const link = {'/': obj} |
137 | 133 | return self.refs.add(link, 'link') |
138 | 134 | }, |
139 | 135 | unwrap: async (ref, cb) => { |
140 | - const obj = self.refs.get(ref, 'link') | |
141 | - const promise = self.actor.tree.dataStore.get(obj) | |
142 | - await self._opsQueue.push(promise) | |
136 | + const link = self.refs.get(ref, 'link') | |
137 | + const promise = self.actor.tree.graph.tree(link) | |
138 | + await self.pushOpsQueue(promise) | |
139 | + const obj = link['/'] | |
140 | + const linkRef = self.refs.add(obj, getType(obj)) | |
141 | + self.instance.exports.table.get(cb)(linkRef) | |
143 | 142 | } |
144 | 143 | }, |
145 | 144 | module: { |
146 | 145 | new: dataRef => { |
@@ -187,17 +186,26 @@ | ||
187 | 186 | return this.refs.add(objects, 'elem') |
188 | 187 | }, |
189 | 188 | internalize: (elemRef, srcOffset, sinkOffset, length) => { |
190 | 189 | let table = self.refs.get(elemRef, 'elem') |
191 | - const buf = table.slice(srcOffset, srcOffset + length).map(obj => self.refs.add(obj)) | |
190 | + const buf = table.slice(srcOffset, srcOffset + length).map(obj => self.refs.add(obj, getType(obj))) | |
192 | 191 | const mem = self.get32Memory(sinkOffset, length) |
193 | 192 | mem.set(buf) |
194 | 193 | }, |
195 | 194 | length (elemRef) { |
196 | 195 | let elem = self.refs.get(elemRef, 'elem') |
197 | 196 | return elem.length |
198 | 197 | } |
199 | 198 | }, |
199 | + storage: { | |
200 | + get: () => { | |
201 | + return this.refs.add(this.actor.storage, getType(this.actor.storage)) | |
202 | + }, | |
203 | + set: ref => { | |
204 | + const object = this.refs.get(ref) | |
205 | + this.actor.storage = object | |
206 | + } | |
207 | + }, | |
200 | 208 | metering: { |
201 | 209 | usegas: amount => { |
202 | 210 | self.actor.incrementTicks(amount) |
203 | 211 | funcRef.gas -= amount |
@@ -239,49 +247,20 @@ | ||
239 | 247 | } |
240 | 248 | index++ |
241 | 249 | }) |
242 | 250 | |
243 | - // setup globals | |
244 | - let numOfGlobals = this.json.persist.length | |
245 | - if (numOfGlobals) { | |
246 | - const refs = [] | |
247 | - while (numOfGlobals--) { | |
248 | - const obj = this.actor.storage[numOfGlobals] || DEFAULTS[this.json.persist[numOfGlobals].type] | |
249 | - refs.push(this.refs.add(obj, this.json.persist[numOfGlobals].type)) | |
250 | - } | |
251 | - this.instance.exports.setter_globals(...refs) | |
251 | + // call entrypoint function | |
252 | + let wasmFunc | |
253 | + if (funcRef.identifier[0]) { | |
254 | + wasmFunc = this.instance.exports.table.get(funcRef.identifier[1]) | |
255 | + } else { | |
256 | + wasmFunc = this.instance.exports[funcRef.identifier[1]] | |
252 | 257 | } |
253 | 258 | |
254 | - try { | |
255 | - // call entrypoint function | |
256 | - let wasmFunc | |
257 | - if (funcRef.identifier[0]) { | |
258 | - wasmFunc = this.instance.exports.table.get(funcRef.identifier[1]) | |
259 | - } else { | |
260 | - wasmFunc = this.instance.exports[funcRef.identifier[1]] | |
261 | - } | |
262 | - | |
263 | - const wrapper = generateWrapper(funcRef) | |
264 | - wrapper.exports.table.set(0, wasmFunc) | |
265 | - wrapper.exports.invoke(...args) | |
266 | - await this.onDone() | |
267 | - } catch (e) { | |
268 | - console.log(e) | |
269 | - } | |
270 | - | |
271 | - // store globals | |
272 | - numOfGlobals = this.json.persist.length | |
273 | - if (numOfGlobals) { | |
274 | - const storage = [] | |
275 | - this.instance.exports.getter_globals() | |
276 | - const mem = this.get32Memory(0, numOfGlobals) | |
277 | - while (numOfGlobals--) { | |
278 | - const ref = mem[numOfGlobals] | |
279 | - storage.push(this.refs.get(ref, this.json.persist[numOfGlobals].type)) | |
280 | - } | |
281 | - this.actor.storage = storage | |
282 | - } | |
283 | - | |
259 | + const wrapper = generateWrapper(funcRef) | |
260 | + wrapper.exports.table.set(0, wasmFunc) | |
261 | + wrapper.exports.invoke(...args) | |
262 | + await this.onDone() | |
284 | 263 | this.refs.clear() |
285 | 264 | } |
286 | 265 | |
287 | 266 | /** |
package-lock.json | ||
---|---|---|
The diff is too large to show. Use a local git client to view these changes. Old file size: 198276 bytes New file size: 198276 bytes |
package.json | ||
---|---|---|
@@ -25,17 +25,17 @@ | ||
25 | 25 | "coveralls": "^3.0.0", |
26 | 26 | "dfinity-radix-tree": "0.2.1", |
27 | 27 | "level-browserify": "^1.1.2", |
28 | 28 | "nyc": "^11.6.0", |
29 | - "primea-hypervisor": "^0.4.5", | |
29 | + "primea-hypervisor": "^0.5.0", | |
30 | 30 | "standard": "^11.0.1", |
31 | 31 | "tape": "^4.9.0", |
32 | 32 | "wabt": "^1.0.0" |
33 | 33 | }, |
34 | 34 | "dependencies": { |
35 | 35 | "borc": "^2.0.2", |
36 | 36 | "primea-annotations": "^0.0.3", |
37 | - "primea-objects": "^0.0.2", | |
37 | + "primea-objects": "^0.0.3", | |
38 | 38 | "reference-map": "1.2.4", |
39 | 39 | "wasm-json-toolkit": "^0.2.3", |
40 | 40 | "wasm-metering": "^0.1.1" |
41 | 41 | } |
tests/index.js | ||
---|---|---|
@@ -1,15 +1,17 @@ | ||
1 | 1 | const tape = require('tape') |
2 | 2 | const fs = require('fs') |
3 | 3 | const path = require('path') |
4 | -const {Message} = require('primea-objects') | |
4 | +const Buffer = require('safe-buffer').Buffer | |
5 | +const {Message, FunctionRef} = require('primea-objects') | |
5 | 6 | const Hypervisor = require('primea-hypervisor') |
7 | +const EgressDriver = require('primea-hypervisor/egressDriver') | |
6 | 8 | const WasmContainer = require('../') |
7 | 9 | |
8 | 10 | const level = require('level-browserify') |
9 | 11 | const RadixTree = require('dfinity-radix-tree') |
10 | 12 | |
11 | -const db = level(__dirname + '/testdb') | |
13 | +const db = level(`${__dirname}/testdb`) | |
12 | 14 | const WASM_PATH = path.join(__dirname, 'wasm') |
13 | 15 | |
14 | 16 | let tester |
15 | 17 | |
@@ -49,10 +51,8 @@ | ||
49 | 51 | funcRef.gas = 322000 |
50 | 52 | |
51 | 53 | const message = new Message({ |
52 | 54 | funcRef |
53 | - }).on('execution:error', e => { | |
54 | - console.log(e) | |
55 | 55 | }) |
56 | 56 | hypervisor.send(message) |
57 | 57 | }) |
58 | 58 | |
@@ -70,10 +70,8 @@ | ||
70 | 70 | funcRef.gas = 322000 |
71 | 71 | |
72 | 72 | const message = new Message({ |
73 | 73 | funcRef |
74 | - }).on('execution:error', e => { | |
75 | - console.log(e) | |
76 | 74 | }) |
77 | 75 | hypervisor.send(message) |
78 | 76 | }) |
79 | 77 | |
@@ -91,10 +89,8 @@ | ||
91 | 89 | funcRef.gas = 322000 |
92 | 90 | |
93 | 91 | const message = new Message({ |
94 | 92 | funcRef |
95 | - }).on('execution:error', e => { | |
96 | - console.log(e) | |
97 | 93 | }) |
98 | 94 | hypervisor.send(message) |
99 | 95 | }) |
100 | 96 | |
@@ -117,10 +113,8 @@ | ||
117 | 113 | |
118 | 114 | const message = new Message({ |
119 | 115 | funcRef, |
120 | 116 | funcArguments: [5] |
121 | - }).on('execution:error', e => { | |
122 | - console.log(e) | |
123 | 117 | }) |
124 | 118 | hypervisor.send(message) |
125 | 119 | // const stateRoot = await hypervisor.createStateRoot() |
126 | 120 | // t.deepEquals(stateRoot, expectedState, 'expected root!') |
@@ -155,11 +149,8 @@ | ||
155 | 149 | |
156 | 150 | tape('two communicating actors', async t => { |
157 | 151 | t.plan(1) |
158 | 152 | tester = t |
159 | - const expectedState = { | |
160 | - '/': Buffer.from('8c230b5f0f680199b24ecd1800c2970dfca7cfdc', 'hex') | |
161 | - } | |
162 | 153 | |
163 | 154 | const tree = new RadixTree({db}) |
164 | 155 | |
165 | 156 | const recieverWasm = fs.readFileSync(WASM_PATH + '/reciever.wasm') |
@@ -179,18 +170,13 @@ | ||
179 | 170 | funcArguments: [recvFuncRef] |
180 | 171 | }) |
181 | 172 | |
182 | 173 | hypervisor.send(message) |
183 | - const stateRoot = await hypervisor.createStateRoot() | |
184 | - // t.deepEquals(stateRoot, expectedState, 'expected root!') | |
185 | 174 | }) |
186 | 175 | |
187 | 176 | tape('two communicating actors with callback', async t => { |
188 | 177 | t.plan(1) |
189 | 178 | tester = t |
190 | - const expectedState = { | |
191 | - '/': Buffer.from('9bf27cf07b75a90e0af530e2df73e3102482b24a', 'hex') | |
192 | - } | |
193 | 179 | |
194 | 180 | const tree = new RadixTree({ |
195 | 181 | db |
196 | 182 | }) |
@@ -211,21 +197,16 @@ | ||
211 | 197 | |
212 | 198 | const message = new Message({ |
213 | 199 | funcRef: callFuncRef, |
214 | 200 | funcArguments: [recvFuncRef] |
215 | - }).on('execution:error', e => console.log(e)) | |
201 | + }) | |
216 | 202 | |
217 | 203 | hypervisor.send(message) |
218 | - const stateRoot = await hypervisor.createStateRoot() | |
219 | - // t.deepEquals(stateRoot, expectedState, 'expected root!') | |
220 | 204 | }) |
221 | 205 | |
222 | 206 | tape('two communicating actors with private callback', async t => { |
223 | 207 | t.plan(1) |
224 | 208 | tester = t |
225 | - const expectedState = { | |
226 | - '/': Buffer.from('9bf27cf07b75a90e0af530e2df73e3102482b24a', 'hex') | |
227 | - } | |
228 | 209 | |
229 | 210 | const tree = new RadixTree({ |
230 | 211 | db |
231 | 212 | }) |
@@ -249,10 +230,8 @@ | ||
249 | 230 | funcArguments: [recvFuncRef] |
250 | 231 | }) |
251 | 232 | |
252 | 233 | hypervisor.send(message) |
253 | - const stateRoot = await hypervisor.createStateRoot() | |
254 | - // t.deepEquals(stateRoot, expectedState, 'expected root!') | |
255 | 234 | }) |
256 | 235 | |
257 | 236 | tape('externalize/internalize memory', async t => { |
258 | 237 | t.plan(1) |
@@ -301,66 +280,134 @@ | ||
301 | 280 | }) |
302 | 281 | hypervisor.send(message) |
303 | 282 | }) |
304 | 283 | |
305 | -tape('load / store globals', async t => { | |
284 | +tape('creation', async t => { | |
306 | 285 | t.plan(1) |
307 | 286 | tester = t |
308 | - const tree = new RadixTree({ | |
309 | - db | |
310 | - }) | |
287 | + const tree = new RadixTree({db}) | |
288 | + let wasm = fs.readFileSync(WASM_PATH + '/creation.wasm') | |
289 | + let receiver = fs.readFileSync(WASM_PATH + '/reciever.wasm') | |
311 | 290 | |
312 | - const wasm = fs.readFileSync(WASM_PATH + '/globals.wasm') | |
313 | - | |
314 | 291 | const hypervisor = new Hypervisor(tree) |
315 | 292 | hypervisor.registerContainer(TestWasmContainer) |
316 | 293 | |
317 | 294 | const {module} = await hypervisor.createActor(TestWasmContainer.typeId, wasm) |
295 | + const funcRef = module.getFuncRef('main') | |
296 | + funcRef.gas = 322000 | |
318 | 297 | |
319 | - await new Promise((resolve, reject) => { | |
320 | - const funcRef = module.getFuncRef('store') | |
321 | - funcRef.gas = 400 | |
322 | - const message = new Message({ | |
323 | - funcRef | |
324 | - }).on('done', actor => { | |
325 | - resolve() | |
326 | - }) | |
327 | - hypervisor.send(message) | |
298 | + const message = new Message({ | |
299 | + funcRef, | |
300 | + funcArguments: [receiver] | |
328 | 301 | }) |
302 | + hypervisor.send(message) | |
303 | +}) | |
329 | 304 | |
330 | - await new Promise((resolve, reject) => { | |
331 | - const funcRef = module.getFuncRef('load') | |
332 | - funcRef.gas = 400 | |
333 | - const message = new Message({ | |
334 | - funcRef | |
335 | - }).on('done', actor => { | |
336 | - const b = actor.container.get8Memory(5, 4) | |
337 | - const result = Buffer.from(b).toString() | |
338 | - t.deepEquals(result, 'test', 'should copy memory correctly') | |
339 | - resolve() | |
340 | - }) | |
341 | - hypervisor.send(message) | |
305 | +tape('storage', async t => { | |
306 | + tester = t | |
307 | + const tree = new RadixTree({db}) | |
308 | + let wasm = fs.readFileSync(WASM_PATH + '/storage.wasm') | |
309 | + | |
310 | + const egress = new EgressDriver() | |
311 | + | |
312 | + egress.on('message', msg => { | |
313 | + t.equals(msg.funcArguments[0].toString(), 'hello world') | |
314 | + t.end() | |
342 | 315 | }) |
316 | + | |
317 | + const hypervisor = new Hypervisor(tree, [TestWasmContainer], [egress]) | |
318 | + | |
319 | + const {module} = await hypervisor.createActor(TestWasmContainer.typeId, wasm) | |
320 | + const funcRef = module.getFuncRef('main') | |
321 | + funcRef.gas = 322000 | |
322 | + | |
323 | + const message = new Message({ | |
324 | + funcRef | |
325 | + }) | |
326 | + | |
327 | + hypervisor.send(message) | |
328 | + | |
329 | + const funcRef2 = module.getFuncRef('load') | |
330 | + funcRef2.gas = 322000 | |
331 | + | |
332 | + await hypervisor.createStateRoot() | |
333 | + | |
334 | + const message2 = new Message({ | |
335 | + funcRef: funcRef2, | |
336 | + funcArguments: [new FunctionRef({actorID: egress.id, params: ['data']})] | |
337 | + }) | |
338 | + | |
339 | + hypervisor.send(message2) | |
343 | 340 | }) |
344 | 341 | |
345 | -tape('creation', async t => { | |
342 | +tape('link', async t => { | |
343 | + tester = t | |
344 | + const tree = new RadixTree({db}) | |
345 | + let wasm = fs.readFileSync(WASM_PATH + '/link.wasm') | |
346 | + | |
347 | + const egress = new EgressDriver() | |
348 | + | |
349 | + egress.on('message', msg => { | |
350 | + t.equals(msg.funcArguments[0].toString(), 'hello world') | |
351 | + t.end() | |
352 | + }) | |
353 | + | |
354 | + const hypervisor = new Hypervisor(tree, [TestWasmContainer], [egress]) | |
355 | + | |
356 | + const {module} = await hypervisor.createActor(TestWasmContainer.typeId, wasm) | |
357 | + const funcRef = module.getFuncRef('main') | |
358 | + funcRef.gas = 322000 | |
359 | + | |
360 | + const message = new Message({ | |
361 | + funcRef | |
362 | + }) | |
363 | + | |
364 | + hypervisor.send(message) | |
365 | + | |
366 | + const funcRef2 = module.getFuncRef('load') | |
367 | + funcRef2.gas = 322000 | |
368 | + | |
369 | + await hypervisor.createStateRoot() | |
370 | + | |
371 | + const message2 = new Message({ | |
372 | + funcRef: funcRef2, | |
373 | + funcArguments: [new FunctionRef({actorID: egress.id, params: ['data']})] | |
374 | + }) | |
375 | + | |
376 | + hypervisor.send(message2) | |
377 | +}) | |
378 | + | |
379 | +tape('invalid binary', async t => { | |
346 | 380 | t.plan(1) |
347 | 381 | tester = t |
348 | 382 | const tree = new RadixTree({db}) |
349 | - let wasm = fs.readFileSync(WASM_PATH + '/creation.wasm') | |
350 | - let receiver = fs.readFileSync(WASM_PATH + '/reciever.wasm') | |
383 | + const wasm = Buffer.from([0]) | |
351 | 384 | |
352 | 385 | const hypervisor = new Hypervisor(tree) |
353 | 386 | hypervisor.registerContainer(TestWasmContainer) |
354 | 387 | |
388 | + try { | |
389 | + await hypervisor.createActor(TestWasmContainer.typeId, wasm) | |
390 | + } catch (e) { | |
391 | + t.pass() | |
392 | + } | |
393 | +}) | |
394 | + | |
395 | +tape('out of gas', async t => { | |
396 | + t.plan(1) | |
397 | + tester = t | |
398 | + const tree = new RadixTree({db}) | |
399 | + let wasm = fs.readFileSync(WASM_PATH + '/i64.wasm') | |
400 | + | |
401 | + const hypervisor = new Hypervisor(tree) | |
402 | + hypervisor.registerContainer(TestWasmContainer) | |
403 | + | |
355 | 404 | const {module} = await hypervisor.createActor(TestWasmContainer.typeId, wasm) |
356 | 405 | const funcRef = module.getFuncRef('main') |
357 | - funcRef.gas = 322000 | |
358 | 406 | |
359 | 407 | const message = new Message({ |
360 | - funcRef, | |
361 | - funcArguments: [receiver] | |
408 | + funcRef | |
362 | 409 | }).on('execution:error', e => { |
363 | - console.log(e) | |
410 | + t.pass() | |
364 | 411 | }) |
365 | 412 | hypervisor.send(message) |
366 | 413 | }) |