Coverage Report

Created: 2026-10-26 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libevmasm/Ethdebug.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
#include <libevmasm/Ethdebug.h>
20
21
#include <libevmasm/EthdebugSchema.h>
22
23
#include <libsolutil/Keccak256.h>
24
25
#include <fmt/format.h>
26
27
#include <range/v3/algorithm/any_of.hpp>
28
29
using namespace solidity;
30
using namespace solidity::evmasm;
31
using namespace solidity::evmasm::ethdebug;
32
33
namespace
34
{
35
36
schema::program::Instruction::Operation instructionOperation(Assembly const& _assembly, LinkerObject const& _linkerObject, size_t const _start, size_t const _end)
37
3.53M
{
38
3.53M
  solAssert(_end <= _linkerObject.bytecode.size());
39
3.53M
  solAssert(_start < _end);
40
3.53M
  schema::program::Instruction::Operation operation;
41
3.53M
  operation.mnemonic = instructionInfo(static_cast<Instruction>(_linkerObject.bytecode[_start]), _assembly.evmVersion()).name;
42
3.53M
  static size_t constexpr instructionSize = 1;
43
3.53M
  if (_start + instructionSize < _end)
44
1.26M
  {
45
1.26M
    bytes const argumentData(
46
1.26M
      _linkerObject.bytecode.begin() + static_cast<std::ptrdiff_t>(_start) + instructionSize,
47
1.26M
      _linkerObject.bytecode.begin() + static_cast<std::ptrdiff_t>(_end)
48
1.26M
    );
49
1.26M
    solAssert(!argumentData.empty());
50
1.26M
    operation.arguments = {{schema::data::HexValue{argumentData}}};
51
1.26M
  }
52
3.53M
  return operation;
53
3.53M
}
54
55
schema::materials::SourceRange::Range locationRange(langutil::SourceLocation const& _location)
56
2.75M
{
57
2.75M
  return {
58
2.75M
    .length = schema::data::Unsigned{_location.end - _location.start},
59
2.75M
    .offset = schema::data::Unsigned{_location.start}
60
2.75M
  };
61
2.75M
}
62
63
schema::materials::Reference sourceReference(unsigned _sourceID)
64
2.75M
{
65
2.75M
  return {
66
2.75M
    .id = schema::materials::ID{_sourceID},
67
2.75M
    .type = std::nullopt
68
2.75M
  };
69
2.75M
}
70
71
std::optional<schema::program::Context> instructionContext(AssemblyItems const& _codeSection, size_t _assemblyItemIndex, unsigned _sourceID)
72
3.53M
{
73
3.53M
  solAssert(_assemblyItemIndex < _codeSection.size());
74
3.53M
  langutil::SourceLocation const& location = _codeSection.at(_assemblyItemIndex).location();
75
3.53M
  if (!location.isValid())
76
776k
    return std::nullopt;
77
78
2.75M
  return schema::program::Context{
79
2.75M
    schema::materials::SourceRange{
80
2.75M
      .source = sourceReference(_sourceID),
81
2.75M
      .range = locationRange(location)
82
2.75M
    },
83
2.75M
    std::nullopt,
84
2.75M
    std::nullopt
85
2.75M
  };
86
3.53M
}
87
88
std::vector<schema::program::Instruction> programInstructions(Assembly const& _assembly, LinkerObject const& _linkerObject, unsigned const _sourceID)
89
22.1k
{
90
22.1k
  auto const& locations = _linkerObject.codeSectionLocation;
91
22.1k
  AssemblyItems const& items = _assembly.items();
92
93
22.1k
  std::vector<schema::program::Instruction> instructions;
94
22.1k
  instructions.reserve(items.size());
95
96
22.1k
  bool const codeSectionContainsVerbatim = ranges::any_of(
97
22.1k
    items,
98
3.53M
    [](auto const& _instruction) { return _instruction.type() == VerbatimBytecode; }
99
22.1k
  );
100
22.1k
  solUnimplementedAssert(!codeSectionContainsVerbatim, "Verbatim bytecode is currently not supported by ethdebug.");
101
102
22.1k
  for (auto const& currentInstruction: locations.instructionLocations)
103
3.53M
  {
104
3.53M
    size_t const start = currentInstruction.start;
105
3.53M
    size_t const end = currentInstruction.end;
106
107
    // some instructions do not contribute to the bytecode
108
3.53M
    if (start == end)
109
0
      continue;
110
111
3.53M
    instructions.emplace_back(schema::program::Instruction{
112
3.53M
      .offset = schema::data::Unsigned{start},
113
3.53M
      .operation = instructionOperation(_assembly, _linkerObject, start, end),
114
3.53M
      .context = instructionContext(items, currentInstruction.assemblyItemIndex, _sourceID)
115
3.53M
    });
116
3.53M
  }
117
118
22.1k
  return instructions;
119
22.1k
}
120
121
std::string lengthPrefixed(std::string_view _value)
122
0
{
123
0
  return fmt::format("{}:{}", _value.size(), _value);
124
0
}
125
126
std::string compilationID(std::vector<Source> const& _sources)
127
0
{
128
  // Keep this independent from VersionString build metadata.
129
  // TODO: take compilation flags into account. Once compiler settings are threaded
130
  // into ETHDebug resources, include a normalized settings object here.
131
0
  std::string rawIDInput = "ethdebug-solc-compilation-v1";
132
0
  rawIDInput += lengthPrefixed(std::to_string(_sources.size()));
133
134
0
  for (auto const& source: _sources)
135
0
  {
136
0
    rawIDInput += lengthPrefixed(std::to_string(source.id));
137
0
    rawIDInput += lengthPrefixed(source.path);
138
0
    rawIDInput += lengthPrefixed(source.contents);
139
0
    rawIDInput += lengthPrefixed(source.language);
140
0
  }
141
142
0
  return fmt::format("solc-{}", util::keccak256(rawIDInput).hex());
143
0
}
144
145
schema::materials::Source materialSource(Source const& _source)
146
0
{
147
0
  schema::materials::Source result;
148
0
  result.id = schema::materials::ID{_source.id};
149
0
  result.path = _source.path;
150
0
  result.contents = _source.contents;
151
0
  result.encoding = std::nullopt;
152
0
  result.language = _source.language;
153
0
  return result;
154
0
}
155
156
schema::materials::Compilation materialCompilation(std::vector<Source> const& _sources, std::string_view _version)
157
0
{
158
0
  std::vector<schema::materials::Source> sources;
159
0
  sources.reserve(_sources.size());
160
0
  for (auto const& source: _sources)
161
0
    sources.emplace_back(materialSource(source));
162
163
0
  schema::materials::Compilation result;
164
0
  result.id = schema::materials::ID{compilationID(_sources)};
165
0
  result.compiler.name = "solc";
166
0
  result.compiler.version = std::string{_version};
167
0
  result.settings = std::nullopt;
168
0
  result.sources = std::move(sources);
169
0
  return result;
170
0
}
171
172
} // anonymous namespace
173
174
Json ethdebug::program(std::string_view _name, unsigned _sourceID, Assembly const& _assembly, LinkerObject const& _linkerObject)
175
22.1k
{
176
22.1k
  return schema::Program{
177
22.1k
    .compilation = std::nullopt,
178
22.1k
    .contract = {
179
22.1k
      .name = std::string{_name},
180
22.1k
      .definition = {
181
22.1k
        .source = {
182
22.1k
          .id = {_sourceID},
183
22.1k
          .type = std::nullopt
184
22.1k
        },
185
22.1k
        .range = std::nullopt
186
22.1k
      }
187
22.1k
    },
188
22.1k
    .environment = _assembly.isCreation() ? schema::Program::Environment::CREATE : schema::Program::Environment::CALL,
189
22.1k
    .context = std::nullopt,
190
22.1k
    .instructions = programInstructions(_assembly, _linkerObject, _sourceID)
191
22.1k
  };
192
22.1k
}
193
194
Json ethdebug::resources(std::vector<Source> const& _sources, std::string_view _version)
195
0
{
196
0
  schema::info::Resources result;
197
0
  result.compilation = materialCompilation(_sources, _version);
198
0
  result.types = Json::object();
199
0
  result.pointers = Json::object();
200
0
  return result;
201
0
}
202
203
Json ethdebug::compilation(std::vector<Source> const& _sources, std::string_view _version)
204
0
{
205
0
  return materialCompilation(_sources, _version);
206
0
}