@@ -568,19 +568,7 @@ pub fn call_function<'a, 'b, 'c>(
568
568
569
569
// Call the function
570
570
func. call ( & mut store, & wasm_args, & mut results)
571
- . map_err ( |e| {
572
- // TODO: If the root cause is a clarity error, we should be able to return that,
573
- // but it is not cloneable, so we can't return it directly.
574
- // If the root cause is a trap from our Wasm code, then we need to translate
575
- // it into a Clarity error.
576
- // See issue stacks-network/clarity-wasm#104
577
- // if let Some(vm_error) = e.root_cause().downcast_ref::<crate::vm::errors::Error>() {
578
- // vm_error.clone()
579
- // } else {
580
- // Error::Wasm(WasmError::Runtime(e))
581
- // }
582
- Error :: Wasm ( WasmError :: Runtime ( e) )
583
- } ) ?;
571
+ . map_err ( |e| error_mapping:: resolve_error ( e, instance, & mut store) ) ?;
584
572
585
573
// If the function returns a value, translate it into a Clarity `Value`
586
574
wasm_to_clarity_value ( & return_type, 0 , & results, memory, & mut & mut store, epoch)
@@ -721,7 +709,7 @@ pub fn is_in_memory_type(ty: &TypeSignature) -> bool {
721
709
722
710
fn clar2wasm_ty ( ty : & TypeSignature ) -> Vec < ValType > {
723
711
match ty {
724
- TypeSignature :: NoType => vec ! [ ValType :: I32 ] , // TODO: can this just be empty?
712
+ TypeSignature :: NoType => vec ! [ ValType :: I32 ] , // TODO: clarity-wasm issue #445. Can this just be empty?
725
713
TypeSignature :: IntType => vec ! [ ValType :: I64 , ValType :: I64 ] ,
726
714
TypeSignature :: UIntType => vec ! [ ValType :: I64 , ValType :: I64 ] ,
727
715
TypeSignature :: ResponseType ( inner_types) => {
@@ -1029,8 +1017,10 @@ fn read_from_wasm(
1029
1017
_ => Err ( Error :: Wasm ( WasmError :: InvalidIndicator ( indicator) ) ) ,
1030
1018
}
1031
1019
}
1032
- TypeSignature :: NoType => todo ! ( "type not yet implemented: {:?}" , ty) ,
1033
- TypeSignature :: ListUnionType ( _subtypes) => todo ! ( "type not yet implemented: {:?}" , ty) ,
1020
+ TypeSignature :: NoType => Err ( Error :: Wasm ( WasmError :: InvalidNoTypeInValue ) ) ,
1021
+ TypeSignature :: ListUnionType ( _subtypes) => {
1022
+ Err ( Error :: Wasm ( WasmError :: InvalidListUnionTypeInValue ) )
1023
+ }
1034
1024
}
1035
1025
}
1036
1026
@@ -1857,7 +1847,7 @@ fn wasm_to_clarity_value(
1857
1847
Ok ( ( Some ( tuple. into ( ) ) , index - value_index) )
1858
1848
}
1859
1849
TypeSignature :: ListUnionType ( _lu) => {
1860
- todo ! ( " Wasm value type not implemented: {:?}" , type_sig )
1850
+ Err ( Error :: Wasm ( WasmError :: InvalidListUnionTypeInValue ) )
1861
1851
}
1862
1852
}
1863
1853
}
@@ -1938,7 +1928,7 @@ fn link_define_variable_fn(linker: &mut Linker<ClarityWasmContext>) -> Result<()
1938
1928
name_length : i32 ,
1939
1929
mut value_offset : i32 ,
1940
1930
mut value_length : i32 | {
1941
- // TODO: Include this cost
1931
+ // TODO: clarity-wasm issue #344 Include this cost
1942
1932
// runtime_cost(ClarityCostFunction::CreateVar, global_context, value_type.size())?;
1943
1933
1944
1934
// Get the memory from the caller
@@ -2497,7 +2487,7 @@ fn link_get_variable_fn(linker: &mut Linker<ClarityWasmContext>) -> Result<(), E
2497
2487
Err ( _e) => data_types. value_type . size ( ) ? as u64 ,
2498
2488
} ;
2499
2489
2500
- // TODO: Include this cost
2490
+ // TODO: clarity-wasm issue #344 Include this cost
2501
2491
// runtime_cost(ClarityCostFunction::FetchVar, env, result_size)?;
2502
2492
2503
2493
let value = result. map ( |data| data. value ) ?;
@@ -2564,7 +2554,7 @@ fn link_set_variable_fn(linker: &mut Linker<ClarityWasmContext>) -> Result<(), E
2564
2554
) ) ) ?
2565
2555
. clone ( ) ;
2566
2556
2567
- // TODO: Include this cost
2557
+ // TODO: clarity-wasm issue #344 Include this cost
2568
2558
// runtime_cost(
2569
2559
// ClarityCostFunction::SetVar,
2570
2560
// env,
@@ -2585,7 +2575,7 @@ fn link_set_variable_fn(linker: &mut Linker<ClarityWasmContext>) -> Result<(), E
2585
2575
epoch,
2586
2576
) ?;
2587
2577
2588
- // TODO: Include this cost
2578
+ // TODO: clarity-wasm issue #344 Include this cost
2589
2579
// env.add_memory(value.get_memory_use())?;
2590
2580
2591
2581
// Store the variable in the global context
@@ -5939,10 +5929,7 @@ fn link_load_constant_fn(linker: &mut Linker<ClarityWasmContext>) -> Result<(),
5939
5929
. contract_context ( )
5940
5930
. variables
5941
5931
. get ( & ClarityName :: from ( const_name. as_str ( ) ) )
5942
- // TODO: create a new WasmError to handle the constant not found issue
5943
- . ok_or ( Error :: Wasm ( WasmError :: WasmGeneratorError (
5944
- "Constant not found on MARF" . to_string ( ) ,
5945
- ) ) ) ?
5932
+ . ok_or ( CheckErrors :: UndefinedVariable ( const_name. to_string ( ) ) ) ?
5946
5933
. clone ( ) ;
5947
5934
5948
5935
// Constant value type
@@ -7456,3 +7443,168 @@ mod tests {
7456
7443
assert_eq ! ( read, expected) ;
7457
7444
}
7458
7445
}
7446
+
7447
+ mod error_mapping {
7448
+ use wasmtime:: { AsContextMut , Instance , Trap } ;
7449
+
7450
+ use crate :: vm:: errors:: { CheckErrors , Error , RuntimeErrorType , ShortReturnType , WasmError } ;
7451
+ use crate :: vm:: types:: ResponseData ;
7452
+ use crate :: vm:: Value ;
7453
+
7454
+ const LOG2_ERROR_MESSAGE : & str = "log2 must be passed a positive integer" ;
7455
+ const SQRTI_ERROR_MESSAGE : & str = "sqrti must be passed a positive integer" ;
7456
+ const POW_ERROR_MESSAGE : & str = "Power argument to (pow ...) must be a u32 integer" ;
7457
+
7458
+ pub enum ErrorMap {
7459
+ NotClarityError = -1 ,
7460
+ ArithmeticOverflow = 0 ,
7461
+ ArithmeticUnderflow = 1 ,
7462
+ DivisionByZero = 2 ,
7463
+ ArithmeticLog2Error = 3 ,
7464
+ ArithmeticSqrtiError = 4 ,
7465
+ UnwrapFailure = 5 ,
7466
+ Panic = 6 ,
7467
+ ShortReturnAssertionFailure = 7 ,
7468
+ ArithmeticPowError = 8 ,
7469
+ NotMapped = 99 ,
7470
+ }
7471
+
7472
+ impl From < i32 > for ErrorMap {
7473
+ fn from ( error_code : i32 ) -> Self {
7474
+ match error_code {
7475
+ -1 => ErrorMap :: NotClarityError ,
7476
+ 0 => ErrorMap :: ArithmeticOverflow ,
7477
+ 1 => ErrorMap :: ArithmeticUnderflow ,
7478
+ 2 => ErrorMap :: DivisionByZero ,
7479
+ 3 => ErrorMap :: ArithmeticLog2Error ,
7480
+ 4 => ErrorMap :: ArithmeticSqrtiError ,
7481
+ 5 => ErrorMap :: UnwrapFailure ,
7482
+ 6 => ErrorMap :: Panic ,
7483
+ 7 => ErrorMap :: ShortReturnAssertionFailure ,
7484
+ 8 => ErrorMap :: ArithmeticPowError ,
7485
+ _ => ErrorMap :: NotMapped ,
7486
+ }
7487
+ }
7488
+ }
7489
+
7490
+ pub fn resolve_error (
7491
+ e : wasmtime:: Error ,
7492
+ instance : Instance ,
7493
+ mut store : impl AsContextMut ,
7494
+ ) -> Error {
7495
+ if let Some ( vm_error) = e. root_cause ( ) . downcast_ref :: < Error > ( ) {
7496
+ // SAFETY:
7497
+ //
7498
+ // This unsafe operation returns the value of a location pointed by `*mut T`.
7499
+ //
7500
+ // The purpose of this code is to take the ownership of the `vm_error` value
7501
+ // since clarity::vm::errors::Error is not a Clonable type.
7502
+ //
7503
+ // Converting a `&T` (vm_error) to a `*mut T` doesn't cause any issues here
7504
+ // because the reference is not borrowed elsewhere.
7505
+ //
7506
+ // The replaced `T` value is deallocated after the operation. Therefore, the chosen `T`
7507
+ // is a dummy value, solely to satisfy the signature of the replace function
7508
+ // and not cause harm when it is deallocated.
7509
+ //
7510
+ // Specifically, Error::Wasm(WasmError::ModuleNotFound) was selected as the placeholder value.
7511
+ return unsafe {
7512
+ core:: ptr:: replace (
7513
+ ( vm_error as * const Error ) as * mut Error ,
7514
+ Error :: Wasm ( WasmError :: ModuleNotFound ) ,
7515
+ )
7516
+ } ;
7517
+ }
7518
+
7519
+ if let Some ( vm_error) = e. root_cause ( ) . downcast_ref :: < CheckErrors > ( ) {
7520
+ // SAFETY:
7521
+ //
7522
+ // This unsafe operation returns the value of a location pointed by `*mut T`.
7523
+ //
7524
+ // The purpose of this code is to take the ownership of the `vm_error` value
7525
+ // since clarity::vm::errors::Error is not a Clonable type.
7526
+ //
7527
+ // Converting a `&T` (vm_error) to a `*mut T` doesn't cause any issues here
7528
+ // because the reference is not borrowed elsewhere.
7529
+ //
7530
+ // The replaced `T` value is deallocated after the operation. Therefore, the chosen `T`
7531
+ // is a dummy value, solely to satisfy the signature of the replace function
7532
+ // and not cause harm when it is deallocated.
7533
+ //
7534
+ // Specifically, CheckErrors::ExpectedName was selected as the placeholder value.
7535
+ return unsafe {
7536
+ let err = core:: ptr:: replace (
7537
+ ( vm_error as * const CheckErrors ) as * mut CheckErrors ,
7538
+ CheckErrors :: ExpectedName ,
7539
+ ) ;
7540
+
7541
+ <CheckErrors as std:: convert:: Into < Error > >:: into ( err)
7542
+ } ;
7543
+ }
7544
+
7545
+ // Check if the error is caused by
7546
+ // an unreachable Wasm trap.
7547
+ //
7548
+ // In this case, runtime errors are handled
7549
+ // by being mapped to the corresponding ClarityWasm Errors.
7550
+ if let Some ( Trap :: UnreachableCodeReached ) = e. root_cause ( ) . downcast_ref :: < Trap > ( ) {
7551
+ return from_runtime_error_code ( instance, & mut store, e) ;
7552
+ }
7553
+
7554
+ // All other errors are treated as general runtime errors.
7555
+ Error :: Wasm ( WasmError :: Runtime ( e) )
7556
+ }
7557
+
7558
+ fn from_runtime_error_code (
7559
+ instance : Instance ,
7560
+ mut store : impl AsContextMut ,
7561
+ e : wasmtime:: Error ,
7562
+ ) -> Error {
7563
+ let global = "runtime-error-code" ;
7564
+ let runtime_error_code = instance
7565
+ . get_global ( & mut store, global)
7566
+ . and_then ( |glob| glob. get ( & mut store) . i32 ( ) )
7567
+ . unwrap_or_else ( || panic ! ( "Could not find {global} global with i32 value" ) ) ;
7568
+
7569
+ match ErrorMap :: from ( runtime_error_code) {
7570
+ ErrorMap :: NotClarityError => Error :: Wasm ( WasmError :: Runtime ( e) ) ,
7571
+ ErrorMap :: ArithmeticOverflow => {
7572
+ Error :: Runtime ( RuntimeErrorType :: ArithmeticOverflow , Some ( Vec :: new ( ) ) )
7573
+ }
7574
+ ErrorMap :: ArithmeticUnderflow => {
7575
+ Error :: Runtime ( RuntimeErrorType :: ArithmeticUnderflow , Some ( Vec :: new ( ) ) )
7576
+ }
7577
+ ErrorMap :: DivisionByZero => {
7578
+ Error :: Runtime ( RuntimeErrorType :: DivisionByZero , Some ( Vec :: new ( ) ) )
7579
+ }
7580
+ ErrorMap :: ArithmeticLog2Error => Error :: Runtime (
7581
+ RuntimeErrorType :: Arithmetic ( LOG2_ERROR_MESSAGE . into ( ) ) ,
7582
+ Some ( Vec :: new ( ) ) ,
7583
+ ) ,
7584
+ ErrorMap :: ArithmeticSqrtiError => Error :: Runtime (
7585
+ RuntimeErrorType :: Arithmetic ( SQRTI_ERROR_MESSAGE . into ( ) ) ,
7586
+ Some ( Vec :: new ( ) ) ,
7587
+ ) ,
7588
+ ErrorMap :: UnwrapFailure => {
7589
+ Error :: Runtime ( RuntimeErrorType :: UnwrapFailure , Some ( Vec :: new ( ) ) )
7590
+ }
7591
+ ErrorMap :: Panic => {
7592
+ panic ! ( "An error has been detected in the code" )
7593
+ }
7594
+ // TODO: UInt(42) value below is just a placeholder.
7595
+ // It should be replaced by the current "thrown-value" when clarity-wasm issue #385 is resolved.
7596
+ // Tests that reach this code are currently ignored.
7597
+ ErrorMap :: ShortReturnAssertionFailure => Error :: ShortReturn (
7598
+ ShortReturnType :: AssertionFailed ( Value :: Response ( ResponseData {
7599
+ committed : false ,
7600
+ data : Box :: new ( Value :: UInt ( 42 ) ) ,
7601
+ } ) ) ,
7602
+ ) ,
7603
+ ErrorMap :: ArithmeticPowError => Error :: Runtime (
7604
+ RuntimeErrorType :: Arithmetic ( POW_ERROR_MESSAGE . into ( ) ) ,
7605
+ Some ( Vec :: new ( ) ) ,
7606
+ ) ,
7607
+ _ => panic ! ( "Runtime error code {} not supported" , runtime_error_code) ,
7608
+ }
7609
+ }
7610
+ }
0 commit comments