Implement struct encoder.

This commit is contained in:
chriseth 2017-08-24 15:20:49 +02:00 committed by Alex Beregszaszi
parent 6385641f6e
commit 70d70e7816
7 changed files with 177 additions and 52 deletions

View File

@ -1735,7 +1735,15 @@ unsigned StructType::calldataEncodedSize(bool _padded) const
bool StructType::isDynamicallyEncoded() const
{
solAssert(false, "Structs are not yet supported in the ABI.");
solAssert(!recursive(), "");
for (auto t: memoryMemberTypes())
{
solAssert(t, "Parameter should have external type.");
t = t->interfaceType(false);
if (t->isDynamicallyEncoded())
return true;
}
return false;
}
u256 StructType::memorySize() const

View File

@ -745,7 +745,7 @@ public:
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override
{
return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer();
return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : shared_from_this();
}
virtual TypePointer interfaceType(bool _inLibrary) const override;

View File

@ -404,9 +404,11 @@ string ABIFunctions::abiEncodingFunction(
else
solAssert(false, "");
}
else if (dynamic_cast<StructType const*>(&to))
else if (auto const* toStruct = dynamic_cast<StructType const*>(&to))
{
solUnimplementedAssert(false, "Structs not yet implemented.");
StructType const* fromStruct = dynamic_cast<StructType const*>(&_from);
solAssert(fromStruct, "");
return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes);
}
else if (_from.category() == Type::Category::Function)
return abiEncodingFunctionFunctionType(
@ -534,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
for { let i := 0 } lt(i, length) { i := add(i, 1) }
{
mstore(pos, sub(tail, headStart))
tail := <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), tail)
tail := <encodeToMemoryFun>(<arrayElementAccess>, tail)
srcPtr := <nextArrayElement>(srcPtr)
pos := add(pos, <elementEncodedSize>)
}
@ -549,7 +551,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
let srcPtr := <dataAreaFun>(value)
for { let i := 0 } lt(i, length) { i := add(i, 1) }
{
<encodeToMemoryFun>(<arrayElementAccess>(srcPtr), pos)
<encodeToMemoryFun>(<arrayElementAccess>, pos)
srcPtr := <nextArrayElement>(srcPtr)
pos := add(pos, <elementEncodedSize>)
}
@ -573,7 +575,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
_encodeAsLibraryTypes,
true
));
templ("arrayElementAccess", inMemory ? "mload" : "sload");
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
templ("nextArrayElement", nextArrayElementFunction(_from));
return templ.render();
});
@ -726,6 +728,122 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
});
}
string ABIFunctions::abiEncodingFunctionStruct(
StructType const& _from,
StructType const& _to,
bool _encodeAsLibraryTypes
)
{
string functionName =
"abi_encode_" +
_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "");
solAssert(&_from.structDefinition() == &_to.structDefinition(), "");
return createFunction(functionName, [&]() {
bool fromStorage = _from.location() == DataLocation::Storage;
bool dynamic = _to.isDynamicallyEncoded();
Whiskers templ(R"(
function <functionName>(value, pos) <return> {
let tail := add(pos, <headSize>)
<init>
<#members>
{
// <memberName>
<encode>
}
</members>
<assignEnd>
}
)");
templ("functionName", functionName);
templ("return", dynamic ? " -> end " : "");
templ("assignEnd", dynamic ? "end := tail" : "");
// to avoid multiple loads from the same slot for subsequent members
templ("init", fromStorage ? "let slotValue := 0" : "");
u256 previousSlotOffset(-1);
u256 encodingOffset = 0;
vector<map<string, string>> members;
for (auto const& member: _to.members(nullptr))
{
solAssert(member.type, "");
if (!member.type->canLiveOutsideStorage())
continue;
solUnimplementedAssert(
member.type->mobileType() &&
member.type->mobileType()->interfaceType(_encodeAsLibraryTypes) &&
member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
"Encoding type \"" + member.type->toString() + "\" not yet implemented."
);
auto memberTypeTo = member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
auto memberTypeFrom = _from.memberType(member.name);
solAssert(memberTypeFrom, "");
bool dynamicMember = memberTypeTo->isDynamicallyEncoded();
if (dynamicMember)
solAssert(dynamic, "");
Whiskers memberTempl(R"(
<preprocess>
let memberValue := <retrieveValue>
)" + (
dynamicMember ?
string(R"(
mstore(add(pos, <encodingOffset>), sub(tail, pos))
tail := <abiEncode>(memberValue, tail)
)") :
string(R"(
<abiEncode>(memberValue, add(pos, <encodingOffset>))
)")
)
);
if (fromStorage)
{
solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), "");
u256 storageSlotOffset;
size_t intraSlotOffset;
tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name);
if (memberTypeFrom->isValueType())
{
if (storageSlotOffset != previousSlotOffset)
{
memberTempl("preprocess", "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))");
previousSlotOffset = storageSlotOffset;
}
else
memberTempl("preprocess", "");
memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8, false) + "(slotValue)");
}
else
{
solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), "");
solAssert(intraSlotOffset == 0, "");
memberTempl("preprocess", "");
memberTempl("retrieveValue", "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")");
}
}
else
{
memberTempl("preprocess", "");
string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name));
memberTempl("retrieveValue", "mload(add(value, " + sourceOffset + "))");
}
memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize();
memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false));
members.push_back({});
members.back()["encode"] = memberTempl.render();
members.back()["memberName"] = member.name;
}
templ("members", members);
templ("headSize", toCompactHexWithPrefix(encodingOffset));
return templ.render();
});
}
string ABIFunctions::abiEncodingFunctionStringLiteral(
Type const& _from,
Type const& _to,

View File

@ -123,6 +123,13 @@ private:
bool _encodeAsLibraryTypes
);
/// Part of @a abiEncodingFunction for struct types.
std::string abiEncodingFunctionStruct(
StructType const& _givenType,
StructType const& _targetType,
bool _encodeAsLibraryTypes
);
// @returns the name of the ABI encoding function with the given type
// and queues the generation of the function to the requested functions.
// Case for _givenType being a string literal

