1use super::{
12 DEF_ADDR, DEF_CALLER, DEF_CD, DEF_GAS_LIMIT, DEF_SPEC, DEF_VALUE, TestHost,
13 insert_call_outcome_test,
14};
15use crate::{Backend, EvmCompiler};
16use revm_bytecode::opcode as op;
17use revm_interpreter::{
18 CallInput, FrameInput, Gas, InputsImpl, InstructionResult, Interpreter, InterpreterAction,
19 InterpreterResult, SharedMemory, interpreter::ExtBytecode,
20};
21use revm_primitives::{Bytes, U256};
22
23matrix_tests!(call_then_push = |compiler| run_call_then_push(compiler));
24matrix_tests!(call_then_return = |compiler| run_call_then_return(compiler));
25matrix_tests!(call_returndatasize = |compiler| run_call_returndatasize(compiler));
26matrix_tests!(
27 call_pop_push_sload_stack_len = |compiler| run_call_pop_push_sload_stack_len(compiler)
28);
29
30fn run_call_then_push<B: Backend>(compiler: &mut EvmCompiler<B>) {
36 #[rustfmt::skip]
37 let bytecode: &[u8] = &[
38 op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0x69, op::GAS, op::CALL, op::PUSH1, 0x42, op::STOP,
50 ];
51
52 unsafe { compiler.clear() }.unwrap();
53 compiler.inspect_stack(true);
54 let f = unsafe { compiler.jit("resume_call", bytecode, DEF_SPEC) }.unwrap();
55
56 let mut host = TestHost::new();
58 let input = InputsImpl {
59 target_address: DEF_ADDR,
60 bytecode_address: None,
61 caller_address: DEF_CALLER,
62 input: CallInput::Bytes(Bytes::from_static(DEF_CD)),
63 call_value: DEF_VALUE,
64 depth: 0,
65 };
66 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
67 let ext_bytecode = ExtBytecode::new(bytecode_obj);
68 let mut interpreter =
69 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
70
71 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
72
73 let return_memory_offset = match &action {
75 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
76 Some(call_inputs.return_memory_offset.clone())
77 }
78 other => panic!("expected NewFrame(Call), got {other:?}"),
79 };
80
81 let call_result = InterpreterResult {
83 result: InstructionResult::Stop,
84 output: Bytes::new(),
85 gas: Gas::new(0),
86 };
87 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
88
89 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
91
92 match &action {
93 InterpreterAction::Return(result) => {
94 assert_eq!(
95 result.result,
96 InstructionResult::Stop,
97 "expected Stop after resume, got {:?}",
98 result.result
99 );
100 assert_eq!(interpreter.stack.len(), 2, "stack should have 2 items");
103 assert_eq!(
104 interpreter.stack.data()[1],
105 U256::from(0x42),
106 "top of stack should be 0x42 (the marker value pushed after CALL)"
107 );
108 }
109 other => panic!("expected Return after resume, got {other:?}"),
110 }
111}
112
113fn run_call_then_return<B: Backend>(compiler: &mut EvmCompiler<B>) {
119 #[rustfmt::skip]
120 let bytecode: &[u8] = &[
121 op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0x69, op::GAS, op::CALL, op::POP, op::PUSH1, 32, op::PUSH0, op::RETURN, ];
136
137 unsafe { compiler.clear() }.unwrap();
138 compiler.inspect_stack(true);
139 let f = unsafe { compiler.jit("resume_return", bytecode, DEF_SPEC) }.unwrap();
140
141 let mut host = TestHost::new();
142 let input = InputsImpl {
143 target_address: DEF_ADDR,
144 bytecode_address: None,
145 caller_address: DEF_CALLER,
146 input: CallInput::Bytes(Bytes::from_static(DEF_CD)),
147 call_value: DEF_VALUE,
148 depth: 0,
149 };
150 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
151 let ext_bytecode = ExtBytecode::new(bytecode_obj);
152 let mut interpreter =
153 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
154
155 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
157 let return_memory_offset = match &action {
158 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
159 Some(call_inputs.return_memory_offset.clone())
160 }
161 other => panic!("expected NewFrame(Call), got {other:?}"),
162 };
163
164 let call_result = InterpreterResult {
166 result: InstructionResult::Stop,
167 output: Bytes::new(),
168 gas: Gas::new(0),
169 };
170 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
171
172 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
174
175 match &action {
176 InterpreterAction::Return(result) => {
177 assert_eq!(result.result, InstructionResult::Return);
178 assert_eq!(result.output.len(), 32, "expected 32-byte return output");
179 }
180 other => panic!("expected Return after resume, got {other:?}"),
181 }
182}
183
184fn run_call_returndatasize<B: Backend>(compiler: &mut EvmCompiler<B>) {
192 #[rustfmt::skip]
193 let bytecode: &[u8] = &[
194 op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0x69, op::GAS, op::CALL, op::POP, op::RETURNDATASIZE, op::PUSH0, op::MSTORE, op::PUSH1, 32, op::PUSH0, op::RETURN,
211 ];
212
213 unsafe { compiler.clear() }.unwrap();
214 compiler.inspect_stack(true);
215 let f = unsafe { compiler.jit("resume_rds", bytecode, DEF_SPEC) }.unwrap();
216
217 let mut host = TestHost::new();
218 let input = InputsImpl {
219 target_address: DEF_ADDR,
220 bytecode_address: None,
221 caller_address: DEF_CALLER,
222 input: CallInput::Bytes(Bytes::from_static(DEF_CD)),
223 call_value: DEF_VALUE,
224 depth: 0,
225 };
226 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
227 let ext_bytecode = ExtBytecode::new(bytecode_obj);
228 let mut interpreter =
229 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
230
231 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
233 let return_memory_offset = match &action {
234 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
235 Some(call_inputs.return_memory_offset.clone())
236 }
237 other => panic!("expected NewFrame(Call), got {other:?}"),
238 };
239
240 let return_data = Bytes::from_static(&[0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0x42]);
242 let call_result = InterpreterResult {
243 result: InstructionResult::Stop,
244 output: return_data,
245 gas: Gas::new(0),
246 };
247 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
248
249 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
251
252 match &action {
253 InterpreterAction::Return(result) => {
254 assert_eq!(result.result, InstructionResult::Return);
255 assert_eq!(result.output.len(), 32, "expected 32-byte return output");
256 let value = U256::from_be_slice(&result.output);
258 assert_eq!(
259 value,
260 U256::from(7),
261 "RETURNDATASIZE should be 7 after CALL with 7-byte return data"
262 );
263 }
264 other => panic!("expected Return after resume, got {other:?}"),
265 }
266}
267
268fn run_call_pop_push_sload_stack_len<B: Backend>(compiler: &mut EvmCompiler<B>) {
282 const JUMPDEST_PC: u8 = 21;
294 #[rustfmt::skip]
295 let bytecode: &[u8] = &[
296 op::PUSH2, 0xBE, 0xEF, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, 0x69, op::GAS, op::CALL, op::POP, op::PUSH1, 0x05, op::SLOAD, op::JUMPDEST, op::POP, op::PUSH0, op::MSTORE, op::PUSH1, 32, op::PUSH0, op::RETURN, op::JUMPDEST, op::PUSH1, 0, op::PUSH1, 0, op::PUSH1, JUMPDEST_PC, op::JUMP, ];
329
330 unsafe { compiler.clear() }.unwrap();
331 compiler.inspect_stack(true);
332 let f = unsafe { compiler.jit("pop_push_sload", bytecode, DEF_SPEC) }.unwrap();
333
334 let mut host = TestHost::new();
335 let input = InputsImpl {
336 target_address: DEF_ADDR,
337 bytecode_address: None,
338 caller_address: DEF_CALLER,
339 input: CallInput::Bytes(Bytes::from_static(DEF_CD)),
340 call_value: DEF_VALUE,
341 depth: 0,
342 };
343 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
344 let ext_bytecode = ExtBytecode::new(bytecode_obj);
345 let mut interpreter =
346 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
347
348 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
350 let return_memory_offset = match &action {
351 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
352 Some(call_inputs.return_memory_offset.clone())
353 }
354 other => panic!("expected NewFrame(Call), got {other:?}"),
355 };
356
357 let call_result = InterpreterResult {
359 result: InstructionResult::Stop,
360 output: Bytes::new(),
361 gas: Gas::new(0),
362 };
363 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
364
365 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
367
368 match &action {
369 InterpreterAction::Return(result) => {
370 assert_eq!(
371 result.result,
372 InstructionResult::Return,
373 "expected Return, got {:?}",
374 result.result
375 );
376 assert_eq!(result.output.len(), 32, "expected 32-byte return output");
377 let value = U256::from_be_slice(&result.output);
378 assert_eq!(
379 value,
380 U256::from(0xBEEF),
381 "returned value should be the marker 0xBEEF; \
382 stale len.addr causes the JUMPDEST section to misalign the stack"
383 );
384 }
385 other => panic!("expected Return after resume, got {other:?}"),
386 }
387}