Skip to content

Commit 381398f

Browse files
authored
Merge pull request #4979 from stacks-network/feat/finish-pass-argument-to-wasm
Clarity-Wasm: Implement "pass_argument_to_wasm" for remaining types
2 parents 96d89ed + ac21217 commit 381398f

File tree

1 file changed

+139
-31
lines changed

1 file changed

+139
-31
lines changed

clarity/src/vm/clarity_wasm.rs

Lines changed: 139 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use super::errors::RuntimeErrorType;
2020
use super::events::*;
2121
use super::functions::crypto::{pubkey_to_address_v1, pubkey_to_address_v2};
2222
use super::types::{
23-
ASCIIData, AssetIdentifier, BlockInfoProperty, BuffData, BurnBlockInfoProperty, CharType,
24-
FixedFunction, FunctionType, ListData, ListTypeData, OptionalData, PrincipalData,
23+
ASCIIData, AssetIdentifier, BlockInfoProperty, BuffData, BurnBlockInfoProperty, CallableData,
24+
CharType, FixedFunction, FunctionType, ListData, ListTypeData, OptionalData, PrincipalData,
2525
QualifiedContractIdentifier, ResponseData, SequenceData, StacksAddressExtensions,
2626
StandardPrincipalData, TraitIdentifier, TupleData, TupleTypeSignature, UTF8Data, BUFF_1,
2727
BUFF_32, BUFF_33,
@@ -539,21 +539,16 @@ pub fn call_function<'a, 'b, 'c>(
539539

540540
// Convert the args into wasmtime values
541541
let mut wasm_args = vec![];
542-
for arg in args {
542+
for (arg, ty) in args.iter().zip(func_types.get_arg_types()) {
543543
let (arg_vec, new_offset, new_in_mem_offset) =
544-
pass_argument_to_wasm(memory, &mut store, arg, offset, in_mem_offset)?;
544+
pass_argument_to_wasm(memory, &mut store, ty, arg, offset, in_mem_offset)?;
545545
wasm_args.extend(arg_vec);
546546
offset = new_offset;
547547
in_mem_offset = new_in_mem_offset;
548548
}
549549

550550
// Reserve stack space for the return value, if necessary.
551-
let return_type = store
552-
.data()
553-
.contract_context()
554-
.functions
555-
.get(function_name)
556-
.ok_or(CheckErrors::UndefinedFunction(function_name.to_string()))?
551+
let return_type = func_types
557552
.get_return_type()
558553
.as_ref()
559554
.ok_or(Error::Wasm(WasmError::ExpectedReturnValue))?
@@ -1459,6 +1454,7 @@ fn write_to_wasm(
14591454
fn pass_argument_to_wasm(
14601455
memory: Memory,
14611456
mut store: impl AsContextMut,
1457+
ty: &TypeSignature,
14621458
value: &Value,
14631459
offset: i32,
14641460
in_mem_offset: i32,
@@ -1482,27 +1478,71 @@ fn pass_argument_to_wasm(
14821478
in_mem_offset,
14831479
)),
14841480
Value::Optional(o) => {
1485-
let mut buffer = vec![Val::I32(if o.data.is_some() { 1 } else { 0 })];
1486-
let (inner, new_offset, new_in_mem_offset) = pass_argument_to_wasm(
1481+
let TypeSignature::OptionalType(inner_ty) = ty else {
1482+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1483+
};
1484+
1485+
if let Some(inner_value) = o.data.as_ref() {
1486+
let mut buffer = vec![Val::I32(1)];
1487+
let (inner_buffer, new_offset, new_in_mem_offset) = pass_argument_to_wasm(
1488+
memory,
1489+
store,
1490+
inner_ty,
1491+
inner_value,
1492+
offset,
1493+
in_mem_offset,
1494+
)?;
1495+
buffer.extend(inner_buffer);
1496+
Ok((buffer, new_offset, new_in_mem_offset))
1497+
} else {
1498+
let buffer = clar2wasm_ty(ty)
1499+
.into_iter()
1500+
.map(|vt| match vt {
1501+
ValType::I32 => Val::I32(0),
1502+
ValType::I64 => Val::I64(0),
1503+
_ => unreachable!("No other types used in Clarity-Wasm"),
1504+
})
1505+
.collect();
1506+
Ok((buffer, offset, in_mem_offset))
1507+
}
1508+
}
1509+
Value::Response(r) => {
1510+
let TypeSignature::ResponseType(inner_tys) = ty else {
1511+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1512+
};
1513+
let mut buffer = vec![Val::I32(r.committed as i32)];
1514+
let (value_buffer, new_offset, new_in_mem_offset) = pass_argument_to_wasm(
14871515
memory,
14881516
store,
1489-
o.data
1490-
.as_ref()
1491-
.map_or(&Value::none(), |boxed_value| &boxed_value),
1517+
if r.committed {
1518+
&inner_tys.0
1519+
} else {
1520+
&inner_tys.1
1521+
},
1522+
&r.data,
14921523
offset,
14931524
in_mem_offset,
14941525
)?;
1495-
buffer.extend(inner);
1496-
Ok((buffer, new_offset, new_in_mem_offset))
1497-
}
1498-
Value::Response(r) => {
1499-
let mut buffer = vec![Val::I32(if r.committed { 1 } else { 0 })];
1500-
let (inner, new_offset, new_in_mem_offset) = if r.committed {
1501-
pass_argument_to_wasm(memory, store, &r.data, offset, in_mem_offset)?
1526+
let empty_buffer = clar2wasm_ty(if r.committed {
1527+
&inner_tys.1
15021528
} else {
1503-
pass_argument_to_wasm(memory, store, &r.data, offset, in_mem_offset)?
1504-
};
1505-
buffer.extend(inner);
1529+
&inner_tys.0
1530+
})
1531+
.into_iter()
1532+
.map(|vt| match vt {
1533+
ValType::I32 => Val::I32(0),
1534+
ValType::I64 => Val::I64(0),
1535+
_ => unreachable!("No other types used in Clarity-Wasm"),
1536+
});
1537+
1538+
if r.committed {
1539+
buffer.extend(value_buffer);
1540+
buffer.extend(empty_buffer);
1541+
} else {
1542+
buffer.extend(empty_buffer);
1543+
buffer.extend(value_buffer);
1544+
}
1545+
15061546
Ok((buffer, new_offset, new_in_mem_offset))
15071547
}
15081548
Value::Sequence(SequenceData::String(CharType::ASCII(s))) => {
@@ -1519,8 +1559,20 @@ fn pass_argument_to_wasm(
15191559
let adjusted_in_mem_offset = in_mem_offset + s.data.len() as i32;
15201560
Ok((buffer, offset, adjusted_in_mem_offset))
15211561
}
1522-
Value::Sequence(SequenceData::String(CharType::UTF8(_s))) => {
1523-
todo!("Value type not yet implemented: {:?}", value)
1562+
Value::Sequence(SequenceData::String(CharType::UTF8(s))) => {
1563+
// For a utf8 string, convert the chars to big-endian i32, convert this into a list of
1564+
// bytes, then pass the offset and length to the wasm function
1565+
let bytes: Vec<u8> = String::from_utf8(s.items().iter().flatten().copied().collect())
1566+
.map_err(|e| Error::Wasm(WasmError::WasmGeneratorError(e.to_string())))?
1567+
.chars()
1568+
.flat_map(|c| (c as u32).to_be_bytes())
1569+
.collect();
1570+
let buffer = vec![Val::I32(in_mem_offset), Val::I32(bytes.len() as i32)];
1571+
memory
1572+
.write(&mut store, in_mem_offset as usize, &bytes)
1573+
.map_err(|e| Error::Wasm(WasmError::UnableToWriteMemory(e.into())))?;
1574+
let adjusted_in_mem_offset = in_mem_offset + bytes.len() as i32;
1575+
Ok((buffer, offset, adjusted_in_mem_offset))
15241576
}
15251577
Value::Sequence(SequenceData::Buffer(b)) => {
15261578
// For a buffer, write the bytes into the memory, then pass the
@@ -1537,14 +1589,18 @@ fn pass_argument_to_wasm(
15371589
Ok((buffer, offset, adjusted_in_mem_offset))
15381590
}
15391591
Value::Sequence(SequenceData::List(l)) => {
1592+
let TypeSignature::SequenceType(SequenceSubtype::ListType(ltd)) = ty else {
1593+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1594+
};
1595+
15401596
let mut buffer = vec![Val::I32(offset)];
15411597
let mut written = 0;
15421598
let mut in_mem_written = 0;
15431599
for item in &l.data {
15441600
let (len, in_mem_len) = write_to_wasm(
15451601
&mut store,
15461602
memory,
1547-
l.type_signature.get_list_item_type(),
1603+
ltd.get_list_item_type(),
15481604
offset + written,
15491605
in_mem_offset + in_mem_written,
15501606
item,
@@ -1556,9 +1612,61 @@ fn pass_argument_to_wasm(
15561612
buffer.push(Val::I32(written));
15571613
Ok((buffer, offset + written, in_mem_offset + in_mem_written))
15581614
}
1559-
Value::Principal(_p) => todo!("Value type not yet implemented: {:?}", value),
1560-
Value::CallableContract(_c) => todo!("Value type not yet implemented: {:?}", value),
1561-
Value::Tuple(_t) => todo!("Value type not yet implemented: {:?}", value),
1615+
Value::Principal(PrincipalData::Standard(StandardPrincipalData(v, h))) => {
1616+
let bytes: Vec<u8> = std::iter::once(v).chain(h.iter()).copied().collect();
1617+
let buffer = vec![Val::I32(in_mem_offset), Val::I32(bytes.len() as i32)];
1618+
memory
1619+
.write(&mut store, in_mem_offset as usize, &bytes)
1620+
.map_err(|e| Error::Wasm(WasmError::UnableToWriteMemory(e.into())))?;
1621+
let adjusted_in_mem_offset = in_mem_offset + bytes.len() as i32;
1622+
Ok((buffer, offset, adjusted_in_mem_offset))
1623+
}
1624+
Value::Principal(PrincipalData::Contract(p))
1625+
| Value::CallableContract(CallableData {
1626+
contract_identifier: p,
1627+
..
1628+
}) => {
1629+
// Callable types can just ignore the optional trait identifier, and
1630+
// is handled like a qualified contract
1631+
let QualifiedContractIdentifier {
1632+
issuer: StandardPrincipalData(v, h),
1633+
name,
1634+
} = p;
1635+
let bytes: Vec<u8> = std::iter::once(v)
1636+
.chain(h.iter())
1637+
.chain(std::iter::once(&name.len()))
1638+
.chain(name.as_bytes())
1639+
.copied()
1640+
.collect();
1641+
let buffer = vec![Val::I32(in_mem_offset), Val::I32(bytes.len() as i32)];
1642+
memory
1643+
.write(&mut store, in_mem_offset as usize, &bytes)
1644+
.map_err(|e| Error::Wasm(WasmError::UnableToWriteMemory(e.into())))?;
1645+
let adjusted_in_mem_offset = in_mem_offset + bytes.len() as i32;
1646+
Ok((buffer, offset, adjusted_in_mem_offset))
1647+
}
1648+
Value::Tuple(TupleData { data_map, .. }) => {
1649+
let TypeSignature::TupleType(tuple_ty) = ty else {
1650+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1651+
};
1652+
1653+
let mut buffer = vec![];
1654+
let mut offset = offset;
1655+
let mut in_mem_offset = in_mem_offset;
1656+
for (name, ty) in tuple_ty.get_type_map() {
1657+
let b;
1658+
(b, offset, in_mem_offset) = pass_argument_to_wasm(
1659+
memory,
1660+
store.as_context_mut(),
1661+
ty,
1662+
&data_map[name],
1663+
offset,
1664+
in_mem_offset,
1665+
)?;
1666+
buffer.extend(b);
1667+
}
1668+
Ok((buffer, offset, in_mem_offset))
1669+
}
15621670
}
15631671
}
15641672

0 commit comments

Comments
 (0)