|
|
@ -194,7 +194,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) |
|
|
|
else |
|
|
|
{ |
|
|
|
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())) |
|
|
|
BOOST_THROW_EXCEPTION(InternalCompilerError()); |
|
|
|
|
|
|
@ -227,50 +227,23 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) |
|
|
|
break; |
|
|
|
} |
|
|
|
case Location::EXTERNAL: |
|
|
|
case Location::BARE: |
|
|
|
{ |
|
|
|
unsigned dataOffset = 1; // reserve one byte for the function index
|
|
|
|
for (unsigned i = 0; i < arguments.size(); ++i) |
|
|
|
{ |
|
|
|
arguments[i]->accept(*this); |
|
|
|
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); |
|
|
|
} |
|
|
|
FunctionCallOptions options; |
|
|
|
options.bare = function.getLocation() == Location::BARE; |
|
|
|
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); }; |
|
|
|
appendExternalFunctionCall(function, arguments, options); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Location::SEND: |
|
|
|
m_context << u256(0) << u256(0) << u256(0) << u256(0); |
|
|
|
arguments.front()->accept(*this); |
|
|
|
//@todo might not be necessary
|
|
|
|
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); |
|
|
|
_functionCall.getExpression().accept(*this); |
|
|
|
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB |
|
|
|
<< eth::Instruction::CALL |
|
|
|
<< eth::Instruction::POP; |
|
|
|
{ |
|
|
|
FunctionCallOptions options; |
|
|
|
options.bare = true; |
|
|
|
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); }; |
|
|
|
options.obtainValue = [&]() { arguments.front()->accept(*this); }; |
|
|
|
appendExternalFunctionCall(FunctionType({}, {}, Location::EXTERNAL), {}, options); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Location::SUICIDE: |
|
|
|
arguments.front()->accept(*this); |
|
|
|
//@todo might not be necessary
|
|
|
@ -292,19 +265,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) |
|
|
|
{Location::SHA256, 2}, |
|
|
|
{Location::RIPEMD160, 3}}; |
|
|
|
u256 contractAddress = contractAddresses.find(function.getLocation())->second; |
|
|
|
// @todo later, combine this code with external function call
|
|
|
|
for (unsigned i = 0; i < arguments.size(); ++i) |
|
|
|
{ |
|
|
|
arguments[i]->accept(*this); |
|
|
|
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); |
|
|
|
// @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); |
|
|
|
FunctionCallOptions options; |
|
|
|
options.bare = true; |
|
|
|
options.obtainAddress = [&]() { m_context << contractAddress; }; |
|
|
|
options.packDensely = false; |
|
|
|
appendExternalFunctionCall(function, arguments, options); |
|
|
|
break; |
|
|
|
} |
|
|
|
default: |
|
|
@ -326,11 +291,9 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) |
|
|
|
IntegerType(0, IntegerType::Modifier::ADDRESS), true); |
|
|
|
m_context << eth::Instruction::BALANCE; |
|
|
|
} |
|
|
|
else if (member == "send") |
|
|
|
{ |
|
|
|
else if (member == "send" || member.substr(0, min<size_t>(member.size(), 4)) == "call") |
|
|
|
appendTypeConversion(*_memberAccess.getExpression().getType(), |
|
|
|
IntegerType(0, IntegerType::Modifier::ADDRESS), true); |
|
|
|
} |
|
|
|
else |
|
|
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); |
|
|
|
break; |
|
|
@ -587,6 +550,53 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack) |
|
|
|
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, |
|
|
|
unsigned _baseStackOffset): |
|
|
|
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset), |
|
|
|