diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 936957e6b..b17267b36 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -1106,11 +1106,9 @@ string ABIFunctions::abiEncodingFunctionStruct( _to.identifier() + _options.toFunctionNameSuffix(); - solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported."); solAssert(&_from.structDefinition() == &_to.structDefinition(), ""); return createFunction(functionName, [&]() { - bool fromStorage = _from.location() == DataLocation::Storage; bool dynamic = _to.isDynamicallyEncoded(); Whiskers templ(R"( // -> @@ -1121,7 +1119,7 @@ string ABIFunctions::abiEncodingFunctionStruct( { // - let memberValue := + let := } @@ -1139,7 +1137,7 @@ string ABIFunctions::abiEncodingFunctionStruct( else templ("assignEnd", ""); // to avoid multiple loads from the same slot for subsequent members - templ("init", fromStorage ? "let slotValue := 0" : ""); + templ("init", _from.dataStoredIn(DataLocation::Storage) ? "let slotValue := 0" : ""); u256 previousSlotOffset(-1); u256 encodingOffset = 0; vector> members; @@ -1159,32 +1157,41 @@ string ABIFunctions::abiEncodingFunctionStruct( members.push_back({}); members.back()["preprocess"] = ""; - if (fromStorage) + switch (_from.location()) { - solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), ""); - u256 storageSlotOffset; - size_t intraSlotOffset; - tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name); - if (memberTypeFrom->isValueType()) + case DataLocation::Storage: { - if (storageSlotOffset != previousSlotOffset) + solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), ""); + u256 storageSlotOffset; + size_t intraSlotOffset; + tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name); + if (memberTypeFrom->isValueType()) { - members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; - previousSlotOffset = storageSlotOffset; + if (storageSlotOffset != previousSlotOffset) + { + members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; + previousSlotOffset = storageSlotOffset; + } + members.back()["retrieveValue"] = extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)"; } - members.back()["retrieveValue"] = extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)"; + else + { + solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), ""); + solAssert(intraSlotOffset == 0, ""); + members.back()["retrieveValue"] = "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")"; + } + break; } - else + case DataLocation::Memory: { - solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), ""); - solAssert(intraSlotOffset == 0, ""); - members.back()["retrieveValue"] = "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")"; + string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name)); + members.back()["retrieveValue"] = "mload(add(value, " + sourceOffset + "))"; + break; } - } - else - { - string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name)); - members.back()["retrieveValue"] = "mload(add(value, " + sourceOffset + "))"; + case DataLocation::CallData: + solUnimplementedAssert(false, "Encoding struct from calldata is not yet supported."); + default: + solAssert(false, ""); } EncodingOptions subOptions(_options); @@ -1192,10 +1199,14 @@ string ABIFunctions::abiEncodingFunctionStruct( // Like with arrays, struct members are always padded. subOptions.padded = true; + string memberValues = m_utils.suffixedVariableNameList("memberValue", 0, numVariablesForType(*memberTypeFrom, subOptions)); + members.back()["memberValues"] = memberValues; + string encode; if (_options.dynamicInplace) - encode = Whiskers{"pos := (memberValue, pos)"} + encode = Whiskers{"pos := (, pos)"} ("encode", abiEncodeAndReturnUpdatedPosFunction(*memberTypeFrom, *memberTypeTo, subOptions)) + ("memberValues", memberValues) .render(); else { @@ -1203,10 +1214,11 @@ string ABIFunctions::abiEncodingFunctionStruct( dynamicMember ? string(R"( mstore(add(pos, ), sub(tail, pos)) - tail := (memberValue, tail) + tail := (, tail) )") : - "(memberValue, add(pos, ))" + "(, add(pos, ))" ); + encodeTempl("memberValues", memberValues); encodeTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset)); encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize(); encodeTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions)); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 25a5fce9c..43596189a 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -330,40 +330,53 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) { string functionName = "array_dataslot_" + _type.identifier(); return m_functionCollector->createFunction(functionName, [&]() { - if (_type.dataStoredIn(DataLocation::Memory)) + switch (_type.location()) { - if (_type.isDynamicallySized()) - return Whiskers(R"( - function (memPtr) -> dataPtr { - dataPtr := add(memPtr, 0x20) - } - )") - ("functionName", functionName) - .render(); - else - return Whiskers(R"( - function (memPtr) -> dataPtr { - dataPtr := memPtr - } - )") - ("functionName", functionName) - .render(); - } - else if (_type.dataStoredIn(DataLocation::Storage)) - { - if (_type.isDynamicallySized()) - { - Whiskers w(R"( - function (slot) -> dataSlot { - mstore(0, slot) - dataSlot := keccak256(0, 0x20) - } - )"); - w("functionName", functionName); - return w.render(); - } - else + case DataLocation::Memory: + if (_type.isDynamicallySized()) + return Whiskers(R"( + function (memPtr) -> dataPtr { + dataPtr := add(memPtr, 0x20) + } + )") + ("functionName", functionName) + .render(); + else + return Whiskers(R"( + function (memPtr) -> dataPtr { + dataPtr := memPtr + } + )") + ("functionName", functionName) + .render(); + case DataLocation::Storage: + if (_type.isDynamicallySized()) + { + Whiskers w(R"( + function (slot) -> dataSlot { + mstore(0, slot) + dataSlot := keccak256(0, 0x20) + } + )"); + w("functionName", functionName); + return w.render(); + } + else + { + Whiskers w(R"( + function (slot) -> dataSlot { + dataSlot := slot + } + )"); + w("functionName", functionName); + return w.render(); + } + case DataLocation::CallData: { + // Calldata arrays are stored as offset of the data area and length + // on the stack, so the offset already points to the data area. + // This might change, if calldata arrays are stored in a single + // stack slot at some point. Whiskers w(R"( function (slot) -> dataSlot { dataSlot := slot @@ -372,11 +385,8 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) w("functionName", functionName); return w.render(); } - } - else - { - // Not used for calldata - solAssert(false, ""); + default: + solAssert(false, ""); } }); } @@ -384,36 +394,40 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) { solAssert(!_type.isByteArray(), ""); - solAssert( - _type.location() == DataLocation::Memory || - _type.location() == DataLocation::Storage, - "" - ); - solAssert( - _type.location() == DataLocation::Memory || - _type.baseType()->storageBytes() > 16, - "" - ); + if (_type.dataStoredIn(DataLocation::Storage)) + solAssert(_type.baseType()->storageBytes() > 16, ""); string functionName = "array_nextElement_" + _type.identifier(); return m_functionCollector->createFunction(functionName, [&]() { - if (_type.location() == DataLocation::Memory) - return Whiskers(R"( - function (memPtr) -> nextPtr { - nextPtr := add(memPtr, 0x20) - } - )") - ("functionName", functionName) - .render(); - else if (_type.location() == DataLocation::Storage) - return Whiskers(R"( - function (slot) -> nextSlot { - nextSlot := add(slot, 1) - } - )") - ("functionName", functionName) - .render(); - else - solAssert(false, ""); + switch (_type.location()) + { + case DataLocation::Memory: + return Whiskers(R"( + function (memPtr) -> nextPtr { + nextPtr := add(memPtr, 0x20) + } + )") + ("functionName", functionName) + .render(); + case DataLocation::Storage: + return Whiskers(R"( + function (slot) -> nextSlot { + nextSlot := add(slot, 1) + } + )") + ("functionName", functionName) + .render(); + case DataLocation::CallData: + return Whiskers(R"( + function (calldataPtr) -> nextPtr { + nextPtr := add(calldataPtr, ) + } + )") + ("stride", toCompactHexWithPrefix(_type.baseType()->isDynamicallyEncoded() ? 32 : _type.baseType()->calldataEncodedSize())) + ("functionName", functionName) + .render(); + default: + solAssert(false, ""); + } }); } diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index da5a17cf2..cffa9efbc 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -79,11 +79,11 @@ public: /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); /// @returns the name of a function that converts a storage slot number - /// or a memory pointer to the slot number / memory pointer for the data position of an array - /// which is stored in that slot / memory area. + /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer + /// for the data position of an array which is stored in that slot / memory area / calldata area. std::string arrayDataAreaFunction(ArrayType const& _type); /// @returns the name of a function that advances an array data pointer to the next element. - /// Only works for memory arrays and storage arrays that store one item per slot. + /// Only works for memory arrays, calldata arrays and storage arrays that store one item per slot. std::string nextArrayElementFunction(ArrayType const& _type); /// @returns the name of a function that allocates memory.