Coverage Report

Created: 2022-08-24 06:52

/src/solidity/libsolidity/codegen/CompilerUtils.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
/**
19
 * @author Christian <c@ethdev.com>
20
 * @date 2014
21
 * Routines used by both the compiler and the expression compiler.
22
 */
23
24
#pragma once
25
26
#include <libsolidity/ast/ASTForward.h>
27
#include <libsolidity/ast/TypeProvider.h>
28
#include <libsolidity/interface/DebugSettings.h>
29
#include <libsolidity/codegen/CompilerContext.h>
30
#include <libsolidity/codegen/CompilerContext.h>
31
32
namespace solidity::frontend
33
{
34
35
class Type; // forward
36
37
class CompilerUtils
38
{
39
public:
40
  explicit CompilerUtils(CompilerContext& _context): m_context(_context)
41
10.1M
  {}
42
43
  /// Stores the initial value of the free-memory-pointer at its position;
44
  void initialiseFreeMemoryPointer();
45
  /// Copies the free memory pointer to the stack.
46
  /// Stack pre:
47
  /// Stack post: <mem_start>
48
  void fetchFreeMemoryPointer();
49
  /// Stores the free memory pointer from the stack.
50
  /// Stack pre: <mem_end>
51
  /// Stack post:
52
  void storeFreeMemoryPointer();
53
  /// Allocates a number of bytes in memory as given on the stack.
54
  /// Stack pre: <size>
55
  /// Stack post: <mem_start>
56
  void allocateMemory();
57
  /// Allocates a number of bytes in memory as given on the stack.
58
  /// Stack pre:
59
  /// Stack post: <mem_start>
60
  void allocateMemory(u256 const& size);
61
  /// Appends code that transforms memptr to (memptr - free_memptr) memptr
62
  /// Stack pre: <mem_end>
63
  /// Stack post: <size> <mem_start>
64
  void toSizeAfterFreeMemoryPointer();
65
66
  /// Appends code that performs a revert, providing the given string data.
67
  /// Will also append an error signature corresponding to Error(string).
68
  /// @param _argumentType the type of the string argument, will be converted to memory string.
69
  /// Stack pre: string data
70
  /// Stack post:
71
  void revertWithStringData(Type const& _argumentType);
72
73
  void revertWithError(
74
    std::string const& _errorName,
75
    std::vector<Type const*> const& _parameterTypes,
76
    std::vector<Type const*> const& _argumentTypes
77
  );
78
79
  /// Allocates a new array and copies the return data to it.
80
  /// If the EVM does not support return data, creates an empty array.
81
  void returnDataToArray();
82
83
  /// Computes the absolute calldata offset of a tail given a base reference and the (absolute)
84
  /// offset of the tail pointer. Performs bounds checks. If @a _type is a dynamically sized array it also
85
  /// returns the array length on the stack.
86
  /// Stack pre: base_ref tail_ptr
87
  /// Stack post: tail_ref [length]
88
  void accessCalldataTail(Type const& _type);
89
90
  /// Loads data from memory to the stack.
91
  /// @param _offset offset in memory (or calldata)
92
  /// @param _type data type to load
93
  /// @param _fromCalldata if true, load from calldata, not from memory
94
  /// @param _padToWords if true, assume the data is padded to full words (32 bytes)
95
  /// @returns the number of bytes consumed in memory.
96
  unsigned loadFromMemory(
97
    unsigned _offset,
98
    Type const& _type,
99
    bool _fromCalldata,
100
    bool _padToWords
101
  );
102
  /// Dynamic version of @see loadFromMemory, expects the memory offset on the stack.
103
  /// Stack pre: memory_offset
104
  /// Stack post: value... (memory_offset+length)
105
  void loadFromMemoryDynamic(
106
    Type const& _type,
107
    bool _fromCalldata = false,
108
    bool _padToWords = true,
109
    bool _keepUpdatedMemoryOffset = true
110
  );
111
  /// Stores a 256 bit integer from stack in memory.
112
  /// @param _offset offset in memory
113
  void storeInMemory(unsigned _offset);
114
115
  /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
116
  /// and also updates that. For reference types, only copies the data pointer. Fails for
117
  /// non-memory-references. For string literals no value is available on the stack.
118
  /// @param _padToWords if true, adds zeros to pad to multiple of 32 bytes. Array elements
119
  ///                    are always padded (except for byte arrays), regardless of this parameter.
120
  /// @param _cleanup if true, adds code to cleanup the value before storing it.
121
  /// Stack pre: memory_offset value...
122
  /// Stack post: (memory_offset+length)
123
  void storeInMemoryDynamic(Type const& _type, bool _padToWords = true, bool _cleanup = true);
124
125
  /// Creates code that unpacks the arguments according to their types specified by a vector of TypePointers.
126
  /// From memory if @a _fromMemory is true, otherwise from call data.
127
  /// Calls revert if the supplied size is shorter than the static data requirements
128
  /// or if dynamic data pointers reach outside of the area.
129
  /// Also has a hard cap of 0x100000000 for any given length/offset field.
130
  /// Stack pre: <source_offset> <length>
131
  /// Stack post: <value0> <value1> ... <valuen>
132
  void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false);
133
134
  /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given
135
  /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes.
136
  /// Removes the values from the stack and leaves the updated memory pointer.
137
  /// Stack pre: <v1> <v2> ... <vn> <memptr>
138
  /// Stack post: <memptr_updated>
139
  /// Does not touch the memory-free pointer.
140
  /// @param _padToWords if false, all values are concatenated without padding.
141
  /// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length)
142
  /// together with fixed-length data.
143
  /// @param _encodeAsLibraryTypes if true, encodes for a library function, e.g. does not
144
  /// convert storage pointer types to memory types.
145
  /// @note the locations of target reference types are ignored, because it will always be
146
  /// memory.
147
  void encodeToMemory(
148
    TypePointers const& _givenTypes,
149
    TypePointers const& _targetTypes,
150
    bool _padToWords,
151
    bool _copyDynamicDataInPlace,
152
    bool _encodeAsLibraryTypes = false
153
  );
154
155
  /// Special case of @a encodeToMemory which assumes tight packing, e.g. no zero padding
156
  /// and dynamic data is encoded in-place.
157
  /// Stack pre: <value0> <value1> ... <valueN-1> <head_start>
158
  /// Stack post: <mem_ptr>
159
  void packedEncode(
160
    TypePointers const& _givenTypes,
161
    TypePointers const& _targetTypes,
162
    bool _encodeAsLibraryTypes = false
163
  )
164
0
  {
165
0
    encodeToMemory(_givenTypes, _targetTypes, false, true, _encodeAsLibraryTypes);
166
0
  }
167
168
  /// Special case of @a encodeToMemory which assumes that everything is padded to words
169
  /// and dynamic data is not copied in place (i.e. a proper ABI encoding).
170
  /// Stack pre: <value0> <value1> ... <valueN-1> <head_start>
171
  /// Stack post: <mem_ptr>
172
  void abiEncode(
173
    TypePointers const& _givenTypes,
174
    TypePointers const& _targetTypes,
175
    bool _encodeAsLibraryTypes = false
176
  )
177
32.4k
  {
178
32.4k
    encodeToMemory(_givenTypes, _targetTypes, true, false, _encodeAsLibraryTypes);
179
32.4k
  }
180
181
  /// Special case of @a encodeToMemory which assumes that everything is padded to words
182
  /// and dynamic data is not copied in place (i.e. a proper ABI encoding).
183
  /// Uses a new, less tested encoder implementation.
184
  /// Stack pre: <value0> <value1> ... <valueN-1> <head_start>
185
  /// Stack post: <mem_ptr>
186
  void abiEncodeV2(
187
    TypePointers const& _givenTypes,
188
    TypePointers const& _targetTypes,
189
    bool _encodeAsLibraryTypes = false,
190
    bool _padToWordBoundaries = true
191
  );
192
193
  /// Decodes data from ABI encoding into internal encoding. If @a _fromMemory is set to true,
194
  /// the data is taken from memory instead of from calldata.
195
  /// Can allocate memory.
196
  /// Stack pre: <source_offset> <length>
197
  /// Stack post: <value0> <value1> ... <valuen>
198
  void abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory = false);
