mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7178 from ethereum/calldataSizeTest
Split calldataEncodedSize.
This commit is contained in:
commit
16efcfdbb3
@ -22,6 +22,7 @@ Compiler Features:
|
|||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* ABI decoder: Ensure that decoded arrays always point to distinct memory locations.
|
* ABI decoder: Ensure that decoded arrays always point to distinct memory locations.
|
||||||
|
* Code Generator: Treat dynamically encoded but statically sized arrays and structs in calldata properly.
|
||||||
* SMTChecker: Fix internal error when inlining functions that contain tuple expressions.
|
* SMTChecker: Fix internal error when inlining functions that contain tuple expressions.
|
||||||
* SMTChecker: Fix pointer knowledge erasing in loops.
|
* SMTChecker: Fix pointer knowledge erasing in loops.
|
||||||
* SMTChecker: Fix internal error when using compound bitwise assignment operators inside branches.
|
* SMTChecker: Fix internal error when using compound bitwise assignment operators inside branches.
|
||||||
|
@ -1647,15 +1647,13 @@ bool ArrayType::validForCalldata() const
|
|||||||
if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType()))
|
if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType()))
|
||||||
if (!arrayBaseType->validForCalldata())
|
if (!arrayBaseType->validForCalldata())
|
||||||
return false;
|
return false;
|
||||||
return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max();
|
return isDynamicallySized() || unlimitedStaticCalldataSize(true) <= numeric_limits<unsigned>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
bigint ArrayType::unlimitedCalldataEncodedSize(bool _padded) const
|
bigint ArrayType::unlimitedStaticCalldataSize(bool _padded) const
|
||||||
{
|
{
|
||||||
if (isDynamicallySized())
|
solAssert(!isDynamicallySized(), "");
|
||||||
return 32;
|
bigint size = bigint(length()) * calldataStride();
|
||||||
// Array elements are always padded.
|
|
||||||
bigint size = bigint(length()) * (isByteArray() ? 1 : baseType()->calldataEncodedSize(true));
|
|
||||||
if (_padded)
|
if (_padded)
|
||||||
size = ((size + 31) / 32) * 32;
|
size = ((size + 31) / 32) * 32;
|
||||||
return size;
|
return size;
|
||||||
@ -1663,7 +1661,20 @@ bigint ArrayType::unlimitedCalldataEncodedSize(bool _padded) const
|
|||||||
|
|
||||||
unsigned ArrayType::calldataEncodedSize(bool _padded) const
|
unsigned ArrayType::calldataEncodedSize(bool _padded) const
|
||||||
{
|
{
|
||||||
bigint size = unlimitedCalldataEncodedSize(_padded);
|
solAssert(!isDynamicallyEncoded(), "");
|
||||||
|
bigint size = unlimitedStaticCalldataSize(_padded);
|
||||||
|
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit unsigned.");
|
||||||
|
return unsigned(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned ArrayType::calldataEncodedTailSize() const
|
||||||
|
{
|
||||||
|
solAssert(isDynamicallyEncoded(), "");
|
||||||
|
if (isDynamicallySized())
|
||||||
|
// We do not know the dynamic length itself, but at least the uint256 containing the
|
||||||
|
// length must still be present.
|
||||||
|
return 32;
|
||||||
|
bigint size = unlimitedStaticCalldataSize(false);
|
||||||
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit unsigned.");
|
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit unsigned.");
|
||||||
return unsigned(size);
|
return unsigned(size);
|
||||||
}
|
}
|
||||||
@ -1832,10 +1843,11 @@ TypeResult ArrayType::interfaceType(bool _inLibrary) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 ArrayType::memorySize() const
|
u256 ArrayType::memoryDataSize() const
|
||||||
{
|
{
|
||||||
solAssert(!isDynamicallySized(), "");
|
solAssert(!isDynamicallySized(), "");
|
||||||
solAssert(m_location == DataLocation::Memory, "");
|
solAssert(m_location == DataLocation::Memory, "");
|
||||||
|
solAssert(!isByteArray(), "");
|
||||||
bigint size = bigint(m_length) * m_baseType->memoryHeadSize();
|
bigint size = bigint(m_length) * m_baseType->memoryHeadSize();
|
||||||
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit u256.");
|
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit u256.");
|
||||||
return u256(size);
|
return u256(size);
|
||||||
@ -1992,20 +2004,33 @@ bool StructType::operator==(Type const& _other) const
|
|||||||
return ReferenceType::operator==(other) && other.m_struct == m_struct;
|
return ReferenceType::operator==(other) && other.m_struct == m_struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned StructType::calldataEncodedSize(bool) const
|
unsigned StructType::calldataEncodedSize(bool) const
|
||||||
{
|
{
|
||||||
|
solAssert(!isDynamicallyEncoded(), "");
|
||||||
|
|
||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
for (auto const& member: members(nullptr))
|
for (auto const& member: members(nullptr))
|
||||||
if (!member.type->canLiveOutsideStorage())
|
{
|
||||||
return 0;
|
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||||
else
|
// Struct members are always padded.
|
||||||
{
|
size += member.type->calldataEncodedSize();
|
||||||
// Struct members are always padded.
|
}
|
||||||
unsigned memberSize = member.type->calldataEncodedSize(true);
|
return size;
|
||||||
if (memberSize == 0)
|
}
|
||||||
return 0;
|
|
||||||
size += memberSize;
|
|
||||||
}
|
unsigned StructType::calldataEncodedTailSize() const
|
||||||
|
{
|
||||||
|
solAssert(isDynamicallyEncoded(), "");
|
||||||
|
|
||||||
|
unsigned size = 0;
|
||||||
|
for (auto const& member: members(nullptr))
|
||||||
|
{
|
||||||
|
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||||
|
// Struct members are always padded.
|
||||||
|
size += member.type->calldataHeadSize();
|
||||||
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2017,12 +2042,8 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
|
|||||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||||
if (member.name == _member)
|
if (member.name == _member)
|
||||||
return offset;
|
return offset;
|
||||||
{
|
// Struct members are always padded.
|
||||||
// Struct members are always padded.
|
offset += member.type->calldataHeadSize();
|
||||||
unsigned memberSize = member.type->calldataEncodedSize(true);
|
|
||||||
solAssert(memberSize != 0, "");
|
|
||||||
offset += memberSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
solAssert(false, "Struct member not found.");
|
solAssert(false, "Struct member not found.");
|
||||||
}
|
}
|
||||||
@ -2040,7 +2061,7 @@ bool StructType::isDynamicallyEncoded() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 StructType::memorySize() const
|
u256 StructType::memoryDataSize() const
|
||||||
{
|
{
|
||||||
u256 size;
|
u256 size;
|
||||||
for (auto const& t: memoryMemberTypes())
|
for (auto const& t: memoryMemberTypes())
|
||||||
|
@ -208,16 +208,32 @@ public:
|
|||||||
virtual bool operator==(Type const& _other) const { return category() == _other.category(); }
|
virtual bool operator==(Type const& _other) const { return category() == _other.category(); }
|
||||||
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
|
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
|
||||||
|
|
||||||
/// @returns number of bytes used by this type when encoded for CALL. If it is a dynamic type,
|
/// @returns number of bytes used by this type when encoded for CALL. Cannot be used for
|
||||||
/// returns the size of the pointer (usually 32). Returns 0 if the type cannot be encoded
|
/// dynamically encoded types.
|
||||||
/// in calldata.
|
/// Always returns a value greater than zero and throws if the type cannot be encoded in calldata
|
||||||
|
/// (or is dynamically encoded).
|
||||||
/// If @a _padded then it is assumed that each element is padded to a multiple of 32 bytes.
|
/// If @a _padded then it is assumed that each element is padded to a multiple of 32 bytes.
|
||||||
virtual unsigned calldataEncodedSize(bool _padded) const { (void)_padded; return 0; }
|
virtual unsigned calldataEncodedSize(bool _padded) const { (void)_padded; solAssert(false, ""); }
|
||||||
|
/// Convenience version of @see calldataEncodedSize(bool)
|
||||||
|
unsigned calldataEncodedSize() const { return calldataEncodedSize(true); }
|
||||||
|
/// @returns the distance between two elements of this type in a calldata array, tuple or struct.
|
||||||
|
/// For statically encoded types this is the same as calldataEncodedSize(true).
|
||||||
|
/// For dynamically encoded types this is the distance between two tail pointers, i.e. 32.
|
||||||
|
/// Always returns a value greater than zero and throws if the type cannot be encoded in calldata.
|
||||||
|
unsigned calldataHeadSize() const { return isDynamicallyEncoded() ? 32 : calldataEncodedSize(true); }
|
||||||
|
/// @returns the (minimal) size of the calldata tail for this type. Can only be used for
|
||||||
|
/// dynamically encoded types. For dynamically-sized arrays this is 32 (the size of the length),
|
||||||
|
/// for statically-sized, but dynamically encoded arrays this is 32*length(), for structs
|
||||||
|
/// this is the sum of the calldataHeadSize's of its members.
|
||||||
|
/// Always returns a value greater than zero and throws if the type cannot be encoded in calldata
|
||||||
|
/// (or is not dynamically encoded).
|
||||||
|
virtual unsigned calldataEncodedTailSize() const { solAssert(false, ""); }
|
||||||
/// @returns the size of this data type in bytes when stored in memory. For memory-reference
|
/// @returns the size of this data type in bytes when stored in memory. For memory-reference
|
||||||
/// types, this is the size of the memory pointer.
|
/// types, this is the size of the memory pointer.
|
||||||
virtual unsigned memoryHeadSize() const { return calldataEncodedSize(); }
|
virtual unsigned memoryHeadSize() const { return calldataEncodedSize(); }
|
||||||
/// Convenience version of @see calldataEncodedSize(bool)
|
/// @returns the size of this data type in bytes when stored in memory. For memory-reference
|
||||||
unsigned calldataEncodedSize() const { return calldataEncodedSize(true); }
|
/// types, this is the size of the actual data area, if it is statically-sized.
|
||||||
|
virtual u256 memoryDataSize() const { return calldataEncodedSize(); }
|
||||||
/// @returns true if the type is a dynamic array
|
/// @returns true if the type is a dynamic array
|
||||||
virtual bool isDynamicallySized() const { return false; }
|
virtual bool isDynamicallySized() const { return false; }
|
||||||
/// @returns true if the type is dynamically encoded in the ABI
|
/// @returns true if the type is dynamically encoded in the ABI
|
||||||
@ -634,6 +650,10 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
unsigned memoryHeadSize() const override { return 32; }
|
unsigned memoryHeadSize() const override { return 32; }
|
||||||
|
u256 memoryDataSize() const override = 0;
|
||||||
|
|
||||||
|
unsigned calldataEncodedSize(bool) const override = 0;
|
||||||
|
unsigned calldataEncodedTailSize() const override = 0;
|
||||||
|
|
||||||
/// @returns a copy of this type with location (recursively) changed to @a _location,
|
/// @returns a copy of this type with location (recursively) changed to @a _location,
|
||||||
/// whereas isPointer is only shallowly changed - the deep copy is always a bound reference.
|
/// whereas isPointer is only shallowly changed - the deep copy is always a bound reference.
|
||||||
@ -701,7 +721,8 @@ public:
|
|||||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
unsigned calldataEncodedSize(bool _padded) const override;
|
unsigned calldataEncodedSize(bool) const override;
|
||||||
|
unsigned calldataEncodedTailSize() const override;
|
||||||
bool isDynamicallySized() const override { return m_hasDynamicLength; }
|
bool isDynamicallySized() const override { return m_hasDynamicLength; }
|
||||||
bool isDynamicallyEncoded() const override;
|
bool isDynamicallyEncoded() const override;
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
@ -724,12 +745,12 @@ public:
|
|||||||
bool isString() const { return m_arrayKind == ArrayKind::String; }
|
bool isString() const { return m_arrayKind == ArrayKind::String; }
|
||||||
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
|
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
|
||||||
u256 const& length() const { return m_length; }
|
u256 const& length() const { return m_length; }
|
||||||
u256 memorySize() const;
|
u256 memoryDataSize() const override;
|
||||||
|
|
||||||
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
|
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
|
||||||
|
|
||||||
/// The offset to advance in calldata to move from one array element to the next.
|
/// The offset to advance in calldata to move from one array element to the next.
|
||||||
unsigned calldataStride() const { return isByteArray() ? 1 : m_baseType->calldataEncodedSize(); }
|
unsigned calldataStride() const { return isByteArray() ? 1 : m_baseType->calldataHeadSize(); }
|
||||||
/// The offset to advance in memory to move from one array element to the next.
|
/// The offset to advance in memory to move from one array element to the next.
|
||||||
unsigned memoryStride() const { return isByteArray() ? 1 : m_baseType->memoryHeadSize(); }
|
unsigned memoryStride() const { return isByteArray() ? 1 : m_baseType->memoryHeadSize(); }
|
||||||
/// The offset to advance in storage to move from one array element to the next.
|
/// The offset to advance in storage to move from one array element to the next.
|
||||||
@ -741,7 +762,7 @@ private:
|
|||||||
/// String is interpreted as a subtype of Bytes.
|
/// String is interpreted as a subtype of Bytes.
|
||||||
enum class ArrayKind { Ordinary, Bytes, String };
|
enum class ArrayKind { Ordinary, Bytes, String };
|
||||||
|
|
||||||
bigint unlimitedCalldataEncodedSize(bool _padded) const;
|
bigint unlimitedStaticCalldataSize(bool _padded) const;
|
||||||
|
|
||||||
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
|
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
|
||||||
ArrayKind m_arrayKind = ArrayKind::Ordinary;
|
ArrayKind m_arrayKind = ArrayKind::Ordinary;
|
||||||
@ -829,9 +850,10 @@ public:
|
|||||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
unsigned calldataEncodedSize(bool _padded) const override;
|
unsigned calldataEncodedSize(bool) const override;
|
||||||
|
unsigned calldataEncodedTailSize() const override;
|
||||||
bool isDynamicallyEncoded() const override;
|
bool isDynamicallyEncoded() const override;
|
||||||
u256 memorySize() const;
|
u256 memoryDataSize() const override;
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
bool canLiveOutsideStorage() const override { return true; }
|
bool canLiveOutsideStorage() const override { return true; }
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
|
@ -88,7 +88,7 @@ string ABIFunctions::tupleEncoder(
|
|||||||
elementTempl("pos", to_string(headPos));
|
elementTempl("pos", to_string(headPos));
|
||||||
elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options));
|
elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options));
|
||||||
encodeElements += elementTempl.render();
|
encodeElements += elementTempl.render();
|
||||||
headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize();
|
headPos += _targetTypes[i]->calldataHeadSize();
|
||||||
stackPos += sizeOnStack;
|
stackPos += sizeOnStack;
|
||||||
}
|
}
|
||||||
solAssert(headPos == headSize_, "");
|
solAssert(headPos == headSize_, "");
|
||||||
@ -225,7 +225,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
|||||||
elementTempl("pos", to_string(headPos));
|
elementTempl("pos", to_string(headPos));
|
||||||
elementTempl("abiDecode", abiDecodingFunction(*_types[i], _fromMemory, true));
|
elementTempl("abiDecode", abiDecodingFunction(*_types[i], _fromMemory, true));
|
||||||
decodeElements += elementTempl.render();
|
decodeElements += elementTempl.render();
|
||||||
headPos += dynamic ? 0x20 : decodingTypes[i]->calldataEncodedSize();
|
headPos += decodingTypes[i]->calldataHeadSize();
|
||||||
}
|
}
|
||||||
templ("valueReturnParams", boost::algorithm::join(valueReturnParams, ", "));
|
templ("valueReturnParams", boost::algorithm::join(valueReturnParams, ", "));
|
||||||
templ("arrow", valueReturnParams.empty() ? "" : "->");
|
templ("arrow", valueReturnParams.empty() ? "" : "->");
|
||||||
@ -691,6 +691,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
|
|||||||
// Multiple items per slot
|
// Multiple items per slot
|
||||||
solAssert(_from.baseType()->storageBytes() <= 16, "");
|
solAssert(_from.baseType()->storageBytes() <= 16, "");
|
||||||
solAssert(!_from.baseType()->isDynamicallyEncoded(), "");
|
solAssert(!_from.baseType()->isDynamicallyEncoded(), "");
|
||||||
|
solAssert(!_to.baseType()->isDynamicallyEncoded(), "");
|
||||||
solAssert(_from.baseType()->isValueType(), "");
|
solAssert(_from.baseType()->isValueType(), "");
|
||||||
bool dynamic = _to.isDynamicallyEncoded();
|
bool dynamic = _to.isDynamicallyEncoded();
|
||||||
size_t storageBytes = _from.baseType()->storageBytes();
|
size_t storageBytes = _from.baseType()->storageBytes();
|
||||||
@ -715,7 +716,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
|
|||||||
let data := sload(srcPtr)
|
let data := sload(srcPtr)
|
||||||
<#items>
|
<#items>
|
||||||
<encodeToMemoryFun>(<extractFromSlot>(data), pos)
|
<encodeToMemoryFun>(<extractFromSlot>(data), pos)
|
||||||
pos := add(pos, <elementEncodedSize>)
|
pos := add(pos, <stride>)
|
||||||
</items>
|
</items>
|
||||||
srcPtr := add(srcPtr, 1)
|
srcPtr := add(srcPtr, 1)
|
||||||
}
|
}
|
||||||
@ -726,7 +727,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
|
|||||||
<#items>
|
<#items>
|
||||||
if <inRange> {
|
if <inRange> {
|
||||||
<encodeToMemoryFun>(<extractFromSlot>(data), pos)
|
<encodeToMemoryFun>(<extractFromSlot>(data), pos)
|
||||||
pos := add(pos, <elementEncodedSize>)
|
pos := add(pos, <stride>)
|
||||||
itemCounter := add(itemCounter, 1)
|
itemCounter := add(itemCounter, 1)
|
||||||
}
|
}
|
||||||
</items>
|
</items>
|
||||||
@ -753,9 +754,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
|
|||||||
else
|
else
|
||||||
templ("useSpill", "0");
|
templ("useSpill", "0");
|
||||||
templ("itemsPerSlot", to_string(itemsPerSlot));
|
templ("itemsPerSlot", to_string(itemsPerSlot));
|
||||||
// We use padded size because array elements are always padded.
|
templ("stride", toCompactHexWithPrefix(_to.calldataStride()));
|
||||||
string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize());
|
|
||||||
templ("elementEncodedSize", elementEncodedSize);
|
|
||||||
|
|
||||||
EncodingOptions subOptions(_options);
|
EncodingOptions subOptions(_options);
|
||||||
subOptions.encodeFunctionFromStack = false;
|
subOptions.encodeFunctionFromStack = false;
|
||||||
@ -914,7 +913,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
|
|||||||
);
|
);
|
||||||
encodeTempl("memberValues", memberValues);
|
encodeTempl("memberValues", memberValues);
|
||||||
encodeTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
|
encodeTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
|
||||||
encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize();
|
encodingOffset += memberTypeTo->calldataHeadSize();
|
||||||
encodeTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions));
|
encodeTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions));
|
||||||
encode = encodeTempl.render();
|
encode = encodeTempl.render();
|
||||||
}
|
}
|
||||||
@ -1080,8 +1079,8 @@ string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromM
|
|||||||
solAssert(decodingType, "");
|
solAssert(decodingType, "");
|
||||||
solAssert(decodingType->sizeOnStack() == 1, "");
|
solAssert(decodingType->sizeOnStack() == 1, "");
|
||||||
solAssert(decodingType->isValueType(), "");
|
solAssert(decodingType->isValueType(), "");
|
||||||
solAssert(decodingType->calldataEncodedSize() == 32, "");
|
|
||||||
solAssert(!decodingType->isDynamicallyEncoded(), "");
|
solAssert(!decodingType->isDynamicallyEncoded(), "");
|
||||||
|
solAssert(decodingType->calldataEncodedSize() == 32, "");
|
||||||
|
|
||||||
string functionName =
|
string functionName =
|
||||||
"abi_decode_" +
|
"abi_decode_" +
|
||||||
@ -1135,7 +1134,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
|||||||
let elementPos := <retrieveElementPos>
|
let elementPos := <retrieveElementPos>
|
||||||
mstore(dst, <decodingFun>(elementPos, end))
|
mstore(dst, <decodingFun>(elementPos, end))
|
||||||
dst := add(dst, 0x20)
|
dst := add(dst, 0x20)
|
||||||
src := add(src, <baseEncodedSize>)
|
src := add(src, <stride>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)"
|
)"
|
||||||
@ -1145,6 +1144,8 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
|||||||
templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)");
|
templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)");
|
||||||
templ("allocate", m_utils.allocationFunction());
|
templ("allocate", m_utils.allocationFunction());
|
||||||
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
|
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
|
||||||
|
string calldataStride = toCompactHexWithPrefix(_type.calldataStride());
|
||||||
|
templ("stride", calldataStride);
|
||||||
if (_type.isDynamicallySized())
|
if (_type.isDynamicallySized())
|
||||||
templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)");
|
templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)");
|
||||||
else
|
else
|
||||||
@ -1153,14 +1154,11 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
|||||||
{
|
{
|
||||||
templ("staticBoundsCheck", "");
|
templ("staticBoundsCheck", "");
|
||||||
templ("retrieveElementPos", "add(offset, " + load + "(src))");
|
templ("retrieveElementPos", "add(offset, " + load + "(src))");
|
||||||
templ("baseEncodedSize", "0x20");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string baseEncodedSize = toCompactHexWithPrefix(_type.baseType()->calldataEncodedSize());
|
templ("staticBoundsCheck", "if gt(add(src, mul(length, " + calldataStride + ")), end) { revert(0, 0) }");
|
||||||
templ("staticBoundsCheck", "if gt(add(src, mul(length, " + baseEncodedSize + ")), end) { revert(0, 0) }");
|
|
||||||
templ("retrieveElementPos", "src");
|
templ("retrieveElementPos", "src");
|
||||||
templ("baseEncodedSize", baseEncodedSize);
|
|
||||||
}
|
}
|
||||||
templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false));
|
templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false));
|
||||||
return templ.render();
|
return templ.render();
|
||||||
@ -1172,8 +1170,8 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
|||||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||||
if (!_type.isDynamicallySized())
|
if (!_type.isDynamicallySized())
|
||||||
solAssert(_type.length() < u256("0xffffffffffffffff"), "");
|
solAssert(_type.length() < u256("0xffffffffffffffff"), "");
|
||||||
solAssert(_type.baseType()->calldataEncodedSize() > 0, "");
|
solAssert(_type.calldataStride() > 0, "");
|
||||||
solAssert(_type.baseType()->calldataEncodedSize() < u256("0xffffffffffffffff"), "");
|
solAssert(_type.calldataStride() < u256("0xffffffffffffffff"), "");
|
||||||
|
|
||||||
string functionName =
|
string functionName =
|
||||||
"abi_decode_" +
|
"abi_decode_" +
|
||||||
@ -1188,7 +1186,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
|||||||
length := calldataload(offset)
|
length := calldataload(offset)
|
||||||
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
||||||
arrayPos := add(offset, 0x20)
|
arrayPos := add(offset, 0x20)
|
||||||
if gt(add(arrayPos, mul(length, <baseEncodedSize>)), end) { revert(0, 0) }
|
if gt(add(arrayPos, mul(length, <stride>)), end) { revert(0, 0) }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
else
|
else
|
||||||
@ -1196,13 +1194,13 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
|||||||
// <readableTypeName>
|
// <readableTypeName>
|
||||||
function <functionName>(offset, end) -> arrayPos {
|
function <functionName>(offset, end) -> arrayPos {
|
||||||
arrayPos := offset
|
arrayPos := offset
|
||||||
if gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) { revert(0, 0) }
|
if gt(add(arrayPos, mul(<length>, <stride>)), end) { revert(0, 0) }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
Whiskers w{templ};
|
Whiskers w{templ};
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
w("readableTypeName", _type.toString(true));
|
w("readableTypeName", _type.toString(true));
|
||||||
w("baseEncodedSize", toCompactHexWithPrefix(_type.isByteArray() ? 1 : _type.baseType()->calldataEncodedSize()));
|
w("stride", toCompactHexWithPrefix(_type.calldataStride()));
|
||||||
if (!_type.isDynamicallySized())
|
if (!_type.isDynamicallySized())
|
||||||
w("length", toCompactHexWithPrefix(_type.length()));
|
w("length", toCompactHexWithPrefix(_type.length()));
|
||||||
return w.render();
|
return w.render();
|
||||||
@ -1246,7 +1244,6 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _
|
|||||||
string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
|
string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
|
||||||
{
|
{
|
||||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||||
solAssert(_type.calldataEncodedSize(true) != 0, "");
|
|
||||||
string functionName =
|
string functionName =
|
||||||
"abi_decode_" +
|
"abi_decode_" +
|
||||||
_type.identifier();
|
_type.identifier();
|
||||||
@ -1261,7 +1258,7 @@ string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
|
|||||||
)"};
|
)"};
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
w("readableTypeName", _type.toString(true));
|
w("readableTypeName", _type.toString(true));
|
||||||
w("minimumSize", to_string(_type.calldataEncodedSize(true)));
|
w("minimumSize", to_string(_type.isDynamicallyEncoded() ? _type.calldataEncodedTailSize() : _type.calldataEncodedSize(true)));
|
||||||
return w.render();
|
return w.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1291,8 +1288,8 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
|||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
templ("readableTypeName", _type.toString(true));
|
templ("readableTypeName", _type.toString(true));
|
||||||
templ("allocate", m_utils.allocationFunction());
|
templ("allocate", m_utils.allocationFunction());
|
||||||
solAssert(_type.memorySize() < u256("0xffffffffffffffff"), "");
|
solAssert(_type.memoryDataSize() < u256("0xffffffffffffffff"), "");
|
||||||
templ("memorySize", toCompactHexWithPrefix(_type.memorySize()));
|
templ("memorySize", toCompactHexWithPrefix(_type.memoryDataSize()));
|
||||||
size_t headPos = 0;
|
size_t headPos = 0;
|
||||||
vector<map<string, string>> members;
|
vector<map<string, string>> members;
|
||||||
for (auto const& member: _type.members(nullptr))
|
for (auto const& member: _type.members(nullptr))
|
||||||
@ -1322,7 +1319,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
|||||||
members.push_back({});
|
members.push_back({});
|
||||||
members.back()["decode"] = memberTempl.render();
|
members.back()["decode"] = memberTempl.render();
|
||||||
members.back()["memberName"] = member.name;
|
members.back()["memberName"] = member.name;
|
||||||
headPos += dynamic ? 0x20 : decodingType->calldataEncodedSize();
|
headPos += decodingType->calldataHeadSize();
|
||||||
}
|
}
|
||||||
templ("members", members);
|
templ("members", members);
|
||||||
templ("minimumSize", toCompactHexWithPrefix(headPos));
|
templ("minimumSize", toCompactHexWithPrefix(headPos));
|
||||||
@ -1376,8 +1373,8 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
|||||||
return createFunction(functionName, [&]() {
|
return createFunction(functionName, [&]() {
|
||||||
if (_type.isDynamicallyEncoded())
|
if (_type.isDynamicallyEncoded())
|
||||||
{
|
{
|
||||||
unsigned int baseEncodedSize = _type.calldataEncodedSize();
|
unsigned int tailSize = _type.calldataEncodedTailSize();
|
||||||
solAssert(baseEncodedSize > 1, "");
|
solAssert(tailSize > 1, "");
|
||||||
Whiskers w(R"(
|
Whiskers w(R"(
|
||||||
function <functionName>(base_ref, ptr) -> <return> {
|
function <functionName>(base_ref, ptr) -> <return> {
|
||||||
let rel_offset_of_tail := calldataload(ptr)
|
let rel_offset_of_tail := calldataload(ptr)
|
||||||
@ -1390,13 +1387,12 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
|||||||
{
|
{
|
||||||
auto const* arrayType = dynamic_cast<ArrayType const*>(&_type);
|
auto const* arrayType = dynamic_cast<ArrayType const*>(&_type);
|
||||||
solAssert(!!arrayType, "");
|
solAssert(!!arrayType, "");
|
||||||
unsigned int calldataStride = arrayType->calldataStride();
|
|
||||||
w("handleLength", Whiskers(R"(
|
w("handleLength", Whiskers(R"(
|
||||||
length := calldataload(value)
|
length := calldataload(value)
|
||||||
value := add(value, 0x20)
|
value := add(value, 0x20)
|
||||||
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
||||||
if sgt(base_ref, sub(calldatasize(), mul(length, <calldataStride>))) { revert(0, 0) }
|
if sgt(base_ref, sub(calldatasize(), mul(length, <calldataStride>))) { revert(0, 0) }
|
||||||
)")("calldataStride", toCompactHexWithPrefix(calldataStride)).render());
|
)")("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride())).render());
|
||||||
w("return", "value, length");
|
w("return", "value, length");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1404,7 +1400,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
|||||||
w("handleLength", "");
|
w("handleLength", "");
|
||||||
w("return", "value");
|
w("return", "value");
|
||||||
}
|
}
|
||||||
w("neededLength", toCompactHexWithPrefix(baseEncodedSize));
|
w("neededLength", toCompactHexWithPrefix(tailSize));
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
return w.render();
|
return w.render();
|
||||||
}
|
}
|
||||||
@ -1483,12 +1479,7 @@ size_t ABIFunctions::headSize(TypePointers const& _targetTypes)
|
|||||||
{
|
{
|
||||||
size_t headSize = 0;
|
size_t headSize = 0;
|
||||||
for (auto const& t: _targetTypes)
|
for (auto const& t: _targetTypes)
|
||||||
{
|
headSize += t->calldataHeadSize();
|
||||||
if (t->isDynamicallyEncoded())
|
|
||||||
headSize += 0x20;
|
|
||||||
else
|
|
||||||
headSize += t->calldataEncodedSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
return headSize;
|
return headSize;
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
else if (_sourceType.location() == DataLocation::Memory)
|
else if (_sourceType.location() == DataLocation::Memory)
|
||||||
_context << sourceBaseType->memoryHeadSize();
|
_context << sourceBaseType->memoryHeadSize();
|
||||||
else
|
else
|
||||||
_context << sourceBaseType->calldataEncodedSize(true);
|
_context << sourceBaseType->calldataHeadSize();
|
||||||
_context
|
_context
|
||||||
<< Instruction::ADD
|
<< Instruction::ADD
|
||||||
<< swapInstruction(2 + byteOffsetSize);
|
<< swapInstruction(2 + byteOffsetSize);
|
||||||
@ -294,20 +294,13 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
"Nested dynamic arrays not implemented here."
|
"Nested dynamic arrays not implemented here."
|
||||||
);
|
);
|
||||||
CompilerUtils utils(m_context);
|
CompilerUtils utils(m_context);
|
||||||
unsigned baseSize = 1;
|
|
||||||
if (!_sourceType.isByteArray())
|
|
||||||
{
|
|
||||||
// We always pad the elements, regardless of _padToWordBoundaries.
|
|
||||||
baseSize = _sourceType.baseType()->calldataEncodedSize();
|
|
||||||
solAssert(baseSize >= 0x20, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sourceType.location() == DataLocation::CallData)
|
if (_sourceType.location() == DataLocation::CallData)
|
||||||
{
|
{
|
||||||
if (!_sourceType.isDynamicallySized())
|
if (!_sourceType.isDynamicallySized())
|
||||||
m_context << _sourceType.length();
|
m_context << _sourceType.length();
|
||||||
if (baseSize > 1)
|
if (!_sourceType.isByteArray())
|
||||||
m_context << u256(baseSize) << Instruction::MUL;
|
convertLengthToSize(_sourceType);
|
||||||
|
|
||||||
string routine = "calldatacopy(target, source, len)\n";
|
string routine = "calldatacopy(target, source, len)\n";
|
||||||
if (_padToWordBoundaries)
|
if (_padToWordBoundaries)
|
||||||
@ -358,15 +351,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
|
m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
|
||||||
m_context << Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
// convert length to size
|
if (!_sourceType.isByteArray())
|
||||||
if (baseSize > 1)
|
convertLengthToSize(_sourceType);
|
||||||
m_context << u256(baseSize) << Instruction::MUL;
|
|
||||||
// stack: <target> <source> <size>
|
// stack: <target> <source> <size>
|
||||||
m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4;
|
m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4;
|
||||||
// We can resort to copying full 32 bytes only if
|
// We can resort to copying full 32 bytes only if
|
||||||
// - the length is known to be a multiple of 32 or
|
// - the length is known to be a multiple of 32 or
|
||||||
// - we will pad to full 32 bytes later anyway.
|
// - we will pad to full 32 bytes later anyway.
|
||||||
if (((baseSize % 32) == 0) || _padToWordBoundaries)
|
if (!_sourceType.isByteArray() || _padToWordBoundaries)
|
||||||
utils.memoryCopy32();
|
utils.memoryCopy32();
|
||||||
else
|
else
|
||||||
utils.memoryCopy();
|
utils.memoryCopy();
|
||||||
@ -374,11 +366,8 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
// stack: <target> <size>
|
// stack: <target> <size>
|
||||||
|
|
||||||
bool paddingNeeded = false;
|
bool paddingNeeded = _padToWordBoundaries && _sourceType.isByteArray();
|
||||||
if (_sourceType.isDynamicallySized())
|
|
||||||
paddingNeeded = _padToWordBoundaries && ((baseSize % 32) != 0);
|
|
||||||
else
|
|
||||||
paddingNeeded = _padToWordBoundaries && (((_sourceType.length() * baseSize) % 32) != 0);
|
|
||||||
if (paddingNeeded)
|
if (paddingNeeded)
|
||||||
{
|
{
|
||||||
// stack: <target> <size>
|
// stack: <target> <size>
|
||||||
@ -455,10 +444,10 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
m_context.appendJumpTo(loopEnd);
|
m_context.appendJumpTo(loopEnd);
|
||||||
m_context << longByteArray;
|
m_context << longByteArray;
|
||||||
}
|
}
|
||||||
// compute memory end offset
|
else
|
||||||
if (baseSize > 1)
|
|
||||||
// convert length to memory size
|
// convert length to memory size
|
||||||
m_context << u256(baseSize) << Instruction::MUL;
|
m_context << _sourceType.baseType()->memoryHeadSize() << Instruction::MUL;
|
||||||
|
|
||||||
m_context << Instruction::DUP3 << Instruction::ADD << Instruction::SWAP2;
|
m_context << Instruction::DUP3 << Instruction::ADD << Instruction::SWAP2;
|
||||||
if (_sourceType.isDynamicallySized())
|
if (_sourceType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
@ -515,7 +504,12 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
||||||
if (haveByteOffset)
|
if (haveByteOffset)
|
||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
if (_padToWordBoundaries && baseSize % 32 != 0)
|
if (!_sourceType.isByteArray())
|
||||||
|
{
|
||||||
|
solAssert(_sourceType.calldataStride() % 32 == 0, "");
|
||||||
|
solAssert(_sourceType.memoryStride() % 32 == 0, "");
|
||||||
|
}
|
||||||
|
if (_padToWordBoundaries && _sourceType.isByteArray())
|
||||||
{
|
{
|
||||||
// memory_end_offset - start is the actual length (we want to compute the ceil of).
|
// memory_end_offset - start is the actual length (we want to compute the ceil of).
|
||||||
// memory_offset - start is its next multiple of 32, but it might be off by 32.
|
// memory_offset - start is its next multiple of 32, but it might be off by 32.
|
||||||
@ -987,9 +981,9 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con
|
|||||||
if (!_arrayType.isByteArray())
|
if (!_arrayType.isByteArray())
|
||||||
{
|
{
|
||||||
if (_arrayType.location() == DataLocation::Memory)
|
if (_arrayType.location() == DataLocation::Memory)
|
||||||
m_context << _arrayType.baseType()->memoryHeadSize();
|
m_context << _arrayType.memoryStride();
|
||||||
else
|
else
|
||||||
m_context << _arrayType.baseType()->calldataEncodedSize();
|
m_context << _arrayType.calldataStride();
|
||||||
m_context << Instruction::MUL;
|
m_context << Instruction::MUL;
|
||||||
}
|
}
|
||||||
else if (_pad)
|
else if (_pad)
|
||||||
@ -1067,10 +1061,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
|
|||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
if (!_arrayType.isByteArray())
|
if (!_arrayType.isByteArray())
|
||||||
{
|
{
|
||||||
if (_arrayType.baseType()->isDynamicallyEncoded())
|
m_context << _arrayType.calldataStride();
|
||||||
m_context << u256(0x20);
|
|
||||||
else
|
|
||||||
m_context << _arrayType.baseType()->calldataEncodedSize();
|
|
||||||
m_context << Instruction::MUL;
|
m_context << Instruction::MUL;
|
||||||
}
|
}
|
||||||
// stack: <base_ref> <index * size>
|
// stack: <base_ref> <index * size>
|
||||||
|
@ -98,16 +98,17 @@ void CompilerUtils::revertWithStringData(Type const& _argumentType)
|
|||||||
void CompilerUtils::accessCalldataTail(Type const& _type)
|
void CompilerUtils::accessCalldataTail(Type const& _type)
|
||||||
{
|
{
|
||||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||||
|
solAssert(_type.isDynamicallyEncoded(), "");
|
||||||
|
|
||||||
unsigned int baseEncodedSize = _type.calldataEncodedSize();
|
unsigned int tailSize = _type.calldataEncodedTailSize();
|
||||||
solAssert(baseEncodedSize > 1, "");
|
solAssert(tailSize > 1, "");
|
||||||
|
|
||||||
// returns the absolute offset of the tail in "base_ref"
|
// returns the absolute offset of the tail in "base_ref"
|
||||||
m_context.appendInlineAssembly(Whiskers(R"({
|
m_context.appendInlineAssembly(Whiskers(R"({
|
||||||
let rel_offset_of_tail := calldataload(ptr_to_tail)
|
let rel_offset_of_tail := calldataload(ptr_to_tail)
|
||||||
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { revert(0, 0) }
|
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { revert(0, 0) }
|
||||||
base_ref := add(base_ref, rel_offset_of_tail)
|
base_ref := add(base_ref, rel_offset_of_tail)
|
||||||
})")("neededLength", toCompactHexWithPrefix(baseEncodedSize)).render(), {"base_ref", "ptr_to_tail"});
|
})")("neededLength", toCompactHexWithPrefix(tailSize)).render(), {"base_ref", "ptr_to_tail"});
|
||||||
// stack layout: <absolute_offset_of_tail> <garbage>
|
// stack layout: <absolute_offset_of_tail> <garbage>
|
||||||
|
|
||||||
if (!_type.isDynamicallySized())
|
if (!_type.isDynamicallySized())
|
||||||
@ -170,7 +171,7 @@ void CompilerUtils::loadFromMemoryDynamic(
|
|||||||
solAssert(!_fromCalldata, "");
|
solAssert(!_fromCalldata, "");
|
||||||
solAssert(_padToWordBoundaries, "");
|
solAssert(_padToWordBoundaries, "");
|
||||||
if (_keepUpdatedMemoryOffset)
|
if (_keepUpdatedMemoryOffset)
|
||||||
m_context << arrayType->memorySize() << Instruction::ADD;
|
m_context << arrayType->memoryDataSize() << Instruction::ADD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -251,7 +252,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem
|
|||||||
//@todo this does not yet support nested dynamic arrays
|
//@todo this does not yet support nested dynamic arrays
|
||||||
size_t encodedSize = 0;
|
size_t encodedSize = 0;
|
||||||
for (auto const& t: _typeParameters)
|
for (auto const& t: _typeParameters)
|
||||||
encodedSize += t->decodingType()->calldataEncodedSize(true);
|
encodedSize += t->decodingType()->calldataHeadSize();
|
||||||
m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"});
|
m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"});
|
||||||
|
|
||||||
m_context << Instruction::DUP2 << Instruction::ADD;
|
m_context << Instruction::DUP2 << Instruction::ADD;
|
||||||
@ -321,7 +322,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem
|
|||||||
// Size has already been checked for this one.
|
// Size has already been checked for this one.
|
||||||
moveIntoStack(2);
|
moveIntoStack(2);
|
||||||
m_context << Instruction::DUP3;
|
m_context << Instruction::DUP3;
|
||||||
m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD;
|
m_context << u256(arrayType.calldataHeadSize()) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -358,7 +359,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem
|
|||||||
// size has already been checked
|
// size has already been checked
|
||||||
// stack: input_end base_offset data_offset
|
// stack: input_end base_offset data_offset
|
||||||
m_context << Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD;
|
m_context << u256(calldataType->calldataHeadSize()) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
if (arrayType.location() == DataLocation::Memory)
|
if (arrayType.location() == DataLocation::Memory)
|
||||||
{
|
{
|
||||||
@ -588,11 +589,6 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: Potential optimization:
|
|
||||||
// When we create a new multi-dimensional dynamic array, each element
|
|
||||||
// is initialized to an empty array. It actually does not hurt
|
|
||||||
// to re-use exactly the same empty array for all elements. Currently,
|
|
||||||
// a new one is created each time.
|
|
||||||
auto repeat = m_context.newTag();
|
auto repeat = m_context.newTag();
|
||||||
m_context << repeat;
|
m_context << repeat;
|
||||||
pushZeroValue(*_type.baseType());
|
pushZeroValue(*_type.baseType());
|
||||||
@ -1014,7 +1010,7 @@ void CompilerUtils::convertType(
|
|||||||
{
|
{
|
||||||
CompilerUtils utils(_context);
|
CompilerUtils utils(_context);
|
||||||
// stack: <source ref>
|
// stack: <source ref>
|
||||||
utils.allocateMemory(typeOnStack->memorySize());
|
utils.allocateMemory(typeOnStack->memoryDataSize());
|
||||||
_context << Instruction::SWAP1 << Instruction::DUP2;
|
_context << Instruction::SWAP1 << Instruction::DUP2;
|
||||||
// stack: <memory ptr> <source ref> <memory ptr>
|
// stack: <memory ptr> <source ref> <memory ptr>
|
||||||
for (auto const& member: typeOnStack->members(nullptr))
|
for (auto const& member: typeOnStack->members(nullptr))
|
||||||
@ -1197,7 +1193,8 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
|||||||
1,
|
1,
|
||||||
[type](CompilerContext& _context) {
|
[type](CompilerContext& _context) {
|
||||||
CompilerUtils utils(_context);
|
CompilerUtils utils(_context);
|
||||||
utils.allocateMemory(max(32u, type->calldataEncodedSize()));
|
|
||||||
|
utils.allocateMemory(max<u256>(32u, type->memoryDataSize()));
|
||||||
_context << Instruction::DUP1;
|
_context << Instruction::DUP1;
|
||||||
|
|
||||||
if (auto structType = dynamic_cast<StructType const*>(type))
|
if (auto structType = dynamic_cast<StructType const*>(type))
|
||||||
@ -1364,6 +1361,8 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
|
|||||||
|
|
||||||
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
|
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
|
||||||
{
|
{
|
||||||
|
solAssert(_type.isValueType(), "");
|
||||||
|
|
||||||
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
||||||
bool isExternalFunctionType = false;
|
bool isExternalFunctionType = false;
|
||||||
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
@ -1436,6 +1435,8 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords)
|
|||||||
"Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")."
|
"Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
solAssert(!_type.isDynamicallyEncoded(), "");
|
||||||
|
|
||||||
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
||||||
|
|
||||||
solAssert(
|
solAssert(
|
||||||
|
@ -317,7 +317,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
|
|||||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
|
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
|
||||||
|
|
||||||
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
|
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
|
||||||
utils().allocateMemory(max(u256(32u), arrayType.memorySize()));
|
utils().allocateMemory(max(u256(32u), arrayType.memoryDataSize()));
|
||||||
m_context << Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
|
|
||||||
for (auto const& component: _tuple.components())
|
for (auto const& component: _tuple.components())
|
||||||
@ -526,7 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||||
|
|
||||||
utils().allocateMemory(max(u256(32u), structType.memorySize()));
|
utils().allocateMemory(max(u256(32u), structType.memoryDataSize()));
|
||||||
m_context << Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
|
|
||||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||||
|
@ -652,11 +652,11 @@ string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
|||||||
<?byteArray>
|
<?byteArray>
|
||||||
size := length
|
size := length
|
||||||
<!byteArray>
|
<!byteArray>
|
||||||
size := <mul>(length, <elementSize>)
|
size := <mul>(length, <stride>)
|
||||||
</byteArray>
|
</byteArray>
|
||||||
})")
|
})")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("elementSize", to_string(_type.location() == DataLocation::Memory ? baseType.memoryHeadSize() : baseType.calldataEncodedSize()))
|
("stride", to_string(_type.location() == DataLocation::Memory ? _type.memoryStride() : _type.calldataStride()))
|
||||||
("byteArray", _type.isByteArray())
|
("byteArray", _type.isByteArray())
|
||||||
("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
|
("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
|
||||||
.render();
|
.render();
|
||||||
@ -812,10 +812,7 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
|||||||
}
|
}
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
{
|
{
|
||||||
u256 size =
|
u256 size = _type.calldataStride();
|
||||||
_type.baseType()->isDynamicallyEncoded() ?
|
|
||||||
32 :
|
|
||||||
_type.baseType()->calldataEncodedSize();
|
|
||||||
solAssert(size >= 32 && size % 32 == 0, "");
|
solAssert(size >= 32 && size % 32 == 0, "");
|
||||||
templ("advance", toCompactHexWithPrefix(size));
|
templ("advance", toCompactHexWithPrefix(size));
|
||||||
break;
|
break;
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
function f(uint256[][2][] calldata x) external returns (uint256) {
|
||||||
|
x[0]; // trigger bounds checks
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 23 # this is the common encoding for x.length == 1 && x[0][0].length == 0 && x[0][1].length == 0 #
|
||||||
|
// f(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 23 # exotic, but still valid encoding #
|
||||||
|
// f(uint256[][2][]): 0x20, 0x01, 0x20, 0x00 -> FAILURE # invalid (too short) encoding, but no failure due to this PR #
|
@ -0,0 +1,13 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
function f(uint256[][2][] calldata x) external returns (uint256) {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
function g(uint256[][2][] calldata x) external returns (uint256) {
|
||||||
|
return this.f(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// g(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 42
|
||||||
|
// g(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 42
|
||||||
|
// g(uint256[][2][]): 0x20, 0x01, 0x20, 0x00 -> FAILURE
|
@ -0,0 +1,23 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct A {
|
||||||
|
uint256 a;
|
||||||
|
uint256[] b;
|
||||||
|
}
|
||||||
|
struct B {
|
||||||
|
A a;
|
||||||
|
uint256 b;
|
||||||
|
}
|
||||||
|
function g(B calldata b) external pure returns(uint256) {
|
||||||
|
return b.b;
|
||||||
|
}
|
||||||
|
function f() public view returns(uint256, uint256) {
|
||||||
|
uint256[] memory arr = new uint256[](20);
|
||||||
|
arr[0] = 31; arr[2] = 84;
|
||||||
|
B memory b = B(A(420, arr), 11);
|
||||||
|
return (b.b, this.g(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f() -> 11, 11
|
Loading…
Reference in New Issue
Block a user