|
18 | 18 |
|
19 | 19 | #include <libevmasm/Ethdebug.h>
|
20 | 20 |
|
| 21 | +#include <libevmasm/EthdebugSchema.h> |
| 22 | + |
| 23 | +#include <range/v3/algorithm/any_of.hpp> |
| 24 | + |
21 | 25 | using namespace solidity;
|
22 | 26 | using namespace solidity::evmasm;
|
23 | 27 | using namespace solidity::evmasm::ethdebug;
|
24 | 28 |
|
25 | 29 | namespace
|
26 | 30 | {
|
27 | 31 |
|
28 |
| -Json programInstructions(Assembly const& _assembly, LinkerObject const& _linkerObject, unsigned _sourceId) |
| 32 | +schema::program::Instruction::Operation instructionOperation(Assembly const& _assembly, LinkerObject const& _linkerObject, size_t const _start, size_t const _end) |
29 | 33 | {
|
30 |
| - solUnimplementedAssert(_assembly.eofVersion() == std::nullopt, "ethdebug does not yet support EOF."); |
31 |
| - solUnimplementedAssert(_assembly.codeSections().size() == 1, "ethdebug does not yet support multiple code-sections."); |
32 |
| - for (auto const& instruction: _assembly.codeSections()[0].items) |
33 |
| - solUnimplementedAssert(instruction.type() != VerbatimBytecode, "Verbatim bytecode is currently not supported by ethdebug."); |
34 |
| - |
35 |
| - solAssert(_linkerObject.codeSectionLocations.size() == 1); |
36 |
| - solAssert(_linkerObject.codeSectionLocations[0].end <= _linkerObject.bytecode.size()); |
37 |
| - Json instructions = Json::array(); |
38 |
| - for (size_t i = 0; i < _linkerObject.codeSectionLocations[0].instructionLocations.size(); ++i) |
| 34 | + solAssert(_end <= _linkerObject.bytecode.size()); |
| 35 | + solAssert(_start < _end); |
| 36 | + schema::program::Instruction::Operation operation; |
| 37 | + operation.mnemonic = instructionInfo(static_cast<Instruction>(_linkerObject.bytecode[_start]), _assembly.evmVersion()).name; |
| 38 | + static size_t constexpr instructionSize = 1; |
| 39 | + if (_start + instructionSize < _end) |
39 | 40 | {
|
40 |
| - LinkerObject::InstructionLocation currentInstruction = _linkerObject.codeSectionLocations[0].instructionLocations[i]; |
41 |
| - size_t start = currentInstruction.start; |
42 |
| - size_t end = currentInstruction.end; |
43 |
| - size_t assemblyItemIndex = currentInstruction.assemblyItemIndex; |
44 |
| - solAssert(end <= _linkerObject.bytecode.size()); |
45 |
| - solAssert(start < end); |
46 |
| - solAssert(assemblyItemIndex < _assembly.codeSections().at(0).items.size()); |
47 |
| - Json operation = Json::object(); |
48 |
| - operation["mnemonic"] = instructionInfo(static_cast<Instruction>(_linkerObject.bytecode[start]), _assembly.evmVersion()).name; |
49 |
| - static size_t constexpr instructionSize = 1; |
50 |
| - if (start + instructionSize < end) |
51 |
| - { |
52 |
| - bytes const argumentData( |
53 |
| - _linkerObject.bytecode.begin() + static_cast<std::ptrdiff_t>(start) + instructionSize, |
54 |
| - _linkerObject.bytecode.begin() + static_cast<std::ptrdiff_t>(end) |
55 |
| - ); |
56 |
| - solAssert(!argumentData.empty()); |
57 |
| - operation["arguments"] = Json::array({util::toHex(argumentData, util::HexPrefix::Add)}); |
58 |
| - } |
59 |
| - langutil::SourceLocation const& location = _assembly.codeSections().at(0).items.at(assemblyItemIndex).location(); |
60 |
| - Json instruction = Json::object(); |
61 |
| - instruction["offset"] = start; |
62 |
| - instruction["operation"] = operation; |
63 |
| - |
64 |
| - instruction["context"] = Json::object(); |
65 |
| - instruction["context"]["code"] = Json::object(); |
66 |
| - instruction["context"]["code"]["source"] = Json::object(); |
67 |
| - instruction["context"]["code"]["source"]["id"] = static_cast<int>(_sourceId); |
68 |
| - |
69 |
| - instruction["context"]["code"]["range"] = Json::object(); |
70 |
| - instruction["context"]["code"]["range"]["offset"] = location.start; |
71 |
| - instruction["context"]["code"]["range"]["length"] = location.end - location.start; |
72 |
| - instructions.emplace_back(instruction); |
| 41 | + bytes const argumentData( |
| 42 | + _linkerObject.bytecode.begin() + static_cast<std::ptrdiff_t>(_start) + instructionSize, |
| 43 | + _linkerObject.bytecode.begin() + static_cast<std::ptrdiff_t>(_end) |
| 44 | + ); |
| 45 | + solAssert(!argumentData.empty()); |
| 46 | + operation.arguments = {{schema::data::HexValue{argumentData}}}; |
73 | 47 | }
|
| 48 | + return operation; |
| 49 | +} |
74 | 50 |
|
75 |
| - return instructions; |
| 51 | +schema::materials::SourceRange::Range locationRange(langutil::SourceLocation const& _location) |
| 52 | +{ |
| 53 | + return { |
| 54 | + .length = schema::data::Unsigned{_location.end - _location.start}, |
| 55 | + .offset = schema::data::Unsigned{_location.start} |
| 56 | + }; |
76 | 57 | }
|
77 | 58 |
|
78 |
| -} // anonymous namespace |
| 59 | +schema::materials::Reference sourceReference(unsigned _sourceID) |
| 60 | +{ |
| 61 | + return { |
| 62 | + .id = schema::materials::ID{_sourceID}, |
| 63 | + .type = std::nullopt |
| 64 | + }; |
| 65 | +} |
79 | 66 |
|
80 |
| -Json ethdebug::program(std::string_view _name, unsigned _sourceId, Assembly const* _assembly, LinkerObject const& _linkerObject) |
| 67 | +std::optional<schema::program::Context> instructionContext(Assembly::CodeSection const& _codeSection, size_t _assemblyItemIndex, unsigned _sourceID) |
81 | 68 | {
|
82 |
| - Json result = Json::object(); |
83 |
| - result["contract"] = Json::object(); |
84 |
| - result["contract"]["name"] = _name; |
85 |
| - result["contract"]["definition"] = Json::object(); |
86 |
| - result["contract"]["definition"]["source"] = Json::object(); |
87 |
| - result["contract"]["definition"]["source"]["id"] = _sourceId; |
88 |
| - if (_assembly) |
| 69 | + solAssert(_assemblyItemIndex < _codeSection.items.size()); |
| 70 | + langutil::SourceLocation const& location = _codeSection.items.at(_assemblyItemIndex).location(); |
| 71 | + if (!location.isValid()) |
| 72 | + return std::nullopt; |
| 73 | + |
| 74 | + return schema::program::Context{ |
| 75 | + schema::materials::SourceRange{ |
| 76 | + .source = sourceReference(_sourceID), |
| 77 | + .range = locationRange(location) |
| 78 | + }, |
| 79 | + std::nullopt, |
| 80 | + std::nullopt |
| 81 | + }; |
| 82 | +} |
| 83 | + |
| 84 | +std::vector<schema::program::Instruction> codeSectionInstructions(Assembly const& _assembly, LinkerObject const& _linkerObject, unsigned const _sourceID, size_t const _codeSectionIndex) |
| 85 | +{ |
| 86 | + solAssert(_codeSectionIndex < _linkerObject.codeSectionLocations.size()); |
| 87 | + solAssert(_codeSectionIndex < _assembly.codeSections().size()); |
| 88 | + auto const& locations = _linkerObject.codeSectionLocations[_codeSectionIndex]; |
| 89 | + auto const& codeSection = _assembly.codeSections().at(_codeSectionIndex); |
| 90 | + |
| 91 | + std::vector<schema::program::Instruction> instructions; |
| 92 | + instructions.reserve(codeSection.items.size()); |
| 93 | + |
| 94 | + bool const codeSectionContainsVerbatim = ranges::any_of( |
| 95 | + codeSection.items, |
| 96 | + [](auto const& _instruction) { return _instruction.type() == VerbatimBytecode; } |
| 97 | + ); |
| 98 | + solUnimplementedAssert(!codeSectionContainsVerbatim, "Verbatim bytecode is currently not supported by ethdebug."); |
| 99 | + |
| 100 | + for (auto const& currentInstruction: locations.instructionLocations) |
89 | 101 | {
|
90 |
| - result["environment"] = _assembly->isCreation() ? "create" : "call"; |
91 |
| - result["instructions"] = programInstructions(*_assembly, _linkerObject, _sourceId); |
| 102 | + size_t const start = currentInstruction.start; |
| 103 | + size_t const end = currentInstruction.end; |
| 104 | + |
| 105 | + // some instructions do not contribute to the bytecode |
| 106 | + if (start == end) |
| 107 | + continue; |
| 108 | + |
| 109 | + instructions.emplace_back(schema::program::Instruction{ |
| 110 | + .offset = schema::data::Unsigned{start}, |
| 111 | + .operation = instructionOperation(_assembly, _linkerObject, start, end), |
| 112 | + .context = instructionContext(codeSection, currentInstruction.assemblyItemIndex, _sourceID) |
| 113 | + }); |
92 | 114 | }
|
93 |
| - return result; |
| 115 | + |
| 116 | + return instructions; |
| 117 | +} |
| 118 | + |
| 119 | +std::vector<schema::program::Instruction> programInstructions(Assembly const& _assembly, LinkerObject const& _linkerObject, unsigned const _sourceID) |
| 120 | +{ |
| 121 | + auto const numCodeSections = _assembly.codeSections().size(); |
| 122 | + solAssert(numCodeSections == _linkerObject.codeSectionLocations.size()); |
| 123 | + |
| 124 | + std::vector<schema::program::Instruction> instructionInfo; |
| 125 | + for (size_t codeSectionIndex = 0; codeSectionIndex < numCodeSections; ++codeSectionIndex) |
| 126 | + instructionInfo += codeSectionInstructions(_assembly, _linkerObject, _sourceID, codeSectionIndex); |
| 127 | + return instructionInfo; |
| 128 | +} |
| 129 | + |
| 130 | +} // anonymous namespace |
| 131 | + |
| 132 | +Json ethdebug::program(std::string_view _name, unsigned _sourceID, Assembly const& _assembly, LinkerObject const& _linkerObject) |
| 133 | +{ |
| 134 | + return schema::Program{ |
| 135 | + .compilation = std::nullopt, |
| 136 | + .contract = { |
| 137 | + .name = std::string{_name}, |
| 138 | + .definition = { |
| 139 | + .source = { |
| 140 | + .id = {_sourceID}, |
| 141 | + .type = std::nullopt |
| 142 | + }, |
| 143 | + .range = std::nullopt |
| 144 | + } |
| 145 | + }, |
| 146 | + .environment = _assembly.isCreation() ? schema::Program::Environment::CREATE : schema::Program::Environment::CALL, |
| 147 | + .context = std::nullopt, |
| 148 | + .instructions = programInstructions(_assembly, _linkerObject, _sourceID) |
| 149 | + }; |
94 | 150 | }
|
95 | 151 |
|
96 | 152 | Json ethdebug::resources(std::vector<std::string> const& _sources, std::string const& _version)
|
|
0 commit comments