mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Arbitrary parameters for call() and all hash functions.
This commit is contained in:
parent
bb6f181d7d
commit
cf4144b702
9
AST.cpp
9
AST.cpp
@ -497,20 +497,21 @@ void FunctionCall::checkTypeRequirements()
|
||||
// and then ask if that is implicitly convertible to the struct represented by the
|
||||
// function parameters
|
||||
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."));
|
||||
|
||||
if (m_names.empty())
|
||||
{
|
||||
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]))
|
||||
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call."));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (functionType->getLocation() == FunctionType::Location::SHA3)
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for SHA3."));
|
||||
if (functionType->takesArbitraryParameters())
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
|
||||
"that take arbitrary parameters."));
|
||||
auto const& parameterNames = functionType->getParameterNames();
|
||||
if (parameterNames.size() != m_names.size())
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
|
||||
|
@ -206,7 +206,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
TypePointers const& parameterTypes = function.getParameterTypes();
|
||||
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
|
||||
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
|
||||
if (function.getLocation() != Location::SHA3)
|
||||
if (!function.takesArbitraryParameters())
|
||||
solAssert(callArguments.size() == parameterTypes.size(), "");
|
||||
|
||||
vector<ASTPointer<Expression const>> arguments;
|
||||
@ -318,7 +318,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
appendTypeConversion(*arguments.front()->getType(),
|
||||
*function.getParameterTypes().front(), true);
|
||||
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
|
||||
Location::External, true, true), {}, true);
|
||||
Location::External, false, true, true), {}, true);
|
||||
break;
|
||||
case Location::Suicide:
|
||||
arguments.front()->accept(*this);
|
||||
@ -327,7 +327,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@ -700,21 +700,30 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
|
||||
|
||||
if (stackTypeCategory == Type::Category::String)
|
||||
{
|
||||
StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
|
||||
if (targetTypeCategory == Type::Category::Integer)
|
||||
{
|
||||
// conversion from string to hash. no need to clean the high bit
|
||||
// only to shift right because of opposite alignment
|
||||
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.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same.");
|
||||
m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear lower-order bytes for conversion to shorter strings - we always clean
|
||||
solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested.");
|
||||
// nothing to do, strings are high-order-bit-aligned
|
||||
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
|
||||
StaticStringType const& targetType = dynamic_cast<StaticStringType const&>(_targetType);
|
||||
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 ||
|
||||
@ -776,7 +785,8 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
|
||||
vector<ASTPointer<Expression const>> const& _arguments,
|
||||
bool bare)
|
||||
{
|
||||
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
|
||||
solAssert(_functionType.takesArbitraryParameters() ||
|
||||
_arguments.size() == _functionType.getParameterTypes().size(), "");
|
||||
|
||||
// Assumed stack content here:
|
||||
// <stack top>
|
||||
@ -800,7 +810,10 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
|
||||
|
||||
// reserve space for the function identifier
|
||||
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
|
||||
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,
|
||||
TypePointers const& _types,
|
||||
unsigned _memoryOffset,
|
||||
bool _padToWordBoundaries)
|
||||
bool _padToWordBoundaries,
|
||||
bool _padExceptionIfFourBytes)
|
||||
{
|
||||
solAssert(_types.empty() || _types.size() == _arguments.size(), "");
|
||||
unsigned length = 0;
|
||||
@ -848,8 +862,12 @@ unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expre
|
||||
_arguments[i]->accept(*this);
|
||||
TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i];
|
||||
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(),
|
||||
_memoryOffset + length, _padToWordBoundaries);
|
||||
_memoryOffset + length, pad);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
@ -97,7 +97,8 @@ private:
|
||||
unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
|
||||
TypePointers const& _types = {},
|
||||
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
|
||||
/// @returns the number of bytes moved to memory
|
||||
unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset,
|
||||
|
@ -40,7 +40,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
|
||||
make_shared<MagicVariableDeclaration>("suicide",
|
||||
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
|
||||
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<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)),
|
||||
make_shared<MagicVariableDeclaration>("log1",
|
||||
@ -52,11 +52,11 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
|
||||
make_shared<MagicVariableDeclaration>("log4",
|
||||
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)),
|
||||
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<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)),
|
||||
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
Types.cpp
15
Types.cpp
@ -210,10 +210,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
|
||||
|
||||
const MemberList IntegerType::AddressMemberList =
|
||||
MemberList({{"balance", make_shared<IntegerType >(256)},
|
||||
{"callstring32", make_shared<FunctionType>(strings{"string32"}, strings{},
|
||||
FunctionType::Location::Bare)},
|
||||
{"callstring32string32", make_shared<FunctionType>(strings{"string32", "string32"},
|
||||
strings{}, FunctionType::Location::Bare)},
|
||||
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)},
|
||||
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}});
|
||||
|
||||
IntegerConstantType::IntegerConstantType(Literal const& _literal)
|
||||
@ -401,13 +398,16 @@ bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
|
||||
bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
if (_convertTo.getCategory() == getCategory())
|
||||
return true;
|
||||
if (_convertTo.getCategory() == Category::Integer)
|
||||
{
|
||||
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
|
||||
if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits()))
|
||||
return true;
|
||||
}
|
||||
return isImplicitlyConvertibleTo(_convertTo);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StaticStringType::operator==(Type const& _other) const
|
||||
@ -766,10 +766,10 @@ MemberList const& FunctionType::getMembers() const
|
||||
map<string, TypePointer> members{
|
||||
{"gas", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
|
||||
TypePointers{copyAndSetGasOrValue(true, false)},
|
||||
Location::SetGas, m_gasSet, m_valueSet)},
|
||||
Location::SetGas, false, m_gasSet, m_valueSet)},
|
||||
{"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
|
||||
TypePointers{copyAndSetGasOrValue(false, true)},
|
||||
Location::SetValue, m_gasSet, m_valueSet)}};
|
||||
Location::SetValue, false, m_gasSet, m_valueSet)}};
|
||||
if (m_location == Location::Creation)
|
||||
members.erase("gas");
|
||||
m_members.reset(new MemberList(members));
|
||||
@ -808,6 +808,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
||||
TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
|
||||
{
|
||||
return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location,
|
||||
m_arbitraryParameters,
|
||||
m_gasSet || _setGas, m_valueSet || _setValue);
|
||||
}
|
||||
|
||||
|
14
Types.h
14
Types.h
@ -367,14 +367,15 @@ public:
|
||||
explicit FunctionType(VariableDeclaration const& _varDecl);
|
||||
explicit FunctionType(EventDefinition const& _event);
|
||||
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
|
||||
Location _location = Location::Internal):
|
||||
Location _location = Location::Internal, bool _arbitraryParameters = false):
|
||||
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
|
||||
_location) {}
|
||||
_location, _arbitraryParameters) {}
|
||||
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
|
||||
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_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; }
|
||||
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
|
||||
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 valueSet() const { return m_valueSet; }
|
||||
|
||||
@ -422,6 +426,8 @@ private:
|
||||
std::vector<std::string> m_parameterNames;
|
||||
std::vector<std::string> m_returnParameterNames;
|
||||
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_valueSet = false; ///< true iff the value to be sent is on the stack
|
||||
bool m_isConstant;
|
||||
|
Loading…
Reference in New Issue
Block a user