From c2709a2d8e53155513fa8002a564e434fce68c68 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 11:07:30 +0100 Subject: Decode dynamic data. --- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/codegen/ABIFunctions.cpp | 3 ++ libsolidity/codegen/CompilerUtils.cpp | 4 +-- libsolidity/codegen/ExpressionCompiler.cpp | 57 ++++++++++++++++++++++-------- 4 files changed, 48 insertions(+), 18 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c8e64c78..999a2a97 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1551,7 +1551,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) _functionCall.expression().annotation().isPure && functionType->isPure(); - bool allowDynamicTypes = false; // @TODO + bool allowDynamicTypes = m_evmVersion.supportsReturndata(); if (!functionType) { m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 00f59065..8e890854 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -253,6 +253,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) templ("body", w.render()); break; } + case Type::Category::InaccessibleDynamic: + templ("body", "cleaned := 0"); + break; default: solAssert(false, "Cleanup of type " + _type.identifier() + " requested."); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index d9177312..1bd1103b 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -198,7 +198,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem if (type->category() == Type::Category::Array) { auto const& arrayType = dynamic_cast(*type); - solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + solUnimplementedAssert(!arrayType.baseType()->isDynamicallyEncoded(), "Nested arrays not yet implemented."); if (_fromMemory) { solUnimplementedAssert( @@ -308,7 +308,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem } else { - solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); + solAssert(!type->isDynamicallyEncoded(), "Unknown dynamically sized type: " + type->toString()); loadFromMemoryDynamic(*type, !_fromMemory, true); // stack: v1 v2 ... v(k-1) input_end base_offset v(k) mem_offset moveToStackTop(1, type->sizeOnStack()); 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(retType.get())) + needToUpdateFreeMemoryPtr = true; + + // Stack: return_data_start + if (dynamicReturnSize) { - utils().loadFromMemoryDynamic(*retType, false, true, true); - if (dynamic_cast(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); } } -- cgit v1.2.3