2
2
3
3
/*
4
4
* TODO:
5
- * [ ] --timeout flag
6
- * [ ] --gas-limit flag
5
+ * [ ] --inspect
6
+ * [ ] check if imports are satisfied, print missing parts
7
+ * [ ] auto-import memory
8
+ * [ ] --validate flag
9
+ * [ ] --imports flag
7
10
*/
8
11
9
12
"use strict" ;
10
13
11
- /*
12
- * Respawn with experimental flags
13
- */
14
-
15
- if ( process . argv [ 2 ] != "--respawn" ) {
16
- const { execFileSync, spawnSync } = require ( 'child_process' ) ;
17
- const node = process . argv [ 0 ] ;
18
- const script = process . argv [ 1 ] ;
19
- const script_args = process . argv . slice ( 2 ) ;
20
-
21
- let allFlags = execFileSync ( node , [ "--v8-options" ] ) . toString ( ) ;
22
- allFlags += execFileSync ( node , [ "--help" ] ) . toString ( ) ;
23
-
24
- const nodeFlags = [ "--experimental-wasm-bigint" ,
25
- "--experimental-wasm-mv" ,
26
- "--experimental-wasm-return-call" ,
27
- "--experimental-wasm-bulk-memory" ,
28
- "--experimental-wasi-unstable-preview1" ,
29
- "--wasm-opt" ] . filter ( x => allFlags . includes ( x ) ) ;
30
-
31
- let res = spawnSync ( node ,
32
- [ ...nodeFlags , script , "--respawn" , ...script_args ] ,
33
- { stdio : [ 'inherit' , 'inherit' , 'inherit' ] } ) ;
34
- process . exit ( res . status ) ;
35
- }
36
-
37
14
/*
38
15
* Author: Volodymyr Shymanskyy
39
16
*/
@@ -64,10 +41,22 @@ const argv = require("yargs")
64
41
describe : "Function to execute" ,
65
42
nargs : 1
66
43
} ,
44
+ "timeout" : {
45
+ alias : "t" ,
46
+ type : "int" ,
47
+ describe : "Execution timeout (ms)" ,
48
+ nargs : 1
49
+ } ,
67
50
"trace" : {
68
51
type : "boolean" ,
69
52
describe : "Trace imported function calls" ,
70
53
} ,
54
+ "gas-limit" : {
55
+ type : "float" ,
56
+ describe : "Gas limit" ,
57
+ default : 100000 ,
58
+ nargs : 1
59
+ } ,
71
60
} )
72
61
. string ( '_' )
73
62
. strict ( )
@@ -76,6 +65,38 @@ const argv = require("yargs")
76
65
. wrap ( null )
77
66
. argv ;
78
67
68
+ /*
69
+ * Respawn with experimental flags
70
+ */
71
+
72
+ if ( ! argv . respawn )
73
+ {
74
+ const { execFileSync, spawnSync } = require ( 'child_process' ) ;
75
+ const node = process . argv [ 0 ] ;
76
+ const script = process . argv [ 1 ] ;
77
+ const script_args = process . argv . slice ( 2 ) ;
78
+
79
+ let allFlags = execFileSync ( node , [ "--v8-options" ] ) . toString ( ) ;
80
+ allFlags += execFileSync ( node , [ "--help" ] ) . toString ( ) ;
81
+
82
+ const nodeFlags = [ "--experimental-wasm-bigint" ,
83
+ "--experimental-wasm-mv" ,
84
+ "--experimental-wasm-return-call" ,
85
+ "--experimental-wasm-bulk-memory" ,
86
+ "--experimental-wasi-unstable-preview1" ,
87
+ "--wasm-opt" ] . filter ( x => allFlags . includes ( x ) ) ;
88
+
89
+ let res = spawnSync ( node ,
90
+ [ ...nodeFlags , script , "--respawn" , ...script_args ] ,
91
+ { stdio : [ 'inherit' , 'inherit' , 'inherit' ] ,
92
+ timeout : argv . timeout } ) ;
93
+
94
+ if ( res . error ) {
95
+ fatal ( res . error ) ;
96
+ }
97
+ process . exit ( res . status ) ;
98
+ }
99
+
79
100
/*
80
101
* Helpers
81
102
*/
@@ -279,7 +300,7 @@ async function parseWasmInfo(binary)
279
300
/*
280
301
* Compile
281
302
*/
282
-
303
+
283
304
/* TODO: caching
284
305
const v8 = require('v8');
285
306
const compiled = await WebAssembly.compile(binary);
@@ -308,12 +329,22 @@ async function parseWasmInfo(binary)
308
329
* Prepare imports
309
330
*/
310
331
332
+ let ctx = {
333
+ gasCurrent : argv . gasLimit ,
334
+ } ;
335
+
311
336
let imports = {
312
- // TODO: add ability to define imports
337
+ metering : {
338
+ usegas : function ( gas ) {
339
+ ctx . gasCurrent -= ( gas / 10000 ) ;
340
+ if ( ctx . gasCurrent < 0 ) {
341
+ throw `Run out of gas` ;
342
+ }
343
+ }
344
+ }
313
345
}
314
346
315
347
let wasi ;
316
- let ctx = { } ;
317
348
if ( wasmInfo . wasiVersion )
318
349
{
319
350
const { WASI } = require ( 'wasi' ) ;
@@ -435,44 +466,52 @@ async function parseWasmInfo(binary)
435
466
* Execute
436
467
*/
437
468
438
- const instance = await WebAssembly . instantiate ( module , imports ) ;
439
-
440
- // TODO: REPL mode
441
-
442
- // If no WASI is detected, and no func specified -> try to run the only function
443
- if ( ! argv . invoke && ! wasmInfo . wasiVersion && wasmInfo . exportedFuncs . length == 1 ) {
444
- argv . invoke = wasmInfo . exportedFuncs [ 0 ] ;
445
- }
469
+ try {
470
+ let instance = await WebAssembly . instantiate ( module , imports ) ;
446
471
447
- if ( argv . invoke ) {
448
- if ( ! wasmInfo . exportedFuncs . includes ( argv . invoke ) ) {
449
- fatal ( `Function not found: ${ argv . invoke } ` ) ;
472
+ // If no WASI is detected, and no func specified -> try to run the only function
473
+ if ( ! argv . invoke && ! wasmInfo . wasiVersion && wasmInfo . exportedFuncs . length == 1 ) {
474
+ argv . invoke = wasmInfo . exportedFuncs [ 0 ] ;
450
475
}
451
- let args = argv . _ . slice ( 1 )
452
-
453
- let wasmInfo2 = await parseWasmInfo ( binary ) ;
454
- //console.log(JSON.stringify(wasmInfo2));
455
- let funcInfo = wasmInfo2 . funcsByName [ argv . invoke ] ;
456
-
457
- for ( let i = 0 ; i < funcInfo . params . length ; i ++ ) {
458
- switch ( funcInfo . params [ i ] ) {
459
- case 'i32' : args [ i ] = parseInt ( args [ i ] ) ; break ;
460
- case 'i64' : args [ i ] = BigInt ( args [ i ] ) ; break ;
461
- case 'f32' :
462
- case 'f64' : args [ i ] = parseFloat ( args [ i ] ) ; break ;
476
+
477
+ if ( argv . invoke ) {
478
+ if ( ! wasmInfo . exportedFuncs . includes ( argv . invoke ) ) {
479
+ fatal ( `Function not found: ${ argv . invoke } ` ) ;
480
+ }
481
+ let args = argv . _ . slice ( 1 )
482
+
483
+ let wasmInfo2 = await parseWasmInfo ( binary ) ;
484
+ //console.log(JSON.stringify(wasmInfo2));
485
+ let funcInfo = wasmInfo2 . funcsByName [ argv . invoke ] ;
486
+
487
+ for ( let i = 0 ; i < funcInfo . params . length ; i ++ ) {
488
+ switch ( funcInfo . params [ i ] ) {
489
+ case 'i32' : args [ i ] = parseInt ( args [ i ] ) ; break ;
490
+ case 'i64' : args [ i ] = BigInt ( args [ i ] ) ; break ;
491
+ case 'f32' :
492
+ case 'f64' : args [ i ] = parseFloat ( args [ i ] ) ; break ;
493
+ }
463
494
}
464
- }
465
495
466
- log ( `Running ${ argv . invoke } (${ args } )...` ) ;
467
- let func = instance . exports [ argv . invoke ] ;
468
- let result = func ( ...args ) ;
469
- log ( `Result: ${ result } ` ) ;
470
- } else {
471
- ctx . memory = instance . exports . memory ;
472
- let exitcode = wasi . start ( instance ) ;
473
- if ( exitcode ) {
474
- log ( `Exit code: ${ exitcode } ` ) ;
496
+ log ( `Running ${ argv . invoke } (${ args } )...` ) ;
497
+ let func = instance . exports [ argv . invoke ] ;
498
+ let result = func ( ...args ) ;
499
+ log ( `Result: ${ result } ` ) ;
500
+ if ( ctx . gasCurrent != argv . gasLimit ) {
501
+ log ( `Gas used: ${ argv . gasLimit - ctx . gasCurrent } ` ) ;
502
+ }
503
+ } else {
504
+ ctx . memory = instance . exports . memory ;
505
+ let exitcode = wasi . start ( instance ) ;
506
+ if ( exitcode ) {
507
+ log ( `Exit code: ${ exitcode } ` ) ;
508
+ }
509
+ if ( ctx . gasCurrent != argv . gasLimit ) {
510
+ log ( `Gas used: ${ ( argv . gasLimit - ctx . gasCurrent ) . toFixed ( 4 ) } ` ) ;
511
+ }
512
+ process . exit ( exitcode ) ;
475
513
}
476
- process . exit ( exitcode ) ;
514
+ } catch ( e ) {
515
+ fatal ( e ) ;
477
516
}
478
517
} ) ( ) ;
0 commit comments