diff options
Diffstat (limited to 'libsolidity/CompilerUtils.cpp')
-rw-r--r-- | libsolidity/CompilerUtils.cpp | 69 |
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) |