Coverage Report

Created: 2026-09-14 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}