Coverage Report

Created: 2022-08-24 06:55

/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
19.7M
    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
70.7M
  {}
74
  AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()):
75
    m_type(_type),
76
    m_location(std::move(_location))
77
57.7M
  {
78
57.7M
    if (m_type == Operation)
79
0
      m_instruction = Instruction(uint8_t(_data));
80
57.7M
    else
81
57.7M
      m_data = std::make_shared<u256>(std::move(_data));
82
57.7M
  }
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
673M
  AssemblyItem(AssemblyItem const&) = default;
90
1.21G
  AssemblyItem(AssemblyItem&&) = default;
91
0
  AssemblyItem& operator=(AssemblyItem const&) = default;
92
153M
  AssemblyItem& operator=(AssemblyItem&&) = default;
93
94
12.2M
  AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, data()); }
95
4.96M
  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
33.4G
  AssemblyItemType type() const { return m_type; }
106
2.16G
  u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; }
107
0
  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
7.49G
  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
645M
  {
124
645M
    if (type() != _other.type())
125
382M
      return false;
126
262M
    if (type() == Operation)
127
202M
      return instruction() == _other.instruction();
128
60.3M
    else if (type() == VerbatimBytecode)
129
0
      return *m_verbatimBytecode == *_other.m_verbatimBytecode;
130
60.3M
    else
131
60.3M
      return data() == _other.data();
132
262M
  }
133
0
  bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); }
134
  /// Less-than operator compatible with operator==.
135
  bool operator<(AssemblyItem const& _other) const
136
0
  {
137
0
    if (type() != _other.type())
138
0
      return type() < _other.type();
139
0
    else if (type() == Operation)
140
0
      return instruction() < _other.instruction();
141
0
    else if (type() == VerbatimBytecode)
142
0
      return *m_verbatimBytecode == *_other.m_verbatimBytecode;
143
0
    else
144
0
      return data() < _other.data();
145
0
  }
146
147
  /// Shortcut that avoids constructing an AssemblyItem just to perform the comparison.
148
  bool operator==(Instruction _instr) const
149
11.0G
  {
150
11.0G
    return type() == Operation && instruction() == _instr;
151
11.0G
  }
152
4.28G
  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
116M
  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
116M
  void setLocation(langutil::SourceLocation const& _location) { m_location = _location; }
173
117M
  langutil::SourceLocation const& location() const { return m_location; }
174
175
6.33M
  void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; }
176
354k
  JumpType getJumpType() const { return m_jumpType; }
177
  std::string getJumpTypeAsString() const;
178
179
4.55k
  void setPushedValue(u256 const& _value) const { m_pushedValue = std::make_shared<u256>(_value); }
180
0
  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
0
  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
55.3k
{
208
55.3k
  size_t size = 0;
209
55.3k
  for (AssemblyItem const& item: _items)
210
628M
    size += item.bytesRequired(_addressLength, _precision);
211
55.3k
  return size;
212
55.3k
}
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
}