aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian <c@ethdev.com>2014-12-11 06:01:40 +0800
committerChristian <c@ethdev.com>2014-12-13 00:26:19 +0800
commitc0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5 (patch)
treeb3fa2b6b006b6d1b9799abe9e2ed25ddc685d370
parent6893d4d4557e9968feaa162c8fc5ea3859aa0565 (diff)
downloaddexon-solidity-c0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5.tar
dexon-solidity-c0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5.tar.gz
dexon-solidity-c0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5.tar.bz2
dexon-solidity-c0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5.tar.lz
dexon-solidity-c0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5.tar.xz
dexon-solidity-c0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5.tar.zst
dexon-solidity-c0bba438b1d93b3ebba46e03935dbdf8b1ba6bd5.zip
Calls to bare contracts.
-rw-r--r--ExpressionCompiler.cpp122
-rw-r--r--ExpressionCompiler.h20
-rw-r--r--Types.cpp7
-rw-r--r--Types.h3
4 files changed, 95 insertions, 57 deletions
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 3beb423d..add5f73b 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -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, 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),
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index c0b173d4..0bba211c 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -20,6 +20,7 @@
* Solidity AST to EVM bytecode compiler for expressions.
*/
+#include <functional>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libsolidity/ASTVisitor.h>
@@ -83,6 +84,25 @@ private:
//// Appends code that cleans higher-order bits for integer types.
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.
* All types except STACK store a reference in a slot on the stack, STACK just
diff --git a/Types.cpp b/Types.cpp
index 00e530c3..a0a809b9 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -194,6 +194,11 @@ u256 IntegerType::literalValue(Literal const& _literal) const
const MemberList IntegerType::AddressMemberList =
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)}),
TypePointers(), FunctionType::Location::SEND)}});
@@ -424,6 +429,8 @@ unsigned FunctionType::getSizeOnStack() const
return 1;
case Location::EXTERNAL:
return 2;
+ case Location::BARE:
+ return 1;
default:
return 0;
}
diff --git a/Types.h b/Types.h
index 8e93bd31..807e60c7 100644
--- a/Types.h
+++ b/Types.h
@@ -296,8 +296,9 @@ class FunctionType: public Type
public:
/// The meaning of the value(s) on the stack referencing the function:
/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
+ /// BARE: contract address (non-abi contract call)
/// 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; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);