cairo_lang_sierra_to_casm/environment/
frame_state.rsuse thiserror::Error;
use super::{ApTracking, ApTrackingBase};
#[derive(Error, Debug, Eq, PartialEq)]
pub enum FrameStateError {
#[error("InvalidTransition")]
InvalidTransition,
#[error("alloc_local is not allowed at this point.")]
InvalidAllocLocal(FrameState),
#[error("finalize_locals is not allowed at this point.")]
InvalidFinalizeLocals(FrameState),
#[error("locals were allocated but finalize_locals was not called.")]
FinalizeLocalsMissing(FrameState),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum FrameState {
BeforeAllocation,
Allocating { allocated: usize, locals_start_ap_offset: usize },
Finalized { allocated: usize },
}
pub fn handle_finalize_locals(
frame_state: FrameState,
ap_tracking: ApTracking,
) -> Result<(usize, FrameState), FrameStateError> {
match frame_state {
FrameState::BeforeAllocation => {
if matches!(
ap_tracking,
ApTracking::Enabled { ap_change: _, base: ApTrackingBase::FunctionStart }
) {
Ok((0, FrameState::Finalized { allocated: 0 }))
} else {
Err(FrameStateError::InvalidFinalizeLocals(frame_state))
}
}
FrameState::Allocating { allocated, locals_start_ap_offset } => {
if matches!(
ap_tracking,
ApTracking::Enabled { ap_change, base: ApTrackingBase::FunctionStart }
if ap_change == locals_start_ap_offset
) {
Ok((allocated, FrameState::Finalized { allocated }))
} else {
Err(FrameStateError::InvalidFinalizeLocals(frame_state))
}
}
FrameState::Finalized { .. } => Err(FrameStateError::InvalidFinalizeLocals(frame_state)),
}
}
pub fn handle_alloc_local(
frame_state: FrameState,
ap_tracking: ApTracking,
allocation_size: usize,
) -> Result<(usize, FrameState), FrameStateError> {
match frame_state {
FrameState::BeforeAllocation => {
if let ApTracking::Enabled { ap_change, base: ApTrackingBase::FunctionStart } =
ap_tracking
{
Ok((
ap_change,
FrameState::Allocating {
allocated: allocation_size,
locals_start_ap_offset: ap_change,
},
))
} else {
Err(FrameStateError::InvalidAllocLocal(frame_state))
}
}
FrameState::Allocating { allocated, locals_start_ap_offset } => {
if matches!(
ap_tracking,
ApTracking::Enabled { ap_change, base: ApTrackingBase::FunctionStart }
if ap_change == locals_start_ap_offset
) {
Ok((
locals_start_ap_offset + allocated,
FrameState::Allocating {
allocated: allocated + allocation_size,
locals_start_ap_offset,
},
))
} else {
Err(FrameStateError::InvalidAllocLocal(frame_state))
}
}
FrameState::Finalized { .. } => Err(FrameStateError::InvalidAllocLocal(frame_state)),
}
}
pub fn validate_final_frame_state(frame_state: &FrameState) -> Result<(), FrameStateError> {
if matches!(frame_state, FrameState::Allocating { .. }) {
Err(FrameStateError::FinalizeLocalsMissing(frame_state.clone()))
} else {
Ok(())
}
}