git ssb

0+

wanderer🌟 / js-primea-hypervisor



Commit d386ffea54d493af4f2eb1eeadc633dd579cdbe3

intial stab at new interface

Signed-off-by: wanderer <mjbecze@gmail.com>
wanderer committed on 2/2/2018, 6:56:19 PM
Parent: c779344f6653a07ed5e2695bca74c9cbd0c66a19

Files changed

actor.jschanged
customTypes.jschanged
package-lock.jsonchanged
package.jsonchanged
scheduler.jschanged
tests/wasm/addTwo.wasmdeleted
tests/wasm/caller.wasmadded
tests/wasm/reciever.wasmadded
tests/wast/addTwo.wastdeleted
tests/wast/caller.jsonadded
tests/wast/caller.wastadded
tests/wast/reciever.jsonadded
tests/wast/reciever.wastadded
tests/typeChecking.jsadded
tests/wasmContainer.jsdeleted
typeCheckWrapper.jsadded
wasmContainer.jsadded
actor.jsView
@@ -18,36 +18,8 @@
1818 this.ticks = 0
1919 this.running = false
2020 }
2121
22- /**
23- * adds a message to this actor's message queue
24- * @param {string} portName
25- * @param {object} message
26- */
27- queue (message) {
28- this.inbox.push(message)
29-
30- if (!this.running) {
31- this.running = true
32- return this._startMessageLoop()
33- }
34- }
35-
36- // waits for the next message
37- async _startMessageLoop () {
38- // this ensure we only every have one loop running at a time
39- while (this.inbox.length) {
40- const message = this.inbox.shift()
41- if (message._fromTicks > this.ticks) {
42- this.hypervisor.scheduler.update(this.ticks, message._fromTicks)
43- this.ticks = message._fromTicks
44- }
45- await this.runMessage(message)
46- }
47- this.running = false
48- }
49-
5022 serializeMetaData () {
5123 return Actor.serializeMetaData(this.type, this.nonce)
5224 }
5325
@@ -57,25 +29,8 @@
5729 destId: this.id
5830 }
5931 }
6032
61- static serializeMetaData (type, nonce = 0) {
62- const p = new Pipe()
63- leb128.write(type, p)
64- leb128.write(nonce, p)
65- return p.buffer
66- }
67-
68- static deserializeMetaData (buffer) {
69- const pipe = new Pipe(buffer)
70- const type = leb128.read(pipe)
71- const nonce = leb128.read(pipe)
72- return {
73- nonce,
74- type
75- }
76- }
77-
7833 /**
7934 * Runs the shutdown routine for the actor
8035 */
8136 async shutdown () {
@@ -96,8 +51,11 @@
9651 * @param {String} method - which method to run
9752 * @returns {Promise}
9853 */
9954 async runMessage (message) {
55+ if (message._fromTicks > this.ticks) {
56+ this.ticks = message._fromTicks
57+ }
10058 try {
10159 this.currentMessage = message
10260 await this.instance.exports[message.funcRef.name](...message.funcArguments)
10361 } catch (e) {
@@ -110,11 +68,9 @@
11068 * updates the number of ticks that the actor has run
11169 * @param {Number} count - the number of ticks to add
11270 */
11371 incrementTicks (count) {
114- const oldValue = this.ticks
11572 this.ticks += count
116- this.hypervisor.scheduler.update(oldValue, this.ticks)
11773 }
11874
11975 /**
12076 * creates an actor
@@ -146,5 +102,22 @@
146102 message._fromId = this.id
147103
148104 this.hypervisor.scheduler.queue([message])
149105 }
106+
107+ static serializeMetaData (type, nonce = 0) {
108+ const p = new Pipe()
109+ leb128.write(type, p)
110+ leb128.write(nonce, p)
111+ return p.buffer
112+ }
113+
114+ static deserializeMetaData (buffer) {
115+ const pipe = new Pipe(buffer)
116+ const type = leb128.read(pipe)
117+ const nonce = leb128.read(pipe)
118+ return {
119+ nonce,
120+ type
121+ }
122+ }
150123 }
customTypes.jsView
@@ -59,10 +59,10 @@
5959 let numOfEntries = leb.unsigned.read(stream)
6060 const json = []
6161 while (numOfEntries--) {
6262 json.push({
63- func: leb.unsigned.read(stream),
64- type: leb.unsigned.read(stream)
63+ func: leb.unsigned.readBn(stream).toNumber(),
64+ type: leb.unsigned.readBn(stream).toNumber()
6565 })
6666 }
6767 return json
6868 }
@@ -126,10 +126,16 @@
126126 const body = wasm.subarray(8)
127127 return Buffer.concat([preramble, custom, body])
128128 }
129129
130+function inject (wasm, json) {
131+ const buf = encodeJSON(json)
132+ return injectCustomSection(buf, wasm)
133+}
134+
130135 module.exports = {
131136 injectCustomSection,
137+ inject,
132138 decodeType,
133139 decodeTypeMap,
134140 encodeType,
135141 encodeTypeMap,
package-lock.jsonView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 341262 bytes
New file size: 342441 bytes
package.jsonView
@@ -33,10 +33,12 @@
3333 "binary-search-insert": "^1.0.3",
3434 "buffer-pipe": "0.0.2",
3535 "events": "^1.1.1",
3636 "leb128": "0.0.4",
37+ "reference-map": "^1.1.0",
3738 "safe-buffer": "^5.1.1",
38- "wasm-json-toolkit": "^0.2.0"
39+ "wasm-json-toolkit": "^0.2.0",
40+ "wasm-metering": "^0.1.1"
3941 },
4042 "devDependencies": {
4143 "coveralls": "^3.0.0",
4244 "dfinity-radix-tree": "0.0.9",
scheduler.jsView
@@ -45,22 +45,14 @@
4545 this.actors.clear()
4646 this.emit('idle')
4747 }
4848
49- // enable for concurrency
50- update (oldTicks, ticks) {
51- }
52-
5349 async _processMessage (message) {
5450 const to = message.funcRef.destId.toString('hex')
5551 let actor = this.actors.get(to)
5652 if (!actor) {
5753 actor = await this.hypervisor.loadActor(message.funcRef.destId)
5854 this.actors.set(to, actor)
5955 }
60- const promise = new Promise((resolve, reject) => {
61- message.on('done', resolve)
62- })
63- actor.queue(message)
64- return promise
56+ return actor.runMessage(message)
6557 }
6658 }
tests/wasm/addTwo.wasmView
@@ -1,4 +1,0 @@
1-asm`
2-addTwo
3-
4- j
tests/wasm/caller.wasmView
@@ -1,0 +1,2 @@
1+asm ``func internalizeptablecall
2+ A A
tests/wasm/reciever.wasmView
@@ -1,0 +1,4 @@
1+asm
2+``testcheck receive
3+
4+ A
tests/wast/addTwo.wastView
@@ -1,8 +1,0 @@
1-(module
2- (func $addTwo (param i32 i32)
3- get_local 0
4- get_local 1
5- i32.add
6- drop
7- )
8- (export "addTwo" (func $addTwo)))
tests/wast/caller.jsonView
@@ -1,0 +1,12 @@
1+{
2+ "type": [{
3+ "form": "func",
4+ "params": [
5+ "func"
6+ ]
7+ }],
8+ "typeMap": [{
9+ "func": 0,
10+ "type": 0
11+ }]
12+}
tests/wast/caller.wastView
@@ -1,0 +1,11 @@
1+(module
2+ (import "func" "internalize" (func $internalize (param i32 i32) (result i32)))
3+ (table (export "table") 1 1 anyfunc)
4+ (func $call (param i32)
5+ i32.const 5
6+ get_local 0
7+ i32.const 0
8+ call $internalize
9+ call_indirect (param i32)
10+ )
11+ (export "call" (func $call)))
tests/wast/reciever.jsonView
@@ -1,0 +1,4 @@
1+{
2+ "type": [],
3+ "typeMap": []
4+}
tests/wast/reciever.wastView
@@ -1,0 +1,8 @@
1+(module
2+ (import "test" "check" (func $check (param i32 i32)))
3+ (func $receive (param i32)
4+ get_local 0
5+ i32.const 5
6+ call $check
7+ )
8+ (export "receive" (func $receive)))
tests/typeChecking.jsView
@@ -1,0 +1,33 @@
1+const customTypes = require('../customTypes.js')
2+const WasmContainer = require('../wasmContainer.js')
3+const fs = require('fs')
4+
5+async function main () {
6+ let callerJSON = JSON.parse(fs.readFileSync('./wast/caller.json'))
7+ let callerWasm = fs.readFileSync('./wasm/caller.wasm')
8+ callerWasm = customTypes.inject(callerWasm, callerJSON)
9+
10+ let recieverJSON = JSON.parse(fs.readFileSync('./wast/reciever.json'))
11+ let recieverWasm = fs.readFileSync('./wasm/reciever.wasm')
12+ recieverWasm = customTypes.inject(recieverWasm, recieverJSON)
13+
14+ const callerContainer = new WasmContainer()
15+ callerContainer.onCreation(callerWasm)
16+ callerJSON = callerContainer.json
17+
18+ const recieverContainer = new WasmContainer()
19+ recieverContainer.onCreation(recieverWasm)
20+ recieverJSON = recieverContainer.json
21+
22+ const callFuncRef = callerContainer.getFuncRef('call')
23+
24+ const funcRef = recieverContainer.getFuncRef('receive')
25+ callFuncRef.args.push({
26+ type: 'funcRef',
27+ arg: funcRef
28+ })
29+
30+ callerContainer.onMessage(callFuncRef)
31+}
32+
33+main()
tests/wasmContainer.jsView
@@ -1,15 +1,0 @@
1-const fs = require('fs')
2-const tape = require('tape')
3-const WasmContainer = require('../wasmContainer.js')
4-// const Message = require('../message.js')
5-// const Hypervisor = require('../')
6-
7-// const level = require('level-browserify')
8-// const RadixTree = require('dfinity-radix-tree')
9-// const db = level('./testdb')
10-tape('basic', async t => {
11- const addTwoBin = fs.readFileSync(`${__dirname}/wasm/addTwo.wasm`)
12- const exports = WasmContainer.actorRef(addTwoBin, 'test')
13- console.log(exports)
14- t.end()
15-})
typeCheckWrapper.jsView
@@ -1,0 +1,166 @@
1+const LANGUAGE_TYPES = {
2+ 'actor': 0x0,
3+ 'buf': 0x1,
4+ 'i32': 0x7f,
5+ 'i64': 0x7e,
6+ 'f32': 0x7d,
7+ 'f64': 0x7c,
8+ 'anyFunc': 0x70,
9+ 'func': 0x60,
10+ 'block_type': 0x40
11+}
12+
13+module.exports = function generateWrapper (type) {
14+ const module = [{
15+ 'name': 'preramble',
16+ 'magic': [
17+ 0,
18+ 97,
19+ 115,
20+ 109
21+ ],
22+ 'version': [
23+ 1,
24+ 0,
25+ 0,
26+ 0
27+ ]
28+ }, {
29+ 'name': 'type',
30+ 'entries': [{
31+ 'form': 'func',
32+ 'params': [
33+ ]
34+ }, {
35+ 'form': 'func',
36+ 'params': [
37+ // imported check
38+ ]
39+ }, {
40+ 'form': 'func',
41+ 'params': [
42+ // exported check
43+ ]
44+ }, {
45+ 'form': 'func',
46+ 'params': [
47+ // invoke
48+ ]
49+ }]
50+ }, {
51+ 'name': 'import',
52+ 'entries': [{
53+ 'moduleStr': 'env',
54+ 'fieldStr': 'checkTypes',
55+ 'kind': 'function',
56+ 'type': 0
57+ }]
58+ }, {
59+ 'name': 'function',
60+ 'entries': [
61+ 1,
62+ 2
63+ ]
64+ }, {
65+ 'name': 'table',
66+ 'entries': [{
67+ 'elementType': 'anyFunc',
68+ 'limits': {
69+ 'flags': 1,
70+ 'intial': 1,
71+ 'maximum': 1
72+ }
73+ }]
74+ }, {
75+ 'name': 'global',
76+ 'entries': []
77+ }, {
78+ 'name': 'export',
79+ 'entries': [{
80+ 'field_str': 'table',
81+ 'kind': 'table',
82+ 'index': 0
83+ }, {
84+ 'field_str': 'invoke',
85+ 'kind': 'function',
86+ 'index': 2
87+ }, {
88+ 'field_str': 'check',
89+ 'kind': 'function',
90+ 'index': 1
91+ }]
92+ }, {
93+ 'name': 'code',
94+ 'entries': [{
95+ 'locals': [],
96+ 'code': []
97+ }, {
98+ 'locals': [],
99+ 'code': []
100+ }]
101+ }]
102+ const definedTypes = new Set(['actor', 'func', 'buf'])
103+ const setGlobals = []
104+ const importType = module[1].entries[0].params
105+ const checkType = module[1].entries[1].params
106+ const invokerType = module[1].entries[2].params
107+ const invokeType = module[1].entries[3].params
108+ const checkCode = module[7].entries[0].code
109+ const invokeCode = module[7].entries[1].code
110+
111+ type.params.forEach((param, index) => {
112+ let baseType = param
113+ const typeCode = LANGUAGE_TYPES[param]
114+ // import type
115+ if (definedTypes.has(param)) {
116+ baseType = 'i32'
117+ } else {
118+ baseType = param
119+ }
120+
121+ // check import
122+ importType.push('i32')
123+ importType.push('i32')
124+ checkCode.push({
125+ 'return_type': 'i32',
126+ 'name': 'const',
127+ 'immediates': typeCode
128+ })
129+ checkCode.push({
130+ 'name': 'get_local',
131+ 'immediates': index
132+ })
133+ invokeCode.push({
134+ 'name': 'get_local',
135+ 'immediates': index
136+ })
137+ // check export
138+ checkType.push(baseType)
139+ // invoke
140+ invokeType.push(baseType)
141+ invokerType.push(baseType)
142+ })
143+
144+ module[7].entries[0].code = checkCode.concat(setGlobals, [{
145+ 'name': 'call',
146+ 'immediates': '0'
147+ }, {
148+ 'name': 'end'
149+ }])
150+ invokeCode.push({
151+ 'return_type': 'i32',
152+ 'name': 'const',
153+ 'immediates': '0'
154+ })
155+ invokeCode.push({
156+ 'name': 'call_indirect',
157+ 'immediates': {
158+ 'index': 3,
159+ 'reserved': 0
160+ }
161+ })
162+ invokeCode.push({
163+ 'name': 'end'
164+ })
165+ return module
166+}
wasmContainer.jsView
@@ -1,0 +1,227 @@
1+const {wasm2json, json2wasm} = require('wasm-json-toolkit')
2+const wasmMetering = require('wasm-metering')
3+const customTypes = require('./customTypes.js')
4+const typeCheckWrapper = require('./typeCheckWrapper.js')
5+const ReferanceMap = require('reference-map')
6+
7+const nativeTypes = new Set(['i32', 'i64', 'f32', 'f64'])
8+const LANGUAGE_TYPES = {
9+ 'actor': 0x0,
10+ 'buf': 0x1,
11+ 'i32': 0x7f,
12+ 'i64': 0x7e,
13+ 'f32': 0x7d,
14+ 'f64': 0x7c,
15+ 'anyFunc': 0x70,
16+ 'func': 0x60,
17+ 'block_type': 0x40,
18+
19+ 0x0: 'actor',
20+ 0x1: 'buf',
21+ 0x7f: 'i32',
22+ 0x7e: 'i64',
23+ 0x7d: 'f32',
24+ 0x7c: 'f64',
25+ 0x70: 'anyFunc',
26+ 0x60: 'func',
27+ 0x40: 'block_type'
28+}
29+
30+class Ref {
31+ serialize () {}
32+}
33+
34+class FunctionRef {
35+ constructor (json, name) {
36+ this.name = name
37+ this.args = []
38+ const typeIndex = json.typeMap[name]
39+ const type = json.type[typeIndex]
40+ const wrapper = typeCheckWrapper(type)
41+ const wasm = json2wasm(wrapper)
42+ this.mod = WebAssembly.Module(wasm)
43+ const self = this
44+ const instance = WebAssembly.Instance(this.mod, {
45+ 'env': {
46+ 'checkTypes': function () {
47+ const args = [...arguments]
48+ while (args.length) {
49+ const type = LANGUAGE_TYPES[args.shift()]
50+ let arg = args.shift()
51+ if (!nativeTypes.has(type)) {
52+ arg = self._container.refs.get(arg)
53+ if (arg.type !== type) {
54+ throw new Error('invalid type')
55+ }
56+ }
57+ self.args.push({
58+ arg,
59+ type
60+ })
61+ }
62+ self._container.sendMessage(instance)
63+ }
64+ }
65+ })
66+ this.wrapper = instance
67+ }
68+ set container (container) {
69+ this._container = container
70+ }
71+}
72+
73+module.exports = class WasmContainer {
74+ constructor () {
75+ this.refs = new ReferanceMap()
76+ }
77+ onCreation (wasm) {
78+ let moduleJSON = wasm2json(wasm)
79+ this.json = mergeTypeSections(moduleJSON)
80+ moduleJSON = wasmMetering.meterJSON(moduleJSON, {
81+ meterType: 'i32'
82+ })
83+ this.wasm = json2wasm(moduleJSON)
84+ this.mod = WebAssembly.Module(this.wasm)
85+ }
86+
87+ sendMessage () {
88+ console.log('send')
89+ }
90+
91+ onMessage (funcRef) {
92+ const self = this
93+ const instance = WebAssembly.Instance(this.mod, {
94+ func: {
95+ externalize: () => {},
96+ internalize: (ref, index) => {
97+ const {type, arg} = self.refs.get(ref, FunctionRef)
98+ arg.container = self
99+ instance.exports.table.set(index, arg.wrapper.exports.check)
100+ },
101+ catch: (ref, catchRef) => {
102+ const {funcRef} = self.refs.get(ref, FunctionRef)
103+ const {funcRef: catchFunc} = self.refs.get(ref, FunctionRef)
104+ funcRef.catch = catchFunc
105+ },
106+ getGasAmount: () => {},
107+ setGasAmount: () => {}
108+ },
109+ storage: {
110+ load: () => {},
111+ store: () => {},
112+ delete: () => {}
113+ },
114+ link: {
115+ wrap: (ref) => {
116+ const obj = this.refs.get(ref)
117+ obj.seriarlize()
118+ },
119+ unwrap: () => {}
120+ },
121+ databuf: {
122+ create: () => {},
123+ load8: () => {},
124+ load16: () => {},
125+ load32: () => {},
126+ load64: () => {},
127+ store8: () => {},
128+ store16: () => {},
129+ store32: () => {},
130+ store64: () => {},
131+ copy: () => {}
132+ },
133+ elembuf: {
134+ create: () => {},
135+ load: () => {},
136+ store: () => {},
137+ delete: () => {}
138+ },
139+ test: {
140+ check: (a, b) => {
141+ console.log('$$$$', a, b)
142+ }
143+ },
144+ metering: {
145+ usegas: () => {}
146+ }
147+ })
148+ const args = funcRef.args.map(arg => {
149+ if (nativeTypes.has(arg.type)) {
150+ return arg.arg
151+ } else {
152+ return this.refs.add(arg)
153+ }
154+ })
155+ instance.exports[funcRef.name](...args)
156+ }
157+
158+ getFuncRef (name, send) {
159+ const funcRef = new FunctionRef(this.json, name, send)
160+ return funcRef
161+ }
162+}
163+
164+function mergeTypeSections (json) {
165+ const typeInfo = {}
166+ let typeSection = {
167+ 'entries': []
168+ }
169+ let importSection = {
170+ 'entries': []
171+ }
172+ let functionSection = {
173+ 'entries': []
174+ }
175+ let exportSection = {
176+ 'entries': []
177+ }
178+ json.forEach(section => {
179+ switch (section.name) {
180+ case 'type':
181+ typeSection = section
182+ break
183+ case 'export':
184+ exportSection = section
185+ break
186+ case 'import':
187+ importSection = section
188+ break
189+ case 'function':
190+ functionSection = section
191+ break
192+ case 'custom':
193+ switch (section.sectionName) {
194+ case 'type':
195+ typeInfo.type = customTypes.decodeType(section.payload)
196+ break
197+ case 'typeMap':
198+ typeInfo.typeMap = customTypes.decodeTypeMap(section.payload)
199+ break
200+ }
201+ break
202+ }
203+ })
204+
205+ const foundTypes = new Map()
206+ const mappedFuncs = new Map()
207+ const newTypeMap = {}
208+ typeInfo.typeMap.forEach(map => mappedFuncs.set(map.func, map.type))
209+ for (let exprt of exportSection.entries) {
210+ if (exprt.kind === 'function') {
211+ if (!mappedFuncs.has(exprt.index)) {
212+ const typeIndex = functionSection.entries[exprt.index - importSection.entries.length]
213+ if (!foundTypes.has(typeIndex)) {
214+ const customIndex = typeInfo.type.push(typeSection.entries[typeIndex]) - 1
215+ foundTypes.set(typeIndex, customIndex)
216+ }
217+ const customIndex = foundTypes.get(typeIndex)
218+ newTypeMap[exprt.field_str] = customIndex
219+ } else {
220+ newTypeMap[exprt.field_str] = mappedFuncs.get(exprt.index)
221+ }
222+ }
223+ }
224+
225+ typeInfo.typeMap = newTypeMap
226+ return typeInfo
227+}

Built with git-ssb-web