@@ -368,15 +368,39 @@ mod tests {
368
368
369
369
fn bytecode ( ) -> Vec < u8 > {
370
370
wat2wasm (
371
- br#"
372
- (module
371
+ br#"(module
373
372
(type $add_t (func (param i32) (result i32)))
374
373
(func $add_one_f (type $add_t) (param $value i32) (result i32)
375
374
local.get $value
376
375
i32.const 1
377
376
i32.add)
378
- (export "add_one" (func $add_one_f)))
379
- "# ,
377
+ (func $short_loop_f
378
+ (local $x f64) (local $j i32)
379
+ (local.set $x (f64.const 5.5))
380
+
381
+ (loop $named_loop
382
+ ;; $j++
383
+ local.get $j
384
+ i32.const 1
385
+ i32.add
386
+ local.set $j
387
+
388
+ ;; if $j < 5, one more time
389
+ local.get $j
390
+ i32.const 5
391
+ i32.lt_s
392
+ br_if $named_loop
393
+ )
394
+ )
395
+ (func $infi_loop_f
396
+ (loop $infi_loop_start
397
+ br $infi_loop_start
398
+ )
399
+ )
400
+ (export "add_one" (func $add_one_f))
401
+ (export "short_loop" (func $short_loop_f))
402
+ (export "infi_loop" (func $infi_loop_f))
403
+ )"# ,
380
404
)
381
405
. unwrap ( )
382
406
. into ( )
@@ -486,4 +510,68 @@ mod tests {
486
510
MeteringPoints :: Remaining ( 4 )
487
511
) ;
488
512
}
513
+
514
+ #[ test]
515
+ fn metering_works_for_loops ( ) {
516
+ const INITIAL_POINTS : u64 = 10_000 ;
517
+
518
+ fn cost ( operator : & Operator ) -> u64 {
519
+ match operator {
520
+ Operator :: Loop { .. } => 1000 ,
521
+ Operator :: Br { .. } | Operator :: BrIf { .. } => 10 ,
522
+ Operator :: F64Const { .. } => 7 ,
523
+ _ => 0 ,
524
+ }
525
+ }
526
+
527
+ // Short loop
528
+
529
+ let metering = Arc :: new ( Metering :: new ( INITIAL_POINTS , cost) ) ;
530
+ let mut compiler_config = Cranelift :: default ( ) ;
531
+ compiler_config. push_middleware ( metering) ;
532
+ let mut store = Store :: new ( EngineBuilder :: new ( compiler_config) ) ;
533
+ let module = Module :: new ( & store, bytecode ( ) ) . unwrap ( ) ;
534
+
535
+ let instance = Instance :: new ( & mut store, & module, & imports ! { } ) . unwrap ( ) ;
536
+ let short_loop: TypedFunction < ( ) , ( ) > = instance
537
+ . exports
538
+ . get_function ( "short_loop" )
539
+ . unwrap ( )
540
+ . typed ( & store)
541
+ . unwrap ( ) ;
542
+ short_loop. call ( & mut store) . unwrap ( ) ;
543
+
544
+ let points_used: u64 = match get_remaining_points ( & mut store, & instance) {
545
+ MeteringPoints :: Exhausted => panic ! ( "Unexpected exhausted" ) ,
546
+ MeteringPoints :: Remaining ( remaining) => INITIAL_POINTS - remaining,
547
+ } ;
548
+
549
+ assert_eq ! (
550
+ points_used,
551
+ 7 /* pre-loop instructions */ +
552
+ 1000 /* loop instruction */ + 50 /* five conditional breaks */
553
+ ) ;
554
+
555
+ // Infinite loop
556
+
557
+ let metering = Arc :: new ( Metering :: new ( INITIAL_POINTS , cost) ) ;
558
+ let mut compiler_config = Cranelift :: default ( ) ;
559
+ compiler_config. push_middleware ( metering) ;
560
+ let mut store = Store :: new ( EngineBuilder :: new ( compiler_config) ) ;
561
+ let module = Module :: new ( & store, bytecode ( ) ) . unwrap ( ) ;
562
+
563
+ let instance = Instance :: new ( & mut store, & module, & imports ! { } ) . unwrap ( ) ;
564
+ let infi_loop: TypedFunction < ( ) , ( ) > = instance
565
+ . exports
566
+ . get_function ( "infi_loop" )
567
+ . unwrap ( )
568
+ . typed ( & store)
569
+ . unwrap ( ) ;
570
+ infi_loop. call ( & mut store) . unwrap_err ( ) ; // exhausted leads to runtime error
571
+
572
+ assert_eq ! (
573
+ get_remaining_points( & mut store, & instance) ,
574
+ MeteringPoints :: Exhausted
575
+ ) ;
576
+ }
489
577
}
0 commit comments