mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8925 from ethereum/abiEncodeCalldataSlice
Allow abi encoding array slices without casting.
This commit is contained in:
commit
1700bdc4ad
@ -12,6 +12,7 @@ Language Features:
|
|||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode.
|
* Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode.
|
||||||
|
* Allow using abi encoding functions for calldata array slices without explicit casts.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
|
|||||||
// Structs are fine in the following circumstances:
|
// Structs are fine in the following circumstances:
|
||||||
// - ABIv2 or,
|
// - ABIv2 or,
|
||||||
// - storage struct for a library
|
// - storage struct for a library
|
||||||
if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage))
|
if (_inLibraryCall && encodingType && encodingType->dataStoredIn(DataLocation::Storage))
|
||||||
return encodingType;
|
return encodingType;
|
||||||
TypePointer baseType = encodingType;
|
TypePointer baseType = encodingType;
|
||||||
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType))
|
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType))
|
||||||
@ -1971,6 +1971,19 @@ string ArraySliceType::toString(bool _short) const
|
|||||||
return m_arrayType.toString(_short) + " slice";
|
return m_arrayType.toString(_short) + " slice";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer ArraySliceType::mobileType() const
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
m_arrayType.dataStoredIn(DataLocation::CallData) &&
|
||||||
|
m_arrayType.isDynamicallySized() &&
|
||||||
|
!m_arrayType.baseType()->isDynamicallyEncoded()
|
||||||
|
)
|
||||||
|
return &m_arrayType;
|
||||||
|
else
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::tuple<std::string, TypePointer>> ArraySliceType::makeStackItems() const
|
std::vector<std::tuple<std::string, TypePointer>> ArraySliceType::makeStackItems() const
|
||||||
{
|
{
|
||||||
return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}};
|
return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}};
|
||||||
|
@ -831,6 +831,7 @@ public:
|
|||||||
bool isDynamicallyEncoded() const override { return true; }
|
bool isDynamicallyEncoded() const override { return true; }
|
||||||
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
|
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
|
TypePointer mobileType() const override;
|
||||||
|
|
||||||
BoolResult validForLocation(DataLocation _loc) const override { return m_arrayType.validForLocation(_loc); }
|
BoolResult validForLocation(DataLocation _loc) const override { return m_arrayType.validForLocation(_loc); }
|
||||||
|
|
||||||
|
@ -279,31 +279,47 @@ string ABIFunctions::abiEncodingFunction(
|
|||||||
return abiEncodingFunctionStringLiteral(_from, to, _options);
|
return abiEncodingFunctionStringLiteral(_from, to, _options);
|
||||||
else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
|
else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
|
||||||
{
|
{
|
||||||
solAssert(_from.category() == Type::Category::Array, "");
|
ArrayType const* fromArray = nullptr;
|
||||||
solAssert(to.dataStoredIn(DataLocation::Memory), "");
|
switch (_from.category())
|
||||||
ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from);
|
{
|
||||||
|
case Type::Category::Array:
|
||||||
|
fromArray = dynamic_cast<ArrayType const*>(&_from);
|
||||||
|
break;
|
||||||
|
case Type::Category::ArraySlice:
|
||||||
|
fromArray = &dynamic_cast<ArraySliceType const*>(&_from)->arrayType();
|
||||||
|
solAssert(
|
||||||
|
fromArray->dataStoredIn(DataLocation::CallData) &&
|
||||||
|
fromArray->isDynamicallySized() &&
|
||||||
|
!fromArray->baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (fromArray.location())
|
switch (fromArray->location())
|
||||||
{
|
{
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
if (
|
if (
|
||||||
fromArray.isByteArray() ||
|
fromArray->isByteArray() ||
|
||||||
*fromArray.baseType() == *TypeProvider::uint256() ||
|
*fromArray->baseType() == *TypeProvider::uint256() ||
|
||||||
*fromArray.baseType() == FixedBytesType(32)
|
*fromArray->baseType() == FixedBytesType(32)
|
||||||
)
|
)
|
||||||
return abiEncodingFunctionCalldataArrayWithoutCleanup(fromArray, *toArray, _options);
|
return abiEncodingFunctionCalldataArrayWithoutCleanup(*fromArray, *toArray, _options);
|
||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
if (fromArray.isByteArray())
|
if (fromArray->isByteArray())
|
||||||
return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionMemoryByteArray(*fromArray, *toArray, _options);
|
||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
if (fromArray.baseType()->storageBytes() <= 16)
|
if (fromArray->baseType()->storageBytes() <= 16)
|
||||||
return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionCompactStorageArray(*fromArray, *toArray, _options);
|
||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
default:
|
default:
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
@ -480,6 +480,16 @@ void CompilerUtils::encodeToMemory(
|
|||||||
convertType(*_givenTypes[i], *targetType, true);
|
convertType(*_givenTypes[i], *targetType, true);
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||||
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
|
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
|
||||||
|
else if (auto arraySliceType = dynamic_cast<ArraySliceType const*>(type))
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
arraySliceType->dataStoredIn(DataLocation::CallData) &&
|
||||||
|
arraySliceType->isDynamicallySized() &&
|
||||||
|
!arraySliceType->arrayType().baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
ArrayUtils(m_context).copyArrayToMemory(arraySliceType->arrayType(), _padToWordBoundaries);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
storeInMemoryDynamic(*type, _padToWordBoundaries);
|
storeInMemoryDynamic(*type, _padToWordBoundaries);
|
||||||
}
|
}
|
||||||
@ -516,22 +526,39 @@ void CompilerUtils::encodeToMemory(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(_givenTypes[i]->category() == Type::Category::Array, "Unknown dynamic type.");
|
ArrayType const* arrayType = nullptr;
|
||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(*_givenTypes[i]);
|
switch (_givenTypes[i]->category())
|
||||||
|
{
|
||||||
|
case Type::Category::Array:
|
||||||
|
arrayType = dynamic_cast<ArrayType const*>(_givenTypes[i]);
|
||||||
|
break;
|
||||||
|
case Type::Category::ArraySlice:
|
||||||
|
arrayType = &dynamic_cast<ArraySliceType const*>(_givenTypes[i])->arrayType();
|
||||||
|
solAssert(
|
||||||
|
arrayType->isDynamicallySized() &&
|
||||||
|
arrayType->dataStoredIn(DataLocation::CallData) &&
|
||||||
|
!arrayType->baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "Unknown dynamic type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
// now copy the array
|
// now copy the array
|
||||||
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.sizeOnStack());
|
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType->sizeOnStack());
|
||||||
// stack: ... <end_of_mem> <value...>
|
// stack: ... <end_of_mem> <value...>
|
||||||
// copy length to memory
|
// copy length to memory
|
||||||
m_context << dupInstruction(1 + arrayType.sizeOnStack());
|
m_context << dupInstruction(1 + arrayType->sizeOnStack());
|
||||||
ArrayUtils(m_context).retrieveLength(arrayType, 1);
|
ArrayUtils(m_context).retrieveLength(*arrayType, 1);
|
||||||
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
|
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
|
||||||
storeInMemoryDynamic(*TypeProvider::uint256(), true);
|
storeInMemoryDynamic(*TypeProvider::uint256(), true);
|
||||||
// stack: ... <end_of_mem> <value...> <end_of_mem''>
|
// stack: ... <end_of_mem> <value...> <end_of_mem''>
|
||||||
// copy the new memory pointer
|
// copy the new memory pointer
|
||||||
m_context << swapInstruction(arrayType.sizeOnStack() + 1) << Instruction::POP;
|
m_context << swapInstruction(arrayType->sizeOnStack() + 1) << Instruction::POP;
|
||||||
// stack: ... <end_of_mem''> <value...>
|
// stack: ... <end_of_mem''> <value...>
|
||||||
// copy data part
|
// copy data part
|
||||||
ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
|
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
|
||||||
// stack: ... <end_of_mem'''>
|
// stack: ... <end_of_mem'''>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
contract C {
|
||||||
|
function enc_packed_bytes(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(data[start:end]);
|
||||||
|
}
|
||||||
|
function enc_packed_bytes_reference(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(bytes(data[start:end]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function enc_bytes(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(data[start:end]);
|
||||||
|
}
|
||||||
|
function enc_bytes_reference(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(bytes(data[start:end]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function enc_uint256(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(x[start:end]);
|
||||||
|
}
|
||||||
|
function enc_uint256_reference(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(x[start:end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enc_packed_uint256(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(x[start:end]);
|
||||||
|
}
|
||||||
|
function enc_packed_uint256_reference(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(x[start:end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compare(bytes memory x, bytes memory y) internal {
|
||||||
|
assert(x.length == y.length);
|
||||||
|
for (uint i = 0; i < x.length; ++i)
|
||||||
|
assert(x[i] == y[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_bytes() public {
|
||||||
|
bytes memory test = new bytes(3);
|
||||||
|
test[0] = 0x41; test[1] = 0x42; test[2] = 0x42;
|
||||||
|
for (uint i = 0; i < test.length; i++)
|
||||||
|
for (uint j = i; j <= test.length; j++)
|
||||||
|
{
|
||||||
|
compare(this.enc_packed_bytes(test, i, j), this.enc_packed_bytes_reference(test, i, j));
|
||||||
|
compare(this.enc_bytes(test, i, j), this.enc_bytes_reference(test, i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_uint256() public {
|
||||||
|
uint256[] memory test = new uint256[](3);
|
||||||
|
test[0] = 0x41; test[1] = 0x42; test[2] = 0x42;
|
||||||
|
for (uint i = 0; i < test.length; i++)
|
||||||
|
for (uint j = i; j <= test.length; j++)
|
||||||
|
{
|
||||||
|
compare(this.enc_packed_uint256(test, i, j), this.enc_packed_uint256_reference(test, i, j));
|
||||||
|
compare(this.enc_uint256(test, i, j), this.enc_uint256_reference(test, i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >homestead
|
||||||
|
// ----
|
||||||
|
// test_bytes() ->
|
||||||
|
// test_uint256() ->
|
@ -0,0 +1,63 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
function enc_packed_bytes(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(data[start:end]);
|
||||||
|
}
|
||||||
|
function enc_packed_bytes_reference(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(bytes(data[start:end]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function enc_bytes(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(data[start:end]);
|
||||||
|
}
|
||||||
|
function enc_bytes_reference(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(bytes(data[start:end]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function enc_uint256(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(x[start:end]);
|
||||||
|
}
|
||||||
|
function enc_uint256_reference(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encode(x[start:end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enc_packed_uint256(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(x[start:end]);
|
||||||
|
}
|
||||||
|
function enc_packed_uint256_reference(uint256[] calldata x, uint256 start, uint256 end) external returns (bytes memory) {
|
||||||
|
return abi.encodePacked(x[start:end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compare(bytes memory x, bytes memory y) internal {
|
||||||
|
assert(x.length == y.length);
|
||||||
|
for (uint i = 0; i < x.length; ++i)
|
||||||
|
assert(x[i] == y[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_bytes() public {
|
||||||
|
bytes memory test = new bytes(3);
|
||||||
|
test[0] = 0x41; test[1] = 0x42; test[2] = 0x42;
|
||||||
|
for (uint i = 0; i < test.length; i++)
|
||||||
|
for (uint j = i; j <= test.length; j++)
|
||||||
|
{
|
||||||
|
compare(this.enc_packed_bytes(test, i, j), this.enc_packed_bytes_reference(test, i, j));
|
||||||
|
compare(this.enc_bytes(test, i, j), this.enc_bytes_reference(test, i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_uint256() public {
|
||||||
|
uint256[] memory test = new uint256[](3);
|
||||||
|
test[0] = 0x41; test[1] = 0x42; test[2] = 0x42;
|
||||||
|
for (uint i = 0; i < test.length; i++)
|
||||||
|
for (uint j = i; j <= test.length; j++)
|
||||||
|
{
|
||||||
|
compare(this.enc_packed_uint256(test, i, j), this.enc_packed_uint256_reference(test, i, j));
|
||||||
|
compare(this.enc_uint256(test, i, j), this.enc_uint256_reference(test, i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >homestead
|
||||||
|
// ----
|
||||||
|
// test_bytes() ->
|
||||||
|
// test_uint256() ->
|
@ -0,0 +1,8 @@
|
|||||||
|
contract c {
|
||||||
|
bytes public b;
|
||||||
|
function f() public {
|
||||||
|
b = msg.data[:];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (63-74): Type bytes calldata slice is not implicitly convertible to expected type bytes storage ref.
|
@ -4,4 +4,3 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError: (85-91): This type cannot be encoded.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user