aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2017-08-24 21:20:49 +0800
committerAlex Beregszaszi <alex@rtfs.hu>2017-09-16 19:31:12 +0800
commit70d70e78160069d28a6b4931c995d0b24c2b09d5 (patch)
tree010da58b648001b2d16848de6658c7d33e4cef62
parent6385641f6e461a1964da793956fbe0ef8cddadd3 (diff)
downloaddexon-solidity-70d70e78160069d28a6b4931c995d0b24c2b09d5.tar.gz
dexon-solidity-70d70e78160069d28a6b4931c995d0b24c2b09d5.tar.zst
dexon-solidity-70d70e78160069d28a6b4931c995d0b24c2b09d5.zip
Implement struct encoder.
-rw-r--r--libsolidity/ast/Types.cpp10
-rw-r--r--libsolidity/ast/Types.h2
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp128
-rw-r--r--libsolidity/codegen/ABIFunctions.h7
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp2
-rw-r--r--test/libsolidity/ABIEncoderTests.cpp36
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp44
7 files changed, 177 insertions, 52 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 14b30df8..c7843b90 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -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
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index dd50c573..cb439693 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -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;
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 3a9f1a48..9f6c55ba 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -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,
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
index 5bbd842f..de2a140a 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -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
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 1e623357..37aa1aea 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -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))
diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp
index 4ddf17ce..05158601 100644
--- a/test/libsolidity/ABIEncoderTests.cpp
+++ b/test/libsolidity/ABIEncoderTests.cpp
@@ -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()
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 2bdcbd48..f885b0d3 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -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)
{