Coverage Report

Created: 2022-08-24 06:55

/src/solidity/libsolidity/codegen/ArrayUtils.cpp
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 2015
21
 * Code generation utils that handle arrays.
22
 */
23
24
#include <libsolidity/codegen/ArrayUtils.h>
25
26
#include <libsolidity/ast/Types.h>
27
#include <libsolidity/ast/TypeProvider.h>
28
#include <libsolidity/codegen/CompilerContext.h>
29
#include <libsolidity/codegen/CompilerUtils.h>
30
#include <libsolidity/codegen/LValue.h>
31
32
#include <libsolutil/FunctionSelector.h>
33
#include <libsolutil/Whiskers.h>
34
#include <libsolutil/StackTooDeepString.h>
35
36
#include <libevmasm/Instruction.h>
37
#include <liblangutil/Exceptions.h>
38
39
using namespace std;
40
using namespace solidity;
41
using namespace solidity::evmasm;
42
using namespace solidity::frontend;
43
using namespace solidity::langutil;
44
45
void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const
46
59.3k
{
47
  // this copies source to target and also clears target if it was larger
48
  // need to leave "target_ref target_byte_off" on the stack at the end
49
50
  // stack layout: [source_ref] [source length] target_ref (top)
51
59.3k
  solAssert(_targetType.location() == DataLocation::Storage, "");
52
53
59.3k
  Type const* targetBaseType = _targetType.baseType();
54
59.3k
  Type const* sourceBaseType = _sourceType.baseType();
55
56
  // TODO unroll loop for small sizes
57
58
59.3k
  bool sourceIsStorage = _sourceType.location() == DataLocation::Storage;
59
59.3k
  bool fromCalldata = _sourceType.location() == DataLocation::CallData;
60
59.3k
  bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
61
59.3k
  bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->storageBytes() <= 16;
62
59.3k
  bool haveByteOffsetTarget = !directCopy && targetBaseType->storageBytes() <= 16;
63
59.3k
  unsigned byteOffsetSize = (haveByteOffsetSource ? 1u : 0u) + (haveByteOffsetTarget ? 1u : 0u);
64
65
  // stack: source_ref [source_length] target_ref
66
  // store target_ref
67
118k
  for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i)
68
59.3k
    m_context << swapInstruction(i);
69
  // stack: target_ref source_ref [source_length]
70
71
59.3k
  if (_targetType.isByteArrayOrString())
72
59.0k
  {
73
    // stack: target_ref source_ref [source_length]
74
59.0k
    if (fromCalldata && _sourceType.isDynamicallySized())
75
56
    {
76
      // stack: target_ref source_ref source_length
77
56
      m_context << Instruction::SWAP1;
78
      // stack: target_ref source_length source_ref
79
56
      m_context << Instruction::DUP3;
80
      // stack: target_ref source_length source_ref target_ref
81
56
      m_context.callYulFunction(
82
56
        m_context.utilFunctions().copyByteArrayToStorageFunction(_sourceType, _targetType),
83
56
        3,
84
56
        0
85
56
      );
86
56
    }
87
58.9k
    else
88
58.9k
    {
89
      // stack: target_ref source_ref
90
58.9k
      m_context << Instruction::DUP2;
91
      // stack: target_ref source_ref target_ref
92
58.9k
      m_context.callYulFunction(
93
58.9k
        m_context.utilFunctions().copyByteArrayToStorageFunction(_sourceType, _targetType),
94
58.9k
        2,
95
58.9k
        0
96
58.9k
      );
97
58.9k
    }
98
    // stack: target_ref
99
59.0k
    return;
100
59.0k
  }
101
102
  // retrieve source length
103
285
  if (_sourceType.location() != DataLocation::CallData || !_sourceType.isDynamicallySized())
104
263
    retrieveLength(_sourceType); // otherwise, length is already there
105
285
  if (_sourceType.location() == DataLocation::Memory && _sourceType.isDynamicallySized())
106
82
  {
107
    // increment source pointer to point to data
108
82
    m_context << Instruction::SWAP1 << u256(0x20);
109
82
    m_context << Instruction::ADD << Instruction::SWAP1;
110
82
  }
111
112
  // stack: target_ref source_ref source_length
113
285
  Type const* targetType = &_targetType;
114
285
  Type const* sourceType = &_sourceType;
115
285
  m_context.callLowLevelFunction(
116
285
    "$copyArrayToStorage_" + sourceType->identifier() + "_to_" + targetType->identifier(),
117
285
    3,
118
285
    1,
119
285
    [=](CompilerContext& _context)
120
285
    {
121
259
      ArrayUtils utils(_context);
122
259
      ArrayType const& _sourceType = dynamic_cast<ArrayType const&>(*sourceType);
123
259
      ArrayType const& _targetType = dynamic_cast<ArrayType const&>(*targetType);
124
      // stack: target_ref source_ref source_length
125
259
      _context << Instruction::DUP3;
126
      // stack: target_ref source_ref source_length target_ref
127
259
      utils.retrieveLength(_targetType);
128
      // stack: target_ref source_ref source_length target_ref target_length
129
259
      if (_targetType.isDynamicallySized())
130
216
      {
131
        // store new target length
132
216
        solAssert(!_targetType.isByteArrayOrString());
133
216
        _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE;
134
216
      }
135
259
      if (sourceBaseType->category() == Type::Category::Mapping)
136
0
      {
137
0
        solAssert(targetBaseType->category() == Type::Category::Mapping, "");
138
0
        solAssert(_sourceType.location() == DataLocation::Storage, "");
139
        // nothing to copy
140
0
        _context
141
0
          << Instruction::POP << Instruction::POP
142
0
          << Instruction::POP << Instruction::POP;
143
0
        return;
144
0
      }
145
      // stack: target_ref source_ref source_length target_ref target_length
146
      // compute hashes (data positions)
147
259
      _context << Instruction::SWAP1;
148
259
      if (_targetType.isDynamicallySized())
149
216
        CompilerUtils(_context).computeHashStatic();
150
      // stack: target_ref source_ref source_length target_length target_data_pos
151
259
      _context << Instruction::SWAP1;
152
259
      utils.convertLengthToSize(_targetType);
153
259
      _context << Instruction::DUP2 << Instruction::ADD;
154
      // stack: target_ref source_ref source_length target_data_pos target_data_end
155
259
      _context << Instruction::SWAP3;
156
      // stack: target_ref target_data_end source_length target_data_pos source_ref
157
158
259
      evmasm::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag();
159
259
      solAssert(!_targetType.isByteArrayOrString());
160
      // skip copying if source length is zero
161
259
      _context << Instruction::DUP3 << Instruction::ISZERO;
162
259
      _context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
163
164
259
      if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized())
165
76
        CompilerUtils(_context).computeHashStatic();
166
      // stack: target_ref target_data_end source_length target_data_pos source_data_pos
167
259
      _context << Instruction::SWAP2;
168
259
      utils.convertLengthToSize(_sourceType);
169
259
      _context << Instruction::DUP3 << Instruction::ADD;
170
      // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
171
259
      if (haveByteOffsetTarget)
172
60
        _context << u256(0);
173
259
      if (haveByteOffsetSource)
174
28
        _context << u256(0);
175
      // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
176
259
      evmasm::AssemblyItem copyLoopStart = _context.newTag();
177
259
      _context << copyLoopStart;
178
      // check for loop condition
179
259
      _context
180
259
        << dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize)
