Skip to content
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

New ABI encoder #2704

Merged
merged 8 commits into from
Aug 14, 2017
Merged

New ABI encoder #2704

merged 8 commits into from
Aug 14, 2017

Conversation

chriseth
Copy link
Contributor

@chriseth chriseth commented Aug 7, 2017

This is disabled for now (waiting for "experimental pragma").

Depends on: #2701 , #2700 , #2690

part of #2433

@axic
Copy link
Member

axic commented Aug 8, 2017

What experimental pragma to use?

abiEncoderV2 ?

@@ -103,6 +103,12 @@ class CompilerUtils
bool _encodeAsLibraryTypes = false
);

void abiEncode(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have some documentation above.

{
// This throws an exception and thus might cause immediate termination, but hey,
// it's a failed assertion anyway :-)
//TODO solAssert(m_requestedFunctions.empty(), "Forgot to call ``requestedFunctions()``.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this here?

@axic
Copy link
Member

axic commented Aug 10, 2017

Can you please rebase?

@chriseth
Copy link
Contributor Author

This allows multi-dimensional arrays in interfaces unconditionally, i.e. now only if the experimental mode is turned on. This is more or less required because it is a type feature.

I don't think this is harmful, because it will only cause an "unimplemented feature" exception in the code generator. I don't think it changes any interfaces, but I'm not 100% positive on that.

@@ -1528,8 +1528,6 @@ TypePointer ArrayType::interfaceType(bool _inLibrary) const
TypePointer baseExt = m_baseType->interfaceType(_inLibrary);
if (!baseExt)
return TypePointer();
if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fold this change into another commit or rename the commit message?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_lib" : "") +
(_compacted ? "_comp" : "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_compact?

@chriseth
Copy link
Contributor Author

Ok, hopefully no failures anymore now.

@axic
Copy link
Member

axic commented Aug 11, 2017

Fails on:

/home/travis/build/ethereum/solidity/libsolidity/codegen/ArrayUtils.cpp(290): fatal error in "void dev::solidity::ArrayUtils::copyArrayToMemory(const dev::solidity::ArrayType &, bool) const": std::exception: Nested dynamic arrays not implemented here.
/home/travis/build/ethereum/solidity/test/RPCSession.cpp(324): last checkpoint

BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)")));
}

BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage)
{
char const* sourceCode = R"(
pragma experimental ABIEncoderV2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chriseth I think this is still added to the wrong test here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay reordered this in the commits as it was confusing.

@axic
Copy link
Member

axic commented Aug 11, 2017

I haven't reviewed the actual encoder yet, but the rest (bar the above) seems ok.

break;
}
case Type::Category::Contract:
templ("body", "cleaned := " + cleanupFunction(IntegerType(120, IntegerType::Modifier::Address)) + "(value)");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be 160, Address, though I guess the bits is ignored if it is an address. It would make sense adding another constructor in that case.

toCategory == Type::Category::Integer ||
toCategory == Type::Category::Contract,
"");
IntegerType const addressType(0, IntegerType::Modifier::Address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, 160, Address I guess.

Whiskers("converted := <shiftLeft>(<clean>(value))")
("shiftLeft", shiftLeftFunction(256 - toBytesType.numBytes() * 8))
("clean", cleanupFunction(_from))
.render();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting on these is a bit misleading, probably this is more readable:

Whiskers(...)
    ("shiftLeft",...)
    ...
    .render()

}
case Type::Category::Function:
{
solAssert(false, "Cleanup should not be called for function types.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is conversion, not cleanup.

}
}

string ABIFunctions::combineExternalFunctionIdFunction()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this next to cleanupCombinedExternalFunctionId?

_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_lib" : "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably no point truncating it from _library to _lib.

<#word>
mstore(add(pos, <offset>), <wordValue>)
</word>
end := add(pos, <overallSize>)
Copy link
Member

@axic axic Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could increment the pos in-place in the loop and use end := add(pos, 64).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this way it is better for the optimizer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so?

if (_to.isDynamicallySized())
{
Whiskers templ(R"(
function <functionName>(pos) -> end {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, this one returns the occupied memory size but the other encoding functions below don't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns the "new memory pointer" if and only if it is dynamically sized. You can take a look at how the functions are called, it is consistent and should be checked by the julia analyzer.

function <functionName>(src, dst, length) {
calldatacopy(dst, src, length)
// clear end
mstore(add(dst, length), 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there an extra slot at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just removes potential garbage. The routine might write to more memory than it actually "returns" in the end. This is documented in the header.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just feels weird this is not handled in the caller, why would it ask for more if the entire slot is cleared?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chriseth this question wasn't sorted out

Copy link
Contributor Author

@chriseth chriseth Aug 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ABI specification requires everything to be padded with zeros to multiples of 32 bytes. The calldatacopy only copies the actual length. This line performs the padding, especially in the case when the memory was pre-occupied by something else.

switch (_type.location())
{
case DataLocation::CallData:
solAssert(false, "called regular array length function on calldata array");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because calldata arrays are stored as (pos, length), so you cannot retrieve the length from pos alone.

// because the encoding is position-independent, but we have to check that.
Whiskers templ(R"(
function <functionName>(start, length, pos) -> end {
<storeLength>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "// might update pos".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants