Conversation
| // We do not check the calldata size, everything is zero-padded | ||
|
|
||
| TODO: What about external functions? | ||
| if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) |
There was a problem hiding this comment.
Perhaps we should have named it differently. Do you want to have a second experimental feature name?
There was a problem hiding this comment.
It is a different feature, isn't? Or do you think they would mature the same rate? I mean the question is: would you consider to promote one without the other out of experimental status or would prefer to only promote them together.
There was a problem hiding this comment.
The main feature is being able to call a function on a different contract and pass structs and nested arrays. You always need both the encoder and the decoder.
I also think that the decoder should be easier to write, because you do not have to deal with storage.
| auto ret = m_context.pushNewTag(); | ||
| m_context << Instruction::SWAP1; | ||
|
|
||
| string decoderName = m_context.abiFunctions().tupleDecoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); |
There was a problem hiding this comment.
tupleDecoder is actually missing
There was a problem hiding this comment.
This was just a quick 'save to github in case anything happens over the weekend'. It practically contains no code ;)
9c759b9 to
053c998
Compare
|
Latest version should work for all value types - but does not yet have any tests. |
| * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. | ||
| * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. | ||
| * Code Generator: New ABI decoder which supports structs and arbitrarily nested | ||
| arrays and checks input size (activate using ``pragma experimental ABIEncoderV2;``. |
db4662d to
0a4cfc6
Compare
libsolidity/ast/Types.h
Outdated
| { | ||
| return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : shared_from_this(); | ||
| } | ||
| virtual TypePointer decodingType() const override; |
There was a problem hiding this comment.
encodingType above seems to be the exact same? Also if two functions must exists it is probably better placing them next to each other, either header or code.
There was a problem hiding this comment.
They are different for some types.
There was a problem hiding this comment.
Ah right, but removed it from struct because the default is to use the encoding type.
| case Type::Category::Struct: | ||
| solAssert(false, "Struct cleanup requested."); | ||
| solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type."); | ||
| templ("body", "cleaned := value"); |
There was a problem hiding this comment.
There is no cleanup to be performed for a storage pointer. All 2**256 values are valid.
There was a problem hiding this comment.
So this part of the change is not restricted to this PR?
There was a problem hiding this comment.
the encoder does not call cleanup on structs - the control flow branches off earlier.
There was a problem hiding this comment.
Or actually, better explanation: The encoder has a "source type" and a "target type", while the decoder only has a single type. For structs, the target type is already an integer and not a struct, and thus the cleanup will be called on the integer.
| for (auto const& t: _types) | ||
| functionName += t->identifier(); | ||
| if (_fromMemory) | ||
| functionName += "_fromMemory"; |
There was a problem hiding this comment.
Hm, wondering if we should commit to camelcase or underscores, this one mixes the two.
There was a problem hiding this comment.
underscores are used to separate components.
e11e681 to
4992c5f
Compare
|
Rebased, fixed some stuff, did manual coverage tests and ran against full EndToEnd tests. The only thing left here is the decoder for structs. |
4992c5f to
5cfde8d
Compare
|
@axic this is ready for review. |
|
Needs rebasing since the other ABI PRs were merged. |
libsolidity/codegen/CompilerUtils.h
Outdated
| /// Can allocate memory. | ||
| /// Stack pre: <source_offset> | ||
| /// Stack post: <value0> <value1> ... <valuen> | ||
| void abiDecode(TypePointers const& _parameterTypes, bool _fromMemory = false); |
There was a problem hiding this comment.
For consistency I'd go with abiDecodeV2 for the moment.
| // Use the new JULIA-based decoding function | ||
| auto stackHeightBefore = m_context.stackHeight(); | ||
| CompilerUtils utils(m_context); | ||
| utils.abiDecode(_typeParameters, _fromMemory); |
There was a problem hiding this comment.
Rest of the code uses CompilerUtils(m_context).
| /// into memory. If @a _fromMemory is true, decodes from memory instead of | ||
| /// from calldata. | ||
| /// Can allocate memory. | ||
| /// Inputs: <source_offset> <source_end> (layout reversed on stack) |
There was a problem hiding this comment.
These are the inputs if _fromMemory is true, otherwise no inputs or is it an offset within the calldata?
There was a problem hiding this comment.
these are either pointers to calldata or to memory.
There was a problem hiding this comment.
Also, are those both absolute pointers or is source_end actually source_len?
There was a problem hiding this comment.
Both are absolute pointers.
| /// Outputs: <value0> <value1> ... <valuen> | ||
| /// The values represent stack slots. If a type occupies more or less than one | ||
| /// stack slot, it takes exactly that number of values. | ||
| std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false); |
There was a problem hiding this comment.
So it returns the function name which has a signature of <name>(source_offset, source_end) -> <v0>, <v1>, ..., <vn>. is that correct?
There was a problem hiding this comment.
Cool, I was going to try it in the IR pull request and that API should work well.
0edf658 to
27e7b46
Compare
27e7b46 to
fe1230a
Compare
libsolidity/codegen/ABIFunctions.h
Outdated
| bool _fromStack | ||
| ); | ||
|
|
||
| /// @returns the name of the ABI decodinf function for the given type |
|
|
||
| ErrorList errors; | ||
| ErrorReporter errorReporter(errors); | ||
| // cout << _assembly << endl; |
| } | ||
| m_context.appendMissingLowLevelFunctions(); | ||
| string abiFunctions = m_context.abiFunctions().requestedFunctions(); | ||
| // cout << abiFunctions << endl; |
test/libsolidity/ABIDecoderTests.cpp
Outdated
|
|
||
| BOOST_FIXTURE_TEST_SUITE(ABIDecoderTest, SolidityExecutionFramework) | ||
|
|
||
| BOOST_AUTO_TEST_CASE(BOTH_ENCODERS_macro) |
6072d29 to
019b816
Compare
|
Closes #3049 |
|
@axic this is ready to be merged from my side. |
019b816 to
cc3b651
Compare
|
Can some of the commits be squashed? Many just say "decoder", "decoder fixes", "decoder tests" :) |
|
Also need to be rebased and changelog updated. |
cc3b651 to
80bce92
Compare
|
Rebased and changed changelog. |
| BOTH_ENCODERS( | ||
| compileAndRun(sourceCode); | ||
| callContractFunction("f(uint256)"); | ||
| callContractFunction("f(uint256)", u256(0)); |
There was a problem hiding this comment.
Just remind me: does the newer decoder changes semantics and requires the minimum length of the encoding present as calldata?
Do we have a specific test for short data with the new decoder?
There was a problem hiding this comment.
Yes, the new decoder does not tolerate short data and there are multiple tests.
test/libsolidity/ABIDecoderTests.cpp
Outdated
| compileAndRun(sourceCode); | ||
| ABI_CHECK(callContractFunction("f(uint256,uint256)", 1, 2), encodeArgs(1)); | ||
| ABI_CHECK(callContractFunctionNoEncoding("f(uint256,uint256)", bytes(64, 0)), encodeArgs(0)); | ||
| ABI_CHECK(callContractFunctionNoEncoding("f(uint256,uint256)", bytes(63, 0)), encodeArgs()); |
There was a problem hiding this comment.
This tests the new encoder to abort on short input. Should this case have the same case for the old encoder allowing it?
There was a problem hiding this comment.
I can add that if you want.
80bce92 to
b3f2daf
Compare
|
Added short input tests for all types the old decoder can handle. |
b3f2daf to
d74599d
Compare
| memPtr := mload(<freeMemoryPointer>) | ||
| let newFreePtr := add(memPtr, size) | ||
| // protect against overflow | ||
| switch or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) case 1 { revert(0, 0) } |
There was a problem hiding this comment.
I will check all switches later, I think we should merge this pr without that change.
There was a problem hiding this comment.
Never mind, the only really safe ones without review are the those checking for case 0.
|
Can this be merged? |
d74599d to
9d8e3ff
Compare
offset < endand notoffset + size < end