|
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