181
259
        << Instruction::GT << Instruction::ISZERO;
182
259
      evmasm::AssemblyItem copyLoopEnd = _context.appendConditionalJump();
183
      // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
184
      // copy
185
259
      if (sourceBaseType->category() == Type::Category::Array)
186
24
      {
187
24
        solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
188
24
        auto const& sourceBaseArrayType = dynamic_cast<ArrayType const&>(*sourceBaseType);
189
190
24
        solUnimplementedAssert(
191
22
          _sourceType.location() != DataLocation::CallData ||
192
22
          !_sourceType.isDynamicallyEncoded() ||
193
22
          !sourceBaseArrayType.isDynamicallySized(),
194
22
          "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator."
195
22
        );
196
22
        _context << Instruction::DUP3;
197
22
        if (sourceBaseArrayType.location() == DataLocation::Memory)
198
11
          _context << Instruction::MLOAD;
199
22
        _context << Instruction::DUP3;
200
22
        utils.copyArrayToStorage(dynamic_cast<ArrayType const&>(*targetBaseType), sourceBaseArrayType);
201
22
        _context << Instruction::POP;
202
22
      }
203
235
      else if (directCopy)
204
64
      {
205
64
        solAssert(byteOffsetSize == 0, "Byte offset for direct copy.");
206
64
        _context
207
64
          << Instruction::DUP3 << Instruction::SLOAD
208
64
          << Instruction::DUP3 << Instruction::SSTORE;
209
64
      }
210
171
      else
211
171
      {
212
        // Note that we have to copy each element on its own in case conversion is involved.
213
        // We might copy too much if there is padding at the last element, but this way end
214
        // checking is easier.
215
        // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
216
171
        _context << dupInstruction(3 + byteOffsetSize);
217
171
        if (_sourceType.location() == DataLocation::Storage)
218
34
        {
219
34
          if (haveByteOffsetSource)
220
28
            _context << Instruction::DUP2;
221
6
          else
222
6
            _context << u256(0);
223
34
          StorageItem(_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
224
34
        }
225
137
        else if (sourceBaseType->isValueType())
226
129
          CompilerUtils(_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false);
227
8
        else
228
137
          solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported.");
229
        // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
230
163
        assertThrow(
231
163
          2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16,
232
163
          StackTooDeepError,
233
163
          util::stackTooDeepString
234
163
        );
235
        // fetch target storage reference
236
163
        _context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack());
237
163
        if (haveByteOffsetTarget)
238
60
          _context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack());
239
103
        else
240
103
          _context << u256(0);
241
163
        StorageItem(_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
242
163
      }
243
      // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
244
      // increment source
245
249
      if (haveByteOffsetSource)
246
28
        utils.incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
247
221
      else
248
221
      {
249
221
        _context << swapInstruction(2 + byteOffsetSize);
250
221
        if (sourceIsStorage)
251
78
          _context << sourceBaseType->storageSize();
252
143
        else if (_sourceType.location() == DataLocation::Memory)
253
124
          _context << sourceBaseType->memoryHeadSize();
254
19
        else
255
19
          _context << sourceBaseType->calldataHeadSize();
256
221
        _context
257
221
          << Instruction::ADD
258
221
          << swapInstruction(2 + byteOffsetSize);
259
221
      }
260
      // increment target
261
249
      if (haveByteOffsetTarget)
262
60
        utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
263
189
      else
264
189
        _context
265
189
          << swapInstruction(1 + byteOffsetSize)
266
189
          << targetBaseType->storageSize()
267
189
          << Instruction::ADD
268
189
          << swapInstruction(1 + byteOffsetSize);
269
249
      _context.appendJumpTo(copyLoopStart);
270
249
      _context << copyLoopEnd;
271
249
      if (haveByteOffsetTarget)
272
60
      {
273
        // clear elements that might be left over in the current slot in target
274
        // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset]
275
60
        _context << dupInstruction(byteOffsetSize) << Instruction::ISZERO;
276
60
        evmasm::AssemblyItem copyCleanupLoopEnd = _context.appendConditionalJump();
277
60
        _context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize);
278
60
        StorageItem(_context, *targetBaseType).setToZero(SourceLocation(), true);
279
60
        utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
280
60
        _context.appendJumpTo(copyLoopEnd);
281
282
60
        _context << copyCleanupLoopEnd;
283
60
        _context << Instruction::POP; // might pop the source, but then target is popped next
284
60
      }
285
249
      if (haveByteOffsetSource)
286
28
        _context << Instruction::POP;
287
249
      _context << copyLoopEndWithoutByteOffset;
288
289
      // zero-out leftovers in target
290
      // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end
291
249
      _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP;
292
      // stack: target_ref target_data_end target_data_pos_updated
293
249
      if (targetBaseType->storageBytes() < 32)
294
113
        utils.clearStorageLoop(TypeProvider::uint256());
295
136
      else
296
136
        utils.clearStorageLoop(targetBaseType);
297
249
      _context << Instruction::POP;
298
249
    }
299
285
  );
300
285
}
301
302
void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const
303
27.1k
{
304
27.1k
  solUnimplementedAssert(
305
27.1k
    !_sourceType.baseType()->isDynamicallySized(),
306
27.1k
    "Nested dynamic arrays not implemented here."
307
27.1k
  );
308
27.1k
  CompilerUtils utils(m_context);
309
310
27.1k
  if (_sourceType.location() == DataLocation::CallData)
311
24.8k
  {
312
24.8k
    if (!_sourceType.isDynamicallySized())
313
9
      m_context << _sourceType.length();
314
24.8k
    if (!_sourceType.isByteArrayOrString())
315
11
      convertLengthToSize(_sourceType);
316
317
24.8k
    string routine = "calldatacopy(target, source, len)\n";
318
24.8k
    if (_padToWordBoundaries)
319
24.8k
      routine += R"(
320
24.8k
        // Set padding suffix to zero
321
24.8k
        mstore(add(target, len), 0)
322
24.8k
        len := and(add(len, 0x1f), not(0x1f))
323
24.8k
      )";
324
24.8k
    routine += "target := add(target, len)\n";
325
24.8k
    m_context.appendInlineAssembly("{" + routine + "}", {"target", "source", "len"});
326
24.8k
    m_context << Instruction::POP << Instruction::POP;
327
24.8k
  }
