mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Implement struct encoder.
This commit is contained in:
parent
6385641f6e
commit
70d70e7816
@ -1735,7 +1735,15 @@ unsigned StructType::calldataEncodedSize(bool _padded) const
|
|||||||
|
|
||||||
bool StructType::isDynamicallyEncoded() 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
|
u256 StructType::memorySize() const
|
||||||
|
@ -745,7 +745,7 @@ public:
|
|||||||
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||||
virtual TypePointer encodingType() 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;
|
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
||||||
|
|
||||||
|
@ -404,9 +404,11 @@ string ABIFunctions::abiEncodingFunction(
|
|||||||
else
|
else
|
||||||
solAssert(false, "");
|
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)
|
else if (_from.category() == Type::Category::Function)
|
||||||
return abiEncodingFunctionFunctionType(
|
return abiEncodingFunctionFunctionType(
|
||||||
@ -534,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||||
{
|
{
|
||||||
mstore(pos, sub(tail, headStart))
|
mstore(pos, sub(tail, headStart))
|
||||||
tail := <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), tail)
|
tail := <encodeToMemoryFun>(<arrayElementAccess>, tail)
|
||||||
srcPtr := <nextArrayElement>(srcPtr)
|
srcPtr := <nextArrayElement>(srcPtr)
|
||||||
pos := add(pos, <elementEncodedSize>)
|
pos := add(pos, <elementEncodedSize>)
|
||||||
}
|
}
|
||||||
@ -549,7 +551,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
let srcPtr := <dataAreaFun>(value)
|
let srcPtr := <dataAreaFun>(value)
|
||||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||||
{
|
{
|
||||||
<encodeToMemoryFun>(<arrayElementAccess>(srcPtr), pos)
|
<encodeToMemoryFun>(<arrayElementAccess>, pos)
|
||||||
srcPtr := <nextArrayElement>(srcPtr)
|
srcPtr := <nextArrayElement>(srcPtr)
|
||||||
pos := add(pos, <elementEncodedSize>)
|
pos := add(pos, <elementEncodedSize>)
|
||||||
}
|
}
|
||||||
@ -573,7 +575,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
_encodeAsLibraryTypes,
|
_encodeAsLibraryTypes,
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
templ("arrayElementAccess", inMemory ? "mload" : "sload");
|
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
|
||||||
templ("nextArrayElement", nextArrayElementFunction(_from));
|
templ("nextArrayElement", nextArrayElementFunction(_from));
|
||||||
return templ.render();
|
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(
|
string ABIFunctions::abiEncodingFunctionStringLiteral(
|
||||||
Type const& _from,
|
Type const& _from,
|
||||||
Type const& _to,
|
Type const& _to,
|
||||||
|
@ -123,6 +123,13 @@ private:
|
|||||||
bool _encodeAsLibraryTypes
|
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
|
// @returns the name of the ABI encoding function with the given type
|
||||||
// and queues the generation of the function to the requested functions.
|
// and queues the generation of the function to the requested functions.
|
||||||
// Case for _givenType being a string literal
|
// Case for _givenType being a string literal
|
||||||
|
@ -121,7 +121,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
{
|
{
|
||||||
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
|
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
|
||||||
{
|
{
|
||||||
solAssert(ref->location() == DataLocation::Memory, "");
|
solUnimplementedAssert(ref->location() == DataLocation::Memory, "");
|
||||||
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
|
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
|
||||||
}
|
}
|
||||||
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
|
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
|
||||||
|
@ -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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
@ -9682,50 +9682,6 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment)
|
|||||||
compileAndRun(sourceCode, 0, "C2");
|
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)
|
BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user