/src/solidity/libevmasm/AssemblyItem.h
Line | Count | Source (jump to first uncovered line) |
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 | | /** @file AssemblyItem.h |
19 | | * @author Gav Wood <i@gavwood.com> |
20 | | * @date 2014 |
21 | | */ |
22 | | |
23 | | #pragma once |
24 | | |
25 | | #include <libevmasm/Instruction.h> |
26 | | #include <libevmasm/Exceptions.h> |
27 | | #include <liblangutil/SourceLocation.h> |
28 | | #include <libsolutil/Common.h> |
29 | | #include <libsolutil/Numeric.h> |
30 | | #include <libsolutil/Assertions.h> |
31 | | #include <optional> |
32 | | #include <iostream> |
33 | | #include <sstream> |
34 | | |
35 | | namespace solidity::evmasm |
36 | | { |
37 | | |
38 | | enum AssemblyItemType |
39 | | { |
40 | | UndefinedItem, |
41 | | Operation, |
42 | | Push, |
43 | | PushTag, |
44 | | PushSub, |
45 | | PushSubSize, |
46 | | PushProgramSize, |
47 | | Tag, |
48 | | PushData, |
49 | | PushLibraryAddress, ///< Push a currently unknown address of another (library) contract. |
50 | | PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. |
51 | | PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor. |
52 | | AssignImmutable, ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code. |
53 | | VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification. |
54 | | }; |
55 | | |
56 | | enum class Precision { Precise , Approximate }; |
57 | | |
58 | | class Assembly; |
59 | | class AssemblyItem; |
60 | | using AssemblyItems = std::vector<AssemblyItem>; |
61 | | |
62 | | class AssemblyItem |
63 | | { |
64 | | public: |
65 | | enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; |
66 | | |
67 | | AssemblyItem(u256 _push, langutil::SourceLocation _location = langutil::SourceLocation()): |
68 | 90.3M | AssemblyItem(Push, std::move(_push), std::move(_location)) { } |
69 | | AssemblyItem(Instruction _i, langutil::SourceLocation _location = langutil::SourceLocation()): |
70 | | m_type(Operation), |
71 | | m_instruction(_i), |
72 | | m_location(std::move(_location)) |
73 | 341M | {} |
74 | | AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()): |
75 | | m_type(_type), |
76 | | m_location(std::move(_location)) |
77 | 246M | { |
78 | 246M | if (m_type == Operation) |
79 | 0 | m_instruction = Instruction(uint8_t(_data)); |
80 | 246M | else |
81 | 246M | m_data = std::make_shared<u256>(std::move(_data)); |
82 | 246M | } |
83 | | explicit AssemblyItem(bytes _verbatimData, size_t _arguments, size_t _returnVariables): |
84 | | m_type(VerbatimBytecode), |
85 | | m_instruction{}, |
86 | | m_verbatimBytecode{{_arguments, _returnVariables, std::move(_verbatimData)}} |
87 | 0 | {} |
88 | | |
89 | 1.47G | AssemblyItem(AssemblyItem const&) = default; |
90 | 2.55G | AssemblyItem(AssemblyItem&&) = default; |
91 | 0 | AssemblyItem& operator=(AssemblyItem const&) = default; |
92 | 337M | AssemblyItem& operator=(AssemblyItem&&) = default; |
93 | | |
94 | 20.8M | AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, data()); } |
95 | 61.5M | AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(PushTag, data()); } |
96 | | /// Converts the tag to a subassembly tag. This has to be called in order to move a tag across assemblies. |
97 | | /// @param _subId the identifier of the subassembly the tag is taken from. |
98 | | AssemblyItem toSubAssemblyTag(size_t _subId) const; |
99 | | /// @returns splits the data of the push tag into sub assembly id and actual tag id. |
100 | | /// The sub assembly id of non-foreign push tags is -1. |
101 | | std::pair<size_t, size_t> splitForeignPushTag() const; |
102 | | /// Sets sub-assembly part and tag for a push tag. |
103 | | void setPushTagSubIdAndTag(size_t _subId, size_t _tag); |
104 | | |
105 | 65.2G | AssemblyItemType type() const { return m_type; } |
106 | 4.34G | u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; } |
107 | 56.9k | void setData(u256 const& _data) { assertThrow(m_type != Operation, util::Exception, ""); m_data = std::make_shared<u256>(_data); } |
108 | | |
109 | | /// This function is used in `Assembly::assemblyJSON`. |
110 | | /// It returns the name & data of the current assembly item. |
111 | | /// @returns a pair, where the first element is the json-assembly |
112 | | /// item name, where second element is the string representation |
113 | | /// of it's data. |
114 | | std::pair<std::string, std::string> nameAndData() const; |
115 | | |
116 | 0 | bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); } |
117 | | |
118 | | /// @returns the instruction of this item (only valid if type() == Operation) |
119 | 15.1G | Instruction instruction() const { assertThrow(m_type == Operation, util::Exception, ""); return m_instruction; } |
120 | | |
121 | | /// @returns true if the type and data of the items are equal. |
122 | | bool operator==(AssemblyItem const& _other) const |
123 | 1.50G | { |
124 | 1.50G | if (type() != _other.type()) |
125 | 945M | return false; |
126 | 557M | if (type() == Operation) |
127 | 404M | return instruction() == _other.instruction(); |
128 | 152M | else if (type() == VerbatimBytecode) |
129 | 0 | return *m_verbatimBytecode == *_other.m_verbatimBytecode; |
130 | 152M | else |
131 | 152M | return data() == _other.data(); |
132 | 557M | } |
133 | 1.07M | bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); } |
134 | | /// Less-than operator compatible with operator==. |
135 | | bool operator<(AssemblyItem const& _other) const |
136 | 97.3M | { |
137 | 97.3M | if (type() != _other.type()) |
138 | 6.84M | return type() < _other.type(); |
139 | 90.5M | else if (type() == Operation) |
140 | 43.4M | return instruction() < _other.instruction(); |
141 | 47.0M | else if (type() == VerbatimBytecode) |
142 | 0 | return *m_verbatimBytecode == *_other.m_verbatimBytecode; |
143 | 47.0M | else |
144 | 47.0M | return data() < _other.data(); |
145 | 97.3M | } |
146 | | |
147 | | /// Shortcut that avoids constructing an AssemblyItem just to perform the comparison. |
148 | | bool operator==(Instruction _instr) const |
149 | 20.3G | { |
150 | 20.3G | return type() == Operation && instruction() == _instr; |
151 | 20.3G | } |
152 | 7.88G | bool operator!=(Instruction _instr) const { return !operator==(_instr); } |
153 | | |
154 | | static std::string computeSourceMapping( |
155 | | AssemblyItems const& _items, |
156 | | std::map<std::string, unsigned> const& _sourceIndicesMap |
157 | | ); |
158 | | |
159 | | /// @returns an upper bound for the number of bytes required by this item, assuming that |
160 | | /// the value of a jump tag takes @a _addressLength bytes. |
161 | | /// @param _precision Whether to return a precise count (which involves |
162 | | /// counting immutable references which are only set after |
163 | | /// a call to `assemble()`) or an approx. count. |
164 | | size_t bytesRequired(size_t _addressLength, Precision _precision = Precision::Precise) const; |
165 | | size_t arguments() const; |
166 | | size_t returnValues() const; |
167 | 261M | size_t deposit() const { return returnValues() - arguments(); } |
168 | | |
169 | | /// @returns true if the assembly item can be used in a functional context. |
170 | | bool canBeFunctional() const; |
171 | | |
172 | 208M | void setLocation(langutil::SourceLocation const& _location) { m_location = _location; } |
173 | 257M | langutil::SourceLocation const& location() const { return m_location; } |
174 | | |
175 | 11.6M | void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } |
176 | 6.05M | JumpType getJumpType() const { return m_jumpType; } |
177 | | std::string getJumpTypeAsString() const; |
178 | | |
179 | 26.9k | void setPushedValue(u256 const& _value) const { m_pushedValue = std::make_shared<u256>(_value); } |
180 | 6.01M | u256 const* pushedValue() const { return m_pushedValue.get(); } |
181 | | |
182 | | std::string toAssemblyText(Assembly const& _assembly) const; |
183 | | |
184 | | size_t m_modifierDepth = 0; |
185 | | |
186 | 707 | void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = _n; } |
187 | | |
188 | | private: |
189 | | size_t opcodeCount() const noexcept; |
190 | | |
191 | | AssemblyItemType m_type; |
192 | | Instruction m_instruction; ///< Only valid if m_type == Operation |
193 | | std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation |
194 | | /// If m_type == VerbatimBytecode, this holds number of arguments, number of |
195 | | /// return variables and verbatim bytecode. |
196 | | std::optional<std::tuple<size_t, size_t, bytes>> m_verbatimBytecode; |
197 | | langutil::SourceLocation m_location; |
198 | | JumpType m_jumpType = JumpType::Ordinary; |
199 | | /// Pushed value for operations with data to be determined during assembly stage, |
200 | | /// e.g. PushSubSize, PushTag, PushSub, etc. |
201 | | mutable std::shared_ptr<u256> m_pushedValue; |
202 | | /// Number of PushImmutable's with the same hash. Only used for AssignImmutable. |
203 | | mutable std::optional<size_t> m_immutableOccurrences; |
204 | | }; |
205 | | |
206 | | inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength, Precision _precision = Precision::Precise) |
207 | 31.3M | { |
208 | 31.3M | size_t size = 0; |
209 | 31.3M | for (AssemblyItem const& item: _items) |
210 | 1.26G | size += item.bytesRequired(_addressLength, _precision); |
211 | 31.3M | return size; |
212 | 31.3M | } |
213 | | |
214 | | std::ostream& operator<<(std::ostream& _out, AssemblyItem const& _item); |
215 | | inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _items) |
216 | 0 | { |
217 | 0 | for (AssemblyItem const& item: _items) |
218 | 0 | _out << item; |
219 | 0 | return _out; |
220 | 0 | } |
221 | | |
222 | | } |