Browse Source

Arbitrary parameters for call() and all hash functions.

cl-refactor
Christian 10 years ago
parent
commit
8b3dec6773
  1. 9
      libsolidity/AST.cpp
  2. 38
      libsolidity/ExpressionCompiler.cpp
  3. 3
      libsolidity/ExpressionCompiler.h
  4. 6
      libsolidity/GlobalContext.cpp
  5. 15
      libsolidity/Types.cpp
  6. 14
      libsolidity/Types.h
  7. 23
      test/SolidityEndToEndTest.cpp

9
libsolidity/AST.cpp

@ -497,20 +497,21 @@ void FunctionCall::checkTypeRequirements()
// and then ask if that is implicitly convertible to the struct represented by the // and then ask if that is implicitly convertible to the struct represented by the
// function parameters // function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes(); TypePointers const& parameterTypes = functionType->getParameterTypes();
if (functionType->getLocation() != FunctionType::Location::SHA3 && parameterTypes.size() != m_arguments.size()) if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
if (m_names.empty()) if (m_names.empty())
{ {
for (size_t i = 0; i < m_arguments.size(); ++i) for (size_t i = 0; i < m_arguments.size(); ++i)
if (functionType->getLocation() != FunctionType::Location::SHA3 && if (!functionType->takesArbitraryParameters() &&
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) !m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call.")); BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call."));
} }
else else
{ {
if (functionType->getLocation() == FunctionType::Location::SHA3) if (functionType->takesArbitraryParameters())
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for SHA3.")); BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
"that take arbitrary parameters."));
auto const& parameterNames = functionType->getParameterNames(); auto const& parameterNames = functionType->getParameterNames();
if (parameterNames.size() != m_names.size()) if (parameterNames.size() != m_names.size())
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing.")); BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));

38
libsolidity/ExpressionCompiler.cpp

@ -206,7 +206,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
TypePointers const& parameterTypes = function.getParameterTypes(); TypePointers const& parameterTypes = function.getParameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments(); vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames(); vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
if (function.getLocation() != Location::SHA3) if (!function.takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), ""); solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments; vector<ASTPointer<Expression const>> arguments;
@ -318,7 +318,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
appendTypeConversion(*arguments.front()->getType(), appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true); *function.getParameterTypes().front(), true);
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
Location::External, true, true), {}, true); Location::External, false, true, true), {}, true);
break; break;
case Location::Suicide: case Location::Suicide:
arguments.front()->accept(*this); arguments.front()->accept(*this);
@ -327,7 +327,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break; break;
case Location::SHA3: case Location::SHA3:
{ {
unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, false); unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, function.padArguments());
m_context << u256(length) << u256(0) << eth::Instruction::SHA3; m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
break; break;
} }
@ -700,21 +700,30 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
if (stackTypeCategory == Type::Category::String) if (stackTypeCategory == Type::Category::String)
{ {
StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
if (targetTypeCategory == Type::Category::Integer) if (targetTypeCategory == Type::Category::Integer)
{ {
// conversion from string to hash. no need to clean the high bit // conversion from string to hash. no need to clean the high bit
// only to shift right because of opposite alignment // only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType); IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed."); solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed.");
solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same.");
m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
} }
else else
{ {
// clear lower-order bytes for conversion to shorter strings - we always clean
solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested."); solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested.");
// nothing to do, strings are high-order-bit-aligned StaticStringType const& targetType = dynamic_cast<StaticStringType const&>(_targetType);
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings if (targetType.getNumBytes() < typeOnStack.getNumBytes())
{
if (targetType.getNumBytes() == 0)
m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
else
m_context << (u256(1) << (256 - targetType.getNumBytes() * 8))
<< eth::Instruction::DUP1 << eth::Instruction::SWAP2
<< eth::Instruction::DIV << eth::Instruction::MUL;
}
} }
} }
else if (stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::Contract || else if (stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::Contract ||
@ -776,7 +785,8 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
vector<ASTPointer<Expression const>> const& _arguments, vector<ASTPointer<Expression const>> const& _arguments,
bool bare) bool bare)
{ {
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), ""); solAssert(_functionType.takesArbitraryParameters() ||
_arguments.size() == _functionType.getParameterTypes().size(), "");
// Assumed stack content here: // Assumed stack content here:
// <stack top> // <stack top>
@ -800,7 +810,10 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
// reserve space for the function identifier // reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset; unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset); // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes.
dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset,
_functionType.padArguments(), bare);
//@todo only return the first return value for now //@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
@ -839,7 +852,8 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments, unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types, TypePointers const& _types,
unsigned _memoryOffset, unsigned _memoryOffset,
bool _padToWordBoundaries) bool _padToWordBoundaries,
bool _padExceptionIfFourBytes)
{ {
solAssert(_types.empty() || _types.size() == _arguments.size(), ""); solAssert(_types.empty() || _types.size() == _arguments.size(), "");
unsigned length = 0; unsigned length = 0;
@ -848,8 +862,12 @@ unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expre
_arguments[i]->accept(*this); _arguments[i]->accept(*this);
TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i]; TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i];
appendTypeConversion(*_arguments[i]->getType(), *expectedType, true); appendTypeConversion(*_arguments[i]->getType(), *expectedType, true);
bool pad = _padToWordBoundaries;
// Do not pad if the first argument has exactly four bytes
if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4)
pad = false;
length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(), length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(),
_memoryOffset + length, _padToWordBoundaries); _memoryOffset + length, pad);
} }
return length; return length;
} }

