mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Calls to bare contracts.
This commit is contained in:
		
							parent
							
								
									6893d4d455
								
							
						
					
					
						commit
						c0bba438b1
					
				@ -194,7 +194,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
 | 
				
			|||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
 | 
							FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
 | 
				
			||||||
		std::vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments();
 | 
							vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments();
 | 
				
			||||||
		if (asserts(arguments.size() == function.getParameterTypes().size()))
 | 
							if (asserts(arguments.size() == function.getParameterTypes().size()))
 | 
				
			||||||
			BOOST_THROW_EXCEPTION(InternalCompilerError());
 | 
								BOOST_THROW_EXCEPTION(InternalCompilerError());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -227,50 +227,23 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
 | 
				
			|||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		case Location::EXTERNAL:
 | 
							case Location::EXTERNAL:
 | 
				
			||||||
 | 
							case Location::BARE:
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			unsigned dataOffset = 1; // reserve one byte for the function index
 | 
								FunctionCallOptions options;
 | 
				
			||||||
			for (unsigned i = 0; i < arguments.size(); ++i)
 | 
								options.bare = function.getLocation() == Location::BARE;
 | 
				
			||||||
			{
 | 
								options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
 | 
				
			||||||
				arguments[i]->accept(*this);
 | 
								appendExternalFunctionCall(function, arguments, options);
 | 
				
			||||||
				Type const& type = *function.getParameterTypes()[i];
 | 
					 | 
				
			||||||
				appendTypeConversion(*arguments[i]->getType(), type);
 | 
					 | 
				
			||||||
				unsigned const numBytes = type.getCalldataEncodedSize();
 | 
					 | 
				
			||||||
				if (numBytes == 0 || numBytes > 32)
 | 
					 | 
				
			||||||
					BOOST_THROW_EXCEPTION(CompilerError()
 | 
					 | 
				
			||||||
										  << errinfo_sourceLocation(arguments[i]->getLocation())
 | 
					 | 
				
			||||||
										  << errinfo_comment("Type " + type.toString() + " not yet supported."));
 | 
					 | 
				
			||||||
				bool const leftAligned = type.getCategory() == Type::Category::STRING;
 | 
					 | 
				
			||||||
				CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
 | 
					 | 
				
			||||||
				dataOffset += numBytes;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			//@todo only return the first return value for now
 | 
					 | 
				
			||||||
			Type const* firstType = function.getReturnParameterTypes().empty() ? nullptr :
 | 
					 | 
				
			||||||
									function.getReturnParameterTypes().front().get();
 | 
					 | 
				
			||||||
			unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
 | 
					 | 
				
			||||||
			// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
 | 
					 | 
				
			||||||
			m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
 | 
					 | 
				
			||||||
			_functionCall.getExpression().accept(*this); // pushes addr and function index
 | 
					 | 
				
			||||||
			m_context << u256(0) << eth::Instruction::MSTORE8
 | 
					 | 
				
			||||||
					  << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
 | 
					 | 
				
			||||||
					  << eth::Instruction::CALL
 | 
					 | 
				
			||||||
					  << eth::Instruction::POP; // @todo do not ignore failure indicator
 | 
					 | 
				
			||||||
			if (retSize > 0)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
 | 
					 | 
				
			||||||
				CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		case Location::SEND:
 | 
							case Location::SEND:
 | 
				
			||||||
			m_context << u256(0) << u256(0) << u256(0) << u256(0);
 | 
							{
 | 
				
			||||||
			arguments.front()->accept(*this);
 | 
								FunctionCallOptions options;
 | 
				
			||||||
			//@todo might not be necessary
 | 
								options.bare = true;
 | 
				
			||||||
			appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
 | 
								options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
 | 
				
			||||||
			_functionCall.getExpression().accept(*this);
 | 
								options.obtainValue = [&]() { arguments.front()->accept(*this); };
 | 
				
			||||||
			m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
 | 
								appendExternalFunctionCall(FunctionType({}, {}, Location::EXTERNAL), {}, options);
 | 
				
			||||||
					  << eth::Instruction::CALL
 | 
					 | 
				
			||||||
					  << eth::Instruction::POP;
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		case Location::SUICIDE:
 | 
							case Location::SUICIDE:
 | 
				
			||||||
			arguments.front()->accept(*this);
 | 
								arguments.front()->accept(*this);
 | 
				
			||||||
			//@todo might not be necessary
 | 
								//@todo might not be necessary
 | 
				
			||||||
@ -292,19 +265,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
 | 
				
			|||||||
															   {Location::SHA256, 2},
 | 
																				   {Location::SHA256, 2},
 | 
				
			||||||
															   {Location::RIPEMD160, 3}};
 | 
																				   {Location::RIPEMD160, 3}};
 | 
				
			||||||
			u256 contractAddress = contractAddresses.find(function.getLocation())->second;
 | 
								u256 contractAddress = contractAddresses.find(function.getLocation())->second;
 | 
				
			||||||
			// @todo later, combine this code with external function call
 | 
								FunctionCallOptions options;
 | 
				
			||||||
			for (unsigned i = 0; i < arguments.size(); ++i)
 | 
								options.bare = true;
 | 
				
			||||||
			{
 | 
								options.obtainAddress = [&]() { m_context << contractAddress; };
 | 
				
			||||||
				arguments[i]->accept(*this);
 | 
								options.packDensely = false;
 | 
				
			||||||
				appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
 | 
								appendExternalFunctionCall(function, arguments, options);
 | 
				
			||||||
				// @todo move this once we actually use memory
 | 
					 | 
				
			||||||
				CompilerUtils(m_context).storeInMemory(i * 32);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
 | 
					 | 
				
			||||||
					  << contractAddress << u256(500) //@todo determine actual gas requirement
 | 
					 | 
				
			||||||
					  << eth::Instruction::CALL
 | 
					 | 
				
			||||||
					  << eth::Instruction::POP;
 | 
					 | 
				
			||||||
			CompilerUtils(m_context).loadFromMemory(0);
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
@ -326,11 +291,9 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
 | 
				
			|||||||
								 IntegerType(0, IntegerType::Modifier::ADDRESS), true);
 | 
													 IntegerType(0, IntegerType::Modifier::ADDRESS), true);
 | 
				
			||||||
			m_context << eth::Instruction::BALANCE;
 | 
								m_context << eth::Instruction::BALANCE;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else if (member == "send")
 | 
							else if (member == "send" || member.substr(0, 4) == "call")
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			appendTypeConversion(*_memberAccess.getExpression().getType(),
 | 
								appendTypeConversion(*_memberAccess.getExpression().getType(),
 | 
				
			||||||
								 IntegerType(0, IntegerType::Modifier::ADDRESS), true);
 | 
													 IntegerType(0, IntegerType::Modifier::ADDRESS), true);
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
 | 
								BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