328
2.27k
  else if (_sourceType.location() == DataLocation::Memory)
329
428
  {
330
428
    retrieveLength(_sourceType);
331
    // stack: target source length
332
428
    if (!_sourceType.baseType()->isValueType())
333
0
    {
334
      // copy using a loop
335
0
      m_context << u256(0) << Instruction::SWAP3;
336
      // stack: counter source length target
337
0
      auto repeat = m_context.newTag();
338
0
      m_context << repeat;
339
0
      m_context << Instruction::DUP2 << Instruction::DUP5;
340
0
      m_context << Instruction::LT << Instruction::ISZERO;
341
0
      auto loopEnd = m_context.appendConditionalJump();
342
0
      m_context << Instruction::DUP3 << Instruction::DUP5;
343
0
      accessIndex(_sourceType, false);
344
0
      MemoryItem(m_context, *_sourceType.baseType(), true).retrieveValue(SourceLocation(), true);
345
0
      if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType()))
346
0
        copyArrayToMemory(*baseArray, _padToWordBoundaries);
347
0
      else
348
0
        utils.storeInMemoryDynamic(*_sourceType.baseType());
349
0
      m_context << Instruction::SWAP3 << u256(1) << Instruction::ADD;
350
0
      m_context << Instruction::SWAP3;
351
0
      m_context.appendJumpTo(repeat);
352
0
      m_context << loopEnd;
353
0
      m_context << Instruction::SWAP3;
354
0
      utils.popStackSlots(3);
355
      // stack: updated_target_pos
356
0
      return;
357
0
    }
358
359
    // memcpy using the built-in contract
360
428
    if (_sourceType.isDynamicallySized())
361
428
    {
362
      // change pointer to data part
363
428
      m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
364
428
      m_context << Instruction::SWAP1;
365
428
    }
366
428
    if (!_sourceType.isByteArrayOrString())
367
0
      convertLengthToSize(_sourceType);
368
    // stack: <target> <source> <size>
369
428
    m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4;
370
    // We can resort to copying full 32 bytes only if
371
    // - the length is known to be a multiple of 32 or
372
    // - we will pad to full 32 bytes later anyway.
373
428
    if (!_sourceType.isByteArrayOrString() || _padToWordBoundaries)
374
428
      utils.memoryCopy32();
375
0
    else
376
0
      utils.memoryCopy();
377
378
428
    m_context << Instruction::SWAP1 << Instruction::POP;
379
    // stack: <target> <size>
380
381
428
    bool paddingNeeded = _padToWordBoundaries && _sourceType.isByteArrayOrString();
382
383
428
    if (paddingNeeded)
384
428
    {
385
      // stack: <target> <size>
386
428
      m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD;
387
      // stack: <length> <target + size>
388
428
      m_context << Instruction::SWAP1 << u256(31) << Instruction::AND;
389
      // stack: <target + size> <remainder = size % 32>
390
428
      evmasm::AssemblyItem skip = m_context.newTag();
391
428
      if (_sourceType.isDynamicallySized())
392
428
      {
393
428
        m_context << Instruction::DUP1 << Instruction::ISZERO;
394
428
        m_context.appendConditionalJumpTo(skip);
395
428
      }
396
      // round off, load from there.
397
      // stack <target + size> <remainder = size % 32>
398
428
      m_context << Instruction::DUP1 << Instruction::DUP3;
399
428
      m_context << Instruction::SUB;
400
      // stack: target+size remainder <target + size - remainder>
401
428
      m_context << Instruction::DUP1 << Instruction::MLOAD;
402
      // Now we AND it with ~(2**(8 * (32 - remainder)) - 1)
403
428
      m_context << u256(1);
404
428
      m_context << Instruction::DUP4 << u256(32) << Instruction::SUB;
405
      // stack: ...<v> 1 <32 - remainder>
406
428
      m_context << u256(0x100) << Instruction::EXP << Instruction::SUB;
407
428
      m_context << Instruction::NOT << Instruction::AND;
408
      // stack: target+size remainder target+size-remainder <v & ...>
409
428
      m_context << Instruction::DUP2 << Instruction::MSTORE;
410
      // stack: target+size remainder target+size-remainder
411
428
      m_context << u256(32) << Instruction::ADD;
412
      // stack: target+size remainder <new_padded_end>
413
428
      m_context << Instruction::SWAP2 << Instruction::POP;
414
415
428
      if (_sourceType.isDynamicallySized())
416
428
        m_context << skip.tag();
417
      // stack <target + "size"> <remainder = size % 32>
418
428
      m_context << Instruction::POP;
419
428
    }
420
0
    else
421
      // stack: <target> <size>
422
0
      m_context << Instruction::ADD;
423
428
  }
424
1.85k
  else
