ethrex_levm/opcode_handlers/
dup.rs1use crate::{
7 errors::{ExceptionalHalt, OpcodeResult, VMError},
8 gas_cost,
9 opcode_handlers::OpcodeHandler,
10 vm::VM,
11};
12
13pub struct OpDupHandler<const N: usize>;
15impl<const N: usize> OpcodeHandler for OpDupHandler<N> {
16 #[inline(always)]
17 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
18 vm.current_call_frame
19 .increase_consumed_gas(gas_cost::DUPN)?;
20
21 vm.current_call_frame.stack.dup::<N>()?;
22
23 Ok(OpcodeResult::Continue)
24 }
25}
26
27pub struct OpDupNHandler;
29impl OpcodeHandler for OpDupNHandler {
30 #[inline(always)]
31 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
32 vm.current_call_frame
33 .increase_consumed_gas(gas_cost::DUPN)?;
34
35 let relative_offset = vm
36 .current_call_frame
37 .bytecode
38 .dispatch_buf()
39 .get(vm.current_call_frame.pc)
40 .copied()
41 .unwrap_or_default();
42
43 if (0x5B..0x80).contains(&relative_offset) {
48 return Err(ExceptionalHalt::InvalidOpcode.into());
49 }
50 let relative_offset = relative_offset.wrapping_add(145);
51
52 let absolute_offset = vm
56 .current_call_frame
57 .stack
58 .offset
59 .checked_add(usize::from(relative_offset).wrapping_sub(1))
60 .ok_or(ExceptionalHalt::StackUnderflow)?;
61
62 if absolute_offset >= vm.current_call_frame.stack.values.len() {
64 return Err(ExceptionalHalt::StackUnderflow.into());
65 }
66
67 #[expect(unsafe_code, reason = "bound already checked")]
68 vm.current_call_frame.stack.push(unsafe {
69 *vm.current_call_frame
70 .stack
71 .values
72 .get_unchecked(absolute_offset)
73 })?;
74
75 vm.current_call_frame.pc = vm.current_call_frame.pc.wrapping_add(1);
76 Ok(OpcodeResult::Continue)
77 }
78}