@ -587,6 +550,53 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
 | 
				
			|||||||
		m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
 | 
							m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
 | 
				
			||||||
 | 
																		vector<ASTPointer<Expression const>> const& _arguments,
 | 
				
			||||||
 | 
																		FunctionCallOptions const& _options)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (asserts(_arguments.size() == _functionType.getParameterTypes().size()))
 | 
				
			||||||
 | 
							BOOST_THROW_EXCEPTION(InternalCompilerError());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned dataOffset = _options.bare ? 0 : 1; // reserve one byte for the function index
 | 
				
			||||||
 | 
						for (unsigned i = 0; i < _arguments.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							_arguments[i]->accept(*this);
 | 
				
			||||||
 | 
							Type const& type = *_functionType.getParameterTypes()[i];
 | 
				
			||||||
 | 
							appendTypeConversion(*_arguments[i]->getType(), type);
 | 
				
			||||||
 | 
							unsigned const numBytes = _options.packDensely ? type.getCalldataEncodedSize() : 32;
 | 
				
			||||||
 | 
							if (numBytes == 0 || numBytes > 32)
 | 
				
			||||||
 | 
								BOOST_THROW_EXCEPTION(CompilerError()
 | 
				
			||||||
 | 
													  << errinfo_sourceLocation(_arguments[i]->getLocation())
 | 
				
			||||||
 | 
													  << errinfo_comment("Type " + type.toString() + " not yet supported."));
 | 
				
			||||||
 | 
							bool const leftAligned = type.getCategory() == Type::Category::STRING;
 | 
				
			||||||
 | 
							CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
 | 
				
			||||||
 | 
							dataOffset += numBytes;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//@todo only return the first return value for now
 | 
				
			||||||
 | 
						Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
 | 
				
			||||||
 | 
												_functionType.getReturnParameterTypes().front().get();
 | 
				
			||||||
 | 
						unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
 | 
				
			||||||
 | 
						if (!_options.packDensely && retSize > 0)
 | 
				
			||||||
 | 
							retSize = 32;
 | 
				
			||||||
 | 
						// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
 | 
				
			||||||
 | 
						m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
 | 
				
			||||||
 | 
						if (_options.obtainValue)
 | 
				
			||||||
 | 
							_options.obtainValue();
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							m_context << u256(0);
 | 
				
			||||||
 | 
						_options.obtainAddress();
 | 
				
			||||||
 | 
						if (!_options.bare)
 | 
				
			||||||
 | 
							m_context << u256(0) << eth::Instruction::MSTORE8;
 | 
				
			||||||
 | 
						m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
 | 
				
			||||||
 | 
								  << eth::Instruction::CALL
 | 
				
			||||||
 | 
								  << eth::Instruction::POP; // @todo do not ignore failure indicator
 | 
				
			||||||
 | 
						if (retSize > 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
 | 
				
			||||||
 | 
							CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
 | 
					ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
 | 
				
			||||||
								   unsigned _baseStackOffset):
 | 
													   unsigned _baseStackOffset):
 | 
				
			||||||
	m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
 | 
						m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@
 | 
				
			|||||||
 * Solidity AST to EVM bytecode compiler for expressions.
 | 
					 * Solidity AST to EVM bytecode compiler for expressions.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
#include <boost/noncopyable.hpp>
 | 
					#include <boost/noncopyable.hpp>
 | 
				
			||||||
#include <libdevcore/Common.h>
 | 
					#include <libdevcore/Common.h>
 | 
				
			||||||
#include <libsolidity/ASTVisitor.h>
 | 
					#include <libsolidity/ASTVisitor.h>
 | 
				
			||||||
@ -83,6 +84,25 @@ private:
 | 
				
			|||||||
	//// Appends code that cleans higher-order bits for integer types.
 | 
						//// Appends code that cleans higher-order bits for integer types.
 | 
				
			||||||
	void appendHighBitsCleanup(IntegerType const& _typeOnStack);
 | 
						void appendHighBitsCleanup(IntegerType const& _typeOnStack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Additional options used in appendExternalFunctionCall.
 | 
				
			||||||
 | 
						struct FunctionCallOptions
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							FunctionCallOptions() {}
 | 
				
			||||||
 | 
							/// Invoked to copy the address to the stack
 | 
				
			||||||
 | 
							std::function<void()> obtainAddress;
 | 
				
			||||||
 | 
							/// Invoked to copy the ethe value to the stack (if not specified, value is 0).
 | 
				
			||||||
 | 
							std::function<void()> obtainValue;
 | 
				
			||||||
 | 
							/// If true, do not prepend function index to call data
 | 
				
			||||||
 | 
							bool bare = false;
 | 
				
			||||||
 | 
							/// If false, use calling convention that all arguments and return values are packed as
 | 
				
			||||||
 | 
							/// 32 byte values with padding.
 | 
				
			||||||
 | 
							bool packDensely = true;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Appends code to call a function of the given type with the given arguments.
 | 
				
			||||||
 | 
						void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
 | 
				
			||||||
 | 
														FunctionCallOptions const& _options = FunctionCallOptions());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Helper class to store and retrieve lvalues to and from various locations.
 | 
						 * Helper class to store and retrieve lvalues to and from various locations.
 | 
				
			||||||
	 * All types except STACK store a reference in a slot on the stack, STACK just
 | 
						 * All types except STACK store a reference in a slot on the stack, STACK just
 | 
				
			||||||
 | 
				
			|||||||
@ -194,6 +194,11 @@ u256 IntegerType::literalValue(Literal const& _literal) const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const MemberList IntegerType::AddressMemberList =
 | 
					const MemberList IntegerType::AddressMemberList =
 | 
				
			||||||
		MemberList({{"balance", make_shared<IntegerType const>(256)},
 | 
							MemberList({{"balance", make_shared<IntegerType const>(256)},
 | 
				
			||||||
 | 
										{"callstring32", make_shared<FunctionType const>(TypePointers({make_shared<StaticStringType const>(32)}),
 | 
				
			||||||
 | 
																				 TypePointers(), FunctionType::Location::BARE)},
 | 
				
			||||||
 | 
										{"callstring32string32", make_shared<FunctionType const>(TypePointers({make_shared<StaticStringType const>(32),
 | 
				
			||||||
 | 
																											   make_shared<StaticStringType const>(32)}),
 | 
				
			||||||
 | 
																				 TypePointers(), FunctionType::Location::BARE)},
 | 
				
			||||||
					{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
 | 
										{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
 | 
				
			||||||
															 TypePointers(), FunctionType::Location::SEND)}});
 | 
																				 TypePointers(), FunctionType::Location::SEND)}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -424,6 +429,8 @@ unsigned FunctionType::getSizeOnStack() const
 | 
				
			|||||||
		return 1;
 | 
							return 1;
 | 
				
			||||||
	case Location::EXTERNAL:
 | 
						case Location::EXTERNAL:
 | 
				
			||||||
		return 2;
 | 
							return 2;
 | 
				
			||||||
 | 
						case Location::BARE:
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								Types.h
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Types.h
									
									
									
									
									
								
							@ -296,8 +296,9 @@ class FunctionType: public Type
 | 
				
			|||||||
public:
 | 
					public:
 | 
				
			||||||
	/// The meaning of the value(s) on the stack referencing the function:
 | 
						/// The meaning of the value(s) on the stack referencing the function:
 | 
				
			||||||
	/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
 | 
						/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
 | 
				
			||||||
 | 
						/// BARE: contract address (non-abi contract call)
 | 
				
			||||||
	/// OTHERS: special virtual function, nothing on the stack
 | 
						/// OTHERS: special virtual function, nothing on the stack
 | 
				
			||||||
	enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
 | 
						enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, BARE };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual Category getCategory() const override { return Category::FUNCTION; }
 | 
						virtual Category getCategory() const override { return Category::FUNCTION; }
 | 
				
			||||||
	explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
 | 
						explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user