425
1.85k
  {
426
1.85k
    solAssert(_sourceType.location() == DataLocation::Storage, "");
427
1.85k
    unsigned storageBytes = _sourceType.baseType()->storageBytes();
428
1.85k
    u256 storageSize = _sourceType.baseType()->storageSize();
429
1.85k
    solAssert(storageSize > 1 || (storageSize == 1 && storageBytes > 0), "");
430
431
1.85k
    retrieveLength(_sourceType);
432
    // stack here: memory_offset storage_offset length
433
    // jump to end if length is zero
434
1.85k
    m_context << Instruction::DUP1 << Instruction::ISZERO;
435
1.85k
    evmasm::AssemblyItem loopEnd = m_context.appendConditionalJump();
436
    // Special case for tightly-stored byte arrays
437
1.85k
    if (_sourceType.isByteArrayOrString())
438
1.15k
    {
439
      // stack here: memory_offset storage_offset length
440
1.15k
      m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
441
1.15k
      evmasm::AssemblyItem longByteArray = m_context.appendConditionalJump();
442
      // store the short byte array (discard lower-order byte)
443
1.15k
      m_context << u256(0x100) << Instruction::DUP1;
444
1.15k
      m_context << Instruction::DUP4 << Instruction::SLOAD;
445
1.15k
      m_context << Instruction::DIV << Instruction::MUL;
446
1.15k
      m_context << Instruction::DUP4 << Instruction::MSTORE;
447
      // stack here: memory_offset storage_offset length
448
      // add 32 or length to memory offset
449
1.15k
      m_context << Instruction::SWAP2;
450
1.15k
      if (_padToWordBoundaries)
451
1.15k
        m_context << u256(32);
452
0
      else
453
0
        m_context << Instruction::DUP3;
454
1.15k
      m_context << Instruction::ADD;
455
1.15k
      m_context << Instruction::SWAP2;
456
1.15k
      m_context.appendJumpTo(loopEnd);
457
1.15k
      m_context << longByteArray;
458
1.15k
    }
459
698
    else
460
      // convert length to memory size
461
698
      m_context << _sourceType.baseType()->memoryHeadSize() << Instruction::MUL;
462
463
1.85k
    m_context << Instruction::DUP3 << Instruction::ADD << Instruction::SWAP2;
464
1.85k
    if (_sourceType.isDynamicallySized())
465
1.60k
    {
466
      // actual array data is stored at KECCAK256(storage_offset)
467
1.60k
      m_context << Instruction::SWAP1;
468
1.60k
      utils.computeHashStatic();
469
1.60k
      m_context << Instruction::SWAP1;
470
1.60k
    }
471
472
    // stack here: memory_end_offset storage_data_offset memory_offset
473
1.85k
    bool haveByteOffset = !_sourceType.isByteArrayOrString() && storageBytes <= 16;
474
1.85k
    if (haveByteOffset)
475
403
      m_context << u256(0) << Instruction::SWAP1;
476
    // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
477
1.85k
    evmasm::AssemblyItem loopStart = m_context.newTag();
478
1.85k
    m_context << loopStart;
479
    // load and store
480
1.85k
    if (_sourceType.isByteArrayOrString())
481
1.15k
    {
482
      // Packed both in storage and memory.
483
1.15k
      m_context << Instruction::DUP2 << Instruction::SLOAD;
484
1.15k
      m_context << Instruction::DUP2 << Instruction::MSTORE;
485
      // increment storage_data_offset by 1
486
1.15k
      m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD;
487
      // increment memory offset by 32
488
1.15k
      m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
489
1.15k
    }
490
698
    else
491
698
    {
492
      // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
493
698
      if (haveByteOffset)
494
403
        m_context << Instruction::DUP3 << Instruction::DUP3;
495
295
      else
496
295
        m_context << Instruction::DUP2 << u256(0);
497
698
      StorageItem(m_context, *_sourceType.baseType()).retrieveValue(SourceLocation(), true);
498
698
      if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType()))
499
0
        copyArrayToMemory(*baseArray, _padToWordBoundaries);
500
698
      else
501
698
        utils.storeInMemoryDynamic(*_sourceType.baseType());
502
      // increment storage_data_offset and byte offset
503
698
      if (haveByteOffset)
504
403
        incrementByteOffset(storageBytes, 2, 3);
505
295
      else
506
295
      {
507
295
        m_context << Instruction::SWAP1;
508
295
        m_context << storageSize << Instruction::ADD;
509
295
        m_context << Instruction::SWAP1;
510
295
      }
511
698
    }
512
    // check for loop condition
513
1.85k
    m_context << Instruction::DUP1 << dupInstruction(haveByteOffset ? 5 : 4);
514
1.85k
    m_context << Instruction::GT;
515
1.85k
    m_context.appendConditionalJumpTo(loopStart);
516
    // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
517
1.85k
    if (haveByteOffset)
518
403
      m_context << Instruction::SWAP1 << Instruction::POP;
519
1.85k
    if (!_sourceType.isByteArrayOrString())
520
698
    {
521
698
      solAssert(_sourceType.calldataStride() % 32 == 0, "");
522
698
      solAssert(_sourceType.memoryStride() % 32 == 0, "");
523
698
    }
524
1.85k
    if (_padToWordBoundaries && _sourceType.isByteArrayOrString())
525
1.15k
    {
526
      // memory_end_offset - start is the actual length (we want to compute the ceil of).
527
      // memory_offset - start is its next multiple of 32, but it might be off by 32.
528
      // so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31
529
1.15k
      m_context << Instruction::DUP3 << Instruction::SWAP1 << Instruction::SUB;
530
1.15k
      m_context << u256(31) << Instruction::AND;
531
1.15k
      m_context << Instruction::DUP3 << Instruction::ADD;
532
1.15k
      m_context << Instruction::SWAP2;
533
1.15k
    }
534
1.85k
    m_context << loopEnd << Instruction::POP << Instruction::POP;
535
1.85k
  }
536
27.1k
}
537
538
void ArrayUtils::clearArray(ArrayType const& _typeIn) const
539
123
{
540
123
  Type const* type = &_typeIn;
541
123
  m_context.callLowLevelFunction(
542
123
    "$clearArray_" + _typeIn.identifier(),
543
123
    2,
544
123
    0,
545
123
    [type](CompilerContext& _context)
546
123
    {
547
98
      ArrayType const& _type = dynamic_cast<ArrayType const&>(*type);
548
98
      unsigned stackHeightStart = _context.stackHeight();
549
98
      solAssert(_type.location() == DataLocation::Storage, "");
550
98
      if (_type.baseType()->storageBytes() < 32)
551
26
      {
552
26
        solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
553
26
        solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type.");
554
26
      }
555
98
      if (_type.baseType()->isValueType())
556
98
        solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
557
558
98
      _context << Instruction::POP; // remove byte offset
559
98
      if (_type.isDynamicallySized())
560
85
        ArrayUtils(_context).clearDynamicArray(_type);
561
13
      else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping)
562
0
        _context << Instruction::POP;
563
13
      else if (_type.baseType()->isValueType() && _type.storageSize() <= 5)
564
10
      {
565
        // unroll loop for small arrays @todo choose a good value
566
        // Note that we loop over storage slots here, not elements.
567
28
        for (unsigned i = 1; i < _type.storageSize(); ++i)
568
18
          _context
569
18
            << u256(0) << Instruction::DUP2 << Instruction::SSTORE
570
18
            << u256(1) << Instruction::ADD;
571
10
        _context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE;
572
10
      }
573
3
      else if (!_type.baseType()->isValueType() && _type.length() <= 4)
574
0
      {
575
        // unroll loop for small arrays @todo choose a good value
576
0
        solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size.");
577
0
        for (unsigned i = 1; i < _type.length(); ++i)
578
0
        {
579
0
          _context << u256(0);
580
0
          StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), false);
581
0
          _context
582
0
            << Instruction::POP
583
0
            << u256(_type.baseType()->storageSize()) << Instruction::ADD;
584
0
        }
585
0
        _context << u256(0);
586
0
        StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), true);
587
0
      }
588
3
      else
589
3
      {
590
3
        _context << Instruction::DUP1 << _type.length();
591
3
        ArrayUtils(_context).convertLengthToSize(_type);
592
3
        _context << Instruction::ADD << Instruction::SWAP1;
593
3
        if (_type.baseType()->storageBytes() < 32)
594
0
          ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256());
595
3
        else