3
libsolidity/ExpressionCompiler.h

@ -97,7 +97,8 @@ private:
unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments, unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {}, TypePointers const& _types = {},
unsigned _memoryOffset = 0, unsigned _memoryOffset = 0,
bool _padToWordBoundaries = true); bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false);
/// Appends code that moves a stack element of the given type to memory /// Appends code that moves a stack element of the given type to memory
/// @returns the number of bytes moved to memory /// @returns the number of bytes moved to memory
unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset,

6
libsolidity/GlobalContext.cpp

@ -40,7 +40,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("suicide", make_shared<MagicVariableDeclaration>("suicide",
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)), make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
make_shared<MagicVariableDeclaration>("sha3", make_shared<MagicVariableDeclaration>("sha3",
make_shared<FunctionType>(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA3)), make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)),
make_shared<MagicVariableDeclaration>("log0", make_shared<MagicVariableDeclaration>("log0",
make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)), make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)),
make_shared<MagicVariableDeclaration>("log1", make_shared<MagicVariableDeclaration>("log1",
@ -52,11 +52,11 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("log4", make_shared<MagicVariableDeclaration>("log4",
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)), make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)),
make_shared<MagicVariableDeclaration>("sha256", make_shared<MagicVariableDeclaration>("sha256",
make_shared<FunctionType>(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA256)), make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)),
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)),
make_shared<MagicVariableDeclaration>("ripemd160", make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(strings{"hash"}, strings{"hash160"}, FunctionType::Location::RIPEMD160))}) make_shared<FunctionType>(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))})
{ {
} }

15
libsolidity/Types.cpp

