/src/solidity/libyul/Object.cpp
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * Yul code and data object container. |
20 | | */ |
21 | | |
22 | | #include <libyul/Object.h> |
23 | | |
24 | | #include <libyul/AsmPrinter.h> |
25 | | #include <libyul/AsmJsonConverter.h> |
26 | | #include <libyul/AST.h> |
27 | | #include <libyul/Exceptions.h> |
28 | | |
29 | | #include <libsolutil/CommonData.h> |
30 | | #include <libsolutil/StringUtils.h> |
31 | | |
32 | | #include <boost/algorithm/string.hpp> |
33 | | |
34 | | #include <range/v3/view/transform.hpp> |
35 | | |
36 | | using namespace solidity; |
37 | | using namespace solidity::langutil; |
38 | | using namespace solidity::util; |
39 | | using namespace solidity::yul; |
40 | | |
41 | | std::string Data::toString(DebugInfoSelection const&, CharStreamProvider const*) const |
42 | 10.3k | { |
43 | 10.3k | return "data " + util::escapeAndQuoteString(name) + " hex\"" + util::toHex(data) + "\""; |
44 | 10.3k | } |
45 | | |
46 | | std::string Object::toString( |
47 | | DebugInfoSelection const& _debugInfoSelection, |
48 | | CharStreamProvider const* _soliditySourceProvider |
49 | | ) const |
50 | 62.3k | { |
51 | 62.3k | yulAssert(hasCode(), "No code"); |
52 | 62.3k | yulAssert(dialect(), "No dialect"); |
53 | 62.3k | yulAssert(debugData, "No debug data"); |
54 | | |
55 | 62.3k | std::string inner = "code " + AsmPrinter::format( |
56 | 62.3k | *code(), |
57 | 62.3k | debugData->sourceNames, |
58 | 62.3k | _debugInfoSelection, |
59 | 62.3k | _soliditySourceProvider |
60 | 62.3k | ); |
61 | | |
62 | 62.3k | for (auto const& obj: subObjects) |
63 | 21.7k | inner += "\n" + obj->toString(_debugInfoSelection, _soliditySourceProvider); |
64 | | |
65 | 62.3k | return |
66 | 62.3k | debugData->formatUseSrcComment() + |
67 | 62.3k | "object " + util::escapeAndQuoteString(name) + " {\n" + |
68 | 62.3k | indent(inner) + "\n" + |
69 | 62.3k | "}"; |
70 | 62.3k | } |
71 | | |
72 | | Json Data::toJson() const |
73 | 0 | { |
74 | 0 | Json ret; |
75 | 0 | ret["nodeType"] = "YulData"; |
76 | 0 | ret["value"] = util::toHex(data); |
77 | 0 | return ret; |
78 | 0 | } |
79 | | |
80 | | std::string ObjectDebugData::formatUseSrcComment() const |
81 | 115k | { |
82 | 115k | if (!sourceNames) |
83 | 88.6k | return ""; |
84 | | |
85 | 28.0k | auto formatIdNamePair = [](auto&& _pair) { |
86 | 28.0k | return std::to_string(_pair.first) + ":" + util::escapeAndQuoteString(*_pair.second); |
87 | 28.0k | }; |
88 | | |
89 | 27.0k | std::string serializedSourceNames = joinHumanReadable( |
90 | 27.0k | ranges::views::transform(*sourceNames, formatIdNamePair) |
91 | 27.0k | ); |
92 | 27.0k | return "/// @use-src " + serializedSourceNames + "\n"; |
93 | 115k | } |
94 | | |
95 | | Json Object::toJson() const |
96 | 0 | { |
97 | 0 | yulAssert(hasCode(), "No code"); |
98 | 0 | yulAssert(dialect(), "No dialect"); |
99 | |
|
100 | 0 | Json codeJson; |
101 | 0 | codeJson["nodeType"] = "YulCode"; |
102 | 0 | codeJson["block"] = AsmJsonConverter(*dialect(), 0 /* sourceIndex */)(code()->root()); |
103 | |
|
104 | 0 | Json subObjectsJson = Json::array(); |
105 | 0 | for (std::shared_ptr<ObjectNode> const& subObject: subObjects) |
106 | 0 | subObjectsJson.emplace_back(subObject->toJson()); |
107 | |
|
108 | 0 | Json ret; |
109 | 0 | ret["nodeType"] = "YulObject"; |
110 | 0 | ret["name"] = name; |
111 | 0 | ret["code"] = codeJson; |
112 | 0 | ret["subObjects"] = subObjectsJson; |
113 | 0 | return ret; |
114 | 0 | } |
115 | | |
116 | | |
117 | | std::set<std::string> Object::Structure::topLevelSubObjectNames() const |
118 | 0 | { |
119 | 0 | std::set<std::string> topLevelObjectNames; |
120 | |
|
121 | 0 | for (auto const& path: objectPaths) |
122 | 0 | if (!util::contains(path, '.') && path != objectName) |
123 | 0 | topLevelObjectNames.insert(path); |
124 | |
|
125 | 0 | return topLevelObjectNames; |
126 | 0 | } |
127 | | |
128 | | Object::Structure Object::summarizeStructure() const |
129 | 1.81M | { |
130 | 1.81M | Structure structure; |
131 | | |
132 | 1.81M | structure.objectPaths = |
133 | 1.81M | name.empty() || util::contains(name, '.') ? |
134 | 15.1k | std::set<std::string>{} : |
135 | 1.81M | std::set<std::string>{name}; |
136 | | |
137 | 1.81M | structure.objectName = name; |
138 | | |
139 | 1.81M | for (std::shared_ptr<ObjectNode> const& subObjectNode: subObjects) |
140 | 1.58M | { |
141 | 1.58M | yulAssert(!structure.contains(subObjectNode->name)); |
142 | 1.58M | if (util::contains(subObjectNode->name, '.')) |
143 | 55.4k | continue; |
144 | | |
145 | 1.52M | if (auto const* subObject = dynamic_cast<Object const*>(subObjectNode.get())) |
146 | 1.17M | { |
147 | 1.17M | structure.objectPaths.insert(subObjectNode->name); |
148 | | |
149 | 1.17M | auto const subObjectStructure = subObject->summarizeStructure(); |
150 | | |
151 | 1.17M | for (auto const& subSubObj: subObjectStructure.objectPaths) |
152 | 9.98M | if (subObject->name != subSubObj) |
153 | 8.80M | { |
154 | 8.80M | yulAssert(!structure.contains(subObject->name + "." + subSubObj)); |
155 | 8.80M | structure.objectPaths.insert(subObject->name + "." + subSubObj); |
156 | 8.80M | } |
157 | 1.17M | for (auto const& subSubObjData: subObjectStructure.dataPaths) |
158 | 2.51M | if (subObject->name != subSubObjData) |
159 | 2.51M | { |
160 | 2.51M | yulAssert(!structure.contains(subObject->name + "." + subSubObjData)); |
161 | 2.51M | structure.dataPaths.insert(subObject->name + "." + subSubObjData); |
162 | 2.51M | } |
163 | 1.17M | } |
164 | 350k | else |
165 | 350k | structure.dataPaths.insert(subObjectNode->name); |
166 | 1.52M | } |
167 | | |
168 | 1.81M | yulAssert(!structure.contains("")); |
169 | 1.81M | return structure; |
170 | 1.81M | } |
171 | | |
172 | | std::vector<evmasm::SubAssemblyID> Object::pathToSubObject(std::string_view _qualifiedName) const |
173 | 144 | { |
174 | 144 | yulAssert(_qualifiedName != name, ""); |
175 | 144 | yulAssert(subIndexByName.count(name) == 0, ""); |
176 | | |
177 | 144 | if (boost::algorithm::starts_with(_qualifiedName, name + ".")) |
178 | 0 | _qualifiedName = _qualifiedName.substr(name.length() + 1); |
179 | 144 | yulAssert(!_qualifiedName.empty(), ""); |
180 | | |
181 | 144 | std::vector<std::string> subObjectPathComponents; |
182 | 144 | boost::algorithm::split(subObjectPathComponents, _qualifiedName, boost::is_any_of(".")); |
183 | | |
184 | 144 | std::vector<evmasm::SubAssemblyID> path; |
185 | 144 | Object const* object = this; |
186 | 144 | for (std::string const& currentSubObjectName: subObjectPathComponents) |
187 | 288 | { |
188 | 288 | yulAssert(!currentSubObjectName.empty(), ""); |
189 | 288 | auto subIndexIt = object->subIndexByName.find(currentSubObjectName); |
190 | 288 | yulAssert( |
191 | 288 | subIndexIt != object->subIndexByName.end(), |
192 | 288 | "Assembly object <" + std::string(_qualifiedName) + "> not found or does not contain code." |
193 | 288 | ); |
194 | 288 | object = dynamic_cast<Object const*>(object->subObjects[subIndexIt->second].get()); |
195 | 288 | yulAssert(object, "Assembly object <" + std::string(_qualifiedName) + "> not found or does not contain code."); |
196 | 288 | yulAssert(!object->subId.empty()); |
197 | 288 | path.emplace_back(object->subId); |
198 | 288 | } |
199 | | |
200 | 144 | return path; |
201 | 144 | } |
202 | | |
203 | | std::shared_ptr<AST const> Object::code() const |
204 | 3.53M | { |
205 | 3.53M | return m_code; |
206 | 3.53M | } |
207 | | |
208 | 1.43M | bool Object::hasCode() const { return code() != nullptr; } |
209 | | |
210 | | void Object::setCode(std::shared_ptr<AST const> const& _ast, std::shared_ptr<yul::AsmAnalysisInfo> _analysisInfo) |
211 | 835k | { |
212 | 835k | m_code = _ast; |
213 | 835k | analysisInfo = std::move(_analysisInfo); |
214 | 835k | } |
215 | | |
216 | | void Object::collectSourceIndices(std::map<std::string, unsigned>& _indices) const |
217 | 0 | { |
218 | 0 | if (debugData && debugData->sourceNames.has_value()) |
219 | 0 | for (auto const& [sourceIndex, sourceName]: debugData->sourceNames.value()) |
220 | 0 | { |
221 | 0 | solAssert(_indices.count(*sourceName) == 0 || _indices[*sourceName] == sourceIndex); |
222 | 0 | _indices[*sourceName] = sourceIndex; |
223 | 0 | } |
224 | 0 | for (std::shared_ptr<ObjectNode> const& subNode: subObjects) |
225 | 0 | if (auto subObject = dynamic_cast<Object*>(subNode.get())) |
226 | 0 | subObject->collectSourceIndices(_indices); |
227 | 0 | } |
228 | | |
229 | | bool Object::hasContiguousSourceIndices() const |
230 | 0 | { |
231 | 0 | std::map<std::string, unsigned> sourceIndices; |
232 | 0 | collectSourceIndices(sourceIndices); |
233 | |
|
234 | 0 | unsigned maxSourceIndex = 0; |
235 | 0 | std::set<unsigned> indices; |
236 | 0 | for (auto const& [sources, sourceIndex]: sourceIndices) |
237 | 0 | { |
238 | 0 | maxSourceIndex = std::max(sourceIndex, maxSourceIndex); |
239 | 0 | indices.insert(sourceIndex); |
240 | 0 | } |
241 | |
|
242 | 0 | solAssert(maxSourceIndex + 1 >= indices.size()); |
243 | 0 | return indices.size() == 0 || indices.size() == maxSourceIndex + 1; |
244 | 0 | } |
245 | | |
246 | | Dialect const* Object::dialect() const |
247 | 1.91M | { |
248 | 1.91M | if (!m_code) |
249 | 0 | return nullptr; |
250 | 1.91M | return &m_code->dialect(); |
251 | 1.91M | } |