@@ -964,7 +964,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
964
964
(" dataAreaFunction" , arrayDataAreaFunction (_type))
965
965
(" isByteArray" , _type.isByteArray ())
966
966
(" indexAccess" , storageArrayIndexAccessFunction (_type))
967
- (" storeValue" , updateStorageValueFunction (*_type.baseType ()))
967
+ (" storeValue" , updateStorageValueFunction (*_type.baseType (), *_type. baseType () ))
968
968
(" maxArrayLength" , (u256 (1 ) << 64 ).str ())
969
969
(" shl" , shiftLeftFunctionDynamic ())
970
970
(" shr" , shiftRightFunction (248 ))
@@ -994,7 +994,7 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
994
994
(" functionName" , functionName)
995
995
(" fetchLength" , arrayLengthFunction (_type))
996
996
(" indexAccess" , storageArrayIndexAccessFunction (_type))
997
- (" storeValue" , updateStorageValueFunction (*_type.baseType ()))
997
+ (" storeValue" , updateStorageValueFunction (*_type.baseType (), *_type. baseType () ))
998
998
(" maxArrayLength" , (u256 (1 ) << 64 ).str ())
999
999
(" zeroValueFunction" , zeroValueFunction (*_type.baseType ()))
1000
1000
.render ();
@@ -1404,34 +1404,55 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
1404
1404
1405
1405
string YulUtilFunctions::readFromStorage (Type const & _type, size_t _offset, bool _splitFunctionTypes)
1406
1406
{
1407
+ if (_type.isValueType ())
1408
+ return readFromStorageValueType (_type, _offset, _splitFunctionTypes);
1409
+ else
1410
+ {
1411
+ solAssert (_offset == 0 , " " );
1412
+ return readFromStorageReferenceType (_type);
1413
+ }
1414
+ }
1415
+
1416
+ string YulUtilFunctions::readFromStorageDynamic (Type const & _type, bool _splitFunctionTypes)
1417
+ {
1418
+ solAssert (_type.isValueType (), " " );
1419
+ return readFromStorageValueTypeDynamic (_type, _splitFunctionTypes);
1420
+ }
1421
+
1422
+ string YulUtilFunctions::readFromStorageValueType (Type const & _type, size_t _offset, bool _splitFunctionTypes)
1423
+ {
1424
+ solAssert (_type.isValueType (), " " );
1425
+
1407
1426
if (_type.category () == Type::Category::Function)
1408
1427
solUnimplementedAssert (!_splitFunctionTypes, " " );
1409
1428
string functionName =
1410
- " read_from_storage_" +
1411
- string (_splitFunctionTypes ? " split_" : " " ) +
1412
- " offset_" +
1413
- to_string (_offset) +
1414
- " _" +
1415
- _type.identifier ();
1429
+ " read_from_storage_" +
1430
+ string (_splitFunctionTypes ? " split_" : " " ) +
1431
+ " offset_" +
1432
+ to_string (_offset) +
1433
+ " _" +
1434
+ _type.identifier ();
1435
+
1416
1436
return m_functionCollector.createFunction (functionName, [&] {
1417
1437
solAssert (_type.sizeOnStack () == 1 , " " );
1418
1438
return Whiskers (R"(
1419
1439
function <functionName>(slot) -> value {
1420
1440
value := <extract>(sload(slot))
1421
1441
}
1422
1442
)" )
1423
- (" functionName" , functionName)
1424
- (" extract" , extractFromStorageValue (_type, _offset, false ))
1425
- .render ();
1443
+ (" functionName" , functionName)
1444
+ (" extract" , extractFromStorageValue (_type, _offset, false ))
1445
+ .render ();
1426
1446
});
1427
1447
}
1428
-
1429
- string YulUtilFunctions::readFromStorageDynamic (Type const & _type, bool _splitFunctionTypes)
1448
+ string YulUtilFunctions::readFromStorageValueTypeDynamic (Type const & _type, bool _splitFunctionTypes)
1430
1449
{
1450
+ solAssert (_type.isValueType (), " " );
1431
1451
if (_type.category () == Type::Category::Function)
1432
1452
solUnimplementedAssert (!_splitFunctionTypes, " " );
1453
+
1433
1454
string functionName =
1434
- " read_from_storage_dynamic " +
1455
+ " read_from_storage_value_type_dynamic " +
1435
1456
string (_splitFunctionTypes ? " split_" : " " ) +
1436
1457
" _" +
1437
1458
_type.identifier ();
@@ -1447,6 +1468,55 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu
1447
1468
.render ();
1448
1469
});
1449
1470
}
1471
+ string YulUtilFunctions::readFromStorageReferenceType (Type const & _type)
1472
+ {
1473
+ solUnimplementedAssert (_type.category () == Type::Category::Struct, " " );
1474
+
1475
+ string functionName = " read_from_storage_reference_type_" + _type.identifier ();
1476
+
1477
+ auto const & structType = dynamic_cast <StructType const &>(_type);
1478
+ solAssert (structType.location () == DataLocation::Memory, " " );
1479
+ MemberList::MemberMap structMembers = structType.nativeMembers (nullptr );
1480
+ vector<map<string, string>> memberSetValues (structMembers.size ());
1481
+ for (size_t i = 0 ; i < structMembers.size (); ++i)
1482
+ {
1483
+ auto const & [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember (structMembers[i].name );
1484
+
1485
+ memberSetValues[i][" setMember" ] = Whiskers (R"(
1486
+ {
1487
+ let <memberValues> := <readFromStorage>(add(slot, <memberSlotDiff>)<?hasOffset>, <memberStorageOffset></hasOffset>)
1488
+ <writeToMemory>(add(value, <memberMemoryOffset>), <memberValues>)
1489
+ }
1490
+ )" )
1491
+ (" memberValues" , suffixedVariableNameList (" memberValue_" , 0 , structMembers[i].type ->stackItems ().size ()))
1492
+ (" memberMemoryOffset" , structType.memoryOffsetOfMember (structMembers[i].name ).str ())
1493
+ (" memberSlotDiff" , memberSlotDiff.str ())
1494
+ (" memberStorageOffset" , to_string (memberStorageOffset))
1495
+ (" readFromStorage" ,
1496
+ structMembers[i].type ->isValueType () ?
1497
+ readFromStorageDynamic (*structMembers[i].type , true ) :
1498
+ readFromStorage (*structMembers[i].type , memberStorageOffset, true )
1499
+ )
1500
+ (" writeToMemory" , writeToMemoryFunction (*structMembers[i].type ))
1501
+ (" hasOffset" , structMembers[i].type ->isValueType ())
1502
+ .render ();
1503
+ }
1504
+
1505
+ return m_functionCollector.createFunction (functionName, [&] {
1506
+ return Whiskers (R"(
1507
+ function <functionName>(slot) -> value {
1508
+ value := <allocStruct>()
1509
+ <#member>
1510
+ <setMember>
1511
+ </member>
1512
+ }
1513
+ )" )
1514
+ (" functionName" , functionName)
1515
+ (" allocStruct" , allocateMemoryStructFunction (structType))
1516
+ (" member" , memberSetValues)
1517
+ .render ();
1518
+ });
1519
+ }
1450
1520
1451
1521
string YulUtilFunctions::readFromMemory (Type const & _type)
1452
1522
{
@@ -1458,18 +1528,25 @@ string YulUtilFunctions::readFromCalldata(Type const& _type)
1458
1528
return readFromMemoryOrCalldata (_type, true );
1459
1529
}
1460
1530
1461
- string YulUtilFunctions::updateStorageValueFunction (Type const & _type, std::optional<unsigned > const & _offset)
1531
+ string YulUtilFunctions::updateStorageValueFunction (
1532
+ Type const & _fromType,
1533
+ Type const & _toType,
1534
+ std::optional<unsigned > const & _offset
1535
+ )
1462
1536
{
1463
1537
string const functionName =
1464
1538
" update_storage_value_" +
1465
1539
(_offset.has_value () ? (" offset_" + to_string (*_offset)) : " " ) +
1466
- _type.identifier ();
1540
+ _fromType.identifier () +
1541
+ " _to_" +
1542
+ _toType.identifier ();
1467
1543
1468
1544
return m_functionCollector.createFunction (functionName, [&] {
1469
- if (_type .isValueType ())
1545
+ if (_toType .isValueType ())
1470
1546
{
1471
- solAssert (_type.storageBytes () <= 32 , " Invalid storage bytes size." );
1472
- solAssert (_type.storageBytes () > 0 , " Invalid storage bytes size." );
1547
+ solAssert (_fromType.isImplicitlyConvertibleTo (_toType), " " );
1548
+ solAssert (_toType.storageBytes () <= 32 , " Invalid storage bytes size." );
1549
+ solAssert (_toType.storageBytes () > 0 , " Invalid storage bytes size." );
1473
1550
1474
1551
return Whiskers (R"(
1475
1552
function <functionName>(slot, <offset>value) {
@@ -1480,19 +1557,83 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::opti
1480
1557
(" functionName" , functionName)
1481
1558
(" update" ,
1482
1559
_offset.has_value () ?
1483
- updateByteSliceFunction (_type .storageBytes (), *_offset) :
1484
- updateByteSliceFunctionDynamic (_type .storageBytes ())
1560
+ updateByteSliceFunction (_toType .storageBytes (), *_offset) :
1561
+ updateByteSliceFunctionDynamic (_toType .storageBytes ())
1485
1562
)
1486
1563
(" offset" , _offset.has_value () ? " " : " offset, " )
1487
- (" prepare" , prepareStoreFunction (_type ))
1564
+ (" prepare" , prepareStoreFunction (_toType ))
1488
1565
.render ();
1489
1566
}
1490
1567
else
1491
1568
{
1492
- if (_type.category () == Type::Category::Array)
1493
- solUnimplementedAssert (false , " " );
1494
- else if (_type.category () == Type::Category::Struct)
1569
+ auto const * toReferenceType = dynamic_cast <ReferenceType const *>(&_toType);
1570
+ auto const * fromReferenceType = dynamic_cast <ReferenceType const *>(&_toType);
1571
+ solAssert (fromReferenceType && toReferenceType, " " );
1572
+ solAssert (*toReferenceType->copyForLocation (
1573
+ fromReferenceType->location (),
1574
+ fromReferenceType->isPointer ()
1575
+ ).get () == *fromReferenceType, " " );
1576
+
1577
+ if (_toType.category () == Type::Category::Array)
1495
1578
solUnimplementedAssert (false , " " );
1579
+ else if (_toType.category () == Type::Category::Struct)
1580
+ {
1581
+ solAssert (_fromType.category () == Type::Category::Struct, " " );
1582
+ auto const & fromStructType = dynamic_cast <StructType const &>(_fromType);
1583
+ auto const & toStructType = dynamic_cast <StructType const &>(_toType);
1584
+ solAssert (fromStructType.structDefinition () == toStructType.structDefinition (), " " );
1585
+ solAssert (fromStructType.location () != DataLocation::Storage, " " );
1586
+ solUnimplementedAssert (_offset.has_value () && _offset.value () == 0 , " " );
1587
+
1588
+ Whiskers templ (R"(
1589
+ function <functionName>(slot, value) {
1590
+ <#member>
1591
+ {
1592
+ <updateMemberCall>
1593
+ }
1594
+ </member>
1595
+ }
1596
+ )" );
1597
+ templ (" functionName" , functionName);
1598
+
1599
+ MemberList::MemberMap structMembers = fromStructType.nativeMembers (nullptr );
1600
+
1601
+ vector<map<string, string>> memberParams (structMembers.size ());
1602
+ for (size_t i = 0 ; i < structMembers.size (); ++i)
1603
+ {
1604
+ solAssert (structMembers[i].type ->memoryHeadSize () == 32 , " " );
1605
+ bool fromCalldata = fromStructType.location () == DataLocation::CallData;
1606
+ auto const & [slotDiff, offset] = toStructType.storageOffsetsOfMember (structMembers[i].name );
1607
+ memberParams[i][" updateMemberCall" ] = Whiskers (R"(
1608
+ let <memberValues> := <loadFromMemoryOrCalldata>(add(value, <memberOffset>))
1609
+ <updateMember>(add(slot, <memberStorageSlotDiff>), <?hasOffset><memberStorageOffset>,</hasOffset> <memberValues>)
1610
+ )" )
1611
+ (" memberValues" , suffixedVariableNameList (
1612
+ " memberValue_" ,
1613
+ 0 ,
1614
+ structMembers[i].type ->stackItems ().size ()
1615
+ ))
1616
+ (" hasOffset" , structMembers[i].type ->isValueType ())
1617
+ (
1618
+ " updateMember" ,
1619
+ structMembers[i].type ->isValueType () ?
1620
+ updateStorageValueFunction (*structMembers[i].type , *structMembers[i].type ) :
1621
+ updateStorageValueFunction (*structMembers[i].type , *structMembers[i].type , offset)
1622
+ )
1623
+ (" memberStorageSlotDiff" , slotDiff.str ())
1624
+ (" memberStorageOffset" , to_string (offset))
1625
+ (" memberOffset" ,
1626
+ fromCalldata ?
1627
+ to_string (fromStructType.calldataOffsetOfMember (structMembers[i].name )) :
1628
+ fromStructType.memoryOffsetOfMember (structMembers[i].name ).str ()
1629
+ )
1630
+ (" loadFromMemoryOrCalldata" , readFromMemoryOrCalldata (*structMembers[i].type , fromCalldata))
1631
+ .render ();
1632
+ }
1633
+ templ (" member" , memberParams);
1634
+
1635
+ return templ.render ();
1636
+ }
1496
1637
else
1497
1638
solAssert (false , " Invalid non-value type for assignment." );
1498
1639
}
@@ -2052,13 +2193,27 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
2052
2193
2053
2194
solUnimplementedAssert (!fromStructType.isDynamicallyEncoded (), " " );
2054
2195
solUnimplementedAssert (toStructType.location () == DataLocation::Memory, " " );
2055
- solUnimplementedAssert (fromStructType.location () == DataLocation::CallData, " " );
2196
+ solUnimplementedAssert (fromStructType.location () != DataLocation::Memory, " " );
2197
+
2198
+ if (fromStructType.location () == DataLocation::CallData)
2199
+ {
2200
+ body = Whiskers (R"(
2201
+ converted := <abiDecode>(value, calldatasize())
2202
+ )" )(" abiDecode" , ABIFunctions (m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder (
2203
+ {&toStructType}
2204
+ )).render ();
2205
+ }
2206
+ else
2207
+ {
2208
+ solAssert (fromStructType.location () == DataLocation::Storage, " " );
2209
+
2210
+ body = Whiskers (R"(
2211
+ converted := <readFromStorage>(value)
2212
+ )" )
2213
+ (" readFromStorage" , readFromStorage (toStructType, 0 , true ))
2214
+ .render ();
2215
+ }
2056
2216
2057
- body = Whiskers (R"(
2058
- converted := <abiDecode>(value, calldatasize())
2059
- )" )(" abiDecode" , ABIFunctions (m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder (
2060
- {&toStructType}
2061
- )).render ();
2062
2217
break ;
2063
2218
}
2064
2219
case Type::Category::FixedBytes:
@@ -2490,7 +2645,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
2490
2645
}
2491
2646
)" )
2492
2647
(" functionName" , functionName)
2493
- (" store" , updateStorageValueFunction (_type))
2648
+ (" store" , updateStorageValueFunction (_type, _type ))
2494
2649
(" zeroValue" , zeroValueFunction (_type))
2495
2650
.render ();
2496
2651
else if (_type.category () == Type::Category::Array)
0 commit comments