use crate::{
error::{panic::ToNativeAssertError, CompilerError, Error},
native_assert, native_panic,
runtime::FeltDict,
starknet::{ArrayAbi, Secp256k1Point, Secp256r1Point},
types::TypeBuilder,
utils::{felt252_bigint, get_integer_layout, layout_repeat, RangeExt, PRIME},
};
use bumpalo::Bump;
use cairo_lang_sierra::{
extensions::{
circuit::CircuitTypeConcrete,
core::{CoreLibfunc, CoreType, CoreTypeConcrete},
starknet::{secp256::Secp256PointTypeConcrete, StarknetTypeConcrete},
utils::Range,
},
ids::ConcreteTypeId,
program_registry::ProgramRegistry,
};
use educe::Educe;
use num_bigint::{BigInt, BigUint, Sign};
use num_traits::{Euclid, One};
use starknet_types_core::felt::Felt;
use std::{
alloc::Layout,
collections::HashMap,
ptr::{null_mut, NonNull},
slice,
};
#[derive(Clone, Educe, serde::Serialize, serde::Deserialize)]
#[educe(Debug, Eq, PartialEq)]
pub enum Value {
Felt252(#[educe(Debug(method(std::fmt::Display::fmt)))] Felt),
Bytes31([u8; 31]),
Array(Vec<Self>),
Struct {
fields: Vec<Self>,
#[educe(PartialEq(ignore))]
debug_name: Option<String>,
}, Enum {
tag: usize,
value: Box<Self>,
#[educe(PartialEq(ignore))]
debug_name: Option<String>,
},
Felt252Dict {
value: HashMap<Felt, Self>,
#[educe(PartialEq(ignore))]
debug_name: Option<String>,
},
Uint8(u8),
Uint16(u16),
Uint32(u32),
Uint64(u64),
Uint128(u128),
Sint8(i8),
Sint16(i16),
Sint32(i32),
Sint64(i64),
Sint128(i128),
EcPoint(Felt, Felt),
EcState(Felt, Felt, Felt, Felt),
QM31(u32, u32, u32, u32),
Secp256K1Point(Secp256k1Point),
Secp256R1Point(Secp256r1Point),
BoundedInt {
value: Felt,
#[serde(with = "range_serde")]
range: Range,
},
IntRange {
x: Box<Value>,
y: Box<Value>,
},
Null,
}
macro_rules! impl_conversions {
( $( $t:ty as $i:ident ; )+ ) => { $(
impl From<$t> for Value {
fn from(value: $t) -> Self {
Self::$i(value)
}
}
impl TryFrom<Value> for $t {
type Error = Value;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::$i(value) => Ok(value),
_ => Err(value),
}
}
}
)+ };
}
impl_conversions! {
Felt as Felt252;
u8 as Uint8;
u16 as Uint16;
u32 as Uint32;
u64 as Uint64;
u128 as Uint128;
i8 as Sint8;
i16 as Sint16;
i32 as Sint32;
i64 as Sint64;
i128 as Sint128;
}
impl<T: Into<Value> + Clone> From<&[T]> for Value {
fn from(value: &[T]) -> Self {
Self::Array(value.iter().map(|x| x.clone().into()).collect())
}
}
impl<T: Into<Value>> From<Vec<T>> for Value {
fn from(value: Vec<T>) -> Self {
Self::Array(value.into_iter().map(Into::into).collect())
}
}
impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
fn from(value: [T; N]) -> Self {
Self::Array(value.into_iter().map(Into::into).collect())
}
}
impl Value {
pub(crate) fn resolve_type<'a>(
ty: &'a CoreTypeConcrete,
registry: &'a ProgramRegistry<CoreType, CoreLibfunc>,
) -> Result<&'a CoreTypeConcrete, Error> {
Ok(match ty {
CoreTypeConcrete::Snapshot(info) => registry.get_type(&info.ty)?,
x => x,
})
}
pub(crate) fn to_ptr(
&self,
arena: &Bump,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
type_id: &ConcreteTypeId,
) -> Result<NonNull<()>, Error> {
let ty = registry.get_type(type_id)?;
Ok(unsafe {
match self {
Self::Felt252(value) => {
let ptr = arena.alloc_layout(get_integer_layout(252)).cast();
let data = felt252_bigint(value.to_bigint()).to_bytes_le();
ptr.cast::<[u8; 32]>().as_mut().copy_from_slice(&data);
ptr
}
Self::BoundedInt {
value,
range: Range { lower, upper },
} => {
let value = value.to_bigint();
if lower >= upper {
return Err(CompilerError::BoundedIntOutOfRange {
value: Box::new(value),
range: Box::new((lower.clone(), upper.clone())),
}
.into());
}
let prime = BigInt::from_biguint(Sign::Plus, PRIME.clone());
let lower = lower.rem_euclid(&prime);
let upper = upper.rem_euclid(&prime);
if !(lower <= value && value < upper) {
return Err(CompilerError::BoundedIntOutOfRange {
value: Box::new(value),
range: Box::new((lower, upper)),
}
.into());
}
let ptr = arena.alloc_layout(get_integer_layout(252)).cast();
let data = felt252_bigint(value).to_bytes_le();
ptr.cast::<[u8; 32]>().as_mut().copy_from_slice(&data);
ptr
}
Self::Bytes31(data) => {
let ptr = arena.alloc_layout(get_integer_layout(248)).cast();
ptr.cast::<[u8; 31]>().as_mut().copy_from_slice(data);
ptr
}
Self::Array(data) => {
if let CoreTypeConcrete::Array(info) = Self::resolve_type(ty, registry)? {
let elem_ty = registry.get_type(&info.ty)?;
let elem_layout = elem_ty.layout(registry)?.pad_to_align();
let len: u32 = data
.len()
.try_into()
.map_err(|_| Error::IntegerConversion)?;
let data_ptr: *mut u8 = match len {
0 => std::ptr::null_mut::<u8>(),
_ => arena
.alloc_layout(Layout::from_size_align(
elem_layout.size() * data.len(),
elem_layout.align(),
)?)
.as_ptr(),
};
for (idx, elem) in data.iter().enumerate() {
let elem = elem.to_ptr(arena, registry, &info.ty)?;
std::ptr::copy_nonoverlapping(
elem.cast::<u8>().as_ptr(),
data_ptr.byte_add(idx * elem_layout.size()).cast::<u8>(),
elem_layout.size(),
);
}
let target = arena.alloc_layout(Layout::new::<ArrayAbi<u8>>()).as_ptr();
*target.cast::<ArrayAbi<u8>>() = ArrayAbi {
ptr: data_ptr,
since: 0,
until: len,
capacity: len,
};
NonNull::new_unchecked(target).cast()
} else {
Err(Error::UnexpectedValue(format!(
"expected value of type {:?} but got an array",
type_id.debug_name
)))?
}
}
Self::Struct {
fields: members, ..
} => {
if let CoreTypeConcrete::Struct(info) = Self::resolve_type(ty, registry)? {
let mut layout: Option<Layout> = None;
let mut data = Vec::with_capacity(info.members.len());
let mut is_memory_allocated = false;
for (member_type_id, member) in info.members.iter().zip(members) {
let member_ty = registry.get_type(member_type_id)?;
let member_layout = member_ty.layout(registry)?;
let (new_layout, offset) = match layout {
Some(layout) => layout.extend(member_layout)?,
None => (member_layout, 0),
};
layout = Some(new_layout);
let member_ptr = member.to_ptr(arena, registry, member_type_id)?;
data.push((
member_layout,
offset,
if member_ty.is_memory_allocated(registry)? {
is_memory_allocated = true;
*member_ptr.cast::<NonNull<()>>().as_ref()
} else {
member_ptr
},
));
}
let ptr = arena
.alloc_layout(layout.unwrap_or(Layout::new::<()>()).pad_to_align())
.as_ptr();
for (layout, offset, member_ptr) in data {
std::ptr::copy_nonoverlapping(
member_ptr.cast::<u8>().as_ptr(),
ptr.byte_add(offset),
layout.size(),
);
}
if is_memory_allocated {
NonNull::new_unchecked(arena.alloc(ptr) as *mut _).cast()
} else {
NonNull::new_unchecked(ptr).cast()
}
} else {
Err(Error::UnexpectedValue(format!(
"expected value of type {:?} but got a struct",
type_id.debug_name
)))?
}
}
Self::Enum { tag, value, .. } => {
if let CoreTypeConcrete::Enum(info) = Self::resolve_type(ty, registry)? {
native_assert!(*tag < info.variants.len(), "Variant index out of range.");
let payload_type_id = &info.variants[*tag];
let payload = value.to_ptr(arena, registry, payload_type_id)?;
let (layout, tag_layout, variant_layouts) =
crate::types::r#enum::get_layout_for_variants(
registry,
&info.variants,
)?;
let ptr = arena.alloc_layout(layout).cast::<()>().as_ptr();
match tag_layout.size() {
0 => native_panic!("An enum without variants cannot be instantiated."),
1 => *ptr.cast::<u8>() = *tag as u8,
2 => *ptr.cast::<u16>() = *tag as u16,
4 => *ptr.cast::<u32>() = *tag as u32,
8 => *ptr.cast::<u64>() = *tag as u64,
_ => native_panic!("reached the maximum size for an enum"),
}
std::ptr::copy_nonoverlapping(
payload.cast::<u8>().as_ptr(),
ptr.byte_add(tag_layout.extend(variant_layouts[*tag])?.1)
.cast(),
variant_layouts[*tag].size(),
);
NonNull::new_unchecked(arena.alloc(ptr) as *mut _).cast()
} else {
Err(Error::UnexpectedValue(format!(
"expected value of type {:?} but got an enum value",
type_id.debug_name
)))?
}
}
Self::Felt252Dict { value: map, .. } => {
if let CoreTypeConcrete::Felt252Dict(info) = Self::resolve_type(ty, registry)? {
let elem_ty = registry.get_type(&info.ty)?;
let elem_layout = elem_ty.layout(registry)?;
let elements = if map.is_empty() {
null_mut()
} else {
crate::runtime::cairo_native__arena_alloc(
(elem_layout.pad_to_align().size() * map.len()) as u64,
elem_layout.align() as u64,
)
.cast()
};
let dict_ptr = crate::runtime::cairo_native__dict_new(
elem_layout.size() as u64,
elem_layout.align() as u64,
);
let dict = &mut *dict_ptr;
dict.mappings.reserve(map.len());
dict.elements = elements;
for (key, value) in map.iter() {
let value = value.to_ptr(arena, registry, &info.ty)?;
let index = dict.mappings.len();
dict.mappings.insert(*key, index);
std::ptr::copy_nonoverlapping(
value.cast::<u8>().as_ptr(),
dict.elements
.byte_add(elem_layout.pad_to_align().size() * index)
.cast(),
elem_layout.size(),
);
}
NonNull::new_unchecked(dict_ptr as *mut ()).cast()
} else {
Err(Error::UnexpectedValue(format!(
"expected value of type {:?} but got a felt dict",
type_id.debug_name
)))?
}
}
Self::Uint8(value) => {
let ptr = arena.alloc_layout(Layout::new::<u8>()).cast();
*ptr.cast::<u8>().as_mut() = *value;
ptr
}
Self::Uint16(value) => {
let ptr = arena.alloc_layout(Layout::new::<u16>()).cast();
*ptr.cast::<u16>().as_mut() = *value;
ptr
}
Self::Uint32(value) => {
let ptr = arena.alloc_layout(Layout::new::<u32>()).cast();
*ptr.cast::<u32>().as_mut() = *value;
ptr
}
Self::Uint64(value) => {
let ptr = arena.alloc_layout(Layout::new::<u64>()).cast();
*ptr.cast::<u64>().as_mut() = *value;
ptr
}
Self::Uint128(value) => {
let ptr = arena.alloc_layout(Layout::new::<u128>()).cast();
*ptr.cast::<u128>().as_mut() = *value;
ptr
}
Self::Sint8(value) => {
let ptr = arena.alloc_layout(Layout::new::<i8>()).cast();
*ptr.cast::<i8>().as_mut() = *value;
ptr
}
Self::Sint16(value) => {
let ptr = arena.alloc_layout(Layout::new::<i16>()).cast();
*ptr.cast::<i16>().as_mut() = *value;
ptr
}
Self::Sint32(value) => {
let ptr = arena.alloc_layout(Layout::new::<i32>()).cast();
*ptr.cast::<i32>().as_mut() = *value;
ptr
}
Self::Sint64(value) => {
let ptr = arena.alloc_layout(Layout::new::<i64>()).cast();
*ptr.cast::<i64>().as_mut() = *value;
ptr
}
Self::Sint128(value) => {
let ptr = arena.alloc_layout(Layout::new::<i128>()).cast();
*ptr.cast::<i128>().as_mut() = *value;
ptr
}
Self::EcPoint(a, b) => {
let ptr = arena
.alloc_layout(layout_repeat(&get_integer_layout(252), 2)?.0.pad_to_align())
.cast();
let a = felt252_bigint(a.to_bigint()).to_bytes_le();
let b = felt252_bigint(b.to_bigint()).to_bytes_le();
let data = [a, b];
ptr.cast::<[[u8; 32]; 2]>().as_mut().copy_from_slice(&data);
ptr
}
Self::EcState(a, b, c, d) => {
let ptr = arena
.alloc_layout(layout_repeat(&get_integer_layout(252), 4)?.0.pad_to_align())
.cast();
let a = felt252_bigint(a.to_bigint()).to_bytes_le();
let b = felt252_bigint(b.to_bigint()).to_bytes_le();
let c = felt252_bigint(c.to_bigint()).to_bytes_le();
let d = felt252_bigint(d.to_bigint()).to_bytes_le();
let data = [a, b, c, d];
ptr.cast::<[[u8; 32]; 4]>().as_mut().copy_from_slice(&data);
ptr
}
Self::QM31(_, _, _, _) => native_panic!("todo: allocate type QM31"),
Self::Secp256K1Point { .. } => native_panic!("todo: allocate type Secp256K1Point"),
Self::Secp256R1Point { .. } => native_panic!("todo: allocate type Secp256R1Point"),
Self::Null => {
native_panic!(
"unimplemented: null is meant as return value for nullable for now"
)
}
Self::IntRange { x, y } => {
if let CoreTypeConcrete::IntRange(info) = Self::resolve_type(ty, registry)? {
let inner = registry.get_type(&info.ty)?;
let inner_layout = inner.layout(registry)?;
let x_ptr = x.to_ptr(arena, registry, &info.ty)?;
let (struct_layout, y_offset) = inner_layout.extend(inner_layout)?;
let y_ptr = y.to_ptr(arena, registry, &info.ty)?;
let ptr = arena.alloc_layout(struct_layout.pad_to_align()).as_ptr();
std::ptr::copy_nonoverlapping(
x_ptr.cast::<u8>().as_ptr(),
ptr,
inner_layout.size(),
);
std::ptr::copy_nonoverlapping(
y_ptr.cast::<u8>().as_ptr(),
ptr.byte_add(y_offset),
inner_layout.size(),
);
NonNull::new_unchecked(ptr).cast()
} else {
native_panic!(
"an IntRange value should always have an IntRange CoreTypeConcrete"
)
}
}
}
})
}
pub(crate) fn from_ptr(
ptr: NonNull<()>,
type_id: &ConcreteTypeId,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
) -> Result<Self, Error> {
let ty = registry.get_type(type_id)?;
Ok(unsafe {
match ty {
CoreTypeConcrete::Array(info) => {
let elem_ty = registry.get_type(&info.ty)?;
let elem_layout = elem_ty.layout(registry)?;
let elem_stride = elem_layout.pad_to_align().size();
let ptr_layout = Layout::new::<*mut ()>();
let len_layout = crate::utils::get_integer_layout(32);
let (ptr_layout, offset) = ptr_layout.extend(len_layout)?;
let start_offset_value = *NonNull::new(ptr.as_ptr().byte_add(offset))
.to_native_assert_error("tried to make a non-null ptr out of a null one")?
.cast::<u32>()
.as_ref();
let (ptr_layout, offset) = ptr_layout.extend(len_layout)?;
let end_offset_value = *NonNull::new(ptr.as_ptr().byte_add(offset))
.to_native_assert_error("tried to make a non-null ptr out of a null one")?
.cast::<u32>()
.as_ref();
let (_ptr_layout, _offset) = ptr_layout.extend(len_layout)?;
let data_ptr = *ptr.cast::<*mut u8>().as_ref();
let array_value = if data_ptr.is_null() {
Vec::new()
} else {
native_assert!(
end_offset_value >= start_offset_value,
"can't have an array with negative length"
);
let num_elems = (end_offset_value - start_offset_value) as usize;
let mut array_value = Vec::with_capacity(num_elems);
for i in start_offset_value..end_offset_value {
let cur_elem_ptr =
NonNull::new(data_ptr.byte_add(elem_stride * i as usize))
.to_native_assert_error(
"tried to make a non-null ptr out of a null one",
)?;
array_value.push(Self::from_ptr(
cur_elem_ptr.cast(),
&info.ty,
registry,
)?);
}
array_value
};
Self::Array(array_value)
}
CoreTypeConcrete::Box(info) => {
let inner = *ptr.cast::<NonNull<()>>().as_ptr();
Self::from_ptr(inner, &info.ty, registry)?
}
CoreTypeConcrete::EcPoint(_) => {
let data = ptr.cast::<[[u8; 32]; 2]>().as_mut();
data[0][31] &= 0x0F; data[1][31] &= 0x0F;
Self::EcPoint(Felt::from_bytes_le(&data[0]), Felt::from_bytes_le(&data[1]))
}
CoreTypeConcrete::EcState(_) => {
let data = ptr.cast::<[[u8; 32]; 4]>().as_mut();
data[0][31] &= 0x0F; data[1][31] &= 0x0F; data[2][31] &= 0x0F; data[3][31] &= 0x0F;
Self::EcState(
Felt::from_bytes_le(&data[0]),
Felt::from_bytes_le(&data[1]),
Felt::from_bytes_le(&data[2]),
Felt::from_bytes_le(&data[3]),
)
}
CoreTypeConcrete::QM31(_) => {
let data = ptr.cast::<[u32; 4]>().as_mut();
Self::QM31(data[0], data[1], data[2], data[3])
}
CoreTypeConcrete::Felt252(_) => {
let data = ptr.cast::<[u8; 32]>().as_mut();
data[31] &= 0x0F; let data = Felt::from_bytes_le_slice(data);
Self::Felt252(data)
}
CoreTypeConcrete::Uint8(_) => Self::Uint8(*ptr.cast::<u8>().as_ref()),
CoreTypeConcrete::Uint16(_) => Self::Uint16(*ptr.cast::<u16>().as_ref()),
CoreTypeConcrete::Uint32(_) => Self::Uint32(*ptr.cast::<u32>().as_ref()),
CoreTypeConcrete::Uint64(_) => Self::Uint64(*ptr.cast::<u64>().as_ref()),
CoreTypeConcrete::Uint128(_) => Self::Uint128(*ptr.cast::<u128>().as_ref()),
CoreTypeConcrete::Uint128MulGuarantee(_) => {
native_panic!("todo: implement uint128mulguarantee from_ptr")
}
CoreTypeConcrete::Sint8(_) => Self::Sint8(*ptr.cast::<i8>().as_ref()),
CoreTypeConcrete::Sint16(_) => Self::Sint16(*ptr.cast::<i16>().as_ref()),
CoreTypeConcrete::Sint32(_) => Self::Sint32(*ptr.cast::<i32>().as_ref()),
CoreTypeConcrete::Sint64(_) => Self::Sint64(*ptr.cast::<i64>().as_ref()),
CoreTypeConcrete::Sint128(_) => Self::Sint128(*ptr.cast::<i128>().as_ref()),
CoreTypeConcrete::NonZero(info) => Self::from_ptr(ptr, &info.ty, registry)?,
CoreTypeConcrete::Nullable(info) => {
let inner_ptr = *ptr.cast::<*mut ()>().as_ptr();
if inner_ptr.is_null() {
Self::Null
} else {
Self::from_ptr(
NonNull::new_unchecked(inner_ptr).cast(),
&info.ty,
registry,
)?
}
}
CoreTypeConcrete::Uninitialized(_) => {
native_panic!("todo: implement uninit from_ptr or ignore the return value")
}
CoreTypeConcrete::Enum(info) => {
let (_, tag_layout, variant_layouts) =
crate::types::r#enum::get_layout_for_variants(registry, &info.variants)?;
let tag_value = match info.variants.len() {
0 => {
native_panic!("An enum without variants is not a valid type.")
}
1 => 0,
_ => match tag_layout.size() {
1 => *ptr.cast::<u8>().as_ref() as usize,
2 => *ptr.cast::<u16>().as_ref() as usize,
4 => *ptr.cast::<u32>().as_ref() as usize,
8 => *ptr.cast::<u64>().as_ref() as usize,
_ => native_panic!("reached the maximum size for an enum"),
},
};
let tag_value = tag_value & (info.variants.len().next_power_of_two() - 1);
let payload_ptr = NonNull::new(
ptr.as_ptr()
.byte_add(tag_layout.extend(variant_layouts[tag_value])?.1),
)
.to_native_assert_error("tried to make a non-null ptr out of a null one")?;
let payload = Self::from_ptr(payload_ptr, &info.variants[tag_value], registry)?;
Self::Enum {
tag: tag_value,
value: Box::new(payload),
debug_name: type_id.debug_name.as_ref().map(|x| x.to_string()),
}
}
CoreTypeConcrete::Struct(info) => {
let mut layout: Option<Layout> = None;
let mut members = Vec::with_capacity(info.members.len());
for member_ty in &info.members {
let member = registry.get_type(member_ty)?;
let member_layout = member.layout(registry)?;
let (new_layout, offset) = match layout {
Some(layout) => layout.extend(member_layout)?,
None => (member_layout, 0),
};
layout = Some(new_layout);
members.push(Self::from_ptr(
NonNull::new(ptr.as_ptr().byte_add(offset)).to_native_assert_error(
"tried to make a non-null ptr out of a null one",
)?,
member_ty,
registry,
)?);
}
Self::Struct {
fields: members,
debug_name: type_id.debug_name.as_ref().map(|x| x.to_string()),
}
}
CoreTypeConcrete::Felt252Dict(info)
| CoreTypeConcrete::SquashedFelt252Dict(info) => {
let dict = &*ptr.cast::<*const FeltDict>().read();
let mut output_map = HashMap::with_capacity(dict.mappings.len());
for (&key, &index) in dict.mappings.iter() {
output_map.insert(
key,
Self::from_ptr(
NonNull::new(
dict.elements
.byte_add(dict.layout.pad_to_align().size() * index),
)
.to_native_assert_error(
"tried to make a non-null ptr out of a null one",
)?
.cast(),
&info.ty,
registry,
)?,
);
}
Self::Felt252Dict {
value: output_map,
debug_name: type_id.debug_name.as_ref().map(|x| x.to_string()),
}
}
CoreTypeConcrete::Felt252DictEntry(_) => {
native_panic!("unimplemented: should be impossible to return")
}
CoreTypeConcrete::Pedersen(_)
| CoreTypeConcrete::Poseidon(_)
| CoreTypeConcrete::Bitwise(_)
| CoreTypeConcrete::BuiltinCosts(_)
| CoreTypeConcrete::RangeCheck(_)
| CoreTypeConcrete::EcOp(_)
| CoreTypeConcrete::GasBuiltin(_)
| CoreTypeConcrete::SegmentArena(_) => {
native_panic!("handled before: {:?}", type_id)
}
CoreTypeConcrete::Starknet(selector) => match selector {
StarknetTypeConcrete::ClassHash(_)
| StarknetTypeConcrete::ContractAddress(_)
| StarknetTypeConcrete::StorageBaseAddress(_)
| StarknetTypeConcrete::StorageAddress(_) => {
let data = ptr.cast::<[u8; 32]>().as_mut();
data[31] &= 0x0F; let data = Felt::from_bytes_le(data);
Self::Felt252(data)
}
StarknetTypeConcrete::System(_) => {
native_panic!("should be handled before")
}
StarknetTypeConcrete::Secp256Point(info) => match info {
Secp256PointTypeConcrete::K1(_) => {
let data = ptr.cast::<Secp256k1Point>().as_ref();
Self::Secp256K1Point(*data)
}
Secp256PointTypeConcrete::R1(_) => {
let data = ptr.cast::<Secp256r1Point>().as_ref();
Self::Secp256R1Point(*data)
}
},
StarknetTypeConcrete::Sha256StateHandle(_) => {
native_panic!("todo: implement Sha256StateHandle from_ptr")
}
StarknetTypeConcrete::Sha512StateHandle(_) => {
native_panic!("todo: implement Sha512StateHandle from_ptr")
}
},
CoreTypeConcrete::Span(_) => native_panic!("implement span from_ptr"),
CoreTypeConcrete::Snapshot(info) => Self::from_ptr(ptr, &info.ty, registry)?,
CoreTypeConcrete::Bytes31(_) => {
let data = *ptr.cast::<[u8; 31]>().as_ref();
Self::Bytes31(data)
}
CoreTypeConcrete::Const(_) => native_panic!("implement const from_ptr"),
CoreTypeConcrete::BoundedInt(info)
| CoreTypeConcrete::BoundedIntGuarantee(info) => {
let mut data = BigInt::from_biguint(
Sign::Plus,
BigUint::from_bytes_le(slice::from_raw_parts(
ptr.cast::<u8>().as_ptr(),
(info.range.repr_bit_width().next_multiple_of(8) >> 3) as usize,
)),
);
data &= (BigInt::one() << info.range.repr_bit_width()) - BigInt::one();
data += &info.range.lower;
Self::BoundedInt {
value: data.into(),
range: info.range.clone(),
}
}
CoreTypeConcrete::Circuit(CircuitTypeConcrete::U96Guarantee(_)) => {
let data = BigInt::from_biguint(
Sign::Plus,
BigUint::from_bytes_le(slice::from_raw_parts(
ptr.cast::<u8>().as_ptr(),
12,
)),
);
Self::BoundedInt {
value: data.into(),
range: Range {
lower: BigInt::ZERO,
upper: BigInt::one() << 96,
},
}
}
CoreTypeConcrete::Coupon(_)
| CoreTypeConcrete::Circuit(_)
| CoreTypeConcrete::RangeCheck96(_) => native_panic!("implement from_ptr"),
CoreTypeConcrete::IntRange(info) => {
let member = registry.get_type(&info.ty)?;
let member_layout = member.layout(registry)?;
let x = Self::from_ptr(
NonNull::new(ptr.as_ptr()).to_native_assert_error(
"tried to make a non-null ptr out of a null one",
)?,
&info.ty,
registry,
)?;
let y = Self::from_ptr(
NonNull::new(
ptr.as_ptr()
.byte_add(member_layout.extend(member_layout)?.1),
)
.to_native_assert_error("tried to make a non-null ptr out of a null one")?,
&info.ty,
registry,
)?;
Self::IntRange {
x: x.into(),
y: y.into(),
}
}
CoreTypeConcrete::GasReserve(_) => Self::Uint128(*ptr.cast::<u128>().as_ref()),
CoreTypeConcrete::Blake(_) => native_panic!("Implement from_ptr for Blake type"),
}
})
}
}
#[cfg(test)]
mod test {
use super::*;
use bumpalo::Bump;
use cairo_lang_sierra::extensions::types::{InfoAndTypeConcreteType, TypeInfo};
use cairo_lang_sierra::program::ConcreteTypeLongId;
use cairo_lang_sierra::program::Program;
use cairo_lang_sierra::program::TypeDeclaration;
use cairo_lang_sierra::ProgramParser;
#[test]
fn test_jit_value_conversion_felt() {
let felt_value: Felt = 42.into();
let jit_value: Value = felt_value.into();
assert_eq!(jit_value, Value::Felt252(Felt::from(42)));
}
#[test]
fn test_jit_value_conversion_u8() {
let u8_value: u8 = 10;
let jit_value: Value = u8_value.into();
assert_eq!(jit_value, Value::Uint8(10));
}
#[test]
fn test_jit_value_conversion_u16() {
let u8_value: u16 = 10;
let jit_value: Value = u8_value.into();
assert_eq!(jit_value, Value::Uint16(10));
}
#[test]
fn test_jit_value_conversion_u32() {
let u32_value: u32 = 10;
let jit_value: Value = u32_value.into();
assert_eq!(jit_value, Value::Uint32(10));
}
#[test]
fn test_jit_value_conversion_u64() {
let u64_value: u64 = 10;
let jit_value: Value = u64_value.into();
assert_eq!(jit_value, Value::Uint64(10));
}
#[test]
fn test_jit_value_conversion_u128() {
let u128_value: u128 = 10;
let jit_value: Value = u128_value.into();
assert_eq!(jit_value, Value::Uint128(10));
}
#[test]
fn test_jit_value_conversion_i8() {
let i8_value: i8 = -10;
let jit_value: Value = i8_value.into();
assert_eq!(jit_value, Value::Sint8(-10));
}
#[test]
fn test_jit_value_conversion_i16() {
let i16_value: i16 = -10;
let jit_value: Value = i16_value.into();
assert_eq!(jit_value, Value::Sint16(-10));
}
#[test]
fn test_jit_value_conversion_i32() {
let i32_value: i32 = -10;
let jit_value: Value = i32_value.into();
assert_eq!(jit_value, Value::Sint32(-10));
}
#[test]
fn test_jit_value_conversion_i64() {
let i64_value: i64 = -10;
let jit_value: Value = i64_value.into();
assert_eq!(jit_value, Value::Sint64(-10));
}
#[test]
fn test_jit_value_conversion_i128() {
let i128_value: i128 = -10;
let jit_value: Value = i128_value.into();
assert_eq!(jit_value, Value::Sint128(-10));
}
#[test]
fn test_jit_value_conversion_array_from_slice() {
let array_slice: &[u8] = &[1, 2, 3];
let jit_value: Value = array_slice.into();
assert_eq!(
jit_value,
Value::Array(vec![Value::Uint8(1), Value::Uint8(2), Value::Uint8(3)])
);
}
#[test]
fn test_jit_value_conversion_array_from_vec() {
let array_vec: Vec<u8> = vec![1, 2, 3];
let jit_value: Value = array_vec.into();
assert_eq!(
jit_value,
Value::Array(vec![Value::Uint8(1), Value::Uint8(2), Value::Uint8(3)])
);
}
#[test]
fn test_jit_value_conversion_array_from_fixed_size_array() {
let array_fixed: [u8; 3] = [1, 2, 3];
let jit_value: Value = array_fixed.into();
assert_eq!(
jit_value,
Value::Array(vec![Value::Uint8(1), Value::Uint8(2), Value::Uint8(3)])
);
}
#[test]
fn test_resolve_type_snapshot() {
let ty = CoreTypeConcrete::Snapshot(InfoAndTypeConcreteType {
info: TypeInfo {
long_id: ConcreteTypeLongId {
generic_id: "generic_type_id".into(),
generic_args: vec![],
},
storable: false,
droppable: false,
duplicatable: false,
zero_sized: false,
},
ty: "test_id".into(),
});
let program = Program {
type_declarations: vec![TypeDeclaration {
id: "test_id".into(),
long_id: ConcreteTypeLongId {
generic_id: "u128".into(),
generic_args: vec![],
},
declared_type_info: None,
}],
libfunc_declarations: vec![],
statements: vec![],
funcs: vec![],
};
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
Value::resolve_type(&ty, ®istry)
.unwrap()
.integer_range(®istry)
.unwrap(),
Range {
lower: BigInt::from(u128::MIN),
upper: BigInt::from(u128::MAX) + BigInt::one(),
}
);
}
#[test]
fn test_to_ptr_felt252() {
let program = ProgramParser::new()
.parse("type felt252 = felt252;")
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Felt252(Felt::from(42))
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<[u32; 8]>()
.as_ptr()
},
[42, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
unsafe {
*Value::Felt252(Felt::MAX)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<[u32; 8]>()
.as_ptr()
},
[0, 0, 0, 0, 0, 0, 17, 134217728]
);
assert_eq!(
unsafe {
*Value::Felt252(Felt::MAX + Felt::ONE)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<[u32; 8]>()
.as_ptr()
},
[0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[test]
fn test_to_ptr_uint8() {
let program = ProgramParser::new().parse("type u8 = u8;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Uint8(9)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<u8>()
.as_ptr()
},
9
);
}
#[test]
fn test_to_ptr_uint16() {
let program = ProgramParser::new().parse("type u16 = u16;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Uint16(17)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<u16>()
.as_ptr()
},
17
);
}
#[test]
fn test_to_ptr_uint32() {
let program = ProgramParser::new().parse("type u32 = u32;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Uint32(33)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<u32>()
.as_ptr()
},
33
);
}
#[test]
fn test_to_ptr_uint64() {
let program = ProgramParser::new().parse("type u64 = u64;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Uint64(65)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<u64>()
.as_ptr()
},
65
);
}
#[test]
fn test_to_ptr_uint128() {
let program = ProgramParser::new().parse("type u128 = u128;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Uint128(129)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<u128>()
.as_ptr()
},
129
);
}
#[test]
fn test_to_ptr_sint8() {
let program = ProgramParser::new().parse("type i8 = i8;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Sint8(-9)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<i8>()
.as_ptr()
},
-9
);
}
#[test]
fn test_to_ptr_sint16() {
let program = ProgramParser::new().parse("type i16 = i16;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Sint16(-17)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<i16>()
.as_ptr()
},
-17
);
}
#[test]
fn test_to_ptr_sint32() {
let program = ProgramParser::new().parse("type i32 = i32;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Sint32(-33)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<i32>()
.as_ptr()
},
-33
);
}
#[test]
fn test_to_ptr_sint64() {
let program = ProgramParser::new().parse("type i64 = i64;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Sint64(-65)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<i64>()
.as_ptr()
},
-65
);
}
#[test]
fn test_to_ptr_sint128() {
let program = ProgramParser::new().parse("type i128 = i128;").unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::Sint128(-129)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<i128>()
.as_ptr()
},
-129
);
}
#[test]
fn test_to_ptr_ec_point() {
let program = ProgramParser::new()
.parse("type EcPoint = EcPoint;")
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::EcPoint(Felt::from(1234), Felt::from(4321))
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<[[u32; 8]; 2]>()
.as_ptr()
},
[[1234, 0, 0, 0, 0, 0, 0, 0], [4321, 0, 0, 0, 0, 0, 0, 0]]
);
}
#[test]
fn test_to_ptr_ec_state() {
let program = ProgramParser::new()
.parse("type EcState = EcState;")
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::EcState(
Felt::from(1234),
Felt::from(4321),
Felt::from(3333),
Felt::from(4444),
)
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap()
.cast::<[[u32; 8]; 4]>()
.as_ptr()
},
[
[1234, 0, 0, 0, 0, 0, 0, 0],
[4321, 0, 0, 0, 0, 0, 0, 0],
[3333, 0, 0, 0, 0, 0, 0, 0],
[4444, 0, 0, 0, 0, 0, 0, 0]
]
);
}
#[test]
fn test_to_ptr_enum() {
let program = ProgramParser::new()
.parse(
"type u8 = u8;
type MyEnum = Enum<ut@MyEnum, u8, u8>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::Enum {
tag: 0,
value: Box::new(Value::Uint8(10)),
debug_name: None,
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id);
assert!(result.is_ok());
}
#[test]
fn test_to_ptr_bounded_int_valid() {
let program = ProgramParser::new()
.parse(
"type felt252 = felt252;
type BoundedInt = BoundedInt<10, 510>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
assert_eq!(
unsafe {
*Value::BoundedInt {
value: Felt::from(16),
range: Range {
lower: BigInt::from(10),
upper: BigInt::from(510),
},
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id)
.unwrap()
.cast::<[u32; 8]>()
.as_ptr()
},
[16, 0, 0, 0, 0, 0, 0, 0]
);
}
#[test]
fn test_to_ptr_bounded_int_lower_bound_greater_than_upper() {
let program = ProgramParser::new()
.parse(
"type felt252 = felt252;
type BoundedInt = BoundedInt<10, 510>;", )
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::BoundedInt {
value: Felt::from(16),
range: Range {
lower: BigInt::from(510),
upper: BigInt::from(10),
},
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id);
assert!(matches!(
result,
Err(Error::Compiler(CompilerError::BoundedIntOutOfRange { .. }))
));
}
#[test]
fn test_to_ptr_bounded_int_value_less_than_lower_bound() {
let program = ProgramParser::new()
.parse(
"type felt252 = felt252;
type BoundedInt = BoundedInt<10, 510>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::BoundedInt {
value: Felt::from(9),
range: Range {
lower: BigInt::from(10),
upper: BigInt::from(510),
},
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id);
assert!(matches!(
result,
Err(Error::Compiler(CompilerError::BoundedIntOutOfRange { .. }))
));
}
#[test]
fn test_to_ptr_bounded_int_value_greater_than_or_equal_to_upper_bound() {
let program = ProgramParser::new()
.parse(
"type felt252 = felt252;
type BoundedInt = BoundedInt<10, 510>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::BoundedInt {
value: Felt::from(512),
range: Range {
lower: BigInt::from(10),
upper: BigInt::from(510),
},
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id);
assert!(matches!(
result,
Err(Error::Compiler(CompilerError::BoundedIntOutOfRange { .. }))
));
}
#[test]
fn test_to_ptr_bounded_int_equal_bounds_and_value() {
let program = ProgramParser::new()
.parse(
"type felt252 = felt252;
type BoundedInt = BoundedInt<10, 10>;", )
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::BoundedInt {
value: Felt::from(10),
range: Range {
lower: BigInt::from(10),
upper: BigInt::from(10),
},
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id);
assert!(matches!(
result,
Err(Error::Compiler(CompilerError::BoundedIntOutOfRange { .. }))
));
}
#[test]
fn test_to_ptr_enum_variant_out_of_range() {
let program = ProgramParser::new()
.parse(
"type u8 = u8;
type MyEnum = Enum<ut@MyEnum, u8, u8>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::Enum {
tag: 2,
value: Box::new(Value::Uint8(10)),
debug_name: None,
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id)
.unwrap_err();
let error = result.to_string().clone();
let error_msg = error.split("\n").collect::<Vec<&str>>()[0];
assert_eq!(error_msg, "Variant index out of range.");
}
#[test]
fn test_to_ptr_enum_no_variant() {
let program = ProgramParser::new()
.parse(
"type u8 = u8;
type MyEnum = Enum<ut@MyEnum, u8>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::Enum {
tag: 0,
value: Box::new(Value::Uint8(10)),
debug_name: None,
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[1].id)
.unwrap_err();
let error = result.to_string().clone();
let error_msg = error.split("\n").collect::<Vec<&str>>()[0];
assert_eq!(
error_msg,
"An enum without variants cannot be instantiated."
);
}
#[test]
fn test_to_ptr_enum_type_error() {
let program = ProgramParser::new()
.parse(
"type felt252 = felt252;
type MyEnum = Enum<ut@MyEnum, felt252, felt252>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::Enum {
tag: 0,
value: Box::new(Value::Struct {
fields: vec![Value::from(2u32)],
debug_name: None,
}),
debug_name: None,
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap_err();
match result {
Error::UnexpectedValue(expected_msg) => {
assert_eq!(
expected_msg,
format!(
"expected value of type {:?} but got an enum value",
program.type_declarations[0].id.debug_name
)
);
}
_ => panic!("Unexpected error type: {:?}", result),
}
}
#[test]
fn test_to_ptr_struct_type_error() {
let program = ProgramParser::new()
.parse(
"type felt252 = felt252;
type MyEnum = Enum<ut@MyEnum, felt252, felt252>;",
)
.unwrap();
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
let result = Value::Struct {
fields: vec![Value::from(2u32)],
debug_name: None,
}
.to_ptr(&Bump::new(), ®istry, &program.type_declarations[0].id)
.unwrap_err();
match result {
Error::UnexpectedValue(expected_msg) => {
assert_eq!(
expected_msg,
format!(
"expected value of type {:?} but got a struct",
program.type_declarations[0].id.debug_name
)
);
}
_ => panic!("Unexpected error type: {:?}", result),
}
}
}
mod range_serde {
use std::fmt;
use cairo_lang_sierra::extensions::utils::Range;
use serde::{
de::{self, Visitor},
ser::SerializeStruct,
Deserializer, Serializer,
};
pub fn serialize<S>(range: &Range, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = ser.serialize_struct("Range", 2)?;
state.serialize_field("lower", &range.lower)?;
state.serialize_field("upper", &range.upper)?;
state.end()
}
pub fn deserialize<'de, D>(de: D) -> Result<Range, D::Error>
where
D: Deserializer<'de>,
{
struct RangeVisitor;
impl<'de> Visitor<'de> for RangeVisitor {
type Value = Range;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an integer between -2^31 and 2^31")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let lower = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let upper = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
Ok(Range { lower, upper })
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut lower = None;
let mut upper = None;
while let Some((field, value)) = map.next_entry()? {
match field {
"lower" => {
lower = Some(value);
}
"upper" => {
upper = Some(value);
}
_ => return Err(de::Error::unknown_field(field, &["lower", "upper"])),
}
}
Ok(Range {
lower: lower.ok_or_else(|| de::Error::missing_field("lower"))?,
upper: upper.ok_or_else(|| de::Error::missing_field("upper"))?,
})
}
}
de.deserialize_struct("Range", &["lower", "upper"], RangeVisitor)
}
}