199
200
  /// Zero-initialises (the data part of) an already allocated memory array.
201
  /// Length has to be nonzero!
202
  /// Stack pre: <length> <memptr>
203
  /// Stack post: <updated_memptr>
204
  void zeroInitialiseMemoryArray(ArrayType const& _type);
205
206
  /// Copies full 32 byte words in memory (regions cannot overlap), i.e. may copy more than length.
207
  /// Length can be zero, in this case, it copies nothing.
208
  /// Stack pre: <size> <target> <source>
209
  /// Stack post:
210
  void memoryCopy32();
211
  /// Copies data in memory (regions cannot overlap).
212
  /// Length can be zero, in this case, it copies nothing.
213
  /// Stack pre: <size> <target> <source>
214
  /// Stack post:
215
  void memoryCopy();
216
217
  /// Stores the given string in memory.
218
  /// Stack pre: mempos
219
  /// Stack post:
220
  void storeStringData(bytesConstRef _data);
221
222
  /// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true)
223
  /// external function type <address><function identifier> into two stack slots:
224
  /// address (right aligned), function identifier (right aligned)
225
  void splitExternalFunctionType(bool _rightAligned);
226
  /// Performs the opposite operation of splitExternalFunctionType(_rightAligned)
227
  void combineExternalFunctionType(bool _rightAligned);
228
  /// Appends code that combines the construction-time (if available) and runtime function
229
  /// entry label of the given function into a single stack slot.
230
  /// Note: This might cause the compilation queue of the runtime context to be extended.
231
  /// If @a _runtimeOnly, the entry label will include the runtime assembly tag.
232
  void pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly = true);
233
234
  /// Appends code for an implicit or explicit type conversion. This includes erasing higher
