mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
						commit
						bbaf8a4eb4
					
				@ -188,6 +188,21 @@ enum class Instruction: uint8_t
 | 
				
			|||||||
	SELFDESTRUCT = 0xff	///< halt execution and register account for later deletion
 | 
						SELFDESTRUCT = 0xff	///< halt execution and register account for later deletion
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @returns true if the instruction is of the CALL opcode family
 | 
				
			||||||
 | 
					constexpr bool isCallInstruction(Instruction _inst) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (_inst)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							case Instruction::CALL:
 | 
				
			||||||
 | 
							case Instruction::CALLCODE:
 | 
				
			||||||
 | 
							case Instruction::DELEGATECALL:
 | 
				
			||||||
 | 
							case Instruction::STATICCALL:
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// @returns true if the instruction is a PUSH
 | 
					/// @returns true if the instruction is a PUSH
 | 
				
			||||||
inline bool isPushInstruction(Instruction _inst)
 | 
					inline bool isPushInstruction(Instruction _inst)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -112,6 +112,7 @@ string EwasmTranslationTest::interpret()
 | 
				
			|||||||
			state,
 | 
								state,
 | 
				
			||||||
			WasmDialect{},
 | 
								WasmDialect{},
 | 
				
			||||||
			*m_object->code,
 | 
								*m_object->code,
 | 
				
			||||||
 | 
								/*disableExternalCalls=*/true,
 | 
				
			||||||
			/*disableMemoryTracing=*/false
 | 
								/*disableMemoryTracing=*/false
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -51,6 +51,7 @@ YulInterpreterTest::YulInterpreterTest(string const& _filename):
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	m_source = m_reader.source();
 | 
						m_source = m_reader.source();
 | 
				
			||||||
	m_expectation = m_reader.simpleExpectations();
 | 
						m_expectation = m_reader.simpleExpectations();
 | 
				
			||||||
 | 
						m_simulateExternalCallsToSelf = m_reader.boolSetting("simulateExternalCall", false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
 | 
					TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
 | 
				
			||||||
@ -98,7 +99,8 @@ string YulInterpreterTest::interpret()
 | 
				
			|||||||
			state,
 | 
								state,
 | 
				
			||||||
			EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}),
 | 
								EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}),
 | 
				
			||||||
			*m_ast,
 | 
								*m_ast,
 | 
				
			||||||
			/*disableMemoryTracing=*/false
 | 
								/*disableExternalCalls=*/ !m_simulateExternalCallsToSelf,
 | 
				
			||||||
 | 
								/*disableMemoryTracing=*/ false
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	catch (InterpreterTerminatedGeneric const&)
 | 
						catch (InterpreterTerminatedGeneric const&)
 | 
				
			||||||
 | 
				
			|||||||
