-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Use returndatacopy for retrieving dynamically sized outputs. #3308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
2cdf44f
32c94f5
cc2f71e
c2709a2
cc0f702
c7860a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just move this into the two parts of the if?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it is a separate step to always use returndatacopy and not allocate the memory on call.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add a comment here stating:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this was the idea, always use returndatacopy to detect badly formatted return data. Yes, please add the comment.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I think this may warrant its own changelog entry as we're changing behaviour. Compiling for byzantium (which is the default) may result in a new contract not working with an old one, if we had a case of invalid short encoding, or if the other one isn't Solidity and doesn't encode properly. Unlikely, but still. |
||
| // 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); | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have
calldataEncodedSizeandisDynamicallyEncodedon theTupleType, do we need to iterate?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently no, but would it make sense to do so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tuples are not fully-fledged types in Solidity, so I'm not sure which implications this would have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's postpone it. Created issue #3769.