aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp11
-rw-r--r--CompilerUtils.cpp7
-rw-r--r--CompilerUtils.h2
-rw-r--r--ExpressionCompiler.cpp91
-rw-r--r--ExpressionCompiler.h18
-rw-r--r--Types.cpp6
-rw-r--r--Types.h6
7 files changed, 111 insertions, 30 deletions
diff --git a/AST.cpp b/AST.cpp
index 78b83d06..59a7b61c 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -450,14 +450,11 @@ void FunctionDefinition::checkTypeRequirements()
{
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
+ // todo delete when will be implemented arrays as parameter type in internal functions
+ if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array)
+ BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions."));
if (getVisibility() >= Visibility::Public && !(var->getType()->externalType()))
- {
- // todo delete when will be implemented arrays as parameter type in internal functions
- if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array)
- BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions."));
- else
- BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions."));
- }
+ BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions."));
}
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements(isConstructor() ?
diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp
index 8d3e9d2a..07bc3cda 100644
--- a/CompilerUtils.cpp
+++ b/CompilerUtils.cpp
@@ -155,6 +155,13 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
m_context << eth::dupInstruction(_stackDepth);
}
+void CompilerUtils::moveToStackTop(unsigned _stackDepth)
+{
+ solAssert(_stackDepth <= 15, "Stack too deep.");
+ for (unsigned i = 0; i < _stackDepth; ++i)
+ m_context << eth::swapInstruction(1 + i);
+}
+
void CompilerUtils::popStackElement(Type const& _type)
{
popStackSlots(_type.getSizeOnStack());
diff --git a/CompilerUtils.h b/CompilerUtils.h
index 5b809bea..45f53e12 100644
--- a/CompilerUtils.h
+++ b/CompilerUtils.h
@@ -77,6 +77,8 @@ public:
/// Copies an item that occupies @a _itemSize stack slots from a stack depth of @a _stackDepth
/// to the top of the stack.
void copyToStackTop(unsigned _stackDepth, unsigned _itemSize);
+ /// Moves a single stack element (with _stackDepth items on top of it) to the top of the stack.
+ void moveToStackTop(unsigned _stackDepth);
/// Removes the current value from the top of the stack.
void popStackElement(Type const& _type);
/// Removes element from the top of the stack _amount times.
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index cf6a01ec..a11c9944 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -532,7 +532,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case Location::SHA3:
{
m_context << u256(0);
- appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments());
+ appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments(), false, true);
m_context << u256(0) << eth::Instruction::SHA3;
break;
}
@@ -575,9 +575,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
solAssert(numIndexed <= 4, "Too many indexed arguments.");
// Copy all non-indexed arguments to memory (data)
m_context << u256(0);
+ vector<ASTPointer<Expression const>> nonIndexedArgs;
+ TypePointers nonIndexedTypes;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
- appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]);
+ {
+ nonIndexedArgs.push_back(arguments[arg]);
+ nonIndexedTypes.push_back(function.getParameterTypes()[arg]);
+ }
+ appendArgumentsCopyToMemory(nonIndexedArgs, nonIndexedTypes);
m_context << u256(0) << eth::logInstruction(numIndexed);
break;
}
@@ -1046,8 +1052,14 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
// For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes.
- appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(),
- _functionType.padArguments(), bare);
+ // If the function takes arbitrary parameters, copy dynamic length data in place.
+ appendArgumentsCopyToMemory(
+ _arguments,
+ _functionType.getParameterTypes(),
+ _functionType.padArguments(),
+ bare,
+ _functionType.takesArbitraryParameters()
+ );
// CALL arguments: outSize, outOff, inSize, (already present up to here)
// inOff, value, addr, gas (stack top)
@@ -1089,20 +1101,72 @@ void ExpressionCompiler::appendArgumentsCopyToMemory(
vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types,
bool _padToWordBoundaries,
- bool _padExceptionIfFourBytes
+ bool _padExceptionIfFourBytes,
+ bool _copyDynamicDataInPlace
)
{
solAssert(_types.empty() || _types.size() == _arguments.size(), "");
+ TypePointers types = _types;
+ if (_types.empty())
+ for (ASTPointer<Expression const> const& argument: _arguments)
+ types.push_back(argument->getType()->getRealType());
+
+ vector<size_t> dynamicArguments;
+ unsigned stackSizeOfDynamicTypes = 0;
for (size_t i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
- TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i];
- appendTypeConversion(*_arguments[i]->getType(), *expectedType, true);
+ TypePointer argType = types[i]->externalType();
+ solAssert(!!argType, "Externalable type expected.");
+ if (argType->isValueType())
+ appendTypeConversion(*_arguments[i]->getType(), *argType, true);
+ else
+ argType = _arguments[i]->getType()->getRealType()->externalType();
+ solAssert(!!argType, "Externalable type expected.");
bool pad = _padToWordBoundaries;
// Do not pad if the first argument has exactly four bytes
- if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize(false) == 4)
+ if (i == 0 && pad && _padExceptionIfFourBytes && argType->getCalldataEncodedSize(false) == 4)
pad = false;
- appendTypeMoveToMemory(*expectedType, pad);
+ if (!_copyDynamicDataInPlace && argType->isDynamicallySized())
+ {
+ solAssert(argType->getCategory() == Type::Category::Array, "Unknown dynamic type.");
+ auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
+ // move memory reference to top of stack
+ CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
+ if (arrayType.getLocation() == ArrayType::Location::CallData)
+ m_context << eth::Instruction::DUP2; // length is on stack
+ else if (arrayType.getLocation() == ArrayType::Location::Storage)
+ m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
+ else
+ {
+ solAssert(arrayType.getLocation() == ArrayType::Location::Memory, "");
+ m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
+ }
+ appendTypeMoveToMemory(IntegerType(256), true);
+ stackSizeOfDynamicTypes += arrayType.getSizeOnStack();
+ dynamicArguments.push_back(i);
+ }
+ else
+ appendTypeMoveToMemory(*argType, pad);
+ }
+
+ // copy dynamic values to memory
+ unsigned dynStackPointer = stackSizeOfDynamicTypes;
+ // stack layout: <dyn arg 1> ... <dyn arg m> <memory pointer>
+ for (size_t i: dynamicArguments)
+ {
+ auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
+ CompilerUtils(m_context).copyToStackTop(1 + dynStackPointer, arrayType.getSizeOnStack());
+ dynStackPointer -= arrayType.getSizeOnStack();
+ appendTypeMoveToMemory(arrayType, true);
+ }
+ solAssert(dynStackPointer == 0, "");
+
+ // remove dynamic values (and retain memory pointer)
+ if (stackSizeOfDynamicTypes > 0)
+ {
+ m_context << eth::swapInstruction(stackSizeOfDynamicTypes);
+ CompilerUtils(m_context).popStackSlots(stackSizeOfDynamicTypes);
}
}
@@ -1114,8 +1178,13 @@ void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWo
void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
{
_expression.accept(*this);
- appendTypeConversion(*_expression.getType(), _expectedType, true);
- appendTypeMoveToMemory(_expectedType);
+ if (_expectedType.isValueType())
+ {
+ appendTypeConversion(*_expression.getType(), _expectedType, true);
+ appendTypeMoveToMemory(_expectedType);
+ }
+ else
+ appendTypeMoveToMemory(*_expression.getType()->getRealType());
}
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index 2577d21b..35526662 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -100,12 +100,18 @@ private:
/// 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,
bool bare = false);
- /// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is
- /// expected to be on the stack and is updated by this call.
- void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
- TypePointers const& _types = {},
- bool _padToWordBoundaries = true,
- bool _padExceptionIfFourBytes = false);
+ /// Appends code that evaluates the given arguments and moves the result to memory encoded as
+ /// specified by the ABI. The memory offset is expected to be on the stack and is updated by
+ /// this call. If @a _padToWordBoundaries is set to false, all values are concatenated without
+ /// padding. If @a _copyDynamicDataInPlace is set, dynamic types is stored (without length)
+ /// together with fixed-length data.
+ void appendArgumentsCopyToMemory(
+ std::vector<ASTPointer<Expression const>> const& _arguments,
+ TypePointers const& _types = {},
+ bool _padToWordBoundaries = true,
+ bool _padExceptionIfFourBytes = false,
+ bool _copyDynamicDataInPlace = false
+ );
/// Appends code that moves a stack element of the given type to memory. The memory offset is
/// expected below the stack element and is updated by this call.
void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
diff --git a/Types.cpp b/Types.cpp
index a445d56e..19bc134e 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -745,8 +745,6 @@ string ArrayType::toString() const
TypePointer ArrayType::externalType() const
{
- if (m_location != Location::CallData)
- return TypePointer();
if (m_isByteArray)
return shared_from_this();
if (!m_baseType->externalType())
@@ -1218,7 +1216,9 @@ string FunctionType::externalSignature(std::string const& _name) const
}
string ret = funcName + "(";
- TypePointers externalParameterTypes = externalFunctionType()->getParameterTypes();
+ FunctionTypePointer external = externalFunctionType();
+ solAssert(!!external, "External function type requested.");
+ TypePointers externalParameterTypes = external->getParameterTypes();
for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it)
{
solAssert(!!(*it), "Parameter should have external type");
diff --git a/Types.h b/Types.h
index ab41d4d4..cc9c455f 100644
--- a/Types.h
+++ b/Types.h
@@ -430,7 +430,7 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
- virtual unsigned getCalldataEncodedSize(bool _padded = true) const override
+ virtual unsigned getCalldataEncodedSize(bool _padded ) const override
{
return externalType()->getCalldataEncodedSize(_padded);
}
@@ -506,7 +506,7 @@ public:
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
- virtual unsigned getCalldataEncodedSize(bool _padded = true) const override
+ virtual unsigned getCalldataEncodedSize(bool _padded) const override
{
return externalType()->getCalldataEncodedSize(_padded);
}
@@ -558,7 +558,7 @@ public:
/// appropriate external types of input/return parameters of current function.
/// Returns an empty shared pointer if one of the input/return parameters does not have an
/// external type.
- virtual FunctionTypePointer externalFunctionType() const;
+ FunctionTypePointer externalFunctionType() const;
virtual TypePointer externalType() const override { return externalFunctionType(); }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);