@ -47,6 +47,7 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	std::shared_ptr<Block> m_ast;
 | 
						std::shared_ptr<Block> m_ast;
 | 
				
			||||||
	std::shared_ptr<AsmAnalysisInfo> m_analysisInfo;
 | 
						std::shared_ptr<AsmAnalysisInfo> m_analysisInfo;
 | 
				
			||||||
 | 
						bool m_simulateExternalCallsToSelf = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								test/libyul/yulInterpreterTests/external_call_to_self.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								test/libyul/yulInterpreterTests/external_call_to_self.yul
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						mstore(0x40, 0x42)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if iszero(calldatasize()) {
 | 
				
			||||||
 | 
							let x := call(gas(), address(), 0, 0x40, 0x20, 0x100, 0x20)
 | 
				
			||||||
 | 
							sstore(0x64, calldataload(0))
 | 
				
			||||||
 | 
							sstore(0x100, x)
 | 
				
			||||||
 | 
							return(0x0, 0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return(0x40, 0x20)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// ====
 | 
				
			||||||
 | 
					// simulateExternalCall: true
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// Trace:
 | 
				
			||||||
 | 
					//   CALL(153, 0x11111111, 0, 64, 32, 256, 32)
 | 
				
			||||||
 | 
					//   RETURN(0, 0)
 | 
				
			||||||
 | 
					// Memory dump:
 | 
				
			||||||
 | 
					//     40: 0000000000000000000000000000000000000000000000000000000000000042
 | 
				
			||||||
 | 
					//    100: 0000000000000000000000000000000000000000000000000000000000000042
 | 
				
			||||||
 | 
					// Storage dump:
 | 
				
			||||||
 | 
					//   0000000000000000000000000000000000000000000000000000000000000100: 0000000000000000000000000000000000000000000000000000000000000001
 | 
				
			||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	let x := call(gas(), 0x45, 0x5, 0, 0x20, 0x30, 0x20)
 | 
						let x := call(gas(), 0x45, 0x5, 0, 0x20, 0x30, 0x20)
 | 
				
			||||||
	sstore(100, x)
 | 
						sstore(0x64, x)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
// ----
 | 
					// ----
 | 
				
			||||||
// Trace:
 | 
					// Trace:
 | 
				
			||||||
@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						let x := callcode(gas(), 0x45, 0x5, 0, 0x20, 0x30, 0x20)
 | 
				
			||||||
 | 
						sstore(100, x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// Trace:
 | 
				
			||||||
 | 
					//   CALLCODE(153, 69, 5, 0, 32, 48, 32)
 | 
				
			||||||
 | 
					// Memory dump:
 | 
				
			||||||
 | 
					// Storage dump:
 | 
				
			||||||
 | 
					//   0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001
 | 
				
			||||||
@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						let x := delegatecall(gas(), 0x45, 0, 0x20, 0x30, 0x20)
 | 
				
			||||||
 | 
						sstore(100, x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// Trace:
 | 
				
			||||||
 | 
					//   DELEGATECALL(153, 69, 0, 32, 48, 32)
 | 
				
			||||||
 | 
					// Memory dump:
 | 
				
			||||||
 | 
					// Storage dump:
 | 
				
			||||||
 | 
					//   0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001
 | 
				
			||||||
@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						let x := staticcall(gas(), 0x45, 0, 0x20, 0x30, 0x20)
 | 
				
			||||||
 | 
						sstore(0x64, x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// ====
 | 
				
			||||||
 | 
					// EVMVersion: >=byzantium
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// Trace:
 | 
				
			||||||
 | 
					//   STATICCALL(153, 69, 0, 32, 48, 32)
 | 
				
			||||||
 | 
					// Memory dump:
 | 
				
			||||||
 | 
					// Storage dump:
 | 
				
			||||||
 | 
					//   0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001
 | 
				
			||||||
@ -53,7 +53,7 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
 | 
				
			|||||||
	TerminationReason reason = TerminationReason::None;
 | 
						TerminationReason reason = TerminationReason::None;
 | 
				
			||||||
	try
 | 
						try
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Interpreter::run(state, _dialect, *_ast, _disableMemoryTracing);
 | 
							Interpreter::run(state, _dialect, *_ast, true, _disableMemoryTracing);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	catch (StepLimitReached const&)
 | 
						catch (StepLimitReached const&)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,8 @@ set(sources
 | 
				
			|||||||
	EwasmBuiltinInterpreter.cpp
 | 
						EwasmBuiltinInterpreter.cpp
 | 
				
			||||||
	Interpreter.h
 | 
						Interpreter.h
 | 
				
			||||||
	Interpreter.cpp
 | 
						Interpreter.cpp
 | 
				
			||||||
 | 
						Inspector.h
 | 
				
			||||||
 | 
						Inspector.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_library(yulInterpreter ${sources})
 | 
					add_library(yulInterpreter ${sources})
 | 
				
			||||||
 | 
				
			|||||||
@ -70,6 +70,10 @@ u256 readZeroExtended(bytes const& _data, u256 const& _offset)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace solidity::yul::test
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
 | 
					/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
 | 
				
			||||||
/// @a _target at offset @a _targetOffset. Behaves as if @a _source would
 | 
					/// @a _target at offset @a _targetOffset. Behaves as if @a _source would
 | 
				
			||||||
/// continue with an infinite sequence of zero bytes beyond its end.
 | 
					/// continue with an infinite sequence of zero bytes beyond its end.
 | 
				
			||||||
@ -185,7 +189,7 @@ u256 EVMInstructionInterpreter::eval(
 | 
				
			|||||||
			return u256("0x1234cafe1234cafe1234cafe") + arg[0];
 | 
								return u256("0x1234cafe1234cafe1234cafe") + arg[0];
 | 
				
			||||||
		uint64_t offset = uint64_t(arg[0] & uint64_t(-1));
 | 
							uint64_t offset = uint64_t(arg[0] & uint64_t(-1));
 | 
				
			||||||
		uint64_t size = uint64_t(arg[1] & uint64_t(-1));
 | 
							uint64_t size = uint64_t(arg[1] & uint64_t(-1));
 | 
				
			||||||
		return u256(keccak256(readMemory(offset, size)));
 | 
							return u256(keccak256(m_state.readMemory(offset, size)));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	case Instruction::ADDRESS:
 | 
						case Instruction::ADDRESS:
 | 
				
			||||||
		return h256(m_state.address, h256::AlignRight);
 | 
							return h256(m_state.address, h256::AlignRight);
 | 
				
			||||||
@ -322,24 +326,34 @@ u256 EVMInstructionInterpreter::eval(
 | 
				
			|||||||
		return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff");
 | 
							return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff");
 | 
				
			||||||
	case Instruction::CALL:
 | 
						case Instruction::CALL:
 | 
				
			||||||
	case Instruction::CALLCODE:
 | 
						case Instruction::CALLCODE:
 | 
				
			||||||
		// TODO assign returndata
 | 
					 | 
				
			||||||
		accessMemory(arg[3], arg[4]);
 | 
							accessMemory(arg[3], arg[4]);
 | 
				
			||||||
		accessMemory(arg[5], arg[6]);
 | 
							accessMemory(arg[5], arg[6]);
 | 
				
			||||||
		logTrace(_instruction, arg);
 | 
							logTrace(_instruction, arg);
 | 
				
			||||||
		return arg[0] & 1;
 | 
							// Randomly fail based on the called address if it isn't a call to self.
 | 
				
			||||||
 | 
							// Used for fuzzing.
 | 
				
			||||||
 | 
							return (
 | 
				
			||||||
 | 
								(arg[0] > 0) &&
 | 
				
			||||||
 | 
								(arg[1] == util::h160::Arith(m_state.address) || (arg[1] & 1))
 | 
				
			||||||
 | 
							) ? 1 : 0;
 | 
				
			||||||
	case Instruction::DELEGATECALL:
 | 
						case Instruction::DELEGATECALL:
 | 
				
			||||||
	case Instruction::STATICCALL:
 | 
						case Instruction::STATICCALL:
 | 
				
			||||||
		accessMemory(arg[2], arg[3]);
 | 
							accessMemory(arg[2], arg[3]);
 | 
				
			||||||
		accessMemory(arg[4], arg[5]);
 | 
							accessMemory(arg[4], arg[5]);
 | 
				
			||||||
		logTrace(_instruction, arg);
 | 
							logTrace(_instruction, arg);
 | 
				
			||||||
		return 0;
 | 
					
 | 
				
			||||||
 | 
							// Randomly fail based on the called address if it isn't a call to self.
 | 
				
			||||||
 | 
							// Used for fuzzing.
 | 
				
			||||||
 | 
							return (
 | 
				
			||||||
 | 
								(arg[0] > 0) &&
 | 
				
			||||||
 | 
								(arg[1] == util::h160::Arith(m_state.address) || (arg[1] & 1))
 | 
				
			||||||
 | 
							) ? 1 : 0;
 | 
				
			||||||
	case Instruction::RETURN:
 | 
						case Instruction::RETURN:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		bytes data;
 | 
							m_state.returndata = {};
 | 
				
			||||||
		if (accessMemory(arg[0], arg[1]))
 | 
							if (accessMemory(arg[0], arg[1]))
 | 
				
			||||||
			data = readMemory(arg[0], arg[1]);
 | 
								m_state.returndata = m_state.readMemory(arg[0], arg[1]);
 | 
				
			||||||
		logTrace(_instruction, arg, data);
 | 
							logTrace(_instruction, arg, m_state.returndata);
 | 
				
			||||||
		BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
 | 
							BOOST_THROW_EXCEPTION(ExplicitlyTerminatedWithReturn());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	case Instruction::REVERT:
 | 
						case Instruction::REVERT:
 | 
				
			||||||
		accessMemory(arg[0], arg[1]);
 | 
							accessMemory(arg[0], arg[1]);
 | 
				
			||||||
@ -475,6 +489,8 @@ u256 EVMInstructionInterpreter::evalBuiltin(
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						else if (fun == "memoryguard")
 | 
				
			||||||
 | 
							return _evaluatedArguments.at(0);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		yulAssert(false, "Unknown builtin: " + fun);
 | 
							yulAssert(false, "Unknown builtin: " + fun);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
@ -497,18 +513,9 @@ bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _s
 | 
				
			|||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bytes EVMInstructionInterpreter::readMemory(u256 const& _offset, u256 const& _size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	yulAssert(_size <= 0xffff, "Too large read.");
 | 
					 | 
				
			||||||
	bytes data(size_t(_size), uint8_t(0));
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < data.size(); ++i)
 | 
					 | 
				
			||||||
		data[i] = m_state.memory[_offset + i];
 | 
					 | 
				
			||||||
	return data;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
u256 EVMInstructionInterpreter::readMemoryWord(u256 const& _offset)
 | 
					u256 EVMInstructionInterpreter::readMemoryWord(u256 const& _offset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return u256(h256(readMemory(_offset, 32)));
 | 
						return u256(h256(m_state.readMemory(_offset, 32)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EVMInstructionInterpreter::writeMemoryWord(u256 const& _offset, u256 const& _value)
 | 
					void EVMInstructionInterpreter::writeMemoryWord(u256 const& _offset, u256 const& _value)
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,14 @@ struct BuiltinFunctionForEVM;
 | 
				
			|||||||
namespace solidity::yul::test
 | 
					namespace solidity::yul::test
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
 | 
				
			||||||
 | 
					/// @a _target at offset @a _targetOffset. Behaves as if @a _source would
 | 
				
			||||||
 | 
					/// continue with an infinite sequence of zero bytes beyond its end.
 | 
				
			||||||
 | 
					void copyZeroExtended(
 | 
				
			||||||
 | 
						std::map<u256, uint8_t>& _target, bytes const& _source,
 | 
				
			||||||
 | 
						size_t _targetOffset, size_t _sourceOffset, size_t _size
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct InterpreterState;
 | 
					struct InterpreterState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										156
									
								
								test/tools/yulInterpreter/Inspector.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								test/tools/yulInterpreter/Inspector.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
						This file is part of solidity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						solidity is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
						it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
						the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
						(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						solidity is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
						but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
						MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
						GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
						along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-3.0
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Yul interpreter.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <test/tools/yulInterpreter/Inspector.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <boost/algorithm/string/predicate.hpp>
 | 
				
			||||||
 | 
					#include <boost/algorithm/string.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace solidity;
 | 
				
			||||||
 | 
					using namespace solidity::yul;
 | 
				
			||||||
 | 
					using namespace solidity::yul::test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void printVariable(YulString const& _name, u256 const& _value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cout << "\t" << _name.str() << " = " << _value.str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (_value != 0)
 | 
				
			||||||
 | 
							cout << " (" << toCompactHexWithPrefix(_value) << ")";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cout << endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void InspectedInterpreter::run(
 | 
				
			||||||
 | 
						std::shared_ptr<Inspector> _inspector,
 | 
				
			||||||
 | 
						InterpreterState& _state,
 | 
				
			||||||
 | 
						Dialect const& _dialect,
 | 
				
			||||||
 | 
						Block const& _ast,
 | 
				
			||||||
 | 
						bool _disableExternalCalls,
 | 
				
			||||||
 | 
						bool _disableMemoryTrace
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Scope scope;
 | 
				
			||||||
 | 
						InspectedInterpreter{_inspector, _state, _dialect, scope, _disableExternalCalls, _disableMemoryTrace}(_ast);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Inspector::NodeAction Inspector::queryUser(DebugData const& _data, map<YulString, u256> const& _variables)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (m_stepMode == NodeAction::RunNode)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Output instructions that are being skipped/run
 | 
				
			||||||
 | 
							cout << "Running " << currentSource(_data) << endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return NodeAction::StepThroughNode;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						string input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (true)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Output sourcecode about to run.
 | 
				
			||||||
 | 
							cout << "> " << currentSource(_data) << endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Ask user for action
 | 
				
			||||||
 | 
							cout << endl
 | 
				
			||||||
 | 
								<< "(s)tep/(n)ext/(i)nspect/(p)rint/all (v)ariables?"
 | 
				
			||||||
 | 
								<< endl
 | 
				
			||||||
 | 
								<< "# ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cout.flush();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							getline(cin, input);
 | 
				
			||||||
 | 
							boost::algorithm::trim(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Imitate GDB and repeat last cmd for empty string input.
 | 
				
			||||||
 | 
							if (input == "")
 | 
				
			||||||
 | 
								input = m_lastInput;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								m_lastInput = input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (input == "next" || input == "n")
 | 
				
			||||||
 | 
								return NodeAction::RunNode;
 | 
				
			||||||
 | 
							else if (input == "step" || input == "s")
 | 
				
			||||||
 | 
								return NodeAction::StepThroughNode;
 | 
				
			||||||
 | 
							else if (input == "inspect" || input == "i")
 | 
				
			||||||
 | 
								m_state.dumpTraceAndState(cout, false);
 | 
				
			||||||
 | 
							else if (input == "variables" || input == "v")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								for (auto &&[yulStr, val]: _variables)
 | 
				
			||||||
 | 
									printVariable(yulStr, val);
 | 
				
			||||||
 | 
								cout << endl;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (
 | 
				
			||||||
 | 
								boost::starts_with(input, "print") ||
 | 
				
			||||||
 | 
								boost::starts_with(input, "p")
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								size_t whitespacePos = input.find(' ');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (whitespacePos == string::npos)
 | 
				
			||||||
 | 
									cout << "Error parsing command! Expected variable name." << endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								string const varname = input.substr(whitespacePos + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								vector<string> candidates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bool found = false;
 | 
				
			||||||
 | 
								for (auto &&[yulStr, val]: _variables)
 | 
				
			||||||
 | 
									if (yulStr.str() == varname)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										printVariable(yulStr, val);
 | 
				
			||||||
 | 
										found = true;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!found)
 | 
				
			||||||
 | 
									cout << varname << " not found." << endl;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string Inspector::currentSource(DebugData const& _data) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return m_source.substr(
 | 
				
			||||||
 | 
							static_cast<size_t>(_data.nativeLocation.start),
 | 
				
			||||||
 | 
							static_cast<size_t>(_data.nativeLocation.end - _data.nativeLocation.start)
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u256 InspectedInterpreter::evaluate(Expression const& _expression)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						InspectedExpressionEvaluator ev(m_inspector, m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace);
 | 
				
			||||||
 | 
						ev.visit(_expression);
 | 
				
			||||||
 | 
						return ev.value();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<u256> InspectedInterpreter::evaluateMulti(Expression const& _expression)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						InspectedExpressionEvaluator ev(m_inspector, m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace);
 | 
				
			||||||
 | 
						ev.visit(_expression);
 | 
				
			||||||
 | 
						return ev.values();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										213
									
								
								test/tools/yulInterpreter/Inspector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								test/tools/yulInterpreter/Inspector.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,213 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
						This file is part of solidity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						solidity is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
						it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
						the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
						(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						solidity is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
						but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
						MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
						GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
						along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-3.0
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Yul inspector.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <test/tools/yulInterpreter/Interpreter.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libyul/AST.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace solidity::yul::test
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Inspector class to respond to queries by the user for:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * * Stepping through and over instructions.
 | 
				
			||||||
 | 
					 * * Printing a specific or all variables.
 | 
				
			||||||
 | 
					 * * Inspecting memory, storage and calldata memory.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Inspector
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						enum class NodeAction
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RunNode,
 | 
				
			||||||
 | 
							StepThroughNode,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Inspector(std::string const& _source, InterpreterState const& _state)
 | 
				
			||||||
 | 
							:m_source(_source), m_state(_state) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Asks the user what action to take.
 | 
				
			||||||
 | 
						 * @returns NodeAction::RunNode if the current AST node (and all children nodes!) should be
 | 
				
			||||||
 | 
						 *          processed without stopping, else NodeAction::StepThroughNode.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						NodeAction queryUser(DebugData const& _data, std::map<YulString, u256> const& _variables);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void stepMode(NodeAction _action) { m_stepMode = _action; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::string const& source() const { return m_source; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void interactiveVisit(DebugData const& _debugData, std::map<YulString, u256> const& _variables, std::function<void()> _visitNode)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Inspector::NodeAction action = queryUser(_debugData, _variables);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (action == NodeAction::RunNode)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								// user requested to run the whole node without stopping
 | 
				
			||||||
 | 
								stepMode(Inspector::NodeAction::RunNode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_visitNode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Reset step mode back
 | 
				
			||||||
 | 
								stepMode(Inspector::NodeAction::StepThroughNode);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								_visitNode();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						std::string currentSource(DebugData const& _data) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Source of the file
 | 
				
			||||||
 | 
						std::string const& m_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// State of the interpreter
 | 
				
			||||||
 | 
						InterpreterState const& m_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Last user query command
 | 
				
			||||||
 | 
						std::string m_lastInput;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Used to run AST nodes without user interaction
 | 
				
			||||||
 | 
						NodeAction m_stepMode = NodeAction::StepThroughNode;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Yul Interpreter with inspection. Allows the user to go through the code step
 | 
				
			||||||
 | 
					 * by step and inspect the state using the `Inspector` class
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class InspectedInterpreter: public Interpreter
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						static void run(
 | 
				
			||||||
 | 
							std::shared_ptr<Inspector> _inspector,
 | 
				
			||||||
 | 
							InterpreterState& _state,
 | 
				
			||||||
 | 
							Dialect const& _dialect,
 | 
				
			||||||
 | 
							Block const& _ast,
 | 
				
			||||||
 | 
							bool _disableExternalCalls,
 | 
				
			||||||
 | 
							bool _disableMemoryTracing
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						InspectedInterpreter(
 | 
				
			||||||
 | 
							std::shared_ptr<Inspector> _inspector,
 | 
				
			||||||
 | 
							InterpreterState& _state,
 | 
				
			||||||
 | 
							Dialect const& _dialect,
 | 
				
			||||||
 | 
							Scope& _scope,
 | 
				
			||||||
 | 
							bool _disableExternalCalls,
 | 
				
			||||||
 | 
							bool _disableMemoryTracing,
 | 
				
			||||||
 | 
							std::map<YulString, u256> _variables = {}
 | 
				
			||||||
 | 
						):
 | 
				
			||||||
 | 
							Interpreter(_state, _dialect, _scope, _disableExternalCalls, _disableMemoryTracing, _variables),
 | 
				
			||||||
 | 
							m_inspector(_inspector)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void operator()(ExpressionStatement const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(Assignment const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(VariableDeclaration const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(If const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(Switch const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(ForLoop const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(Break const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(Continue const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(Leave const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(Block const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						/// Asserts that the expression evaluates to exactly one value and returns it.
 | 
				
			||||||
 | 
						u256 evaluate(Expression const& _expression) override;
 | 
				
			||||||
 | 
						/// Evaluates the expression and returns its value.
 | 
				
			||||||
 | 
						std::vector<u256> evaluateMulti(Expression const& _expression) override;
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						std::shared_ptr<Inspector> m_inspector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						template <typename ConcreteNode>
 | 
				
			||||||
 | 
						void helper(ConcreteNode const& _node)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_inspector->interactiveVisit(*_node.debugData, m_variables, [&]() {
 | 
				
			||||||
 | 
								Interpreter::operator()(_node);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InspectedExpressionEvaluator: public ExpressionEvaluator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						InspectedExpressionEvaluator(
 | 
				
			||||||
 | 
							std::shared_ptr<Inspector> _inspector,
 | 
				
			||||||
 | 
							InterpreterState& _state,
 | 
				
			||||||
 | 
							Dialect const& _dialect,
 | 
				
			||||||
 | 
							Scope& _scope,
 | 
				
			||||||
 | 
							std::map<YulString, u256> const& _variables,
 | 
				
			||||||
 | 
							bool _disableExternalCalls,
 | 
				
			||||||
 | 
							bool _disableMemoryTrace
 | 
				
			||||||
 | 
						):
 | 
				
			||||||
 | 
							ExpressionEvaluator(_state, _dialect, _scope, _variables, _disableExternalCalls, _disableMemoryTrace),
 | 
				
			||||||
 | 
							m_inspector(_inspector)
 | 
				
			||||||
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						template <typename ConcreteNode>
 | 
				
			||||||
 | 
						void helper(ConcreteNode const& _node)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_inspector->interactiveVisit(*_node.debugData, m_variables, [&]() {
 | 
				
			||||||
 | 
								ExpressionEvaluator::operator()(_node);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void operator()(Literal const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(Identifier const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
						void operator()(FunctionCall const& _node) override { helper(_node); }
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						std::unique_ptr<Interpreter> makeInterpreterCopy(std::map<YulString, u256> _variables = {}) const override
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return std::make_unique<InspectedInterpreter>(
 | 
				
			||||||
 | 
								m_inspector,
 | 
				
			||||||
 | 
								m_state,
 | 
				
			||||||
 | 
								m_dialect,
 | 
				
			||||||
 | 
								m_scope,
 | 
				
			||||||
 | 
								m_disableExternalCalls,
 | 
				
			||||||
 | 
								m_disableMemoryTrace,
 | 
				
			||||||
 | 
								std::move(_variables)
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						std::unique_ptr<Interpreter> makeInterpreterNew(InterpreterState& _state, Scope& _scope) const override
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return std::make_unique<InspectedInterpreter>(
 | 
				
			||||||
 | 
								std::make_unique<Inspector>(
 | 
				
			||||||
 | 
									m_inspector->source(),
 | 
				
			||||||
 | 
									_state
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
								_state,
 | 
				
			||||||
 | 
								m_dialect,
 | 
				
			||||||
 | 
								_scope,
 | 
				
			||||||
 | 
								m_disableExternalCalls,
 | 
				
			||||||
 | 
								m_disableMemoryTrace
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						std::shared_ptr<Inspector> m_inspector;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -70,17 +70,46 @@ void InterpreterState::dumpTraceAndState(ostream& _out, bool _disableMemoryTrace
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	_out << "Storage dump:" << endl;
 | 
						_out << "Storage dump:" << endl;
 | 
				
			||||||
	dumpStorage(_out);
 | 
						dumpStorage(_out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!calldata.empty())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							_out << "Calldata dump:";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (size_t offset = 0; offset < calldata.size(); ++offset)
 | 
				
			||||||
 | 
								if (calldata[offset] != 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (offset % 32 == 0)
 | 
				
			||||||
 | 
										_out <<
 | 
				
			||||||
 | 
											std::endl <<
 | 
				
			||||||
 | 
											"  " <<
 | 
				
			||||||
 | 
											std::uppercase <<
 | 
				
			||||||
 | 
											std::hex <<
 | 
				
			||||||
 | 
											std::setfill(' ') <<
 | 
				
			||||||
 | 
											std::setw(4) <<
 | 
				
			||||||
 | 
											offset <<
 | 
				
			||||||
 | 
											": ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									_out <<
 | 
				
			||||||
 | 
										std::hex <<
 | 
				
			||||||
 | 
										std::setw(2) <<
 | 
				
			||||||
 | 
										std::setfill('0') <<
 | 
				
			||||||
 | 
										static_cast<int>(calldata[offset]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_out << endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Interpreter::run(
 | 
					void Interpreter::run(
 | 
				
			||||||
	InterpreterState& _state,
 | 
						InterpreterState& _state,
 | 
				
			||||||
	Dialect const& _dialect,
 | 
						Dialect const& _dialect,
 | 
				
			||||||
	Block const& _ast,
 | 
						Block const& _ast,
 | 
				
			||||||
 | 
						bool _disableExternalCalls,
 | 
				
			||||||
	bool _disableMemoryTrace
 | 
						bool _disableMemoryTrace
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Scope scope;
 | 
						Scope scope;
 | 
				
			||||||
	Interpreter{_state, _dialect, scope, _disableMemoryTrace}(_ast);
 | 
						Interpreter{_state, _dialect, scope, _disableExternalCalls, _disableMemoryTrace}(_ast);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Interpreter::operator()(ExpressionStatement const& _expressionStatement)
 | 
					void Interpreter::operator()(ExpressionStatement const& _expressionStatement)
 | 
				
			||||||
@ -215,14 +244,14 @@ void Interpreter::operator()(Block const& _block)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
u256 Interpreter::evaluate(Expression const& _expression)
 | 
					u256 Interpreter::evaluate(Expression const& _expression)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableMemoryTrace);
 | 
						ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace);
 | 
				
			||||||
	ev.visit(_expression);
 | 
						ev.visit(_expression);
 | 
				
			||||||
	return ev.value();
 | 
						return ev.value();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
vector<u256> Interpreter::evaluateMulti(Expression const& _expression)
 | 
					vector<u256> Interpreter::evaluateMulti(Expression const& _expression)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableMemoryTrace);
 | 
						ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace);
 | 
				
			||||||
	ev.visit(_expression);
 | 
						ev.visit(_expression);
 | 
				
			||||||
	return ev.values();
 | 
						return ev.values();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -286,7 +315,17 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
 | 
				
			|||||||
		if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
 | 
							if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			EVMInstructionInterpreter interpreter(m_state, m_disableMemoryTrace);
 | 
								EVMInstructionInterpreter interpreter(m_state, m_disableMemoryTrace);
 | 
				
			||||||
			setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values()));
 | 
					
 | 
				
			||||||
 | 
								u256 const value = interpreter.evalBuiltin(*fun, _funCall.arguments, values());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (
 | 
				
			||||||
 | 
									!m_disableExternalCalls &&
 | 
				
			||||||
 | 
									fun->instruction &&
 | 
				
			||||||
 | 
									evmasm::isCallInstruction(*fun->instruction)
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
									runExternalCall(*fun->instruction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								setValue(value);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -314,13 +353,13 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
 | 
				
			|||||||
		variables[fun->returnVariables.at(i).name] = 0;
 | 
							variables[fun->returnVariables.at(i).name] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m_state.controlFlowState = ControlFlowState::Default;
 | 
						m_state.controlFlowState = ControlFlowState::Default;
 | 
				
			||||||
	Interpreter interpreter(m_state, m_dialect, *scope, m_disableMemoryTrace, std::move(variables));
 | 
						unique_ptr<Interpreter> interpreter = makeInterpreterCopy(std::move(variables));
 | 
				
			||||||
	interpreter(fun->body);
 | 
						(*interpreter)(fun->body);
 | 
				
			||||||
	m_state.controlFlowState = ControlFlowState::Default;
 | 
						m_state.controlFlowState = ControlFlowState::Default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m_values.clear();
 | 
						m_values.clear();
 | 
				
			||||||
	for (auto const& retVar: fun->returnVariables)
 | 
						for (auto const& retVar: fun->returnVariables)
 | 
				
			||||||
		m_values.emplace_back(interpreter.valueOfVariable(retVar.name));
 | 
							m_values.emplace_back(interpreter->valueOfVariable(retVar.name));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
u256 ExpressionEvaluator::value() const
 | 
					u256 ExpressionEvaluator::value() const
 | 
				
			||||||
@ -349,7 +388,19 @@ void ExpressionEvaluator::evaluateArgs(
 | 
				
			|||||||
		if (!_literalArguments || !_literalArguments->at(_expr.size() - i - 1))
 | 
							if (!_literalArguments || !_literalArguments->at(_expr.size() - i - 1))
 | 
				
			||||||
			visit(expr);
 | 
								visit(expr);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			m_values = {0};
 | 
							{
 | 
				
			||||||
 | 
								string literal = std::get<Literal>(expr).value.str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								try
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									m_values = {u256(literal)};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								catch (exception&)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									m_values = {u256(0)};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		values.push_back(value());
 | 
							values.push_back(value());
 | 
				
			||||||
		++i;
 | 
							++i;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -366,3 +417,90 @@ void ExpressionEvaluator::incrementStep()
 | 
				
			|||||||
		BOOST_THROW_EXCEPTION(ExpressionNestingLimitReached());
 | 
							BOOST_THROW_EXCEPTION(ExpressionNestingLimitReached());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ExpressionEvaluator::runExternalCall(evmasm::Instruction _instruction)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u256 memOutOffset = 0;
 | 
				
			||||||
 | 
						u256 memOutSize = 0;
 | 
				
			||||||
 | 
						u256 callvalue = 0;
 | 
				
			||||||
 | 
						u256 memInOffset = 0;
 | 
				
			||||||
 | 
						u256 memInSize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Setup memOut* values
 | 
				
			||||||
 | 
						if (
 | 
				
			||||||
 | 
							_instruction == evmasm::Instruction::CALL ||
 | 
				
			||||||
 | 
							_instruction == evmasm::Instruction::CALLCODE
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memOutOffset = values()[5];
 | 
				
			||||||
 | 
							memOutSize = values()[6];
 | 
				
			||||||
 | 
							callvalue = values()[2];
 | 
				
			||||||
 | 
							memInOffset = values()[3];
 | 
				
			||||||
 | 
							memInSize = values()[4];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (
 | 
				
			||||||
 | 
							_instruction == evmasm::Instruction::DELEGATECALL ||
 | 
				
			||||||
 | 
							_instruction == evmasm::Instruction::STATICCALL
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memOutOffset = values()[4];
 | 
				
			||||||
 | 
							memOutSize = values()[5];
 | 
				
			||||||
 | 
							memInOffset = values()[2];
 | 
				
			||||||
 | 
							memInSize = values()[3];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							yulAssert(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Don't execute external call if it isn't our own address
 | 
				
			||||||
 | 
						if (values()[1] != util::h160::Arith(m_state.address))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Scope tmpScope;
 | 
				
			||||||
 | 
						InterpreterState tmpState;
 | 
				
			||||||
 | 
						tmpState.calldata = m_state.readMemory(memInOffset, memInSize);
 | 
				
			||||||
 | 
						tmpState.callvalue = callvalue;
 | 
				
			||||||
 | 
						tmpState.numInstance = m_state.numInstance + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						yulAssert(tmpState.numInstance < 1024, "Detected more than 1024 recursive calls, aborting...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create new interpreter for the called contract
 | 
				
			||||||
 | 
						unique_ptr<Interpreter> newInterpreter = makeInterpreterNew(tmpState, tmpScope);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Scope* abstractRootScope = &m_scope;
 | 
				
			||||||
 | 
						Scope* fileScope = nullptr;
 | 
				
			||||||
 | 
						Block const* ast = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Find file scope
 | 
				
			||||||
 | 
						while (abstractRootScope->parent)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fileScope = abstractRootScope;
 | 
				
			||||||
 | 
							abstractRootScope = abstractRootScope->parent;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get AST for file scope
 | 
				
			||||||
 | 
						for (auto&& [block, scope]: abstractRootScope->subScopes)
 | 
				
			||||||
 | 
							if (scope.get() == fileScope)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ast = block;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						yulAssert(ast);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							(*newInterpreter)(*ast);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						catch (ExplicitlyTerminatedWithReturn const&)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Copy return data to our memory
 | 
				
			||||||
 | 
							copyZeroExtended(
 | 
				
			||||||
 | 
								m_state.memory,
 | 
				
			||||||
 | 
								newInterpreter->returnData(),
 | 
				
			||||||
 | 
								memOutOffset.convert_to<size_t>(),
 | 
				
			||||||
 | 
								0,
 | 
				
			||||||
 | 
								memOutSize.convert_to<size_t>()
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							m_state.returndata = newInterpreter->returnData();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,8 @@
 | 
				
			|||||||
#include <libyul/ASTForward.h>
 | 
					#include <libyul/ASTForward.h>
 | 
				
			||||||
#include <libyul/optimiser/ASTWalker.h>
 | 
					#include <libyul/optimiser/ASTWalker.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libevmasm/Instruction.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <libsolutil/FixedHash.h>
 | 
					#include <libsolutil/FixedHash.h>
 | 
				
			||||||
#include <libsolutil/CommonData.h>
 | 
					#include <libsolutil/CommonData.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,6 +49,10 @@ class ExplicitlyTerminated: public InterpreterTerminatedGeneric
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ExplicitlyTerminatedWithReturn: public ExplicitlyTerminated
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StepLimitReached: public InterpreterTerminatedGeneric
 | 
					class StepLimitReached: public InterpreterTerminatedGeneric
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -101,6 +107,9 @@ struct InterpreterState
 | 
				
			|||||||
	size_t maxExprNesting = 0;
 | 
						size_t maxExprNesting = 0;
 | 
				
			||||||
	ControlFlowState controlFlowState = ControlFlowState::Default;
 | 
						ControlFlowState controlFlowState = ControlFlowState::Default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Number of the current state instance, used for recursion protection
 | 
				
			||||||
 | 
						size_t numInstance = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Prints execution trace and non-zero storage to @param _out.
 | 
						/// Prints execution trace and non-zero storage to @param _out.
 | 
				
			||||||
	/// Flag @param _disableMemoryTrace, if set, does not produce a memory dump. This
 | 
						/// Flag @param _disableMemoryTrace, if set, does not produce a memory dump. This
 | 
				
			||||||
	/// avoids false positives reports by the fuzzer when certain optimizer steps are
 | 
						/// avoids false positives reports by the fuzzer when certain optimizer steps are
 | 
				
			||||||
@ -108,6 +117,15 @@ struct InterpreterState
 | 
				
			|||||||
	void dumpTraceAndState(std::ostream& _out, bool _disableMemoryTrace) const;
 | 
						void dumpTraceAndState(std::ostream& _out, bool _disableMemoryTrace) const;
 | 
				
			||||||
	/// Prints non-zero storage to @param _out.
 | 
						/// Prints non-zero storage to @param _out.
 | 
				
			||||||
	void dumpStorage(std::ostream& _out) const;
 | 
						void dumpStorage(std::ostream& _out) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bytes readMemory(u256 const& _offset, u256 const& _size)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							yulAssert(_size <= 0xffff, "Too large read.");
 | 
				
			||||||
 | 
							bytes data(size_t(_size), uint8_t(0));
 | 
				
			||||||
 | 
							for (size_t i = 0; i < data.size(); ++i)
 | 
				
			||||||
 | 
								data[i] = memory[_offset + i];
 | 
				
			||||||
 | 
							return data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -135,6 +153,7 @@ public:
 | 
				
			|||||||
		InterpreterState& _state,
 | 
							InterpreterState& _state,
 | 
				
			||||||
		Dialect const& _dialect,
 | 
							Dialect const& _dialect,
 | 
				
			||||||
		Block const& _ast,
 | 
							Block const& _ast,
 | 
				
			||||||
 | 
							bool _disableExternalCalls,
 | 
				
			||||||
		bool _disableMemoryTracing
 | 
							bool _disableMemoryTracing
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -142,6 +161,7 @@ public:
 | 
				
			|||||||
		InterpreterState& _state,
 | 
							InterpreterState& _state,
 | 
				
			||||||
		Dialect const& _dialect,
 | 
							Dialect const& _dialect,
 | 
				
			||||||
		Scope& _scope,
 | 
							Scope& _scope,
 | 
				
			||||||
 | 
							bool _disableExternalCalls,
 | 
				
			||||||
		bool _disableMemoryTracing,
 | 
							bool _disableMemoryTracing,
 | 
				
			||||||
		std::map<YulString, u256> _variables = {}
 | 
							std::map<YulString, u256> _variables = {}
 | 
				
			||||||
	):
 | 
						):
 | 
				
			||||||
@ -149,6 +169,7 @@ public:
 | 
				
			|||||||
		m_state(_state),
 | 
							m_state(_state),
 | 
				
			||||||
		m_variables(std::move(_variables)),
 | 
							m_variables(std::move(_variables)),
 | 
				
			||||||
		m_scope(&_scope),
 | 
							m_scope(&_scope),
 | 
				
			||||||
 | 
							m_disableExternalCalls(_disableExternalCalls),
 | 
				
			||||||
		m_disableMemoryTrace(_disableMemoryTracing)
 | 
							m_disableMemoryTrace(_disableMemoryTracing)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -165,15 +186,16 @@ public:
 | 
				
			|||||||
	void operator()(Leave const&) override;
 | 
						void operator()(Leave const&) override;
 | 
				
			||||||
	void operator()(Block const& _block) override;
 | 
						void operator()(Block const& _block) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bytes returnData() const { return m_state.returndata; }
 | 
				
			||||||
	std::vector<std::string> const& trace() const { return m_state.trace; }
 | 
						std::vector<std::string> const& trace() const { return m_state.trace; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u256 valueOfVariable(YulString _name) const { return m_variables.at(_name); }
 | 
						u256 valueOfVariable(YulString _name) const { return m_variables.at(_name); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					protected:
 | 
				
			||||||
	/// Asserts that the expression evaluates to exactly one value and returns it.
 | 
						/// Asserts that the expression evaluates to exactly one value and returns it.
 | 
				
			||||||
	u256 evaluate(Expression const& _expression);
 | 
						virtual u256 evaluate(Expression const& _expression);
 | 
				
			||||||
	/// Evaluates the expression and returns its value.
 | 
						/// Evaluates the expression and returns its value.
 | 
				
			||||||
	std::vector<u256> evaluateMulti(Expression const& _expression);
 | 
						virtual std::vector<u256> evaluateMulti(Expression const& _expression);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void enterScope(Block const& _block);
 | 
						void enterScope(Block const& _block);
 | 
				
			||||||
	void leaveScope();
 | 
						void leaveScope();
 | 
				
			||||||
@ -187,6 +209,9 @@ private:
 | 
				
			|||||||
	/// Values of variables.
 | 
						/// Values of variables.
 | 
				
			||||||
	std::map<YulString, u256> m_variables;
 | 
						std::map<YulString, u256> m_variables;
 | 
				
			||||||
	Scope* m_scope;
 | 
						Scope* m_scope;
 | 
				
			||||||
 | 
						/// If not set, external calls (e.g. using `call()`) to the same contract
 | 
				
			||||||
 | 
						/// are evaluated in a new parser instance.
 | 
				
			||||||
 | 
						bool m_disableExternalCalls;
 | 
				
			||||||
	bool m_disableMemoryTrace;
 | 
						bool m_disableMemoryTrace;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -201,12 +226,14 @@ public:
 | 
				
			|||||||
		Dialect const& _dialect,
 | 
							Dialect const& _dialect,
 | 
				
			||||||
		Scope& _scope,
 | 
							Scope& _scope,
 | 
				
			||||||
		std::map<YulString, u256> const& _variables,
 | 
							std::map<YulString, u256> const& _variables,
 | 
				
			||||||
 | 
							bool _disableExternalCalls,
 | 
				
			||||||
		bool _disableMemoryTrace
 | 
							bool _disableMemoryTrace
 | 
				
			||||||
	):
 | 
						):
 | 
				
			||||||
		m_state(_state),
 | 
							m_state(_state),
 | 
				
			||||||
		m_dialect(_dialect),
 | 
							m_dialect(_dialect),
 | 
				
			||||||
		m_variables(_variables),
 | 
							m_variables(_variables),
 | 
				
			||||||
		m_scope(_scope),
 | 
							m_scope(_scope),
 | 
				
			||||||
 | 
							m_disableExternalCalls(_disableExternalCalls),
 | 
				
			||||||
		m_disableMemoryTrace(_disableMemoryTrace)
 | 
							m_disableMemoryTrace(_disableMemoryTrace)
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -219,7 +246,30 @@ public:
 | 
				
			|||||||
	/// Returns the list of values of the expression.
 | 
						/// Returns the list of values of the expression.
 | 
				
			||||||
	std::vector<u256> values() const { return m_values; }
 | 
						std::vector<u256> values() const { return m_values; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					protected:
 | 
				
			||||||
 | 
						void runExternalCall(evmasm::Instruction _instruction);
 | 
				
			||||||
 | 
						virtual std::unique_ptr<Interpreter> makeInterpreterCopy(std::map<YulString, u256> _variables = {}) const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return std::make_unique<Interpreter>(
 | 
				
			||||||
 | 
								m_state,
 | 
				
			||||||
 | 
								m_dialect,
 | 
				
			||||||
 | 
								m_scope,
 | 
				
			||||||
 | 
								m_disableExternalCalls,
 | 
				
			||||||
 | 
								m_disableMemoryTrace,
 | 
				
			||||||
 | 
								std::move(_variables)
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						virtual std::unique_ptr<Interpreter> makeInterpreterNew(InterpreterState& _state, Scope& _scope) const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return std::make_unique<Interpreter>(
 | 
				
			||||||
 | 
								_state,
 | 
				
			||||||
 | 
								m_dialect,
 | 
				
			||||||
 | 
								_scope,
 | 
				
			||||||
 | 
								m_disableExternalCalls,
 | 
				
			||||||
 | 
								m_disableMemoryTrace
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void setValue(u256 _value);
 | 
						void setValue(u256 _value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Evaluates the given expression from right to left and
 | 
						/// Evaluates the given expression from right to left and
 | 
				
			||||||
@ -243,6 +293,7 @@ private:
 | 
				
			|||||||
	std::vector<u256> m_values;
 | 
						std::vector<u256> m_values;
 | 
				
			||||||
	/// Current expression nesting level
 | 
						/// Current expression nesting level
 | 
				
			||||||
	unsigned m_nestingLevel = 0;
 | 
						unsigned m_nestingLevel = 0;
 | 
				
			||||||
 | 
						bool m_disableExternalCalls;
 | 
				
			||||||
	/// Flag to disable memory tracing
 | 
						/// Flag to disable memory tracing
 | 
				
			||||||
	bool m_disableMemoryTrace;
 | 
						bool m_disableMemoryTrace;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <test/tools/yulInterpreter/Interpreter.h>
 | 
					#include <test/tools/yulInterpreter/Interpreter.h>
 | 
				
			||||||
 | 
					#include <test/tools/yulInterpreter/Inspector.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <libyul/AsmAnalysisInfo.h>
 | 
					#include <libyul/AsmAnalysisInfo.h>
 | 
				
			||||||
#include <libyul/AsmAnalysis.h>
 | 
					#include <libyul/AsmAnalysis.h>
 | 
				
			||||||
@ -74,7 +75,7 @@ pair<shared_ptr<Block>, shared_ptr<AsmAnalysisInfo>> parse(string const& _source
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void interpret(string const& _source)
 | 
					void interpret(string const& _source, bool _inspect, bool _disableExternalCalls)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	shared_ptr<Block> ast;
 | 
						shared_ptr<Block> ast;
 | 
				
			||||||
	shared_ptr<AsmAnalysisInfo> analysisInfo;
 | 
						shared_ptr<AsmAnalysisInfo> analysisInfo;
 | 
				
			||||||
@ -87,7 +88,12 @@ void interpret(string const& _source)
 | 
				
			|||||||
	try
 | 
						try
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
 | 
							Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
 | 
				
			||||||
		Interpreter::run(state, dialect, *ast, /*disableMemoryTracing=*/false);
 | 
					
 | 
				
			||||||
 | 
							if (_inspect)
 | 
				
			||||||
 | 
								InspectedInterpreter::run(std::make_shared<Inspector>(_source, state), state, dialect, *ast, _disableExternalCalls, /*disableMemoryTracing=*/false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								Interpreter::run(state, dialect, *ast, _disableExternalCalls, /*disableMemoryTracing=*/false);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	catch (InterpreterTerminatedGeneric const&)
 | 
						catch (InterpreterTerminatedGeneric const&)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@ -110,6 +116,8 @@ Allowed options)",
 | 
				
			|||||||
		po::options_description::m_default_line_length - 23);
 | 
							po::options_description::m_default_line_length - 23);
 | 
				
			||||||
	options.add_options()
 | 
						options.add_options()
 | 
				
			||||||
		("help", "Show this help screen.")
 | 
							("help", "Show this help screen.")
 | 
				
			||||||
 | 
							("enable-external-calls", "Enable external calls")
 | 
				
			||||||
 | 
							("interactive", "Run interactive")
 | 
				
			||||||
		("input-file", po::value<vector<string>>(), "input file");
 | 
							("input-file", po::value<vector<string>>(), "input file");
 | 
				
			||||||
	po::positional_options_description filesPositions;
 | 
						po::positional_options_description filesPositions;
 | 
				
			||||||
	filesPositions.add("input-file", -1);
 | 
						filesPositions.add("input-file", -1);
 | 
				
			||||||
@ -153,7 +161,7 @@ Allowed options)",
 | 
				
			|||||||
		else
 | 
							else
 | 
				
			||||||
			input = readUntilEnd(cin);
 | 
								input = readUntilEnd(cin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		interpret(input);
 | 
							interpret(input, arguments.count("interactive"), !arguments.count("enable-external-calls"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user