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 };
65 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
66 let ext_bytecode = ExtBytecode::new(bytecode_obj);
67 let mut interpreter =
68 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
69
70 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
71
72 let return_memory_offset = match &action {
74 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
75 Some(call_inputs.return_memory_offset.clone())
76 }
77 other => panic!("expected NewFrame(Call), got {other:?}"),
78 };
79
80 let call_result = InterpreterResult {
82 result: InstructionResult::Stop,
83 output: Bytes::new(),
84 gas: Gas::new(0),
85 };
86 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
87
88 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
90
91 match &action {
92 InterpreterAction::Return(result) => {
93 assert_eq!(
94 result.result,
95 InstructionResult::Stop,
96 "expected Stop after resume, got {:?}",
97 result.result
98 );
99 assert_eq!(interpreter.stack.len(), 2, "stack should have 2 items");
102 assert_eq!(
103 interpreter.stack.data()[1],
104 U256::from(0x42),
105 "top of stack should be 0x42 (the marker value pushed after CALL)"
106 );
107 }
108 other => panic!("expected Return after resume, got {other:?}"),
109 }
110}
111
112fn run_call_then_return<B: Backend>(compiler: &mut EvmCompiler<B>) {
118 #[rustfmt::skip]
119 let bytecode: &[u8] = &[
120 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, ];
135
136 unsafe { compiler.clear() }.unwrap();
137 compiler.inspect_stack(true);
138 let f = unsafe { compiler.jit("resume_return", bytecode, DEF_SPEC) }.unwrap();
139
140 let mut host = TestHost::new();
141 let input = InputsImpl {
142 target_address: DEF_ADDR,
143 bytecode_address: None,
144 caller_address: DEF_CALLER,
145 input: CallInput::Bytes(Bytes::from_static(DEF_CD)),
146 call_value: DEF_VALUE,
147 };
148 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
149 let ext_bytecode = ExtBytecode::new(bytecode_obj);
150 let mut interpreter =
151 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
152
153 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
155 let return_memory_offset = match &action {
156 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
157 Some(call_inputs.return_memory_offset.clone())
158 }
159 other => panic!("expected NewFrame(Call), got {other:?}"),
160 };
161
162 let call_result = InterpreterResult {
164 result: InstructionResult::Stop,
165 output: Bytes::new(),
166 gas: Gas::new(0),
167 };
168 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
169
170 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
172
173 match &action {
174 InterpreterAction::Return(result) => {
175 assert_eq!(result.result, InstructionResult::Return);
176 assert_eq!(result.output.len(), 32, "expected 32-byte return output");
177 }
178 other => panic!("expected Return after resume, got {other:?}"),
179 }
180}
181
182fn run_call_returndatasize<B: Backend>(compiler: &mut EvmCompiler<B>) {
190 #[rustfmt::skip]
191 let bytecode: &[u8] = &[
192 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,
209 ];
210
211 unsafe { compiler.clear() }.unwrap();
212 compiler.inspect_stack(true);
213 let f = unsafe { compiler.jit("resume_rds", bytecode, DEF_SPEC) }.unwrap();
214
215 let mut host = TestHost::new();
216 let input = InputsImpl {
217 target_address: DEF_ADDR,
218 bytecode_address: None,
219 caller_address: DEF_CALLER,
220 input: CallInput::Bytes(Bytes::from_static(DEF_CD)),
221 call_value: DEF_VALUE,
222 };
223 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
224 let ext_bytecode = ExtBytecode::new(bytecode_obj);
225 let mut interpreter =
226 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
227
228 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
230 let return_memory_offset = match &action {
231 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
232 Some(call_inputs.return_memory_offset.clone())
233 }
234 other => panic!("expected NewFrame(Call), got {other:?}"),
235 };
236
237 let return_data = Bytes::from_static(&[0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0x42]);
239 let call_result = InterpreterResult {
240 result: InstructionResult::Stop,
241 output: return_data,
242 gas: Gas::new(0),
243 };
244 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
245
246 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
248
249 match &action {
250 InterpreterAction::Return(result) => {
251 assert_eq!(result.result, InstructionResult::Return);
252 assert_eq!(result.output.len(), 32, "expected 32-byte return output");
253 let value = U256::from_be_slice(&result.output);
255 assert_eq!(
256 value,
257 U256::from(7),
258 "RETURNDATASIZE should be 7 after CALL with 7-byte return data"
259 );
260 }
261 other => panic!("expected Return after resume, got {other:?}"),
262 }
263}
264
265fn run_call_pop_push_sload_stack_len<B: Backend>(compiler: &mut EvmCompiler<B>) {
279 const JUMPDEST_PC: u8 = 21;
291 #[rustfmt::skip]
292 let bytecode: &[u8] = &[
293 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, ];
326
327 unsafe { compiler.clear() }.unwrap();
328 compiler.inspect_stack(true);
329 let f = unsafe { compiler.jit("pop_push_sload", bytecode, DEF_SPEC) }.unwrap();
330
331 let mut host = TestHost::new();
332 let input = InputsImpl {
333 target_address: DEF_ADDR,
334 bytecode_address: None,
335 caller_address: DEF_CALLER,
336 input: CallInput::Bytes(Bytes::from_static(DEF_CD)),
337 call_value: DEF_VALUE,
338 };
339 let bytecode_obj = revm_bytecode::Bytecode::new_raw(Bytes::copy_from_slice(bytecode));
340 let ext_bytecode = ExtBytecode::new(bytecode_obj);
341 let mut interpreter =
342 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, DEF_SPEC, DEF_GAS_LIMIT);
343
344 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
346 let return_memory_offset = match &action {
347 InterpreterAction::NewFrame(FrameInput::Call(call_inputs)) => {
348 Some(call_inputs.return_memory_offset.clone())
349 }
350 other => panic!("expected NewFrame(Call), got {other:?}"),
351 };
352
353 let call_result = InterpreterResult {
355 result: InstructionResult::Stop,
356 output: Bytes::new(),
357 gas: Gas::new(0),
358 };
359 insert_call_outcome_test(&mut interpreter, call_result, return_memory_offset);
360
361 let action = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) };
363
364 match &action {
365 InterpreterAction::Return(result) => {
366 assert_eq!(
367 result.result,
368 InstructionResult::Return,
369 "expected Return, got {:?}",
370 result.result
371 );
372 assert_eq!(result.output.len(), 32, "expected 32-byte return output");
373 let value = U256::from_be_slice(&result.output);
374 assert_eq!(
375 value,
376 U256::from(0xBEEF),
377 "returned value should be the marker 0xBEEF; \
378 stale len.addr causes the JUMPDEST section to misalign the stack"
379 );
380 }
381 other => panic!("expected Return after resume, got {other:?}"),
382 }
383}