@@ -53,6 +53,54 @@ using namespace solidity::evmasm;
5353using namespace solidity ::langutil;
5454using namespace solidity ::util;
5555
56+ namespace
57+ {
58+
59+ // / Produces instruction location info in RAII style. When an assembly instruction is added to the bytecode,
60+ // / this class can be instantiated in that scope. It will record the current bytecode size (before addition)
61+ // / and, at destruction time, record the new bytecode size. This information is then added to an external
62+ // / instruction locations vector.
63+ // / If the instruction decomposes into multiple individual evm instructions, `emit` can be
64+ // / called for all but the last one (which will be emitted by the destructor).
65+ class InstructionLocationEmitter
66+ {
67+ public:
68+ InstructionLocationEmitter (
69+ std::vector<LinkerObject::InstructionLocation>& _instructionLocations,
70+ bytes const & _bytecode,
71+ size_t const _assemblyItemIndex
72+ ):
73+ m_instructionLocations (_instructionLocations),
74+ m_bytecode (_bytecode),
75+ m_assemblyItemIndex (_assemblyItemIndex),
76+ m_instructionLocationStart (_bytecode.size())
77+ {}
78+
79+ ~InstructionLocationEmitter ()
80+ {
81+ emit ();
82+ }
83+
84+ void emit ()
85+ {
86+ auto const end = m_bytecode.size ();
87+ m_instructionLocations.push_back (LinkerObject::InstructionLocation{
88+ .start = m_instructionLocationStart,
89+ .end = end,
90+ .assemblyItemIndex = m_assemblyItemIndex
91+ });
92+ m_instructionLocationStart = end;
93+ }
94+
95+ private:
96+ std::vector<LinkerObject::InstructionLocation>& m_instructionLocations;
97+ bytes const & m_bytecode;
98+ size_t const m_assemblyItemIndex{};
99+ size_t m_instructionLocationStart{};
100+ };
101+
102+ }
103+
56104std::map<std::string, std::shared_ptr<std::string const >> Assembly::s_sharedSourceNames;
57105
58106AssemblyItem const & Assembly::append (AssemblyItem _i)
@@ -1606,9 +1654,17 @@ LinkerObject const& Assembly::assembleEOF() const
16061654 for (auto && [codeSectionIndex, codeSection]: m_codeSections | ranges::views::enumerate)
16071655 {
16081656 auto const sectionStart = ret.bytecode .size ();
1657+
1658+ std::vector<LinkerObject::InstructionLocation> instructionLocations;
1659+ instructionLocations.reserve (codeSection.items .size ());
1660+
16091661 solAssert (!codeSection.items .empty (), " Empty code section." );
1610- for (AssemblyItem const & item: codeSection.items )
1662+
1663+ for (auto const & [assemblyItemIndex, item]: codeSection.items | ranges::views::enumerate)
16111664 {
1665+ // collect instruction locations via side effects
1666+ InstructionLocationEmitter instructionLocationEmitter {instructionLocations, ret.bytecode , assemblyItemIndex};
1667+
16121668 // store position of the invalid jump destination
16131669 if (item.type () != Tag && m_tagPositionsInBytecode[0 ] == std::numeric_limits<size_t >::max ())
16141670 m_tagPositionsInBytecode[0 ] = ret.bytecode .size ();
@@ -1724,6 +1780,12 @@ LinkerObject const& Assembly::assembleEOF() const
17241780 " Code section too large for EOF."
17251781 );
17261782 setBigEndianUint16 (ret.bytecode , codeSectionSizePositions[codeSectionIndex], ret.bytecode .size () - sectionStart);
1783+
1784+ ret.codeSectionLocations .push_back (LinkerObject::CodeSectionLocation{
1785+ .start = sectionStart,
1786+ .end = ret.bytecode .size (),
1787+ .instructionLocations = std::move (instructionLocations)
1788+ });
17271789 }
17281790
17291791 for (auto const & [refPos, tagId]: tagRef)
0 commit comments