235
  /// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
236
  /// if a reference type is converted from calldata or storage to memory.
237
  /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
238
  /// necessary.
239
  /// If @a _chopSignBits, the function resets the signed bits out of the width of the signed integer.
240
  /// If @a _asPartOfArgumentDecoding is true, failed conversions are flagged via REVERT,
241
  /// otherwise they are flagged with INVALID.
242
  void convertType(
243
    Type const& _typeOnStack,
244
    Type const& _targetType,
245
    bool _cleanupNeeded = false,
246
    bool _chopSignBits = false,
247
    bool _asPartOfArgumentDecoding = false
248
  );
249
250
  /// Creates a zero-value for the given type and puts it onto the stack. This might allocate
251
  /// memory for memory references.
252
  void pushZeroValue(Type const& _type);
253
  /// Pushes a pointer to the stack that points to a (potentially shared) location in memory
254
  /// that always contains a zero. It is not allowed to write there.
255
  void pushZeroPointer();
256
257
  /// Moves the value that is at the top of the stack to a stack variable.
258
  void moveToStackVariable(VariableDeclaration const& _variable);
259
  /// Copies an item that occupies @a _itemSize stack slots from a stack depth of @a _stackDepth
260
  /// to the top of the stack.
261
  void copyToStackTop(unsigned _stackDepth, unsigned _itemSize);
262
  /// Moves an item that occupies @a _itemSize stack slots and has items occupying @a _stackDepth
263
  /// slots above it to the top of the stack.
264
  void moveToStackTop(unsigned _stackDepth, unsigned _itemSize = 1);
265
  /// Moves @a _itemSize elements past @a _stackDepth other stack elements
266
  void moveIntoStack(unsigned _stackDepth, unsigned _itemSize = 1);
267
  /// Rotates the topmost @a _items items on the stack, such that the previously topmost element
268
  /// is bottom-most.
269
  void rotateStackUp(unsigned _items);
270
  /// Rotates the topmost @a _items items on the stack, such that the previously bottom-most element
271
  /// is now topmost.
272
  void rotateStackDown(unsigned _items);
273
  /// Removes the current value from the top of the stack.
274
  void popStackElement(Type const& _type);
275
  /// Removes element from the top of the stack _amount times.
276
  void popStackSlots(size_t _amount);
277
  /// Pops slots from the stack such that its height is _toHeight.
278
  /// Adds jump to _jumpTo.
279
  /// Readjusts the stack offset to the original value.
280
  void popAndJump(unsigned _toHeight, evmasm::AssemblyItem const& _jumpTo);
281
282
  template <class T>
283
  static unsigned sizeOnStack(std::vector<T> const& _variables);
284
  static unsigned sizeOnStack(std::vector<Type const*> const& _variableTypes);
285
286
  /// Helper function to shift top value on the stack to the left.
287
  /// Stack pre: <value> <shift_by_bits>
288
  /// Stack post: <shifted_value>
289
  void leftShiftNumberOnStack(unsigned _bits);
290
291
  /// Helper function to shift top value on the stack to the right.
292
  /// Stack pre: <value> <shift_by_bits>
293
  /// Stack post: <shifted_value>
294
  void rightShiftNumberOnStack(unsigned _bits);
295
296
  /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
297
  void computeHashStatic();
298
299
  /// Appends code that copies the code of the given contract to memory.
300
  /// Stack pre: Memory position
301
  /// Stack post: Updated memory position
302
  /// @param creation if true, copies creation code, if false copies runtime code.
303
  /// @note the contract has to be compiled already, so beware of cyclic dependencies!
304
  void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode);
305
306
  /// Bytes we need to the start of call data.
307
  ///  - The size in bytes of the function (hash) identifier.
308
  static unsigned const dataStartOffset;
309
310
  /// Position of the free-memory-pointer in memory;
311
  static size_t const freeMemoryPointer;
312
  /// Position of the memory slot that is always zero.
313
  static size_t const zeroPointer;
314
  /// Starting offset for memory available to the user (aka the contract).
315
  static size_t const generalPurposeMemoryStart;
316
317
private:
318
  /// Appends code that cleans higher-order bits for integer types.
319
  void cleanHigherOrderBits(IntegerType const& _typeOnStack);
320
321
  /// Prepares the given type for storing in memory by shifting it if necessary.
322
  /// @param _cleanup if true, also cleanup the value when preparing to store it in memory
323
  unsigned prepareMemoryStore(Type const& _type, bool _padToWords, bool _cleanup = true);
324
  /// Loads type from memory assuming memory offset is on stack top.
325
  unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords);
326
327
  CompilerContext& m_context;
328
};
329
330
331
template <class T>
332
unsigned CompilerUtils::sizeOnStack(std::vector<T> const& _variables)
333
112k
{
334
112k
  unsigned size = 0;
335
112k
  for (T const& variable: _variables)
336
167k
    size += variable->annotation().type->sizeOnStack();
337
112k
  return size;
338
112k
}
339
340
}