Skip to content

Commit 93285f7

Browse files
authored
Merge pull request #5325 from stacks-network/update-clarity-wasm-develop
Update clarity wasm develop
2 parents dfdf66a + 3dc1ae6 commit 93285f7

File tree

1 file changed

+119
-33
lines changed

1 file changed

+119
-33
lines changed

clarity/src/vm/clarity_wasm.rs

Lines changed: 119 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ pub fn call_function<'a, 'b, 'c>(
480480
sponsor: Option<PrincipalData>,
481481
) -> Result<Value, Error> {
482482
let epoch = global_context.epoch_id;
483+
let clarity_version = *contract_context.get_clarity_version();
483484
let context = ClarityWasmContext::new_run(
484485
global_context,
485486
contract_context,
@@ -564,7 +565,9 @@ pub fn call_function<'a, 'b, 'c>(
564565

565566
// Call the function
566567
func.call(&mut store, &wasm_args, &mut results)
567-
.map_err(|e| error_mapping::resolve_error(e, instance, &mut store))?;
568+
.map_err(|e| {
569+
error_mapping::resolve_error(e, instance, &mut store, &epoch, &clarity_version)
570+
})?;
568571

569572
// If the function returns a value, translate it into a Clarity `Value`
570573
wasm_to_clarity_value(&return_type, 0, &results, memory, &mut &mut store, epoch)
@@ -7663,12 +7666,13 @@ mod tests {
76637666
}
76647667

76657668
mod error_mapping {
7669+
use stacks_common::types::StacksEpochId;
76667670
use wasmtime::{AsContextMut, Instance, Trap};
76677671

7668-
use super::read_identifier_from_wasm;
7672+
use super::{read_from_wasm_indirect, read_identifier_from_wasm, signature_from_string};
76697673
use crate::vm::errors::{CheckErrors, Error, RuntimeErrorType, ShortReturnType, WasmError};
7670-
use crate::vm::types::ResponseData;
7671-
use crate::vm::Value;
7674+
use crate::vm::types::{OptionalData, ResponseData};
7675+
use crate::vm::{ClarityVersion, Value};
76727676

76737677
const LOG2_ERROR_MESSAGE: &str = "log2 must be passed a positive integer";
76747678
const SQRTI_ERROR_MESSAGE: &str = "sqrti must be passed a positive integer";
@@ -7719,6 +7723,18 @@ mod error_mapping {
77197723
/// Indicates an attempt to use a name that is already in use, possibly for a variable or function.
77207724
NameAlreadyUsed = 9,
77217725

7726+
/// Represents a short-return error for an expected value that wraps a Response type.
7727+
/// Usually triggered by `(try!...)`.
7728+
ShortReturnExpectedValueResponse = 10,
7729+
7730+
/// Represents a short-return error for an expected value that wraps an Optional type.
7731+
/// Usually triggered by `(try!...)`.
7732+
ShortReturnExpectedValueOptional = 11,
7733+
7734+
/// Represents a short-return error for an expected value.
7735+
/// usually triggered by `(unwrap!...)` and `(unwrap-err!...)`.
7736+
ShortReturnExpectedValue = 12,
7737+
77227738
/// A catch-all for errors that are not mapped to specific error codes.
77237739
/// This might be used for unexpected or unclassified errors.
77247740
NotMapped = 99,
@@ -7738,15 +7754,20 @@ mod error_mapping {
77387754
7 => ErrorMap::ShortReturnAssertionFailure,
77397755
8 => ErrorMap::ArithmeticPowError,
77407756
9 => ErrorMap::NameAlreadyUsed,
7757+
10 => ErrorMap::ShortReturnExpectedValueResponse,
7758+
11 => ErrorMap::ShortReturnExpectedValueOptional,
7759+
12 => ErrorMap::ShortReturnExpectedValue,
77417760
_ => ErrorMap::NotMapped,
77427761
}
77437762
}
77447763
}
77457764

7746-
pub fn resolve_error(
7765+
pub(crate) fn resolve_error(
77477766
e: wasmtime::Error,
77487767
instance: Instance,
77497768
mut store: impl AsContextMut,
7769+
epoch_id: &StacksEpochId,
7770+
clarity_version: &ClarityVersion,
77507771
) -> Error {
77517772
if let Some(vm_error) = e.root_cause().downcast_ref::<Error>() {
77527773
// SAFETY:
@@ -7804,23 +7825,32 @@ mod error_mapping {
78047825
// In this case, runtime errors are handled
78057826
// by being mapped to the corresponding ClarityWasm Errors.
78067827
if let Some(Trap::UnreachableCodeReached) = e.root_cause().downcast_ref::<Trap>() {
7807-
return from_runtime_error_code(instance, &mut store, e);
7828+
return from_runtime_error_code(instance, &mut store, e, epoch_id, clarity_version);
78087829
}
78097830

78107831
// All other errors are treated as general runtime errors.
78117832
Error::Wasm(WasmError::Runtime(e))
78127833
}
78137834

7835+
/// Converts a WebAssembly runtime error code into a Clarity `Error`.
7836+
///
7837+
/// This function interprets an error code from a WebAssembly runtime execution and
7838+
/// translates it into an appropriate Clarity error type. It handles various categories
7839+
/// of errors including arithmetic errors, short returns, and other runtime issues.
7840+
///
7841+
/// # Returns
7842+
///
7843+
/// Returns a Clarity `Error` that corresponds to the runtime error encountered during
7844+
/// WebAssembly execution.
7845+
///
78147846
fn from_runtime_error_code(
78157847
instance: Instance,
78167848
mut store: impl AsContextMut,
78177849
e: wasmtime::Error,
7850+
epoch_id: &StacksEpochId,
7851+
clarity_version: &ClarityVersion,
78187852
) -> Error {
7819-
let global = "runtime-error-code";
7820-
let runtime_error_code = instance
7821-
.get_global(&mut store, global)
7822-
.and_then(|glob| glob.get(&mut store).i32())
7823-
.unwrap_or_else(|| panic!("Could not find {global} global with i32 value"));
7853+
let runtime_error_code = get_global_i32(&instance, &mut store, "runtime-error-code");
78247854

78257855
match ErrorMap::from(runtime_error_code) {
78267856
ErrorMap::NotClarityError => Error::Wasm(WasmError::Runtime(e)),
@@ -7849,33 +7879,20 @@ mod error_mapping {
78497879
// This RuntimeErrorType::UnwrapFailure need to have a proper context.
78507880
Error::Runtime(RuntimeErrorType::UnwrapFailure, Some(Vec::new()))
78517881
}
7852-
// TODO: UInt(42) value below is just a placeholder.
7853-
// It should be replaced by the current "thrown-value" when clarity-wasm issue #385 is resolved.
7854-
// Tests that reach this code are currently ignored.
7855-
ErrorMap::ShortReturnAssertionFailure => Error::ShortReturn(
7856-
ShortReturnType::AssertionFailed(Value::Response(ResponseData {
7857-
committed: false,
7858-
data: Box::new(Value::UInt(42)),
7859-
})),
7860-
),
7882+
ErrorMap::ShortReturnAssertionFailure => {
7883+
let clarity_val =
7884+
short_return_value(&instance, &mut store, epoch_id, clarity_version);
7885+
Error::ShortReturn(ShortReturnType::AssertionFailed(clarity_val))
7886+
}
78617887
ErrorMap::ArithmeticPowError => Error::Runtime(
78627888
RuntimeErrorType::Arithmetic(POW_ERROR_MESSAGE.into()),
78637889
Some(Vec::new()),
78647890
),
78657891
ErrorMap::NameAlreadyUsed => {
7866-
let runtime_error_arg_offset = instance
7867-
.get_global(&mut store, "runtime-error-arg-offset")
7868-
.and_then(|glob| glob.get(&mut store).i32())
7869-
.unwrap_or_else(|| {
7870-
panic!("Could not find $runtime-error-arg-offset global with i32 value")
7871-
});
7872-
7873-
let runtime_error_arg_len = instance
7874-
.get_global(&mut store, "runtime-error-arg-len")
7875-
.and_then(|glob| glob.get(&mut store).i32())
7876-
.unwrap_or_else(|| {
7877-
panic!("Could not find $runtime-error-arg-len global with i32 value")
7878-
});
7892+
let runtime_error_arg_offset =
7893+
get_global_i32(&instance, &mut store, "runtime-error-arg-offset");
7894+
let runtime_error_arg_len =
7895+
get_global_i32(&instance, &mut store, "runtime-error-arg-len");
78797896

78807897
let memory = instance
78817898
.get_memory(&mut store, "memory")
@@ -7890,7 +7907,76 @@ mod error_mapping {
78907907

78917908
Error::Unchecked(CheckErrors::NameAlreadyUsed(arg_name))
78927909
}
7910+
ErrorMap::ShortReturnExpectedValueResponse => {
7911+
let clarity_val =
7912+
short_return_value(&instance, &mut store, epoch_id, clarity_version);
7913+
Error::ShortReturn(ShortReturnType::ExpectedValue(Value::Response(
7914+
ResponseData {
7915+
committed: false,
7916+
data: Box::new(clarity_val),
7917+
},
7918+
)))
7919+
}
7920+
ErrorMap::ShortReturnExpectedValueOptional => Error::ShortReturn(
7921+
ShortReturnType::ExpectedValue(Value::Optional(OptionalData { data: None })),
7922+
),
7923+
ErrorMap::ShortReturnExpectedValue => {
7924+
let clarity_val =
7925+
short_return_value(&instance, &mut store, epoch_id, clarity_version);
7926+
Error::ShortReturn(ShortReturnType::ExpectedValue(clarity_val))
7927+
}
78937928
_ => panic!("Runtime error code {} not supported", runtime_error_code),
78947929
}
78957930
}
7931+
7932+
/// Retrieves the value of a 32-bit integer global variable from a WebAssembly instance.
7933+
///
7934+
/// This function attempts to fetch a global variable by name from the provided WebAssembly
7935+
/// instance and return its value as an `i32`. It's designed to simplify the process of
7936+
/// reading global variables in WebAssembly modules.
7937+
///
7938+
/// # Returns
7939+
///
7940+
/// Returns the value of the global variable as an `i32`.
7941+
///
7942+
fn get_global_i32(instance: &Instance, store: &mut impl AsContextMut, name: &str) -> i32 {
7943+
instance
7944+
.get_global(&mut *store, name)
7945+
.and_then(|glob| glob.get(store).i32())
7946+
.unwrap_or_else(|| panic!("Could not find ${} global with i32 value", name))
7947+
}
7948+
7949+
/// Retrieves and deserializes a Clarity value from WebAssembly memory in the context of a short return.
7950+
///
7951+
/// This function is used to extract a Clarity value that has been stored in WebAssembly memory
7952+
/// as part of a short return operation. It reads necessary metadata from global variables,
7953+
/// deserializes the type information, and then reads and deserializes the actual value.
7954+
///
7955+
/// # Returns
7956+
///
7957+
/// Returns a deserialized Clarity `Value` representing the short return value.
7958+
///
7959+
fn short_return_value(
7960+
instance: &Instance,
7961+
store: &mut impl AsContextMut,
7962+
epoch_id: &StacksEpochId,
7963+
clarity_version: &ClarityVersion,
7964+
) -> Value {
7965+
let val_offset = get_global_i32(instance, store, "runtime-error-value-offset");
7966+
let type_ser_offset = get_global_i32(instance, store, "runtime-error-type-ser-offset");
7967+
let type_ser_len = get_global_i32(instance, store, "runtime-error-type-ser-len");
7968+
7969+
let memory = instance
7970+
.get_memory(&mut *store, "memory")
7971+
.unwrap_or_else(|| panic!("Could not find wasm instance memory"));
7972+
7973+
let type_ser_str = read_identifier_from_wasm(memory, store, type_ser_offset, type_ser_len)
7974+
.unwrap_or_else(|e| panic!("Could not recover stringified type: {}", e));
7975+
7976+
let value_ty = signature_from_string(&type_ser_str, *clarity_version, *epoch_id)
7977+
.unwrap_or_else(|e| panic!("Could not recover thrown value: {}", e));
7978+
7979+
read_from_wasm_indirect(memory, store, &value_ty, val_offset, *epoch_id)
7980+
.unwrap_or_else(|e| panic!("Could not read thrown value from memory: {}", e))
7981+
}
78967982
}

0 commit comments

Comments
 (0)