Merge pull request #5936 from ethereum/calldataStructsV2

ABIEncoderV2: Implement calldata structs without dynamically encoded members.
This commit is contained in:
chriseth
2019-02-11 16:18:27 +01:00
committed by GitHub
16 changed files with 377 additions and 18 deletions
+6 -4
View File
@@ -371,10 +371,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (
!m_scope->isInterface() &&
baseType->category() == Type::Category::Struct &&
baseType->dataStoredIn(DataLocation::CallData)
)
m_errorReporter.typeError(var->location(), "Calldata structs are not yet supported.");
if (auto const* structType = dynamic_cast<StructType const*>(baseType.get()))
if (structType->isDynamicallyEncoded())
m_errorReporter.typeError(var->location(), "Dynamically encoded calldata structs are not yet supported.");
checkArgumentAndReturnParameter(*var);
var->accept(*this);
}
@@ -2085,8 +2087,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
exprType->toString() + " (expected " + funType->selfType()->toString() + ")."
);
if (exprType->category() == Type::Category::Struct)
annotation.isLValue = true;
if (auto const* structType = dynamic_cast<StructType const*>(exprType.get()))
annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData);
else if (exprType->category() == Type::Category::Array)
{
auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType));
+18
View File
@@ -2053,6 +2053,24 @@ unsigned StructType::calldataEncodedSize(bool) const
return size;
}
unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
{
unsigned offset = 0;
for (auto const& member: members(nullptr))
{
solAssert(member.type->canLiveOutsideStorage(), "");
if (member.name == _member)
return offset;
{
// Struct members are always padded.
unsigned memberSize = member.type->calldataEncodedSize(true);
solAssert(memberSize != 0, "");
offset += memberSize;
}
}
solAssert(false, "Struct member not found.");
}
bool StructType::isDynamicallyEncoded() const
{
solAssert(!recursive(), "");
+1
View File
@@ -852,6 +852,7 @@ public:
std::pair<u256, unsigned> const& storageOffsetsOfMember(std::string const& _name) const;
u256 memoryOffsetOfMember(std::string const& _name) const;
unsigned calldataOffsetOfMember(std::string const& _name) const;
StructDefinition const& structDefinition() const { return m_struct; }
+34 -3
View File
@@ -1246,7 +1246,16 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo
return abiDecodingFunctionArray(*arrayType, _fromMemory);
}
else if (auto const* structType = dynamic_cast<StructType const*>(decodingType.get()))
return abiDecodingFunctionStruct(*structType, _fromMemory);
{
if (structType->dataStoredIn(DataLocation::CallData))
{
solAssert(!_fromMemory, "");
solUnimplementedAssert(!structType->isDynamicallyEncoded(), "Dynamically encoded calldata structs are not yet implemented.");
return abiDecodingFunctionCalldataStruct(*structType);
}
else
return abiDecodingFunctionStruct(*structType, _fromMemory);
}
else if (auto const* functionType = dynamic_cast<FunctionType const*>(decodingType.get()))
return abiDecodingFunctionFunctionType(*functionType, _fromMemory, _forUseOnStack);
else
@@ -1423,15 +1432,37 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _
});
}
string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
{
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
solAssert(_type.calldataEncodedSize(true) != 0, "");
string functionName =
"abi_decode_" +
_type.identifier();
return createFunction(functionName, [&]() {
Whiskers w{R"(
// <readableTypeName>
function <functionName>(offset, end) -> value {
if slt(sub(end, offset), <minimumSize>) { revert(0, 0) }
value := offset
}
)"};
w("functionName", functionName);
w("readableTypeName", _type.toString(true));
w("minimumSize", to_string(_type.calldataEncodedSize(true)));
return w.render();
});
}
string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory)
{
solAssert(!_type.dataStoredIn(DataLocation::CallData), "");
string functionName =
"abi_decode_" +
_type.identifier() +
(_fromMemory ? "_fromMemory" : "");
solUnimplementedAssert(!_type.dataStoredIn(DataLocation::CallData), "");
return createFunction(functionName, [&]() {
Whiskers templ(R"(
// <readableTypeName>
+2
View File
@@ -222,6 +222,8 @@ private:
std::string abiDecodingFunctionCalldataArray(ArrayType const& _type);
/// Part of @a abiDecodingFunction for byte array types.
std::string abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunction for calldata struct types.
std::string abiDecodingFunctionCalldataStruct(StructType const& _type);
/// Part of @a abiDecodingFunction for struct types.
std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunction for array types.
+16 -4
View File
@@ -918,8 +918,7 @@ void CompilerUtils::convertType(
auto& targetType = dynamic_cast<StructType const&>(_targetType);
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
solAssert(
targetType.location() != DataLocation::CallData &&
typeOnStack.location() != DataLocation::CallData
targetType.location() != DataLocation::CallData
, "");
switch (targetType.location())
{
@@ -933,9 +932,9 @@ void CompilerUtils::convertType(
break;
case DataLocation::Memory:
// Copy the array to a free position in memory, unless it is already in memory.
if (typeOnStack.location() != DataLocation::Memory)
switch (typeOnStack.location())
{
solAssert(typeOnStack.location() == DataLocation::Storage, "");
case DataLocation::Storage:
// stack: <source ref>
m_context << typeOnStack.memorySize();
allocateMemory();
@@ -955,6 +954,19 @@ void CompilerUtils::convertType(
storeInMemoryDynamic(*targetMemberType, true);
}
m_context << Instruction::POP << Instruction::POP;
break;
case DataLocation::CallData:
{
solUnimplementedAssert(!typeOnStack.isDynamicallyEncoded(), "");
m_context << Instruction::DUP1;
m_context << Instruction::CALLDATASIZE;
m_context << Instruction::SUB;
abiDecode({targetType.shared_from_this()}, false);
break;
}
case DataLocation::Memory:
// nothing to do
break;
}
break;
case DataLocation::CallData:
@@ -1380,6 +1380,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type);
break;
}
case DataLocation::CallData:
{
solUnimplementedAssert(!type.isDynamicallyEncoded(), "");
m_context << type.calldataOffsetOfMember(member) << Instruction::ADD;
// For non-value types the calldata offset is returned directly.
if (_memberAccess.annotation().type->isValueType())
{
solAssert(_memberAccess.annotation().type->calldataEncodedSize(false) > 0, "");
CompilerUtils(m_context).loadFromMemoryDynamic(*_memberAccess.annotation().type, true, true, false);
}
else
solAssert(
_memberAccess.annotation().type->category() == Type::Category::Array ||
_memberAccess.annotation().type->category() == Type::Category::Struct,
""
);
break;
}
default:
solAssert(false, "Illegal data location for struct.");
}