aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp9
-rw-r--r--ExpressionCompiler.cpp38
-rw-r--r--ExpressionCompiler.h3
-rw-r--r--GlobalContext.cpp6
-rw-r--r--Types.cpp15
-rw-r--r--Types.h14
6 files changed, 56 insertions, 29 deletions
diff --git a/AST.cpp b/AST.cpp
index 86f54e25..041b6277 100644
--- a/AST.cpp
+++ b/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."));
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index f649f1a4..1d3f23e6 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -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;
}
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index df8a0516..3c94d74c 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -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,
diff --git a/GlobalContext.cpp b/GlobalContext.cpp
index 8443e7bd..60de5105 100644
--- a/GlobalContext.cpp
+++ b/GlobalContext.cpp
@@ -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))})
{
}
diff --git a/Types.cpp b/Types.cpp
index 02c293b7..6fd9e8b4 100644
--- a/Types.cpp
+++ b/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);
}
diff --git a/Types.h b/Types.h
index d0978295..05090c7a 100644
--- a/Types.h
+++ b/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;