@ -210,10 +210,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
const MemberList IntegerType::AddressMemberList = const MemberList IntegerType::AddressMemberList =
MemberList({{"balance", make_shared<IntegerType >(256)}, MemberList({{"balance", make_shared<IntegerType >(256)},
{"callstring32", make_shared<FunctionType>(strings{"string32"}, strings{}, {"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)},
FunctionType::Location::Bare)},
{"callstring32string32", make_shared<FunctionType>(strings{"string32", "string32"},
strings{}, FunctionType::Location::Bare)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}}); {"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}});
IntegerConstantType::IntegerConstantType(Literal const& _literal) IntegerConstantType::IntegerConstantType(Literal const& _literal)
@ -401,13 +398,16 @@ bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
if (_convertTo.getCategory() == getCategory())
return true;
if (_convertTo.getCategory() == Category::Integer) if (_convertTo.getCategory() == Category::Integer)
{ {
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits())) if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits()))
return true; return true;
} }
return isImplicitlyConvertibleTo(_convertTo);
return false;
} }
bool StaticStringType::operator==(Type const& _other) const bool StaticStringType::operator==(Type const& _other) const
@ -766,10 +766,10 @@ MemberList const& FunctionType::getMembers() const
map<string, TypePointer> members{ map<string, TypePointer> members{
{"gas", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}), {"gas", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
TypePointers{copyAndSetGasOrValue(true, false)}, TypePointers{copyAndSetGasOrValue(true, false)},
Location::SetGas, m_gasSet, m_valueSet)}, Location::SetGas, false, m_gasSet, m_valueSet)},
{"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}), {"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
TypePointers{copyAndSetGasOrValue(false, true)}, TypePointers{copyAndSetGasOrValue(false, true)},
Location::SetValue, m_gasSet, m_valueSet)}}; Location::SetValue, false, m_gasSet, m_valueSet)}};
if (m_location == Location::Creation) if (m_location == Location::Creation)
members.erase("gas"); members.erase("gas");
m_members.reset(new MemberList(members)); m_members.reset(new MemberList(members));
@ -808,6 +808,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
{ {
return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location, return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location,
m_arbitraryParameters,
m_gasSet || _setGas, m_valueSet || _setValue); m_gasSet || _setGas, m_valueSet || _setValue);
} }

14
libsolidity/Types.h

@ -367,14 +367,15 @@ public:
explicit FunctionType(VariableDeclaration const& _varDecl); explicit FunctionType(VariableDeclaration const& _varDecl);
explicit FunctionType(EventDefinition const& _event); explicit FunctionType(EventDefinition const& _event);
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
Location _location = Location::Internal): Location _location = Location::Internal, bool _arbitraryParameters = false):
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
_location) {} _location, _arbitraryParameters) {}
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::Internal, Location _location = Location::Internal,
bool _gasSet = false, bool _valueSet = false): bool _arbitraryParameters = false, bool _gasSet = false, bool _valueSet = false):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {} m_location(_location),
m_arbitraryParameters(_arbitraryParameters), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
TypePointers const& getParameterTypes() const { return m_parameterTypes; } TypePointers const& getParameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; } std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
@ -407,6 +408,9 @@ public:
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> getDocumentation() const; ASTPointer<ASTString> getDocumentation() const;
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); }
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
bool gasSet() const { return m_gasSet; } bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; } bool valueSet() const { return m_valueSet; }
@ -422,6 +426,8 @@ private:
std::vector<std::string> m_parameterNames; std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames; std::vector<std::string> m_returnParameterNames;
Location const m_location; Location const m_location;
/// true iff the function takes an arbitrary number of arguments of arbitrary types
bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool m_isConstant; bool m_isConstant;

23
test/SolidityEndToEndTest.cpp

@ -2201,6 +2201,29 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals)
bytes({0x66, 0x6f, 0x6f})))); bytes({0x66, 0x6f, 0x6f}))));
} }
BOOST_AUTO_TEST_CASE(generic_call)
{
char const* sourceCode = R"**(
contract receiver {
uint public received;
function receive(uint256 x) { received = x; }
}
contract sender {
function doSend(address rec) returns (uint d)
{
string4 signature = string4(string32(sha3("receive(uint256)")));
rec.call.value(2)(signature, 23);
return receiver(rec).received();
}
}
)**";
compileAndRun(sourceCode, 0, "receiver");
u160 const c_receiverAddress = m_contractAddress;
compileAndRun(sourceCode, 50, "sender");
BOOST_REQUIRE(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(23));
BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save