34
34
#include < liblangutil/CharStream.h>
35
35
#include < liblangutil/Exceptions.h>
36
36
37
- #include < json/json.h>
37
+ #include < libsolutil/JSON.h>
38
+ #include < libsolutil/StringUtils.h>
39
+
40
+ #include < fmt/format.h>
38
41
39
42
#include < range/v3/algorithm/any_of.hpp>
43
+ #include < range/v3/view/drop_exactly.hpp>
40
44
#include < range/v3/view/enumerate.hpp>
45
+ #include < range/v3/view/map.hpp>
41
46
42
47
#include < fstream>
43
48
#include < limits>
49
+ #include < iterator>
44
50
45
51
using namespace solidity ;
46
52
using namespace solidity ::evmasm;
47
53
using namespace solidity ::langutil;
48
54
using namespace solidity ::util;
49
55
56
+ std::map<std::string, std::shared_ptr<std::string const >> Assembly::s_sharedSourceNames;
57
+
50
58
AssemblyItem const & Assembly::append (AssemblyItem _i)
51
59
{
52
60
assertThrow (m_deposit >= 0 , AssemblyException, " Stack underflow." );
@@ -73,6 +81,205 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
73
81
}
74
82
}
75
83
84
+ void Assembly::importAssemblyItemsFromJSON (Json::Value const & _code, std::vector<std::string> const & _sourceList)
85
+ {
86
+ solAssert (m_items.empty ());
87
+ solRequire (_code.isArray (), AssemblyImportException, " Supplied JSON is not an array." );
88
+ for (auto jsonItemIter = std::begin (_code); jsonItemIter != std::end (_code); ++jsonItemIter)
89
+ {
90
+ AssemblyItem const & newItem = m_items.emplace_back (createAssemblyItemFromJSON (*jsonItemIter, _sourceList));
91
+ if (newItem == Instruction::JUMPDEST)
92
+ solThrow (AssemblyImportException, " JUMPDEST instruction without a tag" );
93
+ else if (newItem.type () == AssemblyItemType::Tag)
94
+ {
95
+ ++jsonItemIter;
96
+ if (jsonItemIter != std::end (_code) && createAssemblyItemFromJSON (*jsonItemIter, _sourceList) != Instruction::JUMPDEST)
97
+ solThrow (AssemblyImportException, " JUMPDEST expected after tag." );
98
+ }
99
+ }
100
+ }
101
+
102
+ AssemblyItem Assembly::createAssemblyItemFromJSON (Json::Value const & _json, std::vector<std::string> const & _sourceList)
103
+ {
104
+ solRequire (_json.isObject (), AssemblyImportException, " Supplied JSON is not an object." );
105
+ static std::set<std::string> const validMembers{" name" , " begin" , " end" , " source" , " value" , " modifierDepth" , " jumpType" };
106
+ for (std::string const & member: _json.getMemberNames ())
107
+ solRequire (
108
+ validMembers.count (member),
109
+ AssemblyImportException,
110
+ fmt::format (
111
+ " Unknown member '{}'. Valid members are: {}." ,
112
+ member,
113
+ solidity::util::joinHumanReadable (validMembers, " , " )
114
+ )
115
+ );
116
+ solRequire (isOfType<std::string>(_json[" name" ]), AssemblyImportException, " Member 'name' missing or not of type string." );
117
+ solRequire (isOfTypeIfExists<int >(_json, " begin" ), AssemblyImportException, " Optional member 'begin' not of type int." );
118
+ solRequire (isOfTypeIfExists<int >(_json, " end" ), AssemblyImportException, " Optional member 'end' not of type int." );
119
+ solRequire (isOfTypeIfExists<int >(_json, " source" ), AssemblyImportException, " Optional member 'source' not of type int." );
120
+ solRequire (isOfTypeIfExists<std::string>(_json, " value" ), AssemblyImportException, " Optional member 'value' not of type string." );
121
+ solRequire (isOfTypeIfExists<int >(_json, " modifierDepth" ), AssemblyImportException, " Optional member 'modifierDepth' not of type int." );
122
+ solRequire (isOfTypeIfExists<std::string>(_json, " jumpType" ), AssemblyImportException, " Optional member 'jumpType' not of type string." );
123
+
124
+ std::string name = get<std::string>(_json[" name" ]);
125
+ solRequire (!name.empty (), AssemblyImportException, " Member 'name' is empty." );
126
+
127
+ SourceLocation location;
128
+ location.start = get<int >(_json[" begin" ]);
129
+ location.end = get<int >(_json[" end" ]);
130
+ int srcIndex = getOrDefault<int >(_json[" source" ], -1 );
131
+ size_t modifierDepth = static_cast <size_t >(getOrDefault<int >(_json[" modifierDepth" ], 0 ));
132
+ std::string value = getOrDefault<std::string>(_json[" value" ], " " );
133
+ std::string jumpType = getOrDefault<std::string>(_json[" jumpType" ], " " );
134
+
135
+ auto updateUsedTags = [&](u256 const & data)
136
+ {
137
+ m_usedTags = std::max (m_usedTags, static_cast <unsigned >(data) + 1 );
138
+ return data;
139
+ };
140
+
141
+ auto storeImmutableHash = [&](std::string const & _immutableName) -> h256
142
+ {
143
+ h256 hash (util::keccak256 (_immutableName));
144
+ solAssert (m_immutables.count (hash) == 0 || m_immutables[hash] == _immutableName);
145
+ m_immutables[hash] = _immutableName;
146
+ return hash;
147
+ };
148
+
149
+ auto storeLibraryHash = [&](std::string const & _libraryName) -> h256
150
+ {
151
+ h256 hash (util::keccak256 (_libraryName));
152
+ solAssert (m_libraries.count (hash) == 0 || m_libraries[hash] == _libraryName);
153
+ m_libraries[hash] = _libraryName;
154
+ return hash;
155
+ };
156
+
157
+ auto requireValueDefinedForInstruction = [&](std::string const & _name, std::string const & _value)
158
+ {
159
+ solRequire (
160
+ !_value.empty (),
161
+ AssemblyImportException,
162
+ " Member 'value' is missing for instruction '" + _name + " ', but the instruction needs a value."
163
+ );
164
+ };
165
+
166
+ auto requireValueUndefinedForInstruction = [&](std::string const & _name, std::string const & _value)
167
+ {
168
+ solRequire (
169
+ _value.empty (),
170
+ AssemblyImportException,
171
+ " Member 'value' defined for instruction '" + _name + " ', but the instruction does not need a value."
172
+ );
173
+ };
174
+
175
+ solRequire (srcIndex >= -1 && srcIndex < static_cast <int >(_sourceList.size ()), AssemblyImportException, " Source index out of bounds." );
176
+ if (srcIndex != -1 )
177
+ location.sourceName = sharedSourceName (_sourceList[static_cast <size_t >(srcIndex)]);
178
+
179
+ AssemblyItem result (0 );
180
+
181
+ if (c_instructions.count (name))
182
+ {
183
+ AssemblyItem item{c_instructions.at (name), location};
184
+ if (!jumpType.empty ())
185
+ {
186
+ if (item.instruction () == Instruction::JUMP || item.instruction () == Instruction::JUMPI)
187
+ {
188
+ std::optional<AssemblyItem::JumpType> parsedJumpType = AssemblyItem::parseJumpType (jumpType);
189
+ if (!parsedJumpType.has_value ())
190
+ solThrow (AssemblyImportException, " Invalid jump type." );
191
+ item.setJumpType (parsedJumpType.value ());
192
+ }
193
+ else
194
+ solThrow (
195
+ AssemblyImportException,
196
+ " Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction '" + name + " ')"
197
+ );
198
+ }
199
+ requireValueUndefinedForInstruction (name, value);
200
+ result = item;
201
+ }
202
+ else
203
+ {
204
+ solRequire (
205
+ jumpType.empty (),
206
+ AssemblyImportException,
207
+ " Member 'jumpType' set on instruction different from JUMP or JUMPI (was set on instruction '" + name + " ')"
208
+ );
209
+ if (name == " PUSH" )
210
+ {
211
+ requireValueDefinedForInstruction (name, value);
212
+ result = {AssemblyItemType::Push, u256 (" 0x" + value)};
213
+ }
214
+ else if (name == " PUSH [ErrorTag]" )
215
+ {
216
+ requireValueUndefinedForInstruction (name, value);
217
+ result = {AssemblyItemType::PushTag, 0 };
218
+ }
219
+ else if (name == " PUSH [tag]" )
220
+ {
221
+ requireValueDefinedForInstruction (name, value);
222
+ result = {AssemblyItemType::PushTag, updateUsedTags (u256 (value))};
223
+ }
224
+ else if (name == " PUSH [$]" )
225
+ {
226
+ requireValueDefinedForInstruction (name, value);
227
+ result = {AssemblyItemType::PushSub, u256 (" 0x" + value)};
228
+ }
229
+ else if (name == " PUSH #[$]" )
230
+ {
231
+ requireValueDefinedForInstruction (name, value);
232
+ result = {AssemblyItemType::PushSubSize, u256 (" 0x" + value)};
233
+ }
234
+ else if (name == " PUSHSIZE" )
235
+ {
236
+ requireValueUndefinedForInstruction (name, value);
237
+ result = {AssemblyItemType::PushProgramSize, 0 };
238
+ }
239
+ else if (name == " PUSHLIB" )
240
+ {
241
+ requireValueDefinedForInstruction (name, value);
242
+ result = {AssemblyItemType::PushLibraryAddress, storeLibraryHash (value)};
243
+ }
244
+ else if (name == " PUSHDEPLOYADDRESS" )
245
+ {
246
+ requireValueUndefinedForInstruction (name, value);
247
+ result = {AssemblyItemType::PushDeployTimeAddress, 0 };
248
+ }
249
+ else if (name == " PUSHIMMUTABLE" )
250
+ {
251
+ requireValueDefinedForInstruction (name, value);
252
+ result = {AssemblyItemType::PushImmutable, storeImmutableHash (value)};
253
+ }
254
+ else if (name == " ASSIGNIMMUTABLE" )
255
+ {
256
+ requireValueDefinedForInstruction (name, value);
257
+ result = {AssemblyItemType::AssignImmutable, storeImmutableHash (value)};
258
+ }
259
+ else if (name == " tag" )
260
+ {
261
+ requireValueDefinedForInstruction (name, value);
262
+ result = {AssemblyItemType::Tag, updateUsedTags (u256 (value))};
263
+ }
264
+ else if (name == " PUSH data" )
265
+ {
266
+ requireValueDefinedForInstruction (name, value);
267
+ result = {AssemblyItemType::PushData, u256 (" 0x" + value)};
268
+ }
269
+ else if (name == " VERBATIM" )
270
+ {
271
+ requireValueDefinedForInstruction (name, value);
272
+ AssemblyItem item (fromHex (value), 0 , 0 );
273
+ result = item;
274
+ }
275
+ else
276
+ solThrow (InvalidOpcode, " Invalid opcode: " + name);
277
+ }
278
+ result.setLocation (location);
279
+ result.m_modifierDepth = modifierDepth;
280
+ return result;
281
+ }
282
+
76
283
namespace
77
284
{
78
285
@@ -297,6 +504,154 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
297
504
return root;
298
505
}
299
506
507
+ std::pair<std::shared_ptr<Assembly>, std::vector<std::string>> Assembly::fromJSON (
508
+ Json::Value const & _json,
509
+ std::vector<std::string> const & _sourceList,
510
+ size_t _level
511
+ )
512
+ {
513
+ solRequire (_json.isObject (), AssemblyImportException, " Supplied JSON is not an object." );
514
+ static std::set<std::string> const validMembers{" .code" , " .data" , " .auxdata" , " sourceList" };
515
+ for (std::string const & attribute: _json.getMemberNames ())
516
+ solRequire (validMembers.count (attribute), AssemblyImportException, " Unknown attribute '" + attribute + " '." );
517
+
518
+ if (_level == 0 )
519
+ {
520
+ if (_json.isMember (" sourceList" ))
521
+ {
522
+ solRequire (_json[" sourceList" ].isArray (), AssemblyImportException, " Optional member 'sourceList' is not an array." );
523
+ for (Json::Value const & sourceName: _json[" sourceList" ])
524
+ solRequire (sourceName.isString (), AssemblyImportException, " The 'sourceList' array contains an item that is not a string." );
525
+ }
526
+ }
527
+ else
528
+ solRequire (
529
+ !_json.isMember (" sourceList" ),
530
+ AssemblyImportException,
531
+ " Member 'sourceList' may only be present in the root JSON object."
532
+ );
533
+
534
+ auto result = std::make_shared<Assembly>(EVMVersion{}, _level == 0 /* _creation */ , " " /* _name */ );
535
+ std::vector<std::string> parsedSourceList;
536
+ if (_json.isMember (" sourceList" ))
537
+ {
538
+ solAssert (_level == 0 );
539
+ solAssert (_sourceList.empty ());
540
+ for (Json::Value const & sourceName: _json[" sourceList" ])
541
+ {
542
+ solRequire (
543
+ std::find (parsedSourceList.begin (), parsedSourceList.end (), sourceName.asString ()) == parsedSourceList.end (),
544
+ AssemblyImportException,
545
+ " Items in 'sourceList' array are not unique."
546
+ );
547
+ parsedSourceList.emplace_back (sourceName.asString ());
548
+ }
549
+ }
550
+
551
+ solRequire (_json.isMember (" .code" ), AssemblyImportException, " Member '.code' is missing." );
552
+ solRequire (_json[" .code" ].isArray (), AssemblyImportException, " Member '.code' is not an array." );
553
+ for (Json::Value const & codeItem: _json[" .code" ])
554
+ solRequire (codeItem.isObject (), AssemblyImportException, " The '.code' array contains an item that is not an object." );
555
+
556
+ result->importAssemblyItemsFromJSON (_json[" .code" ], _level == 0 ? parsedSourceList : _sourceList);
557
+
558
+ if (_json[" .auxdata" ])
559
+ {
560
+ solRequire (_json[" .auxdata" ].isString (), AssemblyImportException, " Optional member '.auxdata' is not a string." );
561
+ result->m_auxiliaryData = fromHex (_json[" .auxdata" ].asString ());
562
+ solRequire (!result->m_auxiliaryData .empty (), AssemblyImportException, " Optional member '.auxdata' is not a valid hexadecimal string." );
563
+ }
564
+
565
+ if (_json.isMember (" .data" ))
566
+ {
567
+ solRequire (_json[" .data" ].isObject (), AssemblyImportException, " Optional member '.data' is not an object." );
568
+ Json::Value const & data = _json[" .data" ];
569
+ std::map<size_t , std::shared_ptr<Assembly>> subAssemblies;
570
+ for (Json::ValueConstIterator dataIter = data.begin (); dataIter != data.end (); dataIter++)
571
+ {
572
+ solAssert (dataIter.key ().isString ());
573
+ std::string dataItemID = dataIter.key ().asString ();
574
+ Json::Value const & dataItem = data[dataItemID];
575
+ if (dataItem.isString ())
576
+ {
577
+ solRequire (
578
+ dataItem.asString ().empty () || !fromHex (dataItem.asString ()).empty (),
579
+ AssemblyImportException,
580
+ " The value for key '" + dataItemID + " ' inside '.data' is not a valid hexadecimal string."
581
+ );
582
+ result->m_data [h256 (fromHex (dataItemID))] = fromHex (dataItem.asString ());
583
+ }
584
+ else if (dataItem.isObject ())
585
+ {
586
+ size_t index {};
587
+ try
588
+ {
589
+ // Using signed variant because stoul() still accepts negative numbers and
590
+ // just lets them wrap around.
591
+ int parsedDataItemID = std::stoi (dataItemID, nullptr , 16 );
592
+ solRequire (parsedDataItemID >= 0 , AssemblyImportException, " The key '" + dataItemID + " ' inside '.data' is out of the supported integer range." );
593
+ index = static_cast <size_t >(parsedDataItemID);
594
+ }
595
+ catch (std::invalid_argument const &)
596
+ {
597
+ solThrow (AssemblyImportException, " The key '" + dataItemID + " ' inside '.data' is not an integer." );
598
+ }
599
+ catch (std::out_of_range const &)
600
+ {
601
+ solThrow (AssemblyImportException, " The key '" + dataItemID + " ' inside '.data' is out of the supported integer range." );
602
+ }
603
+
604
+ auto [subAssembly, emptySourceList] = Assembly::fromJSON (dataItem, _level == 0 ? parsedSourceList : _sourceList, _level + 1 );
605
+ solAssert (subAssembly);
606
+ solAssert (emptySourceList.empty ());
607
+ solAssert (subAssemblies.count (index ) == 0 );
608
+ subAssemblies[index ] = subAssembly;
609
+ }
610
+ else
611
+ solThrow (AssemblyImportException, " The value of key '" + dataItemID + " ' inside '.data' is neither a hex string nor an object." );
612
+ }
613
+
614
+ if (!subAssemblies.empty ())
615
+ solRequire (
616
+ ranges::max (subAssemblies | ranges::views::keys) == subAssemblies.size () - 1 ,
617
+ AssemblyImportException,
618
+ fmt::format (
619
+ " Invalid subassembly indices in '.data'. Not all numbers between 0 and {} are present." ,
620
+ subAssemblies.size () - 1
621
+ )
622
+ );
623
+
624
+ result->m_subs = subAssemblies | ranges::views::values | ranges::to<std::vector>;
625
+ }
626
+
627
+ if (_level == 0 )
628
+ result->encodeAllPossibleSubPathsInAssemblyTree ();
629
+
630
+ return std::make_pair (result, _level == 0 ? parsedSourceList : std::vector<std::string>{});
631
+ }
632
+
633
+ void Assembly::encodeAllPossibleSubPathsInAssemblyTree (std::vector<size_t > _pathFromRoot, std::vector<Assembly*> _assembliesOnPath)
634
+ {
635
+ _assembliesOnPath.push_back (this );
636
+ for (_pathFromRoot.push_back (0 ); _pathFromRoot.back () < m_subs.size (); ++_pathFromRoot.back ())
637
+ {
638
+ for (size_t distanceFromRoot = 0 ; distanceFromRoot < _assembliesOnPath.size (); ++distanceFromRoot)
639
+ _assembliesOnPath[distanceFromRoot]->encodeSubPath (
640
+ _pathFromRoot | ranges::views::drop_exactly (distanceFromRoot) | ranges::to<std::vector>
641
+ );
642
+
643
+ m_subs[_pathFromRoot.back ()]->encodeAllPossibleSubPathsInAssemblyTree (_pathFromRoot, _assembliesOnPath);
644
+ }
645
+ }
646
+
647
+ std::shared_ptr<std::string const > Assembly::sharedSourceName (std::string const & _name) const
648
+ {
649
+ if (s_sharedSourceNames.find (_name) == s_sharedSourceNames.end ())
650
+ s_sharedSourceNames[_name] = std::make_shared<std::string>(_name);
651
+
652
+ return s_sharedSourceNames[_name];
653
+ }
654
+
300
655
AssemblyItem Assembly::namedTag (std::string const & _name, size_t _params, size_t _returns, std::optional<uint64_t > _sourceID)
301
656
{
302
657
assertThrow (!_name.empty (), AssemblyException, " Empty named tag." );
0 commit comments