/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 | } |