aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/CompilerUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/CompilerUtils.cpp')
-rw-r--r--libsolidity/CompilerUtils.cpp69
1 files changed, 63 insertions, 6 deletions
diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp
index e1152202..f0dea708 100644
--- a/libsolidity/CompilerUtils.cpp
+++ b/libsolidity/CompilerUtils.cpp
@@ -550,6 +550,61 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
}
break;
}
+ case Type::Category::Tuple:
+ {
+ TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack);
+ TupleType const& targetTuple = dynamic_cast<TupleType const&>(_targetType);
+ // fillRight: remove excess values at right side, !fillRight: remove eccess values at left side
+ bool fillRight = !targetTuple.components().empty() && (
+ !targetTuple.components().back() ||
+ targetTuple.components().front()
+ );
+ unsigned depth = sourceTuple.sizeOnStack();
+ for (size_t i = 0; i < sourceTuple.components().size(); ++i)
+ {
+ TypePointer sourceType = sourceTuple.components()[i];
+ TypePointer targetType;
+ if (fillRight && i < targetTuple.components().size())
+ targetType = targetTuple.components()[i];
+ else if (!fillRight && targetTuple.components().size() + i >= sourceTuple.components().size())
+ targetType = targetTuple.components()[targetTuple.components().size() - (sourceTuple.components().size() - i)];
+ if (!sourceType)
+ {
+ solAssert(!targetType, "");
+ continue;
+ }
+ unsigned sourceSize = sourceType->sizeOnStack();
+ unsigned targetSize = targetType ? targetType->sizeOnStack() : 0;
+ if (!targetType || *sourceType != *targetType || _cleanupNeeded)
+ {
+ if (targetType)
+ {
+ if (sourceSize > 0)
+ copyToStackTop(depth, sourceSize);
+ convertType(*sourceType, *targetType, _cleanupNeeded);
+ }
+ if (sourceSize > 0 || targetSize > 0)
+ {
+ // Move it back into its place.
+ for (unsigned j = 0; j < min(sourceSize, targetSize); ++j)
+ m_context <<
+ eth::swapInstruction(depth + targetSize - sourceSize) <<
+ eth::Instruction::POP;
+ // Value shrank
+ for (unsigned j = targetSize; j < sourceSize; ++j)
+ {
+ moveToStackTop(depth - 1, 1);
+ m_context << eth::Instruction::POP;
+ }
+ // Value grew
+ if (targetSize > sourceSize)
+ moveIntoStack(depth + targetSize - sourceSize, targetSize - sourceSize);
+ }
+ }
+ depth -= sourceSize;
+ }
+ break;
+ }
default:
// All other types should not be convertible to non-equal types.
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
@@ -631,18 +686,20 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
m_context << eth::dupInstruction(_stackDepth);
}
-void CompilerUtils::moveToStackTop(unsigned _stackDepth)
+void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize)
{
solAssert(_stackDepth <= 15, "Stack too deep, try removing local variables.");
- for (unsigned i = 0; i < _stackDepth; ++i)
- m_context << eth::swapInstruction(1 + i);
+ for (unsigned j = 0; j < _itemSize; ++j)
+ for (unsigned i = 0; i < _stackDepth + _itemSize - 1; ++i)
+ m_context << eth::swapInstruction(1 + i);
}
-void CompilerUtils::moveIntoStack(unsigned _stackDepth)
+void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
{
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
- for (unsigned i = _stackDepth; i > 0; --i)
- m_context << eth::swapInstruction(i);
+ for (unsigned j = 0; j < _itemSize; ++j)
+ for (unsigned i = _stackDepth; i > 0; --i)
+ m_context << eth::swapInstruction(i + _itemSize - 1);
}
void CompilerUtils::popStackElement(Type const& _type)