mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
ABIEncoderV2: Implement calldata structs without dynamically encoded members.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -2071,8 +2073,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));
|
||||
|
||||
@@ -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(), "");
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user