3434#include < liblangutil/CharStream.h>
3535#include < liblangutil/Exceptions.h>
3636
37- #include < json/json.h>
37+ #include < libsolutil/JSON.h>
38+ #include < libsolutil/StringUtils.h>
39+
40+ #include < fmt/format.h>
3841
3942#include < range/v3/algorithm/any_of.hpp>
43+ #include < range/v3/view/drop_exactly.hpp>
4044#include < range/v3/view/enumerate.hpp>
45+ #include < range/v3/view/map.hpp>
4146
4247#include < fstream>
4348#include < limits>
49+ #include < iterator>
4450
4551using namespace solidity ;
4652using namespace solidity ::evmasm;
4753using namespace solidity ::langutil;
4854using namespace solidity ::util;
4955
56+ std::map<std::string, std::shared_ptr<std::string const >> Assembly::s_sharedSourceNames;
57+
5058AssemblyItem const & Assembly::append (AssemblyItem _i)
5159{
5260 assertThrow (m_deposit >= 0 , AssemblyException, " Stack underflow." );
@@ -73,6 +81,205 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
7381 }
7482}
7583
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+
76283namespace
77284{
78285
@@ -297,6 +504,154 @@ Json::Value Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourc
297504 return root;
298505}
299506
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+
300655AssemblyItem Assembly::namedTag (std::string const & _name, size_t _params, size_t _returns, std::optional<uint64_t > _sourceID)
301656{
302657 assertThrow (!_name.empty (), AssemblyException, " Empty named tag." );
0 commit comments