View File

@ -121,7 +121,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
{
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
{
solAssert(ref->location() == DataLocation::Memory, "");
solUnimplementedAssert(ref->location() == DataLocation::Memory, "");
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
}
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))

View File

@ -424,7 +424,43 @@ BOOST_AUTO_TEST_CASE(function_name_collision)
)
}
BOOST_AUTO_TEST_CASE(structs)
{
string sourceCode = R"(
contract C {
struct S { uint16 a; uint16 b; T[] sub; uint16 c; }
struct T { uint64[2] x; }
S s;
event e(uint16, S);
function f() returns (uint, S) {
uint16 x = 7;
s.a = 8;
s.b = 9;
s.c = 10;
s.sub.length = 3;
s.sub[0].x[0] = 11;
s.sub[1].x[0] = 12;
s.sub[2].x[1] = 13;
e(x, s);
return (x, s);
}
}
)";
NEW_ENCODER(
compileAndRun(sourceCode, 0, "C");
bytes encoded = encodeArgs(
u256(7), 0x40,
8, 9, 0x80, 10,
3,
11, 0,
12, 0,
0, 13
);
BOOST_CHECK(callContractFunction("f()") == encoded);
REQUIRE_LOG_DATA(encoded);
)
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -9682,50 +9682,6 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment)
compileAndRun(sourceCode, 0, "C2");
}
BOOST_AUTO_TEST_CASE(return_structs)
{
char const* sourceCode = R"(
pragma experimental ABIEncoderV2;
contract C {
struct S { uint a; T[] sub; }
struct T { uint[2] x; }
function f() returns (uint x, S s) {
x = 7;
s.a = 8;
s.sub = new T[](3);
s.sub[0].x[0] = 9;
s.sub[1].x[0] = 10;
s.sub[2].x[1] = 11;
}
}
)";
// This will throw "unimplemented" until it is implemented.
BOOST_CHECK_THROW(
compileAndRun(sourceCode, 0, "C"),
Exception);
// Will calculate the exact encoding later.
// BOOST_CHECK(callContractFunction("f()") == encodeArgs(
// u256(7), u256(0x40),
// u256(8), u256(0x40),
// u256(3),
// // s.sub[0]
// u256(9), u256(0xc0),
// // s.sub[1]
// u256(10), u256(0xe0),
// // s.sub[2]
// u256(11), u256(0x100),
// // s.sub[0].sub
// u256(2)
// // s.sub[0].sub[0].a
// u256(8),
// u256()
// // s.sub[1].sub
// u256(0)
// // s.sub[2].sub
// u256(2)
// ));
}
BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once)
{