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