596
3
          ArrayUtils(_context).clearStorageLoop(_type.baseType());
597
3
        _context << Instruction::POP;
598
3
      }
599
98
      solAssert(_context.stackHeight() == stackHeightStart - 2, "");
600
98
    }
601
123
  );
602
123
}
603
604
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
605
85
{
606
85
  solAssert(_type.location() == DataLocation::Storage, "");
607
85
  solAssert(_type.isDynamicallySized(), "");
608
609
  // fetch length
610
85
  retrieveLength(_type);
611
  // set length to zero
612
85
  m_context << u256(0) << Instruction::DUP3 << Instruction::SSTORE;
613
  // Special case: short byte arrays are stored togeher with their length
614
85
  evmasm::AssemblyItem endTag = m_context.newTag();
615
85
  if (_type.isByteArrayOrString())
616
13
  {
617
    // stack: ref old_length
618
13
    m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
619
13
    evmasm::AssemblyItem longByteArray = m_context.appendConditionalJump();
620
13
    m_context << Instruction::POP;
621
13
    m_context.appendJumpTo(endTag);
622
13
    m_context.adjustStackOffset(1); // needed because of jump
623
13
    m_context << longByteArray;
624
13
  }
625
  // stack: ref old_length
626
85
  convertLengthToSize(_type);
627
  // compute data positions
628
85
  m_context << Instruction::SWAP1;
629
85
  CompilerUtils(m_context).computeHashStatic();
630
  // stack: len data_pos
631
85
  m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD
632
85
    << Instruction::SWAP1;
633
  // stack: data_pos_end data_pos
634
85
  if (_type.storageStride() < 32)
635
22
    clearStorageLoop(TypeProvider::uint256());
636
63
  else
637
63
    clearStorageLoop(_type.baseType());
638
  // cleanup
639
85
  m_context << endTag;
640
85
  m_context << Instruction::POP;
641
85
}
642
643
void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
644
0
{
645
0
  Type const* type = &_typeIn;
646
0
  m_context.callLowLevelFunction(
647
0
    "$resizeDynamicArray_" + _typeIn.identifier(),
648
0
    2,
649
0
    0,
650
0
    [type](CompilerContext& _context)
651
0
    {
652
0
      ArrayType const& _type = dynamic_cast<ArrayType const&>(*type);
653
0
      solAssert(_type.location() == DataLocation::Storage, "");
654
0
      solAssert(_type.isDynamicallySized(), "");
655
0
      if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32)
656
0
        solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
657
658
0
      unsigned stackHeightStart = _context.stackHeight();
659
0
      evmasm::AssemblyItem resizeEnd = _context.newTag();
660
661
      // stack: ref new_length
662
      // fetch old length
663
0
      ArrayUtils(_context).retrieveLength(_type, 1);
664
      // stack: ref new_length old_length
665
0
      solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2");
666
667
      // Special case for short byte arrays, they are stored together with their length
668
0
      if (_type.isByteArrayOrString())
669
0
      {
670
0
        evmasm::AssemblyItem regularPath = _context.newTag();
671
        // We start by a large case-distinction about the old and new length of the byte array.
672
673
0
        _context << Instruction::DUP3 << Instruction::SLOAD;
674
        // stack: ref new_length current_length ref_value
675
676
0
        solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
677
0
        _context << Instruction::DUP2 << u256(31) << Instruction::LT;
678
0
        evmasm::AssemblyItem currentIsLong = _context.appendConditionalJump();
679
0
        _context << Instruction::DUP3 << u256(31) << Instruction::LT;
680
0
        evmasm::AssemblyItem newIsLong = _context.appendConditionalJump();
681
682
        // Here: short -> short
683
684
        // Compute 1 << (256 - 8 * new_size)
685
0
        evmasm::AssemblyItem shortToShort = _context.newTag();
686
0
        _context << shortToShort;
687
0
        _context << Instruction::DUP3 << u256(8) << Instruction::MUL;
688
0
        _context << u256(0x100) << Instruction::SUB;
689
0
        _context << u256(2) << Instruction::EXP;
690
        // Divide and multiply by that value, clearing bits.
691
0
        _context << Instruction::DUP1 << Instruction::SWAP2;
692
0
        _context << Instruction::DIV << Instruction::MUL;
693
        // Insert 2*length.
694
0
        _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD;
695
0
        _context << Instruction::OR;
696
        // Store.
697
0
        _context << Instruction::DUP4 << Instruction::SSTORE;
698
0
        solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3");
699
0
        _context.appendJumpTo(resizeEnd);
700
701
0
        _context.adjustStackOffset(1); // we have to do that because of the jumps
702
        // Here: short -> long
703
704
0
        _context << newIsLong;
705
        // stack: ref new_length current_length ref_value
706
0
        solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
707
        // Zero out lower-order byte.
708
0
        _context << u256(0xff) << Instruction::NOT << Instruction::AND;
709
        // Store at data location.
710
0
        _context << Instruction::DUP4;
711
0
        CompilerUtils(_context).computeHashStatic();
712
0
        _context << Instruction::SSTORE;
713
        // stack: ref new_length current_length
714
        // Store new length: Compule 2*length + 1 and store it.
715
0
        _context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD;
716
0
        _context << u256(1) << Instruction::ADD;
717
        // stack: ref new_length current_length 2*new_length+1
718
0
        _context << Instruction::DUP4 << Instruction::SSTORE;
719
0
        solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3");
720
0
        _context.appendJumpTo(resizeEnd);
721
722
0
        _context.adjustStackOffset(1); // we have to do that because of the jumps
723
724
0
        _context << currentIsLong;
725
0
        _context << Instruction::DUP3 << u256(31) << Instruction::LT;
726
0
        _context.appendConditionalJumpTo(regularPath);
727
728
        // Here: long -> short
729
        // Read the first word of the data and store it on the stack. Clear the data location and
730
        // then jump to the short -> short case.
731
732
        // stack: ref new_length current_length ref_value
733
0
        solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
734
0
        _context << Instruction::POP << Instruction::DUP3;
735
0
        CompilerUtils(_context).computeHashStatic();
736
0
        _context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1;
737
        // stack: ref new_length current_length first_word data_location
738
0
        _context << Instruction::DUP3;
739
0
        ArrayUtils(_context).convertLengthToSize(_type);
740
0
        _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1;
741
        // stack: ref new_length current_length first_word data_location_end data_location
742
0
        ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256());
743
0
        _context << Instruction::POP;
744
        // stack: ref new_length current_length first_word
745
0
        solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
746
0
        _context.appendJumpTo(shortToShort);
747
748
0
        _context << regularPath;
749
        // stack: ref new_length current_length ref_value
750
0
        _context << Instruction::POP;
751
0
      }
752
753
      // Change of length for a regular array (i.e. length at location, data at KECCAK256(location)).
754
      // stack: ref new_length old_length
755
      // store new length
756
0
      _context << Instruction::DUP2;
757
0
      if (_type.isByteArrayOrString())
758
        // For a "long" byte array, store length as 2*length+1
759
0
        _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD;
760
0
      _context << Instruction::DUP4 << Instruction::SSTORE;
761
      // skip if size is not reduced
762
0
      _context << Instruction::DUP2 << Instruction::DUP2
763
0
        << Instruction::GT << Instruction::ISZERO;
764
0
      _context.appendConditionalJumpTo(resizeEnd);
765
766
      // size reduced, clear the end of the array
767
      // stack: ref new_length old_length
768
0
      ArrayUtils(_context).convertLengthToSize(_type);
769
0
      _context << Instruction::DUP2;
770
0
      ArrayUtils(_context).convertLengthToSize(_type);
771
      // stack: ref new_length old_size new_size
772
      // compute data positions
773
0
      _context << Instruction::DUP4;
774
0
      CompilerUtils(_context).computeHashStatic();
775
      // stack: ref new_length old_size new_size data_pos
776
0
      _context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD;
777
      // stack: ref new_length data_pos new_size delete_end
778
0
      _context << Instruction::SWAP2 << Instruction::ADD;
779
      // stack: ref new_length delete_end delete_start
780
0
      if (_type.storageStride() < 32)
781
0
        ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256());
782
0
      else
783
0
        ArrayUtils(_context).clearStorageLoop(_type.baseType());
784
785
0
      _context << resizeEnd;
786
      // cleanup
787
0
      _context << Instruction::POP << Instruction::POP << Instruction::POP;
788
0
      solAssert(_context.stackHeight() == stackHeightStart - 2, "");
789
0
    }
790
0
  );
791
0
}
792
793
void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const
794
74.3k
{
795
74.3k
  solAssert(_type.location() == DataLocation::Storage, "");
796
74.3k
  solAssert(_type.isDynamicallySized(), "");
797
74.3k
  if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32)
798
74.3k
    solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
799
800
74.3k
  if (_type.isByteArrayOrString())
801
105
  {
802
    // We almost always just add 2 (length of byte arrays is shifted left by one)
803
    // except for the case where we transition from a short byte array
804
    // to a long byte array, there we have to copy.
805
    // This happens if the length is exactly 31, which means that the
806
    // lowest-order byte (we actually use a mask with fewer bits) must
807
    // be (31*2+0) = 62
808
809
105
    m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1;
810
105
    m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
811
105
    m_context.appendInlineAssembly(R"({
812
105
      // We have to copy if length is exactly 31, because that marks
813
105
      // the transition between in-place and out-of-place storage.
814
105
      switch length
815
105
      case 31
816
105
      {
817
105
        mstore(0, ref)
818
105
        let data_area := keccak256(0, 0x20)
819
105
        sstore(data_area, and(data, not(0xff)))
820
105
        // Set old length in new format (31 * 2 + 1)
821
105
        data := 63
822
105
      }
823
105
      sstore(ref, add(data, 2))
824
105
      // return new length in ref
825
105
      ref := add(length, 1)
826
105
    })", {"ref", "data", "length"});
827
105
    m_context << Instruction::POP << Instruction::POP;
828
105
  }
829
74.2k
  else
830
74.2k
    m_context.appendInlineAssembly(R"({
831
74.2k
      let new_length := add(sload(ref), 1)
832
74.2k
      sstore(ref, new_length)
833
74.2k
      ref := new_length
834
74.2k
    })", {"ref"});
835
74.3k
}
836
837
void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
838
247
{
839
247
  solAssert(_type.location() == DataLocation::Storage, "");
840
247
  solAssert(_type.isDynamicallySized(), "");
841
247
  if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32)
842
247
    solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
843
844
247
  if (_type.isByteArrayOrString())
845
45
  {
846
45
    m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1;
847
45
    m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
848
45
    util::Whiskers code(R"({
849
45
      if iszero(length) {
850
45
        mstore(0, <panicSelector>)
851
45
        mstore(4, <emptyArrayPop>)
852
45
        revert(0, 0x24)
853
45
      }
854
45
      switch gt(length, 31)
855
45
      case 0 {
856
45
        // short byte array
857
45
        // Zero-out the suffix including the least significant byte.
858
45
        let mask := sub(exp(0x100, sub(33, length)), 1)
859
45
        length := sub(length, 1)
860
45
        slot_value := or(and(not(mask), slot_value), mul(length, 2))
861
45
      }
862
45
      case 1 {
863
45
        // long byte array
864
45
        mstore(0, ref)
865
45
        let slot := keccak256(0, 0x20)
866
45
        switch length
867
45
        case 32
868
45
        {
869
45
          let data := sload(slot)
870
45
          sstore(slot, 0)
871
45
          data := and(data, not(0xff))
872
45
          slot_value := or(data, 62)
873
45
        }
874
45
        default
875
45
        {
876
45
          let offset_inside_slot := and(sub(length, 1), 0x1f)
877
45
          slot := add(slot, div(sub(length, 1), 32))
878
45
          let data := sload(slot)
879
45
880
45
          // Zero-out the suffix of the byte array by masking it.
881
45
          // ((1<<(8 * (32 - offset))) - 1)
882
45
          let mask := sub(exp(0x100, sub(32, offset_inside_slot)), 1)
883
45
          data := and(not(mask), data)
884
45
          sstore(slot, data)
885
45
886
45
          // Reduce the length by 1
887
45
          slot_value := sub(slot_value, 2)
888
45
        }
889
45
      }
890
45
      sstore(ref, slot_value)
891
45
    })");
892
45
    code("panicSelector", util::selectorFromSignature("Panic(uint256)").str());
893
45
    code("emptyArrayPop", to_string(unsigned(util::PanicCode::EmptyArrayPop)));
894
45
    m_context.appendInlineAssembly(code.render(), {"ref", "slot_value", "length"});
895
45
    m_context << Instruction::POP << Instruction::POP << Instruction::POP;
896
45
  }
897
202
  else
898
202
  {
899
    // stack: ArrayReference
900
202
    retrieveLength(_type);
901
    // stack: ArrayReference oldLength
902
202
    m_context << Instruction::DUP1;
903
    // stack: ArrayReference oldLength oldLength
904
202
    m_context << Instruction::ISZERO;
905
202
    m_context.appendConditionalPanic(util::PanicCode::EmptyArrayPop);
906
907
    // Stack: ArrayReference oldLength
908
202
    m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB;
909
    // Stack ArrayReference newLength
910
911
202
    if (_type.baseType()->category() != Type::Category::Mapping)
912
198
    {
913
198
      m_context << Instruction::DUP2 << Instruction::DUP2;
914
      // Stack ArrayReference newLength ArrayReference newLength;
915
198
      accessIndex(_type, false);
916
      // Stack: ArrayReference newLength storage_slot byte_offset
917
198
      StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true);
918
198
    }
919
920
    // Stack: ArrayReference newLength
921
202
    m_context << Instruction::SWAP1 << Instruction::SSTORE;
922
202
  }
923
247
}
924
925
void ArrayUtils::clearStorageLoop(Type const* _type) const
926
337
{
927
337
  solAssert(_type->storageBytes() >= 32, "");
928
337
  m_context.callLowLevelFunction(
929
337
    "$clearStorageLoop_" + _type->identifier(),
930
337
    2,
931
337
    1,
932
337
    [_type](CompilerContext& _context)
933
337
    {
934
304
      unsigned stackHeightStart = _context.stackHeight();
935
304
      if (_type->category() == Type::Category::Mapping)
936
3
      {
937
3
        _context << Instruction::POP;
938
3
        return;
939
3
      }
940
      // stack: end_pos pos
941
942
301
      evmasm::AssemblyItem loopStart = _context.appendJumpToNew();
943
301
      _context << loopStart;
944
      // check for loop condition
945
301
      _context <<
946
301
        Instruction::DUP1 <<
947
301
        Instruction::DUP3 <<
948
301
        Instruction::GT <<
949
301
        Instruction::ISZERO;
950
301
      evmasm::AssemblyItem zeroLoopEnd = _context.newTag();
951
301
      _context.appendConditionalJumpTo(zeroLoopEnd);
952
      // delete
953
301
      _context << u256(0);
954
301
      StorageItem(_context, *_type).setToZero(SourceLocation(), false);
955
301
      _context << Instruction::POP;
956
      // increment
957
301
      _context << _type->storageSize() << Instruction::ADD;
958
301
      _context.appendJumpTo(loopStart);
959
      // cleanup
960
301
      _context << zeroLoopEnd;
961
301
      _context << Instruction::POP;
962
963
301
      solAssert(_context.stackHeight() == stackHeightStart - 1, "");
964
301
    }
965
337
  );
966
337
}
967
968
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
969
29.0k
{
970
29.0k
  if (_arrayType.location() == DataLocation::Storage)
971
453
  {
972
453
    if (_arrayType.baseType()->storageSize() <= 1)
973
416
    {
974
416
      unsigned baseBytes = _arrayType.baseType()->storageBytes();
975
416
      if (baseBytes == 0)
976
0
        m_context << Instruction::POP << u256(1);
977
416
      else if (baseBytes <= 16)
978
178
      {
979
178
        unsigned itemsPerSlot = 32 / baseBytes;
980
178
        m_context
981
178
          << u256(itemsPerSlot - 1) << Instruction::ADD
982
178
          << u256(itemsPerSlot) << Instruction::SWAP1 << Instruction::DIV;
983
178
      }
984
416
    }
985
37
    else
986
37
      m_context << _arrayType.baseType()->storageSize() << Instruction::MUL;
987
453
  }
988
28.6k
  else
989
28.6k
  {
990
28.6k
    if (!_arrayType.isByteArrayOrString())
991
2.66k
    {
992
2.66k
      if (_arrayType.location() == DataLocation::Memory)
993
2.62k
        m_context << _arrayType.memoryStride();
994
36
      else
995
36
        m_context << _arrayType.calldataStride();
996
2.66k
      m_context << Instruction::MUL;
997
2.66k
    }
998
25.9k
    else if (_pad)
999
25.9k
      m_context << u256(31) << Instruction::ADD
1000
25.9k
        << u256(32) << Instruction::DUP1
1001
25.9k
        << Instruction::SWAP2 << Instruction::DIV << Instruction::MUL;
1002
28.6k
  }
1003
29.0k
}
1004
1005
void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDepth) const
1006
5.34M
{
1007
5.34M
  if (!_arrayType.isDynamicallySized())
1008
2.03M
    m_context << _arrayType.length();
1009
3.31M
  else
1010
3.31M
  {
1011
3.31M
    m_context << dupInstruction(1 + _stackDepth);
1012
3.31M
    switch (_arrayType.location())
1013
3.31M
    {
1014
406k
    case DataLocation::CallData:
1015
      // length is stored on the stack
1016
406k
      break;
1017
2.38M
    case DataLocation::Memory:
1018
2.38M
      m_context << Instruction::MLOAD;
1019
2.38M
      break;
1020
519k
    case DataLocation::Storage:
1021
519k
      m_context << Instruction::SLOAD;
1022
519k
      if (_arrayType.isByteArrayOrString())
1023
2.52k
        m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
1024
519k
      break;
1025
3.31M
    }
1026
3.31M
  }
1027
5.34M
}
1028
1029
void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, bool _keepReference) const
1030
5.38M
{
1031
  /// Stack: reference [length] index
1032
5.38M
  DataLocation location = _arrayType.location();
1033
1034
5.38M
  if (_doBoundsCheck)
1035
5.30M
  {
1036
    // retrieve length
1037
5.30M
    ArrayUtils::retrieveLength(_arrayType, 1);
1038
    // Stack: ref [length] index length
1039
    // check out-of-bounds access
1040
5.30M
    m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
1041
    // out-of-bounds access throws exception
1042
5.30M
    m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds);
1043
5.30M
  }
1044
5.38M
  if (location == DataLocation::CallData && _arrayType.isDynamicallySized())
1045
    // remove length if present
1046
382k
    m_context << Instruction::SWAP1 << Instruction::POP;
1047
1048
  // stack: <base_ref> <index>
1049
5.38M
  switch (location)
1050
5.38M
  {
1051
3.69M
  case DataLocation::Memory:
1052
    // stack: <base_ref> <index>
1053
3.69M
    if (!_arrayType.isByteArrayOrString())
1054
3.64M
      m_context << u256(_arrayType.memoryHeadSize()) << Instruction::MUL;
1055
3.69M
    if (_arrayType.isDynamicallySized())
1056
2.38M
      m_context << u256(32) << Instruction::ADD;
1057
3.69M
    if (_keepReference)
1058
0
      m_context << Instruction::DUP2;
1059
3.69M
    m_context << Instruction::ADD;
1060
3.69M
    break;
1061
599k
  case DataLocation::CallData:
1062
599k
    if (!_arrayType.isByteArrayOrString())
1063
599k
    {
1064
599k
      m_context << _arrayType.calldataStride();
1065
599k
      m_context << Instruction::MUL;
1066
599k
    }
1067
    // stack: <base_ref> <index * size>
1068
599k
    if (_keepReference)
1069
444k
      m_context << Instruction::DUP2;
1070
599k
    m_context << Instruction::ADD;
1071
599k
    break;
1072
1.09M
  case DataLocation::Storage:
1073
1.09M
  {
1074
1.09M
    if (_keepReference)
1075
0
      m_context << Instruction::DUP2;
1076
1.09M
    else
1077
1.09M
      m_context << Instruction::SWAP1;
1078
    // stack: [<base_ref>] <index> <base_ref>
1079
1080
1.09M
    evmasm::AssemblyItem endTag = m_context.newTag();
1081
1.09M
    if (_arrayType.isByteArrayOrString())
1082
247
    {
1083
      // Special case of short byte arrays.
1084
247
      m_context << Instruction::SWAP1;
1085
247
      m_context << Instruction::DUP2 << Instruction::SLOAD;
1086
247
      m_context << u256(1) << Instruction::AND << Instruction::ISZERO;
1087
      // No action needed for short byte arrays.
1088
247
      m_context.appendConditionalJumpTo(endTag);
1089
247
      m_context << Instruction::SWAP1;
1090
247
    }
1091
1.09M
    if (_arrayType.isDynamicallySized())
1092
589k
      CompilerUtils(m_context).computeHashStatic();
1093
1.09M
    m_context << Instruction::SWAP1;
1094
1.09M
    if (_arrayType.baseType()->storageBytes() <= 16)
1095
53.6k
    {
1096
      // stack: <data_ref> <index>
1097
      // goal:
1098
      // <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
1099
53.6k
      unsigned byteSize = _arrayType.baseType()->storageBytes();
1100
53.6k
      solAssert(byteSize != 0, "");
1101
53.6k
      unsigned itemsPerSlot = 32 / byteSize;
1102
53.6k
      m_context << u256(itemsPerSlot) << Instruction::SWAP2;
1103
      // stack: itemsPerSlot index data_ref
1104
53.6k
      m_context
1105
53.6k
        << Instruction::DUP3 << Instruction::DUP3
1106
53.6k
        << Instruction::DIV << Instruction::ADD
1107
      // stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
1108
53.6k
        << Instruction::SWAP2 << Instruction::SWAP1
1109
53.6k
        << Instruction::MOD;
1110
53.6k
      if (byteSize != 1)
1111
20.7k
        m_context << u256(byteSize) << Instruction::MUL;
1112
53.6k
    }
1113
1.03M
    else
1114
1.03M
    {
1115
1.03M
      if (_arrayType.baseType()->storageSize() != 1)
1116
579k
        m_context << _arrayType.baseType()->storageSize() << Instruction::MUL;
1117
1.03M
      m_context << Instruction::ADD << u256(0);
1118
1.03M
    }
1119
1.09M
    m_context << endTag;
1120
1.09M
    break;
1121
1.09M
  }
1122
5.38M
  }
1123
5.38M
}
1124
1125
void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _doBoundsCheck) const
1126
599k
{
1127
599k
  solAssert(_arrayType.location() == DataLocation::CallData, "");
1128
599k
  if (_arrayType.baseType()->isDynamicallyEncoded())
1129
444k
  {
1130
    // stack layout: <base_ref> <length> <index>
1131
444k
    ArrayUtils(m_context).accessIndex(_arrayType, _doBoundsCheck, true);
1132
    // stack layout: <base_ref> <ptr_to_tail>
1133
1134
444k
    CompilerUtils(m_context).accessCalldataTail(*_arrayType.baseType());
1135
    // stack layout: <tail_ref> [length]
1136
444k
  }
1137
155k
  else
1138
155k
  {
1139
155k
    ArrayUtils(m_context).accessIndex(_arrayType, _doBoundsCheck);
1140
155k
    if (_arrayType.baseType()->isValueType())
1141
48.2k
    {
1142
48.2k
      solAssert(_arrayType.baseType()->storageBytes() <= 32, "");
1143
48.2k
      if (
1144
48.2k
        !_arrayType.isByteArrayOrString() &&
1145
48.2k
        _arrayType.baseType()->storageBytes() < 32 &&
1146
48.2k
        m_context.useABICoderV2()
1147
48.2k
      )
1148
45.9k
      {
1149
45.9k
        m_context << u256(32);
1150
45.9k
        CompilerUtils(m_context).abiDecodeV2({_arrayType.baseType()}, false);
1151
45.9k
      }
1152
2.26k
      else
1153
2.26k
        CompilerUtils(m_context).loadFromMemoryDynamic(
1154
2.26k
          *_arrayType.baseType(),
1155
2.26k
          true,
1156
2.26k
          !_arrayType.isByteArrayOrString(),
1157
2.26k
          false
1158
2.26k
        );
1159
48.2k
    }
1160
106k
    else
1161
155k
      solAssert(
1162
155k
        _arrayType.baseType()->category() == Type::Category::Struct ||
1163
155k
        _arrayType.baseType()->category() == Type::Category::Array,
1164
155k
        "Invalid statically sized non-value base type on array access."
1165
155k
      );
1166
155k
  }
1167
599k
}
1168
1169
void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const
1170
551
{
1171
551
  solAssert(_byteSize < 32, "");
1172
551
  solAssert(_byteSize != 0, "");
1173
  // We do the following, but avoiding jumps:
1174
  // byteOffset += byteSize
1175
  // if (byteOffset + byteSize > 32)
1176
  // {
1177
  //     storageOffset++;
1178
  //     byteOffset = 0;
1179
  // }
1180
551
  if (_byteOffsetPosition > 1)
1181
441
    m_context << swapInstruction(_byteOffsetPosition - 1);
1182
551
  m_context << u256(_byteSize) << Instruction::ADD;
1183
551
  if (_byteOffsetPosition > 1)
1184
441
    m_context << swapInstruction(_byteOffsetPosition - 1);
1185
  // compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
1186
551
  m_context
1187
551
    << u256(32) << dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1)
1188
551
    << Instruction::ADD << Instruction::DIV;
1189
  // increment storage offset if X == 1 (just add X to it)
1190
  // stack: X
1191
551
  m_context
1192
551
    << swapInstruction(_storageOffsetPosition) << dupInstruction(_storageOffsetPosition + 1)
1193
551
    << Instruction::ADD << swapInstruction(_storageOffsetPosition);
1194
  // stack: X
1195
  // set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
1196
551
  m_context << u256(1) << Instruction::SUB;
1197
  // stack: 1 - X
1198
551
  if (_byteOffsetPosition == 1)
1199
110
    m_context << Instruction::MUL;
1200
441
  else
1201
441
    m_context
1202
441
      << dupInstruction(_byteOffsetPosition + 1) << Instruction::MUL
1203
441
      << swapInstruction(_byteOffsetPosition) << Instruction::POP;
1204
551
}