mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -1556,13 +1556,14 @@ string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::copyArrayToStorage(ArrayType const& _fromType, ArrayType const& _toType)
|
||||
string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
||||
{
|
||||
solAssert(
|
||||
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
||||
""
|
||||
);
|
||||
solUnimplementedAssert(!_fromType.isByteArray(), "");
|
||||
if (_fromType.isByteArray())
|
||||
return copyByteArrayToStorageFunction(_fromType, _toType);
|
||||
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
|
||||
|
||||
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
||||
@@ -1655,6 +1656,84 @@ string YulUtilFunctions::copyArrayToStorage(ArrayType const& _fromType, ArrayTyp
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
||||
{
|
||||
solAssert(
|
||||
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
||||
""
|
||||
);
|
||||
solAssert(_fromType.isByteArray(), "");
|
||||
solAssert(_toType.isByteArray(), "");
|
||||
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
|
||||
|
||||
string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&](){
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(slot, src<?fromCalldata>, len</fromCalldata>) {
|
||||
let newLen := <arrayLength>(src<?fromCalldata>, len</fromCalldata>)
|
||||
// Make sure array length is sane
|
||||
if gt(newLen, 0xffffffffffffffff) { <panic>() }
|
||||
|
||||
let oldLen := <byteArrayLength>(sload(slot))
|
||||
|
||||
<?fromMemory>
|
||||
src := add(src, 0x20)
|
||||
</fromMemory>
|
||||
|
||||
// This is not needed in all branches.
|
||||
let dstDataArea
|
||||
if or(gt(oldLen, 31), gt(newLen, 31)) {
|
||||
dstDataArea := <dstDataLocation>(slot)
|
||||
}
|
||||
|
||||
if gt(oldLen, 31) {
|
||||
// potentially truncate data
|
||||
let deleteStart := add(dstDataArea, div(add(newLen, 31), 32))
|
||||
if lt(newLen, 32) { deleteStart := dstDataArea }
|
||||
<clearStorageRange>(deleteStart, add(dstDataArea, div(add(oldLen, 31), 32)))
|
||||
}
|
||||
switch gt(newLen, 31)
|
||||
case 1 {
|
||||
let loopEnd := and(newLen, not(0x1f))
|
||||
let dstPtr := dstDataArea
|
||||
let i := 0
|
||||
for { } lt(i, loopEnd) { i := add(i, 32) } {
|
||||
sstore(dstPtr, <readFromCalldataOrMemory>(add(src, i)))
|
||||
dstPtr := add(dstPtr, 1)
|
||||
}
|
||||
if lt(loopEnd, newLen) {
|
||||
let lastValue := <readFromCalldataOrMemory>(add(src, i))
|
||||
sstore(dstPtr, <maskBytes>(lastValue, and(newLen, 0x1f)))
|
||||
}
|
||||
sstore(slot, add(mul(newLen, 2), 1))
|
||||
}
|
||||
default {
|
||||
let value := 0
|
||||
if newLen {
|
||||
value := <readFromCalldataOrMemory>(src)
|
||||
}
|
||||
sstore(slot, <byteArrayCombineShort>(value, newLen))
|
||||
}
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
|
||||
templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
|
||||
templ("fromCalldata", fromCalldata);
|
||||
templ("arrayLength", arrayLengthFunction(_fromType));
|
||||
templ("panic", panicFunction(PanicCode::ResourceError));
|
||||
templ("byteArrayLength", extractByteArrayLengthFunction());
|
||||
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
||||
templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType()));
|
||||
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*TypeProvider::uint256(), fromCalldata));
|
||||
templ("maskBytes", maskBytesFunctionDynamic());
|
||||
templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction());
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
||||
{
|
||||
string functionName = "array_convert_length_to_size_" + _type.identifier();
|
||||
@@ -2207,8 +2286,9 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(slot, <offset>value) {
|
||||
sstore(slot, <update>(sload(slot), <offset><prepare>(value)))
|
||||
function <functionName>(slot, <offset><fromValues>) {
|
||||
let <toValues> := <convert>(<fromValues>)
|
||||
sstore(slot, <update>(sload(slot), <offset><prepare>(<toValues>)))
|
||||
}
|
||||
|
||||
)")
|
||||
@@ -2219,6 +2299,9 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
updateByteSliceFunctionDynamic(_toType.storageBytes())
|
||||
)
|
||||
("offset", _offset.has_value() ? "" : "offset, ")
|
||||
("convert", conversionFunction(_fromType, _toType))
|
||||
("fromValues", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()))
|
||||
("toValues", suffixedVariableNameList("convertedValue_", 0, _toType.sizeOnStack()))
|
||||
("prepare", prepareStoreFunction(_toType))
|
||||
.render();
|
||||
}
|
||||
@@ -2248,7 +2331,7 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
|
||||
templ("copyArrayToStorage", copyArrayToStorage(
|
||||
templ("copyArrayToStorage", copyArrayToStorageFunction(
|
||||
dynamic_cast<ArrayType const&>(_fromType),
|
||||
dynamic_cast<ArrayType const&>(_toType)
|
||||
));
|
||||
@@ -2478,9 +2561,16 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
||||
return templ.render();
|
||||
}
|
||||
|
||||
bool leftAligned = false;
|
||||
if (
|
||||
_type.category() != Type::Category::Function ||
|
||||
dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
|
||||
)
|
||||
leftAligned = _type.leftAligned();
|
||||
|
||||
if (storageBytes == 32)
|
||||
templ("cleaned", "value");
|
||||
else if (_type.leftAligned())
|
||||
else if (leftAligned)
|
||||
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
||||
else
|
||||
templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
|
||||
@@ -2491,7 +2581,8 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
||||
|
||||
string YulUtilFunctions::prepareStoreFunction(Type const& _type)
|
||||
{
|
||||
solUnimplementedAssert(_type.category() != Type::Category::Function, "");
|
||||
if (_type.category() == Type::Category::Function)
|
||||
solUnimplementedAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::Internal, "");
|
||||
|
||||
string functionName = "prepare_store_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
@@ -2718,12 +2809,13 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
_to.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(addr, functionId) -> outAddr, outFunctionId {
|
||||
outAddr := addr
|
||||
function <functionName>(<?external>addr, </external>functionId) -> <?external>outAddr, </external>outFunctionId {
|
||||
<?external>outAddr := addr</external>
|
||||
outFunctionId := functionId
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("external", fromType.kind() == FunctionType::Kind::External)
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user