diff options
Diffstat (limited to 'libsolidity/codegen/ExpressionCompiler.cpp')
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 57 |
1 files changed, 42 insertions, 15 deletions
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7d148c0c..37069c3e 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1618,22 +1618,27 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context.experimentalFeatureActive(ExperimentalFeature::V050) && m_context.evmVersion().hasStaticCall(); - bool allowDynamicTypes = false; // @TODO + bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); unsigned retSize = 0; TypePointers returnTypes; if (returnSuccessCondition) retSize = 0; // return value actually is success condition - else if (allowDynamicTypes) + else if (haveReturndatacopy) returnTypes = _functionType.returnParameterTypes(); else - { returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes(); - for (auto const& retType: returnTypes) + + bool dynamicReturnSize = false; + for (auto const& retType: returnTypes) + if (retType->isDynamicallyEncoded()) { - solAssert(!retType->isDynamicallySized(), "Unable to return dynamic type from external call."); - retSize += retType->calldataEncodedSize(); + solAssert(haveReturndatacopy, ""); + dynamicReturnSize = true; + retSize = 0; + break; } - } + else + retSize += retType->calldataEncodedSize(); // Evaluate arguments. TypePointers argumentTypes; @@ -1834,17 +1839,39 @@ void ExpressionCompiler::appendExternalFunctionCall( else if (!returnTypes.empty()) { utils().fetchFreeMemoryPointer(); - bool memoryNeeded = false; - for (auto const& retType: returnTypes) + // Stack: return_data_start + + // The old decoder did not allocate any memory (i.e. did not touch the free + // memory pointer), but kept references to the return data for + // (statically-sized) arrays + bool needToUpdateFreeMemoryPtr = false; + if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + needToUpdateFreeMemoryPtr = true; + else + for (auto const& retType: returnTypes) + if (dynamic_cast<ReferenceType const*>(retType.get())) + needToUpdateFreeMemoryPtr = true; + + // Stack: return_data_start + if (dynamicReturnSize) { - utils().loadFromMemoryDynamic(*retType, false, true, true); - if (dynamic_cast<ReferenceType const*>(retType.get())) - memoryNeeded = true; + solAssert(haveReturndatacopy, ""); + m_context.appendInlineAssembly("{ returndatacopy(return_data_start, 0, returndatasize()) }", {"return_data_start"}); } - if (memoryNeeded) - utils().storeFreeMemoryPointer(); else - m_context << Instruction::POP; + solAssert(retSize > 0, ""); + // Always use the actual return length, and not our calculated expected length, if returndatacopy is supported. + // This ensures it can catch badly formatted input from external calls. + m_context << (haveReturndatacopy ? eth::AssemblyItem(Instruction::RETURNDATASIZE) : u256(retSize)); + // Stack: return_data_start return_data_size + if (needToUpdateFreeMemoryPtr) + m_context.appendInlineAssembly(R"({ + // round size to the next multiple of 32 + let newMem := add(start, and(add(size, 0x1f), not(0x1f))) + mstore(0x40, newMem) + })", {"start", "size"}); + + utils().abiDecode(returnTypes, true, true); } } |