diff --git a/src/arch.rs b/src/arch.rs index 15f84a65c..d4fdbd86b 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -195,6 +195,20 @@ impl AbiArgument for ValueWithInfoWrapper<'_> { .as_ptr() .to_bytes(buffer, find_dict_drop_override)? } + #[cfg(test)] + (Value::Felt252DictEntry { .. }, CoreTypeConcrete::Felt252DictEntry(_)) => { + let abi_ptr = self.value.to_ptr( + self.arena, + self.registry, + self.type_id, + find_dict_drop_override, + )?; + + let abi = unsafe { abi_ptr.cast::().as_ref() }; + + abi.dict.to_bytes(buffer, find_dict_drop_override)?; + abi.value_ptr.to_bytes(buffer, find_dict_drop_override)?; + } ( Value::Secp256K1Point(Secp256k1Point { x, y, is_infinity }), CoreTypeConcrete::Starknet(StarknetTypeConcrete::Secp256Point( diff --git a/src/execution_result.rs b/src/execution_result.rs index c3b56e19c..b324a2fcb 100644 --- a/src/execution_result.rs +++ b/src/execution_result.rs @@ -31,7 +31,8 @@ pub struct BuiltinStats { } /// The result of the JIT execution. -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(not(test), derive(serde::Serialize, serde::Deserialize))] pub struct ExecutionResult { pub remaining_gas: Option, pub return_value: Value, diff --git a/src/libfuncs/felt252.rs b/src/libfuncs/felt252.rs index 65f079bdc..3e2c947d6 100644 --- a/src/libfuncs/felt252.rs +++ b/src/libfuncs/felt252.rs @@ -332,36 +332,45 @@ pub fn build_is_zero<'ctx, 'this>( #[cfg(test)] pub mod test { use crate::{ - utils::test::{load_cairo, run_program}, + utils::{ + sierra_gen::SierraGenerator, + test::{load_cairo, run_program, run_sierra_program}, + }, values::Value, }; - use cairo_lang_sierra::program::Program; + use cairo_lang_sierra::{ + extensions::{ + felt252::{Felt252BinaryOperationWithVarLibfunc, Felt252Traits}, + is_zero::IsZeroLibfunc, + }, + program::Program, + }; use lazy_static::lazy_static; use starknet_types_core::felt::Felt; lazy_static! { - static ref FELT252_ADD: (String, Program) = load_cairo! { - fn run_test(lhs: felt252, rhs: felt252) -> felt252 { - lhs + rhs - } + static ref FELT252_ADD: Program = { + let generator = SierraGenerator::::default(); + + generator.build_with_generic_id("felt252_add".into(), &[]) }; - static ref FELT252_SUB: (String, Program) = load_cairo! { - fn run_test(lhs: felt252, rhs: felt252) -> felt252 { - lhs - rhs - } + static ref FELT252_SUB: Program = { + let generator = SierraGenerator::::default(); + + generator.build_with_generic_id("felt252_sub".into(), &[]) }; - static ref FELT252_MUL: (String, Program) = load_cairo! { - fn run_test(lhs: felt252, rhs: felt252) -> felt252 { - lhs * rhs - } + static ref FELT252_MUL: Program = { + let generator = SierraGenerator::::default(); + + generator.build_with_generic_id("felt252_mul".into(), &[]) }; - static ref FELT252_DIV: (String, Program) = load_cairo! { - fn run_test(lhs: felt252, rhs: felt252) -> felt252 { - felt252_div(lhs, rhs.try_into().unwrap()) - } + static ref FELT252_DIV: Program = { + let generator = SierraGenerator::::default(); + + generator.build_with_generic_id("felt252_div".into(), &[]) }; // TODO: Add test program for `felt252_add_const`. @@ -382,12 +391,15 @@ pub mod test { } }; - static ref FELT252_IS_ZERO: (String, Program) = load_cairo! { - fn run_test(x: felt252) -> bool { - match x { - 0 => true, - _ => false, - } + static ref FELT252_IS_ZERO: Program = { + let generator = SierraGenerator::>::default(); + + generator.build(&[]) + }; + + static ref FELT252_DIV_CAIRO: (String, Program) = load_cairo! { + fn run_test(lhs: felt252, rhs: felt252) -> felt252 { + felt252_div(lhs, rhs.try_into().unwrap()) } }; } @@ -399,12 +411,8 @@ pub mod test { #[test] fn felt252_add() { fn r(lhs: Felt, rhs: Felt) -> Felt { - match run_program( - &FELT252_ADD, - "run_test", - &[Value::Felt252(lhs), Value::Felt252(rhs)], - ) - .return_value + match run_sierra_program(&FELT252_ADD, &[Value::Felt252(lhs), Value::Felt252(rhs)]) + .return_value { Value::Felt252(x) => x, _ => panic!("invalid return type"), @@ -437,12 +445,8 @@ pub mod test { #[test] fn felt252_sub() { fn r(lhs: Felt, rhs: Felt) -> Felt { - match run_program( - &FELT252_SUB, - "run_test", - &[Value::Felt252(lhs), Value::Felt252(rhs)], - ) - .return_value + match run_sierra_program(&FELT252_SUB, &[Value::Felt252(lhs), Value::Felt252(rhs)]) + .return_value { Value::Felt252(x) => x, _ => panic!("invalid return type"), @@ -473,12 +477,8 @@ pub mod test { #[test] fn felt252_mul() { fn r(lhs: Felt, rhs: Felt) -> Felt { - match run_program( - &FELT252_MUL, - "run_test", - &[Value::Felt252(lhs), Value::Felt252(rhs)], - ) - .return_value + match run_sierra_program(&FELT252_MUL, &[Value::Felt252(lhs), Value::Felt252(rhs)]) + .return_value { Value::Felt252(x) => x, _ => panic!("invalid return type"), @@ -508,10 +508,47 @@ pub mod test { #[test] fn felt252_div() { + // Helper function to run the test and extract the return value. + fn r(lhs: Felt, rhs: Felt) -> Felt { + let Value::Felt252(x) = + run_sierra_program(&FELT252_DIV, &[Value::Felt252(lhs), Value::Felt252(rhs)]) + .return_value + else { + panic!("invalid result"); + }; + + x + } + + // Test cases for valid division results. + assert_eq!(r(f("0"), f("1")), f("0")); + assert_eq!(r(f("0"), f("-2")), f("0")); + assert_eq!(r(f("0"), f("-1")), f("0")); + assert_eq!(r(f("1"), f("1")), f("1")); + assert_eq!( + r(f("1"), f("-2")), + f("1809251394333065606848661391547535052811553607665798349986546028067936010240") + ); + assert_eq!(r(f("1"), f("-1")), f("-1")); + assert_eq!(r(f("-2"), f("1")), f("-2")); + assert_eq!(r(f("-2"), f("-2")), f("1")); + assert_eq!(r(f("-2"), f("-1")), f("2")); + assert_eq!(r(f("-1"), f("1")), f("-1")); + assert_eq!( + r(f("-1"), f("-2")), + f("1809251394333065606848661391547535052811553607665798349986546028067936010241") + ); + assert_eq!(r(f("-1"), f("-1")), f("1")); + assert_eq!(r(f("6"), f("2")), f("3")); + assert_eq!(r(f("1000"), f("2")), f("500")); + } + + #[test] + fn felt252_div_panic() { // Helper function to run the test and extract the return value. fn r(lhs: Felt, rhs: Felt) -> Option { match run_program( - &FELT252_DIV, + &FELT252_DIV_CAIRO, "run_test", &[Value::Felt252(lhs), Value::Felt252(rhs)], ) @@ -540,32 +577,6 @@ pub mod test { assert_panics(f("0"), f("0")); assert_panics(f("1"), f("0")); assert_panics(f("-2"), f("0")); - - // Test cases for valid division results. - assert_eq!(r(f("0"), f("1")), Some(f("0"))); - assert_eq!(r(f("0"), f("-2")), Some(f("0"))); - assert_eq!(r(f("0"), f("-1")), Some(f("0"))); - assert_eq!(r(f("1"), f("1")), Some(f("1"))); - assert_eq!( - r(f("1"), f("-2")), - Some(f( - "1809251394333065606848661391547535052811553607665798349986546028067936010240" - )) - ); - assert_eq!(r(f("1"), f("-1")), Some(f("-1"))); - assert_eq!(r(f("-2"), f("1")), Some(f("-2"))); - assert_eq!(r(f("-2"), f("-2")), Some(f("1"))); - assert_eq!(r(f("-2"), f("-1")), Some(f("2"))); - assert_eq!(r(f("-1"), f("1")), Some(f("-1"))); - assert_eq!( - r(f("-1"), f("-2")), - Some(f( - "1809251394333065606848661391547535052811553607665798349986546028067936010241" - )) - ); - assert_eq!(r(f("-1"), f("-1")), Some(f("1"))); - assert_eq!(r(f("6"), f("2")), Some(f("3"))); - assert_eq!(r(f("1000"), f("2")), Some(f("500"))); } #[test] @@ -584,8 +595,8 @@ pub mod test { #[test] fn felt252_is_zero() { fn r(x: Felt) -> bool { - match run_program(&FELT252_IS_ZERO, "run_test", &[Value::Felt252(x)]).return_value { - Value::Enum { tag, .. } => tag != 0, + match run_sierra_program(&FELT252_IS_ZERO, &[Value::Felt252(x)]).return_value { + Value::Enum { tag, .. } => tag == 0, _ => panic!("invalid return type"), } } diff --git a/src/libfuncs/felt252_dict.rs b/src/libfuncs/felt252_dict.rs index 43d2165e3..19476128a 100644 --- a/src/libfuncs/felt252_dict.rs +++ b/src/libfuncs/felt252_dict.rs @@ -143,23 +143,48 @@ pub fn build_squash<'ctx, 'this>( #[cfg(test)] mod test { + use std::collections::HashMap; + + use cairo_lang_sierra::{ + extensions::{ + felt252_dict::{ + Felt252DictEntryGetLibfunc, Felt252DictNewLibfunc, Felt252DictSquashLibfunc, + }, + int::unsigned::{Uint32Type, Uint64Type}, + }, + program::GenericArg, + }; + use cairo_vm::Felt252; + use crate::{ - utils::test::{jit_dict, jit_enum, jit_struct, load_cairo, run_program_assert_output}, + utils::{ + sierra_gen::SierraGenerator, + test::{ + jit_dict, jit_enum, jit_struct, load_cairo, run_program_assert_output, + run_sierra_program, + }, + }, values::Value, }; #[test] fn run_dict_new() { - let program = load_cairo!( - use traits::Default; - use dict::Felt252DictTrait; + let program = { + let mut generator = SierraGenerator::::default(); - fn run_test() { - let mut _dict: Felt252Dict = Default::default(); - } - ); + let u32_ty = generator.push_type_declaration::(&[]).clone(); + + generator.build(&[GenericArg::Type(u32_ty)]) + }; - run_program_assert_output(&program, "run_test", &[], jit_struct!()); + let result = run_sierra_program(&program, &[]).return_value; + + let dict = Value::Felt252Dict { + value: HashMap::new(), + debug_name: None, + }; + + assert_eq!(result, dict); } #[test] @@ -177,6 +202,39 @@ mod test { run_program_assert_output(&program, "run_test", &[], 1u32.into()); } + #[test] + fn run_dict_get() { + let program = { + let mut generator = SierraGenerator::::default(); + + let u32_ty = generator.push_type_declaration::(&[]).clone(); + + generator.build(&[GenericArg::Type(u32_ty)]) + }; + + let dict = Value::Felt252Dict { + value: [ + (Felt252::from(1), 2u32.into()), + (Felt252::from(2), 1u32.into()), + ] + .into(), + debug_name: None, + }; + + let result = run_sierra_program(&program, &[dict, Value::Felt252(2.into())]).return_value; + + let dict_entry = Value::Felt252DictEntry { + dict: [ + (Felt252::from(1), 2u32.into()), + (Felt252::from(2), 1u32.into()), + ] + .into(), + key: 2.into(), + }; + + assert_eq!(result, jit_struct!(dict_entry, 1u32.into())); + } + #[test] fn run_dict_insert_ret_dict() { let program = load_cairo!( @@ -208,6 +266,24 @@ mod test { ); } + #[test] + fn run_dict_squash() { + let program = { + let mut generator = SierraGenerator::::default(); + + let u64_ty = generator.push_type_declaration::(&[]).clone(); + + generator.build(&[GenericArg::Type(u64_ty)]) + }; + + let dict = Value::Felt252Dict { + value: HashMap::new(), + debug_name: None, + }; + + run_sierra_program(&program, &[dict]); + } + #[test] fn run_dict_deserialize() { let program = load_cairo!( diff --git a/src/libfuncs/felt252_dict_entry.rs b/src/libfuncs/felt252_dict_entry.rs index e0a2fb2d3..67740e6ec 100644 --- a/src/libfuncs/felt252_dict_entry.rs +++ b/src/libfuncs/felt252_dict_entry.rs @@ -191,23 +191,21 @@ pub fn build_finalize<'ctx, 'this>( #[cfg(test)] mod test { - use crate::utils::test::{jit_dict, load_cairo, run_program, run_program_assert_output}; - - #[test] - fn run_dict_insert() { - let program = load_cairo!( - use traits::Default; - use dict::Felt252DictTrait; - - fn run_test() -> u32 { - let mut dict: Felt252Dict = Default::default(); - dict.insert(2, 1_u32); - dict.get(2) - } - ); + use cairo_lang_sierra::{ + extensions::{ + felt252_dict::{Felt252DictEntryFinalizeLibfunc, Felt252DictEntryGetLibfunc}, + int::unsigned::Uint32Type, + }, + program::GenericArg, + }; - run_program_assert_output(&program, "run_test", &[], 1u32.into()); - } + use crate::{ + utils::{ + sierra_gen::SierraGenerator, + test::{jit_dict, load_cairo, run_program_assert_output, run_sierra_program}, + }, + Value, + }; #[test] fn run_dict_insert_big() { @@ -227,75 +225,89 @@ mod test { #[test] fn run_dict_insert_ret_dict() { - let program = load_cairo!( - use traits::Default; - use dict::Felt252DictTrait; + let program = { + let mut geneator = SierraGenerator::::default(); - fn run_test() -> Felt252Dict { - let mut dict: Felt252Dict = Default::default(); - dict.insert(2, 1_u32); - dict - } - ); + let u32_ty = geneator.push_type_declaration::(&[]).clone(); + + geneator.build(&[GenericArg::Type(u32_ty)]) + }; + + let dict = Value::Felt252DictEntry { + dict: [(2.into(), 0.into())].into(), + key: 2.into(), + }; - run_program_assert_output( - &program, - "run_test", - &[], + let result = run_sierra_program(&program, &[dict, Value::Uint32(3)]).return_value; + + assert_eq!( jit_dict!( - 2 => 1u32 + 2 => 3u32 ), + result ); } #[test] fn run_dict_insert_multiple() { - let program = load_cairo!( - use traits::Default; - use dict::Felt252DictTrait; - - fn run_test() -> u32 { - let mut dict: Felt252Dict = Default::default(); - dict.insert(2, 1_u32); - dict.insert(3, 1_u32); - dict.insert(4, 1_u32); - dict.insert(5, 1_u32); - dict.insert(6, 1_u32); - dict.insert(7, 1_u32); - dict.insert(8, 1_u32); - dict.insert(9, 1_u32); - dict.insert(10, 1_u32); - dict.insert(11, 1_u32); - dict.insert(12, 1_u32); - dict.insert(13, 1_u32); - dict.insert(14, 1_u32); - dict.insert(15, 1_u32); - dict.insert(16, 1_u32); - dict.insert(17, 1_u32); - dict.insert(18, 1345432_u32); - dict.get(18) - } - ); - - run_program_assert_output(&program, "run_test", &[], 1345432_u32.into()); - } + let program_insert = { + let mut generator = SierraGenerator::::default(); + + let u32_ty = generator.push_type_declaration::(&[]).clone(); + + generator.build(&[GenericArg::Type(u32_ty)]) + }; + let program_get = { + let mut generator = SierraGenerator::::default(); + + let u32_ty = generator.push_type_declaration::(&[]).clone(); + + generator.build(&[GenericArg::Type(u32_ty)]) + }; + let dict_entry = Value::Felt252DictEntry { + dict: [(0.into(), 0.into())].into(), + key: 0.into(), + }; + let mut result_dict = { + let Value::Felt252Dict { mut value, .. } = + run_sierra_program(&program_insert, &[dict_entry, Value::Uint32(1)]).return_value + else { + panic!("Should be a Felt252Dict"); + }; - #[test] - fn run_dict_clone_ptr_update() { - let program = load_cairo!( - use core::dict::Felt252Dict; + // prepare next entry + value.insert((1).into(), 0.into()); - fn run_test() { - let mut dict: Felt252Dict = Default::default(); + value + }; - let snapshot = @dict; - dict.insert(1, 1); - drop(snapshot); + for i in 1..=16 { + let dict_entry = Value::Felt252DictEntry { + dict: result_dict, + key: i.into(), + }; + result_dict = { + let Value::Felt252Dict { mut value, .. } = + run_sierra_program(&program_insert, &[dict_entry, Value::Uint32(1)]) + .return_value + else { + panic!("Should be a Felt252Dict"); + }; + + // prepare next entry + value.insert((i + 1).into(), 0.into()); + + value + }; + } - dict.insert(2, 2); - } - ); + let dict_entry = Value::Felt252DictEntry { + dict: result_dict, + key: 17.into(), + }; + let result = run_sierra_program(&program_insert, &[dict_entry, Value::Uint32(1345432_u32)]) + .return_value; - run_program(&program, "run_test", &[]); + run_sierra_program(&program_get, &[result, Value::Felt252(17.into())]); } } diff --git a/src/runtime.rs b/src/runtime.rs index e8a86b283..8f5efc8a5 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -155,6 +155,14 @@ pub struct FeltDict { pub count: u64, } +#[cfg(test)] +#[repr(C)] +#[derive(Debug)] +pub struct FeltDictEntry { + pub dict: *const FeltDict, + pub value_ptr: *mut (), +} + impl Drop for FeltDict { fn drop(&mut self) { // Free the entries manually. diff --git a/src/types.rs b/src/types.rs index 0cdd6c840..1241fbc8b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -696,9 +696,7 @@ impl TypeBuilder for CoreTypeConcrete { .unwrap_or(Layout::from_size_align(0, 1)?), CoreTypeConcrete::Felt252Dict(_) => Layout::new::<*mut std::ffi::c_void>(), // ptr CoreTypeConcrete::Felt252DictEntry(_) => { - get_integer_layout(252) - .extend(Layout::new::<*mut std::ffi::c_void>())? - .0 + Layout::new::<*mut std::ffi::c_void>() .extend(Layout::new::<*mut std::ffi::c_void>())? .0 } diff --git a/src/utils.rs b/src/utils.rs index 1dda89185..cd918c362 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -622,6 +622,34 @@ pub mod test { .unwrap() } + // This function receives a sierra program and runs its first entrypoint + // It is used primarely along with the sierra generator + // The difference between this function and run_program is that the latter + // also receives the name of the entrypoint to run + pub fn run_sierra_program(program: &Program, args: &[Value]) -> ExecutionResult { + let entry_point_id = &program + .funcs + .first() + .expect("program entry point not found.") + .id; + + let context = NativeContext::new(); + + let module = context + .compile(program, false, Some(Default::default())) + .expect("Could not compile test program to MLIR."); + + let executor = JitNativeExecutor::from_native_module(module, OptLevel::Less).unwrap(); + executor + .invoke_dynamic_with_syscall_handler( + entry_point_id, + args, + Some(u64::MAX), + &mut StubSyscallHandler::default(), + ) + .unwrap() + } + #[track_caller] pub fn run_program_assert_output( program: &(String, Program), diff --git a/src/values.rs b/src/values.rs index 7e7c8ecf9..977d8b242 100644 --- a/src/values.rs +++ b/src/values.rs @@ -44,7 +44,8 @@ use std::{ /// The debug_name field on some variants is `Some` when receiving a [`Value`] as a result. /// /// A Boxed value or a non-null Nullable value is returned with it's inner value. -#[derive(Clone, Educe, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Educe)] +#[cfg_attr(not(test), derive(serde::Serialize, serde::Deserialize))] #[educe(Debug, Eq, PartialEq)] pub enum Value { Felt252(#[educe(Debug(method(std::fmt::Display::fmt)))] Felt), @@ -67,6 +68,11 @@ pub enum Value { #[educe(PartialEq(ignore))] debug_name: Option, }, + #[cfg(test)] + Felt252DictEntry { + dict: HashMap, + key: Felt, + }, Uint8(u8), Uint16(u16), Uint32(u32), @@ -83,7 +89,7 @@ pub enum Value { Secp256R1Point(Secp256r1Point), BoundedInt { value: Felt, - #[serde(with = "range_serde")] + #[cfg_attr(not(test), serde(with = "range_serde"))] range: Range, }, IntRange { @@ -170,6 +176,16 @@ impl Value { ) -> Result, Error> { let ty = registry.get_type(type_id)?; + match ty { + CoreTypeConcrete::Box(info) + | CoreTypeConcrete::NonZero(info) + | CoreTypeConcrete::Nullable(info) + | CoreTypeConcrete::Snapshot(info) => { + return self.to_ptr(arena, registry, &info.ty, find_dict_drop_override); + } + _ => {} + } + Ok(unsafe { match self { Self::Felt252(value) => { @@ -456,6 +472,75 @@ impl Value { )))? } } + #[cfg(test)] + Self::Felt252DictEntry { + dict, + key: entry_key, + } => { + if let CoreTypeConcrete::Felt252DictEntry(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 drop_fn = find_dict_drop_override(&info.ty); + + let mut felt_dict = FeltDict { + mappings: HashMap::with_capacity(dict.len()), + layout: elem_layout, + elements: if dict.is_empty() { + null_mut() + } else { + alloc(Layout::from_size_align_unchecked( + elem_layout.pad_to_align().size() * dict.len(), + elem_layout.align(), + )) + .cast() + }, + drop_fn, + count: 0, + }; + + let mut value_ptr = None; + for (key, value) in dict.iter() { + let key = key.to_bytes_le(); + let value = + value.to_ptr(arena, registry, &info.ty, find_dict_drop_override)?; + + let index = felt_dict.mappings.len(); + felt_dict.mappings.insert(key, index); + + if value_ptr.is_none() && key == entry_key.to_bytes_le() { + value_ptr = Some( + felt_dict + .elements + .byte_add(elem_layout.pad_to_align().size() * index), + ); + } + + std::ptr::copy_nonoverlapping( + value.cast::().as_ptr(), + felt_dict + .elements + .byte_add(elem_layout.pad_to_align().size() * index) + .cast(), + elem_layout.size(), + ); + } + + let felt_dict_entry = crate::runtime::FeltDictEntry { + dict: Rc::into_raw(Rc::new(felt_dict)), + value_ptr: value_ptr.unwrap(), + }; + + NonNull::new_unchecked(arena.alloc(felt_dict_entry) as *mut _).cast() + } else { + Err(Error::UnexpectedValue(format!( + "expected value of type {:?} but got a felt dict entry", + type_id.debug_name + )))? + } + } Self::Uint8(value) => { let ptr = arena.alloc_layout(Layout::new::()).cast(); *ptr.cast::().as_mut() = *value; @@ -747,9 +832,7 @@ impl Value { CoreTypeConcrete::Uint32(_) => Self::Uint32(*ptr.cast::().as_ref()), CoreTypeConcrete::Uint64(_) => Self::Uint64(*ptr.cast::().as_ref()), CoreTypeConcrete::Uint128(_) => Self::Uint128(*ptr.cast::().as_ref()), - CoreTypeConcrete::Uint128MulGuarantee(_) => { - native_panic!("todo: implement uint128mulguarantee from_ptr") - } + CoreTypeConcrete::Uint128MulGuarantee(_) => Self::Null, CoreTypeConcrete::Sint8(_) => Self::Sint8(*ptr.cast::().as_ref()), CoreTypeConcrete::Sint16(_) => Self::Sint16(*ptr.cast::().as_ref()), CoreTypeConcrete::Sint32(_) => Self::Sint32(*ptr.cast::().as_ref()), @@ -898,6 +981,54 @@ impl Value { debug_name: type_id.debug_name.as_ref().map(|x| x.to_string()), } } + #[cfg(test)] + CoreTypeConcrete::Felt252DictEntry(info) => { + let dict_entry = ptr.cast::().read(); + let dict = Rc::from_raw(dict_entry.dict); + + let mut map = HashMap::with_capacity(dict.mappings.len()); + + let mut entry_key = None; + for (&key, &index) in dict.mappings.iter() { + let mut key = key; + key[31] &= 0x0F; + + let key = Felt::from_bytes_le(&key); + if entry_key.is_none() + && dict_entry.value_ptr + == dict + .elements + .byte_add(dict.layout.pad_to_align().size() * index) + { + entry_key = Some(key); + } + + map.insert( + key, + Self::from_ptr( + NonNull::new_unchecked( + dict.elements + .byte_add(dict.layout.pad_to_align().size() * index), + ), + &info.ty, + registry, + should_drop, + )?, + ); + } + + if should_drop { + drop(dict); + } else { + forget(dict); + } + + Value::Felt252DictEntry { + dict: map, + key: entry_key.unwrap(), + } + } + #[cfg(not(test))] CoreTypeConcrete::Felt252DictEntry(_) => { native_panic!("unimplemented: should be impossible to return") } @@ -1892,6 +2023,7 @@ mod test { } } +#[cfg(not(test))] mod range_serde { use std::fmt;