Commit 0c79dfe47f09117aac1561665a4c2933c4cc17d7
Merge pull request #6 from ewasm/proper-wasm-export
Export methods to WASM properlywanderer authored on 8/3/2016, 6:45:23 PM
GitHub committed on 8/3/2016, 6:45:23 PM
Parent: c137aba03aa9981099698af3a30df2969c42a73b
Parent: 184bf886b55ace71c3b15786ecd494c3973ca8d6
Files changed
index.js | changed |
interface.js | changed |
index.js | ||
---|---|---|
@@ -26,9 +26,9 @@ | ||
26 | 26 | |
27 | 27 | // handles running code. |
28 | 28 | static codeHandler (code, ethInterface = new Interface(new Environment())) { |
29 | 29 | const instance = Wasm.instantiateModule(code, { |
30 | - 'ethereum': ethInterface | |
30 | + 'ethereum': ethInterface.exportTable | |
31 | 31 | }) |
32 | 32 | |
33 | 33 | ethInterface.setModule(instance) |
34 | 34 | if (instance.exports.main) { |
interface.js | ||
---|---|---|
@@ -2,63 +2,98 @@ | ||
2 | 2 | * This is the Ethereum interface that is exposed to the WASM instance which |
3 | 3 | * enables to interact with the Ethereum Environment |
4 | 4 | */ |
5 | 5 | const constants = require('./constants.js') |
6 | -// const Graph = require('generic-digraph') | |
7 | -// function.bind is not working corretly whith Wasm imports. So instead create | |
8 | -// a global for now. TODO REMOVE | |
9 | -let ENV | |
10 | -let MOD | |
6 | + | |
11 | 7 | // The interface exposed to the WebAessembly Core |
12 | 8 | module.exports = class Interface { |
13 | 9 | print (a) { |
14 | 10 | console.log(a) |
15 | 11 | } |
16 | 12 | |
17 | 13 | memPrint () { |
18 | - console.log((new Uint8Array(MOD.exports.memory)).toString()) | |
14 | + console.log((new Uint8Array(this.module.exports.memory)).toString()) | |
19 | 15 | } |
20 | 16 | |
21 | 17 | constructor (environment) { |
22 | - ENV = this.environment = environment | |
18 | + this.environment = environment | |
23 | 19 | } |
24 | 20 | |
21 | + get exportTable () { | |
22 | + let exportMethods = [ | |
23 | + // include all the public methods according to the Ethereum Environment Interface (EEI) | |
24 | + // FIXME: this currently doesn't match EEI r0 | |
25 | + 'useGas', | |
26 | + 'gas', | |
27 | + 'address', | |
28 | + 'balance', | |
29 | + 'origin', | |
30 | + 'caller', | |
31 | + 'callValue', | |
32 | + 'callDataSize', | |
33 | + 'callDataCopy', | |
34 | + 'codeSize', | |
35 | + 'codeCopy', | |
36 | + 'extCodeSize', | |
37 | + 'extCodeCopy', | |
38 | + 'gasPrice', | |
39 | + 'blockHash', | |
40 | + 'coinbase', | |
41 | + 'timestamp', | |
42 | + 'number', | |
43 | + 'difficulty', | |
44 | + 'gasLimit', | |
45 | + 'log', | |
46 | + 'create', | |
47 | + 'call', | |
48 | + 'callDelegate', | |
49 | + 'sstore', | |
50 | + 'sload', | |
51 | + 'return', | |
52 | + 'suicide' | |
53 | + ] | |
54 | + let ret = {} | |
55 | + exportMethods.forEach((method) => { | |
56 | + ret[method] = this[method].bind(this) | |
57 | + }) | |
58 | + return ret | |
59 | + } | |
25 | 60 | |
26 | 61 | // FIXME: this shouldn't be needed |
27 | 62 | get env () { |
28 | - return ENV | |
63 | + return this.environment | |
29 | 64 | } |
30 | 65 | |
31 | 66 | setModule (mod) { |
32 | - this.module = MOD = mod | |
67 | + this.module = mod | |
33 | 68 | } |
34 | 69 | |
35 | 70 | /** |
36 | 71 | * Subtracts an amount to the gas counter |
37 | 72 | * @param {integer} amount the amount to subtract to the gas counter |
38 | 73 | */ |
39 | 74 | useGas (amount) { |
40 | 75 | if (amount > 0) { |
41 | - ENV.gasLimit -= amount | |
76 | + this.environment.gasLimit -= amount | |
42 | 77 | } |
43 | 78 | } |
44 | 79 | |
45 | 80 | /** |
46 | 81 | * Returns the current amount of gas |
47 | 82 | * @return {integer} |
48 | 83 | */ |
49 | 84 | gas () { |
50 | - return ENV.gasLimit | |
85 | + return this.environment.gasLimit | |
51 | 86 | } |
52 | 87 | |
53 | 88 | /** |
54 | 89 | * Gets address of currently executing account and loads it into memory at |
55 | 90 | * the given offset. |
56 | 91 | * @param {integer} offset |
57 | 92 | */ |
58 | 93 | address (offset) { |
59 | - const address = ENV.address | |
60 | - const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
94 | + const address = this.environment.address | |
95 | + const memory = new Uint8Array(this.module.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
61 | 96 | memory.set(address) |
62 | 97 | } |
63 | 98 | |
64 | 99 | /** |
@@ -67,12 +102,12 @@ | ||
67 | 102 | * @param {integer} addressOffset the memory offset to laod the address |
68 | 103 | * @param {integer} resultOffset |
69 | 104 | */ |
70 | 105 | balance (addressOffset, offset) { |
71 | - const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
72 | - const memory = new Uint8Array(MOD.exports.memory, offset, constants.MAX_BAL_BYTES) | |
106 | + const address = new Uint8Array(this.module.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
107 | + const memory = new Uint8Array(this.module.exports.memory, offset, constants.MAX_BAL_BYTES) | |
73 | 108 | // call the parent contract and ask for the balance of one of its child contracts |
74 | - const balance = ENV.parent.environment.getBalance(address) | |
109 | + const balance = this.environment.parent.environment.getBalance(address) | |
75 | 110 | memory.set(balance) |
76 | 111 | } |
77 | 112 | |
78 | 113 | /** |
@@ -81,10 +116,10 @@ | ||
81 | 116 | * account with non-empty associated code. |
82 | 117 | * @param {integer} offset |
83 | 118 | */ |
84 | 119 | origin (offset) { |
85 | - const origin = ENV.origin | |
86 | - const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
120 | + const origin = this.environment.origin | |
121 | + const memory = new Uint8Array(this.module.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
87 | 122 | memory.set(origin) |
88 | 123 | } |
89 | 124 | |
90 | 125 | /** |
@@ -92,10 +127,10 @@ | ||
92 | 127 | * the address of the account that is directly responsible for this execution. |
93 | 128 | * @param {integer} offset |
94 | 129 | */ |
95 | 130 | caller (offset) { |
96 | - const caller = ENV.caller | |
97 | - const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
131 | + const caller = this.environment.caller | |
132 | + const memory = new Uint8Array(this.module.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
98 | 133 | memory.set(caller) |
99 | 134 | } |
100 | 135 | |
101 | 136 | /** |
@@ -103,10 +138,10 @@ | ||
103 | 138 | * this execution and loads it into memory at the given location. |
104 | 139 | * @param {integer} offset |
105 | 140 | */ |
106 | 141 | callValue (offset) { |
107 | - const callValue = ENV.callValue | |
108 | - const memory = new Uint8Array(MOD.exports.memory, offset, constants.MAX_BAL_BYTES) | |
142 | + const callValue = this.environment.callValue | |
143 | + const memory = new Uint8Array(this.module.exports.memory, offset, constants.MAX_BAL_BYTES) | |
109 | 144 | memory.set(callValue) |
110 | 145 | } |
111 | 146 | |
112 | 147 | /** |
@@ -114,9 +149,9 @@ | ||
114 | 149 | * data passed with the message call instruction or transaction. |
115 | 150 | * @return {integer} |
116 | 151 | */ |
117 | 152 | callDataSize () { |
118 | - return ENV.callData.byteLength | |
153 | + return this.environment.callData.byteLength | |
119 | 154 | } |
120 | 155 | |
121 | 156 | /** |
122 | 157 | * Copys the input data in current environment to memory. This pertains to |
@@ -125,19 +160,19 @@ | ||
125 | 160 | * @param {integer} dataOffset the offset in the input data |
126 | 161 | * @param {integer} length the length of data to copy |
127 | 162 | */ |
128 | 163 | callDataCopy (offset, dataOffset, length) { |
129 | - const callData = new Uint8Array(ENV.callData, offset, length) | |
130 | - const memory = new Uint8Array(MOD.exports.memory, offset, length) | |
164 | + const callData = new Uint8Array(this.environment.callData, offset, length) | |
165 | + const memory = new Uint8Array(this.module.exports.memory, offset, length) | |
131 | 166 | memory.set(callData) |
132 | 167 | } |
133 | 168 | |
134 | 169 | /** |
135 | 170 | * Gets the size of code running in current environment. |
136 | 171 | * @return {interger} |
137 | 172 | */ |
138 | 173 | codeSize () { |
139 | - return ENV.code.byteLength | |
174 | + return this.environment.code.byteLength | |
140 | 175 | } |
141 | 176 | |
142 | 177 | /** |
143 | 178 | * Copys the code running in current environment to memory. |
@@ -145,10 +180,10 @@ | ||
145 | 180 | * @param {integer} codeOffset the code offset |
146 | 181 | * @param {integer} length the length of code to copy |
147 | 182 | */ |
148 | 183 | codeCopy (offset, codeOffset, length) { |
149 | - const code = new Uint8Array(ENV.code, codeOffset, length) | |
150 | - const memory = new Uint8Array(MOD.exports.memory, offset, length) | |
184 | + const code = new Uint8Array(this.environment.code, codeOffset, length) | |
185 | + const memory = new Uint8Array(this.module.exports.memory, offset, length) | |
151 | 186 | memory.set(code) |
152 | 187 | } |
153 | 188 | |
154 | 189 | /** |
@@ -156,9 +191,9 @@ | ||
156 | 191 | * @param {integer} addressOffset the offset in memory to load the address from |
157 | 192 | * @return {integer} |
158 | 193 | */ |
159 | 194 | extCodeSize (addressOffset) { |
160 | - const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
195 | + const address = new Uint8Array(this.module.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
161 | 196 | const code = this.environment.getCode(address) |
162 | 197 | return code.byteLength |
163 | 198 | } |
164 | 199 | |
@@ -169,21 +204,21 @@ | ||
169 | 204 | * @param {integer} codeOffset the code offset |
170 | 205 | * @param {integer} length the length of code to copy |
171 | 206 | */ |
172 | 207 | extCodeCopy (addressOffset, offset, codeOffset, length) { |
173 | - const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
208 | + const address = new Uint8Array(this.module.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
174 | 209 | let code = this.environment.getCode(address) |
175 | 210 | code = new Uint8Array(code, codeOffset, length) |
176 | - const memory = new Uint8Array(MOD.exports.memory, offset, length) | |
211 | + const memory = new Uint8Array(this.module.exports.memory, offset, length) | |
177 | 212 | memory.set(code) |
178 | 213 | } |
179 | 214 | |
180 | 215 | /** |
181 | 216 | * Gets price of gas in current environment. |
182 | 217 | * @return {integer} |
183 | 218 | */ |
184 | 219 | gasPrice () { |
185 | - return ENV.gasPrice | |
220 | + return this.environment.gasPrice | |
186 | 221 | } |
187 | 222 | |
188 | 223 | /** |
189 | 224 | * Gets the hash of one of the 256 most recent complete blocks. |
@@ -191,51 +226,51 @@ | ||
191 | 226 | * @param {integer} offset the offset to load the hash into |
192 | 227 | */ |
193 | 228 | blockHash (number, offset) { |
194 | 229 | const hash = this.environment.getBlockHash(number) |
195 | - const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
230 | + const memory = new Uint8Array(this.module.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
196 | 231 | memory.set(hash) |
197 | 232 | } |
198 | 233 | |
199 | 234 | /** |
200 | 235 | * Gets the block’s beneficiary address and loads into memory. |
201 | 236 | * @param offset |
202 | 237 | */ |
203 | 238 | coinbase (offset) { |
204 | - const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
205 | - memory.set(ENV.coinbase) | |
239 | + const memory = new Uint8Array(this.module.exports.memory, offset, constants.ADD_SIZE_BYTES) | |
240 | + memory.set(this.environment.coinbase) | |
206 | 241 | } |
207 | 242 | |
208 | 243 | /** |
209 | 244 | * Get the block’s timestamp. |
210 | 245 | * @return {integer} |
211 | 246 | */ |
212 | 247 | timestamp () { |
213 | - return ENV.timestamp | |
248 | + return this.environment.timestamp | |
214 | 249 | } |
215 | 250 | |
216 | 251 | /** |
217 | 252 | * Get the block’s number. |
218 | 253 | * @return {integer} |
219 | 254 | */ |
220 | 255 | number () { |
221 | - return ENV.number | |
256 | + return this.environment.number | |
222 | 257 | } |
223 | 258 | |
224 | 259 | /** |
225 | 260 | * Get the block’s difficulty. |
226 | 261 | * @return {integer} |
227 | 262 | */ |
228 | 263 | difficulty () { |
229 | - return ENV.difficulty | |
264 | + return this.environment.difficulty | |
230 | 265 | } |
231 | 266 | |
232 | 267 | /** |
233 | 268 | * Get the block’s gas limit. |
234 | 269 | * @return {integer} |
235 | 270 | */ |
236 | 271 | gasLimit () { |
237 | - return ENV.gasLimit | |
272 | + return this.environment.gasLimit | |
238 | 273 | } |
239 | 274 | |
240 | 275 | /** |
241 | 276 | * Creates a new log in the current environment |
@@ -243,10 +278,10 @@ | ||
243 | 278 | * @param {integer} length the data length |
244 | 279 | * TODO: replace with variadic |
245 | 280 | */ |
246 | 281 | log (dataOffset, length, topic1, topic2, topic3, topic4, topic5) { |
247 | - const data = new Uint8Array(MOD.exports.memory, dataOffset, length) | |
248 | - ENV.logs.push({ | |
282 | + const data = new Uint8Array(this.module.exports.memory, dataOffset, length) | |
283 | + this.environment.logs.push({ | |
249 | 284 | data: data, |
250 | 285 | topics: [topic1, topic2, topic3, topic4, topic5] |
251 | 286 | }) |
252 | 287 | } |
@@ -257,11 +292,11 @@ | ||
257 | 292 | * @param {integer} dataOffset the offset to load the code for the new contract from |
258 | 293 | * @param {integer} length the data length |
259 | 294 | */ |
260 | 295 | create (valueOffset, dataOffset, length) { |
261 | - const value = new Uint8Array(MOD.exports.memory, valueOffset, constants.MAX_BAL_BYTES) | |
262 | - const data = new Uint8Array(MOD.exports.memory, dataOffset, length) | |
263 | - const result = ENV.create(value, data) | |
296 | + const value = new Uint8Array(this.module.exports.memory, valueOffset, constants.MAX_BAL_BYTES) | |
297 | + const data = new Uint8Array(this.module.exports.memory, dataOffset, length) | |
298 | + const result = this.environment.create(value, data) | |
264 | 299 | return result |
265 | 300 | } |
266 | 301 | |
267 | 302 | /** |
@@ -280,14 +315,14 @@ | ||
280 | 315 | if (gas === undefined) { |
281 | 316 | gas = this.gasLeft() |
282 | 317 | } |
283 | 318 | // Load the params from mem |
284 | - const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
285 | - const value = new Uint8Array(MOD.exports.memory, valueOffset, constants.MAX_BAL_BYTES) | |
286 | - const data = new Uint8Array(MOD.exports.memory, dataOffset, dataLength) | |
319 | + const address = new Uint8Array(this.module.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
320 | + const value = new Uint8Array(this.module.exports.memory, valueOffset, constants.MAX_BAL_BYTES) | |
321 | + const data = new Uint8Array(this.module.exports.memory, dataOffset, dataLength) | |
287 | 322 | // Run the call |
288 | - const [result, errorCode] = ENV.call(gas, address, value, data) | |
289 | - const memory = new Uint8Array(MOD.exports.memory, resultOffset, resultLength) | |
323 | + const [result, errorCode] = this.environment.call(gas, address, value, data) | |
324 | + const memory = new Uint8Array(this.module.exports.memory, resultOffset, resultLength) | |
290 | 325 | memory.set(result) |
291 | 326 | |
292 | 327 | return errorCode |
293 | 328 | } |
@@ -304,12 +339,12 @@ | ||
304 | 339 | * @param {integer} resultLength |
305 | 340 | * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not |
306 | 341 | */ |
307 | 342 | callDelegate (gas, addressOffset, dataOffset, dataLength, resultOffset, resultLength) { |
308 | - const data = new Uint8Array(MOD.exports.memory, dataOffset, dataLength) | |
309 | - const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
343 | + const data = new Uint8Array(this.module.exports.memory, dataOffset, dataLength) | |
344 | + const address = new Uint8Array(this.module.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
310 | 345 | const [result, errorCode] = this.environment.callDelegate(gas, address, data) |
311 | - const memory = new Uint8Array(MOD.exports.memory, resultOffset, resultLength) | |
346 | + const memory = new Uint8Array(this.module.exports.memory, resultOffset, resultLength) | |
312 | 347 | memory.set(result) |
313 | 348 | |
314 | 349 | return errorCode |
315 | 350 | } |
@@ -320,24 +355,24 @@ | ||
320 | 355 | * @param {interger} pathOffest the memory offset to load the the path from |
321 | 356 | * @param {interger} valueOffset the memory offset to load the value from |
322 | 357 | */ |
323 | 358 | sstore (pathOffest, valueOffset) { |
324 | - const path = new Buffer(MOD.exports.memory, pathOffest, 32).toString('hex') | |
325 | - const value = new Uint8Array(MOD.exports.memory, valueOffset, 32) | |
326 | - const oldValue = ENV.state.get(path) | |
359 | + const path = new Buffer(this.module.exports.memory, pathOffest, 32).toString('hex') | |
360 | + const value = new Uint8Array(this.module.exports.memory, valueOffset, 32) | |
361 | + const oldValue = this.environment.state.get(path) | |
327 | 362 | const valIsZero = value.every((i) => i === 0) |
328 | 363 | |
329 | 364 | // write |
330 | 365 | if (!valIsZero && !oldValue) { |
331 | - ENV.gasLimit -= 15000 | |
366 | + this.environment.gasLimit -= 15000 | |
332 | 367 | } |
333 | 368 | |
334 | 369 | // delete |
335 | 370 | if (valIsZero && oldValue) { |
336 | - ENV.gasRefund += 15000 | |
337 | - ENV.state.delete(path) | |
371 | + this.environment.gasRefund += 15000 | |
372 | + this.environment.state.delete(path) | |
338 | 373 | } else { |
339 | - ENV.state.set(path, value) | |
374 | + this.environment.state.set(path, value) | |
340 | 375 | } |
341 | 376 | } |
342 | 377 | |
343 | 378 | /** |
@@ -345,11 +380,11 @@ | ||
345 | 380 | * @param {interger} pathOffest the memory offset to load the the path from |
346 | 381 | * @param {interger} resultOffset the memory offset to load the value from |
347 | 382 | */ |
348 | 383 | sload (pathOffest, resultOffset) { |
349 | - const path = new Buffer(MOD.exports.memory, pathOffest, 32).toString('hex') | |
350 | - const result = ENV.state.get(path) | |
351 | - const memory = new Uint8Array(MOD.exports.memory, resultOffset, 32) | |
384 | + const path = new Buffer(this.module.exports.memory, pathOffest, 32).toString('hex') | |
385 | + const result = this.environment.state.get(path) | |
386 | + const memory = new Uint8Array(this.module.exports.memory, resultOffset, 32) | |
352 | 387 | memory.set(result) |
353 | 388 | } |
354 | 389 | |
355 | 390 | /** |
@@ -357,17 +392,47 @@ | ||
357 | 392 | * @param {integer} offset the offset of the output data. |
358 | 393 | * @param {integer} length the length of the output data. |
359 | 394 | */ |
360 | 395 | return (offset, length) { |
361 | - ENV.returnValue = new Uint8Array(MOD.exports.memory, offset, length) | |
396 | + this.environment.returnValue = new Uint8Array(this.module.exports.memory, offset, length) | |
362 | 397 | } |
363 | 398 | |
364 | 399 | /** |
365 | 400 | * Halt execution and register account for later deletion giving the remaining |
366 | 401 | * balance to an address path |
367 | 402 | * @param {integer} offset the offset to load the address from |
368 | 403 | */ |
369 | 404 | suicide (addressOffset) { |
370 | - const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
405 | + const address = new Uint8Array(this.module.exports.memory, addressOffset, constants.ADD_SIZE_BYTES) | |
371 | 406 | this.environment.suicideAddress = address |
372 | 407 | } |
373 | 408 | } |
409 | + | |
410 | +// | |
411 | +// Polyfill required unless this is sorted: https://bugs.chromium.org/p/chromium/issues/detail?id=633895 | |
412 | +// | |
413 | +// Polyfill from: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind | |
414 | +// | |
415 | +Function.prototype.bind = function (oThis) { // eslint-disable-line | |
416 | + if (typeof this !== 'function') { | |
417 | + // closest thing possible to the ECMAScript 5 | |
418 | + // internal IsCallable function | |
419 | + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable') | |
420 | + } | |
421 | + | |
422 | + var aArgs = Array.prototype.slice.call(arguments, 1) | |
423 | + var fToBind = this | |
424 | + var fNOP = function () {} | |
425 | + var fBound = function () { | |
426 | + return fToBind.apply(this instanceof fNOP ? this : oThis, | |
427 | + aArgs.concat(Array.prototype.slice.call(arguments))) | |
428 | + } | |
429 | + | |
430 | + if (this.prototype) { | |
431 | + // Function.prototype doesn't have a prototype property | |
432 | + fNOP.prototype = this.prototype | |
433 | + } | |
434 | + | |
435 | + fBound.prototype = new fNOP() // eslint-disable-line new-cap | |
436 | + | |
437 | + return fBound | |
438 | +} |
Built with git-ssb-web