Coverage Report

Created: 2025-09-08 08:10

/src/solidity/libsolidity/codegen/ABIFunctions.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 <chris@ethereum.org>
20
 * @date 2017
21
 * Routines that generate Yul code related to ABI encoding, decoding and type conversions.
22
 */
23
24
#include <libsolidity/codegen/ABIFunctions.h>
25
26
#include <libsolidity/codegen/CompilerUtils.h>
27
#include <libsolutil/CommonData.h>
28
#include <libsolutil/Whiskers.h>
29
#include <libsolutil/StringUtils.h>
30
31
#include <boost/algorithm/string/join.hpp>
32
33
using namespace solidity;
34
using namespace solidity::util;
35
using namespace solidity::frontend;
36
37
std::string ABIFunctions::tupleEncoder(
38
  TypePointers const& _givenTypes,
39
  TypePointers _targetTypes,
40
  bool _encodeAsLibraryTypes,
41
  bool _reversed
42
)
43
17.3k
{
44
17.3k
  solAssert(_givenTypes.size() == _targetTypes.size(), "");
45
17.3k
  EncodingOptions options;
46
17.3k
  options.encodeAsLibraryTypes = _encodeAsLibraryTypes;
47
17.3k
  options.encodeFunctionFromStack = true;
48
17.3k
  options.padded = true;
49
17.3k
  options.dynamicInplace = false;
50
51
17.3k
  for (Type const*& t: _targetTypes)
52
19.2k
  {
53
19.2k
    solAssert(t, "");
54
19.2k
    t = t->fullEncodingType(options.encodeAsLibraryTypes, true, !options.padded);
55
19.2k
    solAssert(t, "");
56
19.2k
  }
57
58
17.3k
  std::string functionName = std::string("abi_encode_tuple_");
59
17.3k
  for (auto const& t: _givenTypes)
60
19.2k
    functionName += t->identifier() + "_";
61
17.3k
  functionName += "_to_";
62
17.3k
  for (auto const& t: _targetTypes)
63
19.2k
    functionName += t->identifier() + "_";
64
17.3k
  functionName += options.toFunctionNameSuffix();
65
17.3k
  if (_reversed)
66
10.7k
    functionName += "_reversed";
67
68
17.3k
  return createFunction(functionName, [&]() {
69
    // Note that the values are in reverse due to the difference in calling semantics.
70
12.0k
    Whiskers templ(R"(
71
12.0k
      function <functionName>(headStart <valueParams>) -> tail {
72
12.0k
        tail := add(headStart, <headSize>)
73
12.0k
        <encodeElements>
74
12.0k
      }
75
12.0k
    )");
76
12.0k
    templ("functionName", functionName);
77
12.0k
    size_t const headSize_ = headSize(_targetTypes);
78
12.0k
    templ("headSize", std::to_string(headSize_));
79
12.0k
    std::string encodeElements;
80
12.0k
    size_t headPos = 0;
81
12.0k
    size_t stackPos = 0;
82
25.4k
    for (size_t i = 0; i < _givenTypes.size(); ++i)
83
13.3k
    {
84
13.3k
      solAssert(_givenTypes[i], "");
85
13.3k
      solAssert(_targetTypes[i], "");
86
13.3k
      size_t sizeOnStack = _givenTypes[i]->sizeOnStack();
87
13.3k
      bool dynamic = _targetTypes[i]->isDynamicallyEncoded();
88
13.3k
      Whiskers elementTempl(
89
13.3k
        dynamic ?
90
4.09k
        std::string(R"(
91
4.09k
          mstore(add(headStart, <pos>), sub(tail, headStart))
92
4.09k
          tail := <abiEncode>(<values> tail)
93
4.09k
        )") :
94
13.3k
        std::string(R"(
95
9.29k
          <abiEncode>(<values> add(headStart, <pos>))
96
9.29k
        )")
97
13.3k
      );
98
13.3k
      std::string values = suffixedVariableNameList("value", stackPos, stackPos + sizeOnStack);
99
13.3k
      elementTempl("values", values.empty() ? "" : values + ", ");
100
13.3k
      elementTempl("pos", std::to_string(headPos));
101
13.3k
      elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options));
102
13.3k
      encodeElements += elementTempl.render();
103
13.3k
      headPos += _targetTypes[i]->calldataHeadSize();
104
13.3k
      stackPos += sizeOnStack;
105
13.3k
    }
106
12.0k
    solAssert(headPos == headSize_, "");
107
12.0k
    std::string valueParams =
108
12.0k
      _reversed ?
109
6.46k
      suffixedVariableNameList("value", stackPos, 0) :
110
12.0k
      suffixedVariableNameList("value", 0, stackPos);
111
12.0k
    templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
112
12.0k
    templ("encodeElements", encodeElements);
113
114
12.0k
    return templ.render();
115
12.0k
  });
116
17.3k
}
117
118
std::string ABIFunctions::tupleEncoderPacked(
119
  TypePointers const& _givenTypes,
120
  TypePointers _targetTypes,
121
  bool _reversed
122
)
123
1.43k
{
124
1.43k
  EncodingOptions options;
125
1.43k
  options.encodeAsLibraryTypes = false;
126
1.43k
  options.encodeFunctionFromStack = true;
127
1.43k
  options.padded = false;
128
1.43k
  options.dynamicInplace = true;
129
130
1.43k
  for (Type const*& t: _targetTypes)
131
1.78k
  {
132
1.78k
    solAssert(t, "");
133
1.78k
    t = t->fullEncodingType(options.encodeAsLibraryTypes, true, !options.padded);
134
1.78k
    solAssert(t, "");
135
1.78k
  }
136
137
1.43k
  std::string functionName = std::string("abi_encode_tuple_packed_");
138
1.43k
  for (auto const& t: _givenTypes)
139
1.78k
    functionName += t->identifier() + "_";
140
1.43k
  functionName += "_to_";
141
1.43k
  for (auto const& t: _targetTypes)
142
1.78k
    functionName += t->identifier() + "_";
143
1.43k
  functionName += options.toFunctionNameSuffix();
144
1.43k
  if (_reversed)
145
1.05k
    functionName += "_reversed";
146
147
1.43k
  return createFunction(functionName, [&]() {
148
    // Note that the values are in reverse due to the difference in calling semantics.
149
1.06k
    Whiskers templ(R"(
150
1.06k
      function <functionName>(pos <valueParams>) -> end {
151
1.06k
        <encodeElements>
152
1.06k
        end := pos
153
1.06k
      }
154
1.06k
    )");
155
1.06k
    templ("functionName", functionName);
156
1.06k
    std::string encodeElements;
157
1.06k
    size_t stackPos = 0;
158
2.45k
    for (size_t i = 0; i < _givenTypes.size(); ++i)
159
1.39k
    {
160
1.39k
      solAssert(_givenTypes[i], "");
161
1.39k
      solAssert(_targetTypes[i], "");
162
1.39k
      size_t sizeOnStack = _givenTypes[i]->sizeOnStack();
163
1.39k
      bool dynamic = _targetTypes[i]->isDynamicallyEncoded();
164
1.39k
      Whiskers elementTempl(
165
1.39k
        dynamic ?
166
1.09k
        std::string(R"(
167
1.09k
          pos := <abiEncode>(<values> pos)
168
1.09k
        )") :
169
1.39k
        std::string(R"(
170
304
          <abiEncode>(<values> pos)
171
304
          pos := add(pos, <calldataEncodedSize>)
172
304
        )")
173
1.39k
      );
174
1.39k
      std::string values = suffixedVariableNameList("value", stackPos, stackPos + sizeOnStack);
175
1.39k
      elementTempl("values", values.empty() ? "" : values + ", ");
176
1.39k
      if (!dynamic)
177
304
        elementTempl("calldataEncodedSize", std::to_string(_targetTypes[i]->calldataEncodedSize(false)));
178
1.39k
      elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options));
179
1.39k
      encodeElements += elementTempl.render();
180
1.39k
      stackPos += sizeOnStack;
181
1.39k
    }
182
1.06k
    std::string valueParams =
183
1.06k
      _reversed ?
184
690
      suffixedVariableNameList("value", stackPos, 0) :
185
1.06k
      suffixedVariableNameList("value", 0, stackPos);
186
1.06k
    templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
187
1.06k
    templ("encodeElements", encodeElements);
188
189
1.06k
    return templ.render();
190
1.06k
  });
191
1.43k
}
192
std::string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
193
17.1k
{
194
17.1k
  std::string functionName = std::string("abi_decode_tuple_");
195
17.1k
  for (auto const& t: _types)
196
18.5k
    functionName += t->identifier();
197
17.1k
  if (_fromMemory)
198
3.33k
    functionName += "_fromMemory";
199
200
17.1k
  return createFunction(functionName, [&]() {
201
10.2k
    TypePointers decodingTypes;
202
10.2k
    for (auto const& t: _types)
203
11.5k
      decodingTypes.emplace_back(t->decodingType());
204
205
10.2k
    Whiskers templ(R"(
206
10.2k
      function <functionName>(headStart, dataEnd) <arrow> <valueReturnParams> {
207
10.2k
        if slt(sub(dataEnd, headStart), <minimumSize>) { <revertString>() }
208
10.2k
        <decodeElements>
209
10.2k
      }
210
10.2k
    )");
211
10.2k
    templ("functionName", functionName);
212
10.2k
    templ("revertString", revertReasonIfDebugFunction("ABI decoding: tuple data too short"));
213
10.2k
    templ("minimumSize", std::to_string(headSize(decodingTypes)));
214
215
10.2k
    std::string decodeElements;
216
10.2k
    std::vector<std::string> valueReturnParams;
217
10.2k
    size_t headPos = 0;
218
10.2k
    size_t stackPos = 0;
219
21.7k
    for (size_t i = 0; i < _types.size(); ++i)
220
11.5k
    {
221
11.5k
      solAssert(_types[i], "");
222
11.5k
      solAssert(decodingTypes[i], "");
223
11.5k
      size_t sizeOnStack = _types[i]->sizeOnStack();
224
11.5k
      solAssert(sizeOnStack == decodingTypes[i]->sizeOnStack(), "");
225
11.5k
      solAssert(sizeOnStack > 0, "");
226
11.5k
      std::vector<std::string> valueNamesLocal;
227
23.5k
      for (size_t j = 0; j < sizeOnStack; j++)
228
11.9k
      {
229
11.9k
        valueNamesLocal.emplace_back("value" + std::to_string(stackPos));
230
11.9k
        valueReturnParams.emplace_back("value" + std::to_string(stackPos));
231
11.9k
        stackPos++;
232
11.9k
      }
233
11.5k
      Whiskers elementTempl(R"(
234
11.5k
        {
235
11.5k
          <?dynamic>
236
11.5k
            let offset := <load>(add(headStart, <pos>))
237
11.5k
            if gt(offset, 0xffffffffffffffff) { <revertString>() }
238
11.5k
          <!dynamic>
239
11.5k
            let offset := <pos>
240
11.5k
          </dynamic>
241
11.5k
          <values> := <abiDecode>(add(headStart, offset), dataEnd)
242
11.5k
        }
243
11.5k
      )");
244
11.5k
      elementTempl("dynamic", decodingTypes[i]->isDynamicallyEncoded());
245
      // TODO add test
246
11.5k
      elementTempl("revertString", revertReasonIfDebugFunction("ABI decoding: invalid tuple offset"));
247
11.5k
      elementTempl("load", _fromMemory ? "mload" : "calldataload");
248
11.5k
      elementTempl("values", boost::algorithm::join(valueNamesLocal, ", "));
249
11.5k
      elementTempl("pos", std::to_string(headPos));
250
11.5k
      elementTempl("abiDecode", abiDecodingFunction(*_types[i], _fromMemory, true));
251
11.5k
      decodeElements += elementTempl.render();
252
11.5k
      headPos += decodingTypes[i]->calldataHeadSize();
253
11.5k
    }
254
10.2k
    templ("valueReturnParams", boost::algorithm::join(valueReturnParams, ", "));
255
10.2k
    templ("arrow", valueReturnParams.empty() ? "" : "->");
256
10.2k
    templ("decodeElements", decodeElements);
257
258
10.2k
    return templ.render();
259
10.2k
  });
260
17.1k
}
261
262
std::string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const
263
100k
{
264
100k
  std::string suffix;
265
100k
  if (!padded)
266
3.85k
    suffix += "_nonPadded";
267
100k
  if (dynamicInplace)
268
4.03k
    suffix += "_inplace";
269
100k
  if (encodeFunctionFromStack)
270
37.9k
    suffix += "_fromStack";
271
100k
  if (encodeAsLibraryTypes)
272
651
    suffix += "_library";
273
100k
  return suffix;
274
100k
}
275
276
std::string ABIFunctions::abiEncodingFunction(
277
  Type const& _from,
278
  Type const& _to,
279
  EncodingOptions const& _options
280
)
281
67.6k
{
282
67.6k
  Type const* toInterface = _to.fullEncodingType(_options.encodeAsLibraryTypes, true, false);
283
67.6k
  solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented.");
284
67.6k
  Type const& to = *toInterface;
285
286
67.6k
  if (_from.category() == Type::Category::StringLiteral)
287
694
    return abiEncodingFunctionStringLiteral(_from, to, _options);
288
66.9k
  else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
289
11.4k
  {
290
11.4k
    ArrayType const* fromArray = nullptr;
291
11.4k
    switch (_from.category())
292
11.4k
    {
293
11.3k
      case Type::Category::Array:
294
11.3k
        fromArray = dynamic_cast<ArrayType const*>(&_from);
295
11.3k
        break;
296
20
      case Type::Category::ArraySlice:
297
20
        fromArray = &dynamic_cast<ArraySliceType const*>(&_from)->arrayType();
298
20
        solAssert(
299
20
          fromArray->dataStoredIn(DataLocation::CallData) &&
300
20
          fromArray->isDynamicallySized() &&
301
20
          !fromArray->baseType()->isDynamicallyEncoded(),
302
20
          ""
303
20
        );
304
20
        break;
305
20
      default:
306
0
        solAssert(false, "");
307
0
        break;
308
11.4k
    }
309
310
11.4k
    switch (fromArray->location())
311
11.4k
    {
312
2.00k
      case DataLocation::CallData:
313
2.00k
        if (
314
2.00k
          fromArray->isByteArrayOrString() ||
315
2.00k
          *fromArray->baseType() == *TypeProvider::uint256() ||
316
2.00k
          *fromArray->baseType() == FixedBytesType(32)
317
2.00k
        )
318
991
          return abiEncodingFunctionCalldataArrayWithoutCleanup(*fromArray, *toArray, _options);
319
1.01k
        else
320
1.01k
          return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
321
7.88k
      case DataLocation::Memory:
322
7.88k
        if (fromArray->isByteArrayOrString())
323
3.44k
          return abiEncodingFunctionMemoryByteArray(*fromArray, *toArray, _options);
324
4.43k
        else
325
4.43k
          return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
326
1.51k
      case DataLocation::Storage:
327
1.51k
        if (fromArray->baseType()->storageBytes() <= 16)
328
922
          return abiEncodingFunctionCompactStorageArray(*fromArray, *toArray, _options);
329
594
        else
330
594
          return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
331
0
      default:
332
0
        solAssert(false, "");
333
11.4k
    }
334
11.4k
  }
335
55.5k
  else if (auto const* toStruct = dynamic_cast<StructType const*>(&to))
336
20.9k
  {
337
20.9k
    StructType const* fromStruct = dynamic_cast<StructType const*>(&_from);
338
20.9k
    solAssert(fromStruct, "");
339
20.9k
    return abiEncodingFunctionStruct(*fromStruct, *toStruct, _options);
340
20.9k
  }
341
34.6k
  else if (_from.category() == Type::Category::Function)
342
119
    return abiEncodingFunctionFunctionType(
343
119
      dynamic_cast<FunctionType const&>(_from),
344
119
      to,
345
119
      _options
346
119
    );
347
348
34.5k
  solAssert(_from.sizeOnStack() == 1, "");
349
34.5k
  solAssert(to.isValueType(), "");
350
34.5k
  solAssert(to.calldataEncodedSize() == 32, "");
351
34.5k
  std::string functionName =
352
34.5k
    "abi_encode_" +
353
34.5k
    _from.identifier() +
354
34.5k
    "_to_" +
355
34.5k
    to.identifier() +
356
34.5k
    _options.toFunctionNameSuffix();
357
34.5k
  return createFunction(functionName, [&]() {
358
10.3k
    solAssert(!to.isDynamicallyEncoded(), "");
359
360
10.3k
    Whiskers templ(R"(
361
10.3k
      function <functionName>(value, pos) {
362
10.3k
        mstore(pos, <cleanupConvert>)
363
10.3k
      }
364
10.3k
    )");
365
10.3k
    templ("functionName", functionName);
366
367
10.3k
    if (_from.dataStoredIn(DataLocation::Storage))
368
36
    {
369
      // special case: convert storage reference type to value type - this is only
370
      // possible for library calls where we just forward the storage reference
371
36
      solAssert(_options.encodeAsLibraryTypes, "");
372
36
      solAssert(_options.padded && !_options.dynamicInplace, "Non-padded / inplace encoding for library call requested.");
373
36
      solAssert(to == *TypeProvider::uint256(), "");
374
36
      templ("cleanupConvert", "value");
375
36
    }
376
10.3k
    else
377
10.3k
    {
378
10.3k
      std::string cleanupConvert;
379
10.3k
      if (_from == to)
380
9.67k
        cleanupConvert = m_utils.cleanupFunction(_from) + "(value)";
381
637
      else
382
637
        cleanupConvert = m_utils.conversionFunction(_from, to) + "(value)";
383
10.3k
      if (!_options.padded)
384
190
        cleanupConvert = m_utils.leftAlignFunction(to) + "(" + cleanupConvert + ")";
385
10.3k
      templ("cleanupConvert", cleanupConvert);
386
10.3k
    }
387
10.3k
    return templ.render();
388
10.3k
  });
389
34.5k
}
390
391
std::string ABIFunctions::abiEncodeAndReturnUpdatedPosFunction(
392
  Type const& _givenType,
393
  Type const& _targetType,
394
  ABIFunctions::EncodingOptions const& _options
395
)
396
5.26k
{
397
5.26k
  std::string functionName =
398
5.26k
    "abi_encodeUpdatedPos_" +
399
5.26k
    _givenType.identifier() +
400
5.26k
    "_to_" +
401
5.26k
    _targetType.identifier() +
402
5.26k
    _options.toFunctionNameSuffix();
403
5.26k
  return createFunction(functionName, [&]() {
404
5.02k
    std::string values = suffixedVariableNameList("value", 0, numVariablesForType(_givenType, _options));
405
5.02k
    std::string encoder = abiEncodingFunction(_givenType, _targetType, _options);
406
5.02k
    Type const* targetEncoding = _targetType.fullEncodingType(_options.encodeAsLibraryTypes, true, false);
407
5.02k
    solAssert(targetEncoding, "");
408
5.02k
    if (targetEncoding->isDynamicallyEncoded())
409
2.87k
      return Whiskers(R"(
410
2.87k
        function <functionName>(<values>, pos) -> updatedPos {
411
2.87k
          updatedPos := <encode>(<values>, pos)
412
2.87k
        }
413
2.87k
      )")
414
2.87k
      ("functionName", functionName)
415
2.87k
      ("encode", encoder)
416
2.87k
      ("values", values)
417
2.87k
      .render();
418
2.15k
    else
419
2.15k
    {
420
2.15k
      unsigned encodedSize = targetEncoding->calldataEncodedSize(_options.padded);
421
2.15k
      solAssert(encodedSize != 0, "Invalid encoded size.");
422
2.15k
      return Whiskers(R"(
423
2.15k
        function <functionName>(<values>, pos) -> updatedPos {
424
2.15k
          <encode>(<values>, pos)
425
2.15k
          updatedPos := add(pos, <encodedSize>)
426
2.15k
        }
427
2.15k
      )")
428
2.15k
      ("functionName", functionName)
429
2.15k
      ("encode", encoder)
430
2.15k
      ("encodedSize", toCompactHexWithPrefix(encodedSize))
431
2.15k
      ("values", values)
432
2.15k
      .render();
433
2.15k
    }
434
5.02k
  });
435
5.26k
}
436
437
std::string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
438
  Type const& _from,
439
  Type const& _to,
440
  EncodingOptions const& _options
441
)
442
991
{
443
991
  solAssert(_from.category() == Type::Category::Array, "Unknown dynamic type.");
444
991
  solAssert(_to.category() == Type::Category::Array, "Unknown dynamic type.");
445
991
  auto const& fromArrayType = dynamic_cast<ArrayType const&>(_from);
446
991
  auto const& toArrayType = dynamic_cast<ArrayType const&>(_to);
447
448
991
  solAssert(fromArrayType.location() == DataLocation::CallData, "");
449
991
  solAssert(
450
991
    fromArrayType.isByteArrayOrString() ||
451
991
    *fromArrayType.baseType() == *TypeProvider::uint256() ||
452
991
    *fromArrayType.baseType() == FixedBytesType(32),
453
991
    ""
454
991
  );
455
991
  solAssert(fromArrayType.calldataStride() == toArrayType.memoryStride(), "");
456
457
991
  solAssert(
458
991
    *fromArrayType.copyForLocation(DataLocation::Memory, true) ==
459
991
    *toArrayType.copyForLocation(DataLocation::Memory, true),
460
991
    ""
461
991
  );
462
463
991
  std::string functionName =
464
991
    "abi_encode_" +
465
991
    _from.identifier() +
466
991
    "_to_" +
467
991
    _to.identifier() +
468
991
    _options.toFunctionNameSuffix();
469
991
  return createFunction(functionName, [&]() {
470
290
    bool bytesOrString = fromArrayType.isByteArrayOrString();
471
290
    bool needsPadding = _options.padded && bytesOrString;
472
290
    if (fromArrayType.isDynamicallySized())
473
282
    {
474
282
      Whiskers templ(R"(
475
282
        // <readableTypeNameFrom> -> <readableTypeNameTo>
476
282
        function <functionName>(start, length, pos) -> end {
477
282
          pos := <storeLength>(pos, length)
478
282
          <scaleLengthByStride>
479
282
          <copyFun>(start, pos, length)
480
282
          end := add(pos, <lengthPadded>)
481
282
        }
482
282
      )");
483
282
      templ("storeLength", arrayStoreLengthForEncodingFunction(toArrayType, _options));
484
282
      templ("functionName", functionName);
485
282
      if (fromArrayType.isByteArrayOrString() || fromArrayType.calldataStride() == 1)
486
256
        templ("scaleLengthByStride", "");
487
26
      else
488
26
        templ("scaleLengthByStride",
489
26
          Whiskers(R"(
490
26
            if gt(length, <maxLength>) { <revertString>() }
491
26
            length := mul(length, <stride>)
492
26
          )")
493
26
          ("stride", toCompactHexWithPrefix(fromArrayType.calldataStride()))
494
26
          ("maxLength", toCompactHexWithPrefix(u256(-1) / fromArrayType.calldataStride()))
495
26
          ("revertString", revertReasonIfDebugFunction("ABI encoding: array data too long"))
496
26
          .render()
497
          // TODO add revert test
498
26
        );
499
282
      templ("readableTypeNameFrom", _from.toString(true));
500
282
      templ("readableTypeNameTo", _to.toString(true));
501
282
      templ("copyFun", m_utils.copyToMemoryFunction(true, /*cleanup*/bytesOrString));
502
282
      templ("lengthPadded", needsPadding ? m_utils.roundUpFunction() + "(length)" : "length");
503
282
      return templ.render();
504
282
    }
505
8
    else
506
8
    {
507
8
      solAssert(fromArrayType.calldataStride() == 32, "");
508
8
      Whiskers templ(R"(
509
8
        // <readableTypeNameFrom> -> <readableTypeNameTo>
510
8
        function <functionName>(start, pos) {
511
8
          <copyFun>(start, pos, <byteLength>)
512
8
        }
513
8
      )");
514
8
      templ("functionName", functionName);
515
8
      templ("readableTypeNameFrom", _from.toString(true));
516
8
      templ("readableTypeNameTo", _to.toString(true));
517
8
      templ("copyFun", m_utils.copyToMemoryFunction(true, /*cleanup*/bytesOrString));
518
8
      templ("byteLength", toCompactHexWithPrefix(fromArrayType.length() * fromArrayType.calldataStride()));
519
8
      return templ.render();
520
8
    }
521
290
  });
522
991
}
523
524
std::string ABIFunctions::abiEncodingFunctionSimpleArray(
525
  ArrayType const& _from,
526
  ArrayType const& _to,
527
  EncodingOptions const& _options
528
)
529
6.04k
{
530
6.04k
  std::string functionName =
531
6.04k
    "abi_encode_" +
532
6.04k
    _from.identifier() +
533
6.04k
    "_to_" +
534
6.04k
    _to.identifier() +
535
6.04k
    _options.toFunctionNameSuffix();
536
537
6.04k
  solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
538
6.04k
  solAssert(_from.length() == _to.length(), "");
539
6.04k
  solAssert(!_from.isByteArrayOrString(), "");
540
6.04k
  if (_from.dataStoredIn(DataLocation::Storage))
541
6.04k
    solAssert(_from.baseType()->storageBytes() > 16, "");
542
543
6.04k
  return createFunction(functionName, [&]() {
544
5.05k
    bool dynamic = _to.isDynamicallyEncoded();
545
5.05k
    bool dynamicBase = _to.baseType()->isDynamicallyEncoded();
546
5.05k
    bool const usesTail = dynamicBase && !_options.dynamicInplace;
547
5.05k
    EncodingOptions subOptions(_options);
548
5.05k
    subOptions.encodeFunctionFromStack = false;
549
5.05k
    subOptions.padded = true;
550
5.05k
    std::string elementValues = suffixedVariableNameList("elementValue", 0, numVariablesForType(*_from.baseType(), subOptions));
551
5.05k
    Whiskers templ(
552
5.05k
      usesTail ?
553
2.72k
      R"(
554
2.72k
        // <readableTypeNameFrom> -> <readableTypeNameTo>
555
2.72k
        function <functionName>(value,<maybeLength> pos) <return> {
556
2.72k
          <declareLength>
557
2.72k
          pos := <storeLength>(pos, length)
558
2.72k
          let headStart := pos
559
2.72k
          let tail := add(pos, mul(length, 0x20))
560
2.72k
          let baseRef := <dataAreaFun>(value)
561
2.72k
          let srcPtr := baseRef
562
2.72k
          for { let i := 0 } lt(i, length) { i := add(i, 1) }
563
2.72k
          {
564
2.72k
            mstore(pos, sub(tail, headStart))
565
2.72k
            let <elementValues> := <arrayElementAccess>
566
2.72k
            tail := <encodeToMemoryFun>(<elementValues>, tail)
567
2.72k
            srcPtr := <nextArrayElement>(srcPtr)
568
2.72k
            pos := add(pos, 0x20)
569
2.72k
          }
570
2.72k
          pos := tail
571
2.72k
          <assignEnd>
572
2.72k
        }
573
2.72k
      )" :
574
5.05k
      R"(
575
2.33k
        // <readableTypeNameFrom> -> <readableTypeNameTo>
576
2.33k
        function <functionName>(value,<maybeLength> pos) <return> {
577
2.33k
          <declareLength>
578
2.33k
          pos := <storeLength>(pos, length)
579
2.33k
          let baseRef := <dataAreaFun>(value)
580
2.33k
          let srcPtr := baseRef
581
2.33k
          for { let i := 0 } lt(i, length) { i := add(i, 1) }
582
2.33k
          {
583
2.33k
            let <elementValues> := <arrayElementAccess>
584
2.33k
            pos := <encodeToMemoryFun>(<elementValues>, pos)
585
2.33k
            srcPtr := <nextArrayElement>(srcPtr)
586
2.33k
          }
587
2.33k
          <assignEnd>
588
2.33k
        }
589
2.33k
      )"
590
5.05k
    );
591
5.05k
    templ("functionName", functionName);
592
5.05k
    templ("elementValues", elementValues);
593
5.05k
    bool lengthAsArgument = _from.dataStoredIn(DataLocation::CallData) && _from.isDynamicallySized();
594
5.05k
    if (lengthAsArgument)
595
525
    {
596
525
      templ("maybeLength", " length,");
597
525
      templ("declareLength", "");
598
525
    }
599
4.52k
    else
600
4.52k
    {
601
4.52k
      templ("maybeLength", "");
602
4.52k
      templ("declareLength", "let length := " + m_utils.arrayLengthFunction(_from) + "(value)");
603
4.52k
    }
604
5.05k
    templ("readableTypeNameFrom", _from.toString(true));
605
5.05k
    templ("readableTypeNameTo", _to.toString(true));
606
5.05k
    templ("return", dynamic ? " -> end " : "");
607
5.05k
    templ("assignEnd", dynamic ? "end := pos" : "");
608
5.05k
    templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
609
5.05k
    templ("dataAreaFun", m_utils.arrayDataAreaFunction(_from));
610
611
5.05k
    templ("encodeToMemoryFun", abiEncodeAndReturnUpdatedPosFunction(*_from.baseType(), *_to.baseType(), subOptions));
612
5.05k
    switch (_from.location())
613
5.05k
    {
614
3.96k
      case DataLocation::Memory:
615
3.96k
        templ("arrayElementAccess", "mload(srcPtr)");
616
3.96k
        break;
617
435
      case DataLocation::Storage:
618
435
        if (_from.baseType()->isValueType())
619
65
          templ("arrayElementAccess", m_utils.readFromStorage(*_from.baseType(), 0, false, VariableDeclaration::Location::Unspecified) + "(srcPtr)");
620
370
        else
621
370
          templ("arrayElementAccess", "srcPtr");
622
435
        break;
623
658
      case DataLocation::CallData:
624
658
        templ("arrayElementAccess", calldataAccessFunction(*_from.baseType()) + "(baseRef, srcPtr)");
625
658
        break;
626
0
      default:
627
0
        solAssert(false, "");
628
5.05k
    }
629
5.05k
    templ("nextArrayElement", m_utils.nextArrayElementFunction(_from));
630
5.05k
    return templ.render();
631
5.05k
  });
632
6.04k
}
633
634
std::string ABIFunctions::abiEncodingFunctionMemoryByteArray(
635
  ArrayType const& _from,
636
  ArrayType const& _to,
637
  EncodingOptions const& _options
638
)
639
3.44k
{
640
3.44k
  std::string functionName =
641
3.44k
    "abi_encode_" +
642
3.44k
    _from.identifier() +
643
3.44k
    "_to_" +
644
3.44k
    _to.identifier() +
645
3.44k
    _options.toFunctionNameSuffix();
646
647
3.44k
  solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
648
3.44k
  solAssert(_from.length() == _to.length(), "");
649
3.44k
  solAssert(_from.dataStoredIn(DataLocation::Memory), "");
650
3.44k
  solAssert(_from.isByteArrayOrString(), "");
651
652
3.44k
  return createFunction(functionName, [&]() {
653
2.32k
    solAssert(_to.isByteArrayOrString(), "");
654
2.32k
    Whiskers templ(R"(
655
2.32k
      function <functionName>(value, pos) -> end {
656
2.32k
        let length := <lengthFun>(value)
657
2.32k
        pos := <storeLength>(pos, length)
658
2.32k
        <copyFun>(add(value, 0x20), pos, length)
659
2.32k
        end := add(pos, <lengthPadded>)
660
2.32k
      }
661
2.32k
    )");
662
2.32k
    templ("functionName", functionName);
663
2.32k
    templ("lengthFun", m_utils.arrayLengthFunction(_from));
664
2.32k
    templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
665
2.32k
    templ("copyFun", m_utils.copyToMemoryFunction(false, /*cleanup*/true));
666
2.32k
    templ("lengthPadded", _options.padded ? m_utils.roundUpFunction() + "(length)" : "length");
667
2.32k
    return templ.render();
668
2.32k
  });
669
3.44k
}
670
671
std::string ABIFunctions::abiEncodingFunctionCompactStorageArray(
672
  ArrayType const& _from,
673
  ArrayType const& _to,
674
  EncodingOptions const& _options
675
)
676
922
{
677
922
  std::string functionName =
678
922
    "abi_encode_" +
679
922
    _from.identifier() +
680
922
    "_to_" +
681
922
    _to.identifier() +
682
922
    _options.toFunctionNameSuffix();
683
684
922
  solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
685
922
  solAssert(_from.length() == _to.length(), "");
686
922
  solAssert(_from.dataStoredIn(DataLocation::Storage), "");
687
688
922
  return createFunction(functionName, [&]() {
689
432
    if (_from.isByteArrayOrString())
690
328
    {
691
328
      solAssert(_to.isByteArrayOrString(), "");
692
328
      Whiskers templ(R"(
693
328
        // <readableTypeNameFrom> -> <readableTypeNameTo>
694
328
        function <functionName>(value, pos) -> ret {
695
328
          let slotValue := sload(value)
696
328
          let length := <byteArrayLengthFunction>(slotValue)
697
328
          pos := <storeLength>(pos, length)
698
328
          switch and(slotValue, 1)
699
328
          case 0 {
700
328
            // short byte array
701
328
            mstore(pos, and(slotValue, not(0xff)))
702
328
            ret := add(pos, mul(<lengthPaddedShort>, iszero(iszero(length))))
703
328
          }
704
328
          case 1 {
705
328
            // long byte array
706
328
            let dataPos := <arrayDataSlot>(value)
707
328
            let i := 0
708
328
            for { } lt(i, length) { i := add(i, 0x20) } {
709
328
              mstore(add(pos, i), sload(dataPos))
710
328
              dataPos := add(dataPos, 1)
711
328
            }
712
328
            ret := add(pos, <lengthPaddedLong>)
713
328
          }
714
328
        }
715
328
      )");
716
328
      templ("functionName", functionName);
717
328
      templ("readableTypeNameFrom", _from.toString(true));
718
328
      templ("readableTypeNameTo", _to.toString(true));
719
328
      templ("byteArrayLengthFunction", m_utils.extractByteArrayLengthFunction());
720
328
      templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
721
328
      templ("lengthPaddedShort", _options.padded ? "0x20" : "length");
722
328
      templ("lengthPaddedLong", _options.padded ? "i" : "length");
723
328
      templ("arrayDataSlot", m_utils.arrayDataAreaFunction(_from));
724
328
      return templ.render();
725
328
    }
726
104
    else
727
104
    {
728
      // Multiple items per slot
729
104
      solAssert(_from.baseType()->storageBytes() <= 16, "");
730
104
      solAssert(!_from.baseType()->isDynamicallyEncoded(), "");
731
104
      solAssert(!_to.baseType()->isDynamicallyEncoded(), "");
732
104
      solAssert(_from.baseType()->isValueType(), "");
733
104
      bool dynamic = _to.isDynamicallyEncoded();
734
104
      size_t storageBytes = _from.baseType()->storageBytes();
735
104
      size_t itemsPerSlot = 32 / storageBytes;
736
104
      solAssert(itemsPerSlot > 0, "");
737
      // The number of elements we need to handle manually after the loop.
738
104
      size_t spill = static_cast<size_t>(_from.length() % itemsPerSlot);
739
104
      Whiskers templ(
740
104
        R"(
741
104
          // <readableTypeNameFrom> -> <readableTypeNameTo>
742
104
          function <functionName>(value, pos) <return> {
743
104
            let length := <lengthFun>(value)
744
104
            pos := <storeLength>(pos, length)
745
104
            let originalPos := pos
746
104
            let srcPtr := <dataArea>(value)
747
104
            let itemCounter := 0
748
104
            if <useLoop> {
749
104
              // Run the loop over all full slots
750
104
              for { } lt(add(itemCounter, sub(<itemsPerSlot>, 1)), length)
751
104
                    { itemCounter := add(itemCounter, <itemsPerSlot>) }
752
104
              {
753
104
                let data := sload(srcPtr)
754
104
                <#items>
755
104
                  <encodeToMemoryFun>(<extractFromSlot>(data), pos)
756
104
                  pos := add(pos, <stride>)
757
104
                </items>
758
104
                srcPtr := add(srcPtr, 1)
759
104
              }
760
104
            }
761
104
            // Handle the last (not necessarily full) slot specially
762
104
            if <useSpill> {
763
104
              let data := sload(srcPtr)
764
104
              <#items>
765
104
                if <inRange> {
766
104
                  <encodeToMemoryFun>(<extractFromSlot>(data), pos)
767
104
                  pos := add(pos, <stride>)
768
104
                  itemCounter := add(itemCounter, 1)
769
104
                }
770
104
              </items>
771
104
            }
772
104
            <assignEnd>
773
104
          }
774
104
        )"
775
104
      );
776
104
      templ("functionName", functionName);
777
104
      templ("readableTypeNameFrom", _from.toString(true));
778
104
      templ("readableTypeNameTo", _to.toString(true));
779
104
      templ("return", dynamic ? " -> end " : "");
780
104
      templ("assignEnd", dynamic ? "end := pos" : "");
781
104
      templ("lengthFun", m_utils.arrayLengthFunction(_from));
782
104
      templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
783
104
      templ("dataArea", m_utils.arrayDataAreaFunction(_from));
784
      // We skip the loop for arrays that fit a single slot.
785
104
      if (_from.isDynamicallySized() || _from.length() >= itemsPerSlot)
786
69
        templ("useLoop", "1");
787
35
      else
788
35
        templ("useLoop", "0");
789
104
      if (_from.isDynamicallySized() || spill != 0)
790
103
        templ("useSpill", "1");
791
1
      else
792
1
        templ("useSpill", "0");
793
104
      templ("itemsPerSlot", std::to_string(itemsPerSlot));
794
104
      templ("stride", toCompactHexWithPrefix(_to.calldataStride()));
795
796
104
      EncodingOptions subOptions(_options);
797
104
      subOptions.encodeFunctionFromStack = false;
798
104
      subOptions.padded = true;
799
104
      std::string encodeToMemoryFun = abiEncodingFunction(
800
104
        *_from.baseType(),
801
104
        *_to.baseType(),
802
104
        subOptions
803
104
      );
804
104
      templ("encodeToMemoryFun", encodeToMemoryFun);
805
104
      std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
806
2.72k
      for (size_t i = 0; i < itemsPerSlot; ++i)
807
2.62k
      {
808
2.62k
        if (_from.isDynamicallySized())
809
1.63k
          items[i]["inRange"] = "lt(itemCounter, length)";
810
986
        else if (i < spill)
811
97
          items[i]["inRange"] = "1";
812
889
        else
813
889
          items[i]["inRange"] = "0";
814
2.62k
        items[i]["extractFromSlot"] = m_utils.extractFromStorageValue(*_from.baseType(), i * storageBytes);
815
2.62k
      }
816
104
      templ("items", items);
817
104
      return templ.render();
818
104
    }
819
432
  });
820
922
}
821
822
std::string ABIFunctions::abiEncodingFunctionStruct(
823
  StructType const& _from,
824
  StructType const& _to,
825
  EncodingOptions const& _options
826
)
827
20.9k
{
828
20.9k
  std::string functionName =
829
20.9k
    "abi_encode_" +
830
20.9k
    _from.identifier() +
831
20.9k
    "_to_" +
832
20.9k
    _to.identifier() +
833
20.9k
    _options.toFunctionNameSuffix();
834
835
20.9k
  solAssert(&_from.structDefinition() == &_to.structDefinition(), "");
836
837
20.9k
  return createFunction(functionName, [&]() {
838
20.8k
    bool dynamic = _to.isDynamicallyEncoded();
839
20.8k
    Whiskers templ(R"(
840
20.8k
      // <readableTypeNameFrom> -> <readableTypeNameTo>
841
20.8k
      function <functionName>(value, pos) <return> {
842
20.8k
        let tail := add(pos, <headSize>)
843
20.8k
        <init>
844
20.8k
        <#members>
845
20.8k
        {
846
20.8k
          // <memberName>
847
20.8k
          <preprocess>
848
20.8k
          let <memberValues> := <retrieveValue>
849
20.8k
          <encode>
850
20.8k
        }
851
20.8k
        </members>
852
20.8k
        <assignEnd>
853
20.8k
      }
854
20.8k
    )");
855
20.8k
    templ("functionName", functionName);
856
20.8k
    templ("readableTypeNameFrom", _from.toString(true));
857
20.8k
    templ("readableTypeNameTo", _to.toString(true));
858
20.8k
    templ("return", dynamic ? " -> end " : "");
859
20.8k
    if (dynamic && _options.dynamicInplace)
860
8
      templ("assignEnd", "end := pos");
861
20.8k
    else if (dynamic && !_options.dynamicInplace)
862
4.75k
      templ("assignEnd", "end := tail");
863
16.0k
    else
864
16.0k
      templ("assignEnd", "");
865
    // to avoid multiple loads from the same slot for subsequent members
866
20.8k
    templ("init", _from.dataStoredIn(DataLocation::Storage) ? "let slotValue := 0" : "");
867
20.8k
    u256 previousSlotOffset(-1);
868
20.8k
    u256 encodingOffset = 0;
869
20.8k
    std::vector<std::map<std::string, std::string>> members;
870
20.8k
    for (auto const& member: _to.members(nullptr))
871
47.8k
    {
872
47.8k
      solAssert(member.type, "");
873
47.8k
      solAssert(!member.type->containsNestedMapping(), "");
874
47.8k
      Type const* memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
875
47.8k
      solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
876
47.8k
      auto memberTypeFrom = _from.memberType(member.name);
877
47.8k
      solAssert(memberTypeFrom, "");
878
47.8k
      bool dynamicMember = memberTypeTo->isDynamicallyEncoded();
879
47.8k
      if (dynamicMember)
880
47.8k
        solAssert(dynamic, "");
881
882
47.8k
      members.emplace_back();
883
47.8k
      members.back()["preprocess"] = "";
884
885
47.8k
      switch (_from.location())
886
47.8k
      {
887
12.0k
        case DataLocation::Storage:
888
12.0k
        {
889
12.0k
          solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), "");
890
12.0k
          u256 storageSlotOffset;
891
12.0k
          size_t intraSlotOffset;
892
12.0k
          std::tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name);
893
12.0k
          if (memberTypeFrom->isValueType())
894
6.09k
          {
895
6.09k
            if (storageSlotOffset != previousSlotOffset)
896
3.91k
            {
897
3.91k
              members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))";
898
3.91k
              previousSlotOffset = storageSlotOffset;
899
3.91k
            }
900
6.09k
            members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset) + "(slotValue)";
901
6.09k
          }
902
5.90k
          else
903
5.90k
          {
904
5.90k
            solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), "");
905
5.90k
            solAssert(intraSlotOffset == 0, "");
906
5.90k
            members.back()["retrieveValue"] = "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")";
907
5.90k
          }
908
12.0k
          break;
909
12.0k
        }
910
19.1k
        case DataLocation::Memory:
911
19.1k
        {
912
19.1k
          std::string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name));
913
19.1k
          members.back()["retrieveValue"] = "mload(add(value, " + sourceOffset + "))";
914
19.1k
          break;
915
12.0k
        }
916
16.6k
        case DataLocation::CallData:
917
16.6k
        {
918
16.6k
          std::string sourceOffset = toCompactHexWithPrefix(_from.calldataOffsetOfMember(member.name));
919
16.6k
          members.back()["retrieveValue"] = calldataAccessFunction(*memberTypeFrom) + "(value, add(value, " + sourceOffset + "))";
920
16.6k
          break;
921
12.0k
        }
922
0
        default:
923
0
          solAssert(false, "");
924
47.8k
      }
925
926
47.8k
      EncodingOptions subOptions(_options);
927
47.8k
      subOptions.encodeFunctionFromStack = false;
928
      // Like with arrays, struct members are always padded.
929
47.8k
      subOptions.padded = true;
930
931
47.8k
      std::string memberValues = suffixedVariableNameList("memberValue", 0, numVariablesForType(*memberTypeFrom, subOptions));
932
47.8k
      members.back()["memberValues"] = memberValues;
933
934
47.8k
      std::string encode;
935
47.8k
      if (_options.dynamicInplace)
936
42
        encode = Whiskers{"pos := <encode>(<memberValues>, pos)"}
937
42
          ("encode", abiEncodeAndReturnUpdatedPosFunction(*memberTypeFrom, *memberTypeTo, subOptions))
938
42
          ("memberValues", memberValues)
939
42
          .render();
940
47.7k
      else
941
47.7k
      {
942
47.7k
        Whiskers encodeTempl(
943
47.7k
          dynamicMember ?
944
8.18k
          std::string(R"(
945
8.18k
            mstore(add(pos, <encodingOffset>), sub(tail, pos))
946
8.18k
            tail := <abiEncode>(<memberValues>, tail)
947
8.18k
          )") :
948
47.7k
          "<abiEncode>(<memberValues>, add(pos, <encodingOffset>))"
949
47.7k
        );
950
47.7k
        encodeTempl("memberValues", memberValues);
951
47.7k
        encodeTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
952
47.7k
        encodingOffset += memberTypeTo->calldataHeadSize();
953
47.7k
        encodeTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions));
954
47.7k
        encode = encodeTempl.render();
955
47.7k
      }
956
47.8k
      members.back()["encode"] = encode;
957
958
47.8k
      members.back()["memberName"] = member.name;
959
47.8k
    }
960
20.8k
    templ("members", members);
961
20.8k
    if (_options.dynamicInplace)
962
20.8k
      solAssert(encodingOffset == 0, "In-place encoding should enforce zero head size.");
963
20.8k
    templ("headSize", toCompactHexWithPrefix(encodingOffset));
964
20.8k
    return templ.render();
965
20.8k
  });
966
20.9k
}
967
968
std::string ABIFunctions::abiEncodingFunctionStringLiteral(
969
  Type const& _from,
970
  Type const& _to,
971
  EncodingOptions const& _options
972
)
973
694
{
974
694
  solAssert(_from.category() == Type::Category::StringLiteral, "");
975
976
694
  std::string functionName =
977
694
    "abi_encode_" +
978
694
    _from.identifier() +
979
694
    "_to_" +
980
694
    _to.identifier() +
981
694
    _options.toFunctionNameSuffix();
982
694
  return createFunction(functionName, [&]() {
983
658
    auto const& strType = dynamic_cast<StringLiteralType const&>(_from);
984
658
    std::string const& value = strType.value();
985
658
    solAssert(_from.sizeOnStack() == 0, "");
986
987
658
    if (_to.isDynamicallySized())
988
619
    {
989
619
      solAssert(_to.category() == Type::Category::Array, "");
990
619
      Whiskers templ(R"(
991
619
        function <functionName>(pos) -> end {
992
619
          pos := <storeLength>(pos, <length>)
993
619
          <storeLiteralInMemory>(pos)
994
619
          end := add(pos, <overallSize>)
995
619
        }
996
619
      )");
997
619
      templ("functionName", functionName);
998
999
      // TODO this can make use of CODECOPY for large strings once we have that in Yul
1000
619
      templ("length", std::to_string(value.size()));
1001
619
      templ("storeLength", arrayStoreLengthForEncodingFunction(dynamic_cast<ArrayType const&>(_to), _options));
1002
619
      if (_options.padded)
1003
115
        templ("overallSize", std::to_string(((value.size() + 31) / 32) * 32));
1004
504
      else
1005
504
        templ("overallSize", std::to_string(value.size()));
1006
619
      templ("storeLiteralInMemory", m_utils.storeLiteralInMemoryFunction(value));
1007
619
      return templ.render();
1008
619
    }
1009
39
    else
1010
39
    {
1011
39
      solAssert(_to.category() == Type::Category::FixedBytes, "");
1012
39
      solAssert(value.size() <= 32, "");
1013
39
      Whiskers templ(R"(
1014
39
        function <functionName>(pos) {
1015
39
          mstore(pos, <wordValue>)
1016
39
        }
1017
39
      )");
1018
39
      templ("functionName", functionName);
1019
39
      templ("wordValue", formatAsStringOrNumber(value));
1020
39
      return templ.render();
1021
39
    }
1022
658
  });
1023
694
}
1024
1025
std::string ABIFunctions::abiEncodingFunctionFunctionType(
1026
  FunctionType const& _from,
1027
  Type const& _to,
1028
  EncodingOptions const& _options
1029
)
1030
119
{
1031
119
  solAssert(
1032
119
    _from.kind() == FunctionType::Kind::External &&
1033
119
    _from.isImplicitlyConvertibleTo(_to) &&
1034
119
    _from.sizeOnStack() == _to.sizeOnStack(),
1035
119
    "Invalid function type conversion requested"
1036
119
  );
1037
1038
119
  std::string functionName =
1039
119
    "abi_encode_" +
1040
119
    _from.identifier() +
1041
119
    "_to_" +
1042
119
    _to.identifier() +
1043
119
    _options.toFunctionNameSuffix();
1044
1045
119
  if (_options.encodeFunctionFromStack)
1046
108
    return createFunction(functionName, [&]() {
1047
98
      return Whiskers(R"(
1048
98
        function <functionName>(addr, function_id, pos) {
1049
98
          addr, function_id := <convert>(addr, function_id)
1050
98
          mstore(pos, <combineExtFun>(addr, function_id))
1051
98
        }
1052
98
      )")
1053
98
      ("functionName", functionName)
1054
98
      ("combineExtFun", m_utils.combineExternalFunctionIdFunction())
1055
98
      ("convert", m_utils.conversionFunction(_from, _to))
1056
98
      .render();
1057
98
    });
1058
11
  else
1059
11
    return createFunction(functionName, [&]() {
1060
9
      return Whiskers(R"(
1061
9
        function <functionName>(addr_and_function_id, pos) {
1062
9
          mstore(pos, <cleanExtFun>(addr_and_function_id))
1063
9
        }
1064
9
      )")
1065
9
      ("functionName", functionName)
1066
9
      ("cleanExtFun", m_utils.cleanupFunction(_to))
1067
9
      .render();
1068
9
    });
1069
119
}
1070
1071
std::string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bool _forUseOnStack)
1072
32.4k
{
1073
  // The decoding function has to perform bounds checks unless it decodes a value type.
1074
  // Conversely, bounds checks have to be performed before the decoding function
1075
  // of a value type is called.
1076
1077
32.4k
  Type const* decodingType = _type.decodingType();
1078
32.4k
  solAssert(decodingType, "");
1079
1080
32.4k
  if (auto arrayType = dynamic_cast<ArrayType const*>(decodingType))
1081
4.71k
  {
1082
4.71k
    if (arrayType->dataStoredIn(DataLocation::CallData))
1083
433
    {
1084
433
      solAssert(!_fromMemory, "");
1085
433
      return abiDecodingFunctionCalldataArray(*arrayType);
1086
433
    }
1087
4.27k
    else
1088
4.27k
      return abiDecodingFunctionArray(*arrayType, _fromMemory);
1089
4.71k
  }
1090
27.7k
  else if (auto const* structType = dynamic_cast<StructType const*>(decodingType))
1091
9.13k
  {
1092
9.13k
    if (structType->dataStoredIn(DataLocation::CallData))
1093
437
    {
1094
437
      solAssert(!_fromMemory, "");
1095
437
      return abiDecodingFunctionCalldataStruct(*structType);
1096
437
    }
1097
8.70k
    else
1098
8.70k
      return abiDecodingFunctionStruct(*structType, _fromMemory);
1099
9.13k
  }
1100
18.6k
  else if (auto const* functionType = dynamic_cast<FunctionType const*>(decodingType))
1101
97
    return abiDecodingFunctionFunctionType(*functionType, _fromMemory, _forUseOnStack);
1102
18.5k
  else
1103
18.5k
    return abiDecodingFunctionValueType(_type, _fromMemory);
1104
32.4k
}
1105
1106
std::string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromMemory)
1107
20.0k
{
1108
20.0k
  Type const* decodingType = _type.decodingType();
1109
20.0k
  solAssert(decodingType, "");
1110
20.0k
  solAssert(decodingType->sizeOnStack() == 1, "");
1111
20.0k
  solAssert(decodingType->isValueType(), "");
1112
20.0k
  solAssert(!decodingType->isDynamicallyEncoded(), "");
1113
20.0k
  solAssert(decodingType->calldataEncodedSize() == 32, "");
1114
1115
20.0k
  std::string functionName =
1116
20.0k
    "abi_decode_" +
1117
20.0k
    _type.identifier() +
1118
20.0k
    (_fromMemory ? "_fromMemory" : "");
1119
20.0k
  return createFunction(functionName, [&]() {
1120
8.85k
    Whiskers templ(R"(
1121
8.85k
      function <functionName>(offset, end) -> value {
1122
8.85k
        value := <load>(offset)
1123
8.85k
        <validator>(value)
1124
8.85k
      }
1125
8.85k
    )");
1126
8.85k
    templ("functionName", functionName);
1127
8.85k
    templ("load", _fromMemory ? "mload" : "calldataload");
1128
    // Validation should use the type and not decodingType, because e.g.
1129
    // the decoding type of an enum is a plain int.
1130
8.85k
    templ("validator", m_utils.validatorFunction(_type, true));
1131
8.85k
    return templ.render();
1132
8.85k
  });
1133
1134
20.0k
}
1135
1136
std::string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory)
1137
4.27k
{
1138
4.27k
  solAssert(_type.dataStoredIn(DataLocation::Memory), "");
1139
1140
4.27k
  std::string functionName =
1141
4.27k
    "abi_decode_" +
1142
4.27k
    _type.identifier() +
1143
4.27k
    (_fromMemory ? "_fromMemory" : "");
1144
1145
4.27k
  return createFunction(functionName, [&]() {
1146
2.45k
    std::string load = _fromMemory ? "mload" : "calldataload";
1147
2.45k
    Whiskers templ(
1148
2.45k
      R"(
1149
2.45k
        // <readableTypeName>
1150
2.45k
        function <functionName>(offset, end) -> array {
1151
2.45k
          if iszero(slt(add(offset, 0x1f), end)) { <revertString>() }
1152
2.45k
          let length := <retrieveLength>
1153
2.45k
          array := <abiDecodeAvailableLen>(<offset>, length, end)
1154
2.45k
        }
1155
2.45k
      )"
1156
2.45k
    );
1157
    // TODO add test
1158
2.45k
    templ("revertString", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset"));
1159
2.45k
    templ("functionName", functionName);
1160
2.45k
    templ("readableTypeName", _type.toString(true));
1161
2.45k
    templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length()));
1162
2.45k
    templ("offset", _type.isDynamicallySized() ? "add(offset, 0x20)" : "offset");
1163
2.45k
    templ("abiDecodeAvailableLen", abiDecodingFunctionArrayAvailableLength(_type, _fromMemory));
1164
2.45k
    return templ.render();
1165
2.45k
  });
1166
4.27k
}
1167
1168
std::string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
1169
2.48k
{
1170
2.48k
  solAssert(_type.dataStoredIn(DataLocation::Memory), "");
1171
2.48k
  if (_type.isByteArrayOrString())
1172
799
    return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory);
1173
1.69k
  solAssert(_type.calldataStride() > 0, "");
1174
1175
1.69k
  std::string functionName =
1176
1.69k
    "abi_decode_available_length_" +
1177
1.69k
    _type.identifier() +
1178
1.69k
    (_fromMemory ? "_fromMemory" : "");
1179
1180
1.69k
  return createFunction(functionName, [&]() {
1181
1.68k
    Whiskers templ(R"(
1182
1.68k
      // <readableTypeName>
1183
1.68k
      function <functionName>(offset, length, end) -> array {
1184
1.68k
        array := <allocate>(<allocationSize>(length))
1185
1.68k
        let dst := array
1186
1.68k
        <?dynamic>
1187
1.68k
          mstore(array, length)
1188
1.68k
          dst := add(array, 0x20)
1189
1.68k
        </dynamic>
1190
1.68k
        let srcEnd := add(offset, mul(length, <stride>))
1191
1.68k
        if gt(srcEnd, end) {
1192
1.68k
          <revertInvalidStride>()
1193
1.68k
        }
1194
1.68k
        for { let src := offset } lt(src, srcEnd) { src := add(src, <stride>) }
1195
1.68k
        {
1196
1.68k
          <?dynamicBase>
1197
1.68k
            let innerOffset := <load>(src)
1198
1.68k
            if gt(innerOffset, 0xffffffffffffffff) { <revertStringOffset>() }
1199
1.68k
            let elementPos := add(offset, innerOffset)
1200
1.68k
          <!dynamicBase>
1201
1.68k
            let elementPos := src
1202
1.68k
          </dynamicBase>
1203
1.68k
          mstore(dst, <decodingFun>(elementPos, end))
1204
1.68k
          dst := add(dst, 0x20)
1205
1.68k
        }
1206
1.68k
      }
1207
1.68k
    )");
1208
1.68k
    templ("functionName", functionName);
1209
1.68k
    templ("readableTypeName", _type.toString(true));
1210
1.68k
    templ("allocate", m_utils.allocationFunction());
1211
1.68k
    templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
1212
1.68k
    templ("stride", toCompactHexWithPrefix(_type.calldataStride()));
1213
1.68k
    templ("dynamic", _type.isDynamicallySized());
1214
1.68k
    templ("load", _fromMemory ? "mload" : "calldataload");
1215
1.68k
    templ("dynamicBase", _type.baseType()->isDynamicallyEncoded());
1216
1.68k
    templ(
1217
1.68k
      "revertInvalidStride",
1218
1.68k
      revertReasonIfDebugFunction("ABI decoding: invalid calldata array stride")
1219
1.68k
    );
1220
1.68k
    templ("revertStringOffset", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset"));
1221
1.68k
    templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false));
1222
1.68k
    return templ.render();
1223
1.68k
  });
1224
1.69k
}
1225
1226
std::string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
1227
433
{
1228
433
  solAssert(_type.dataStoredIn(DataLocation::CallData), "");
1229
433
  if (!_type.isDynamicallySized())
1230
433
    solAssert(_type.length() < u256("0xffffffffffffffff"), "");
1231
433
  solAssert(_type.calldataStride() > 0, "");
1232
433
  solAssert(_type.calldataStride() < u256("0xffffffffffffffff"), "");
1233
1234
433
  std::string functionName =
1235
433
    "abi_decode_" +
1236
433
    _type.identifier();
1237
433
  return createFunction(functionName, [&]() {
1238
411
    Whiskers w;
1239
411
    if (_type.isDynamicallySized())
1240
335
    {
1241
335
      w = Whiskers(R"(
1242
335
        // <readableTypeName>
1243
335
        function <functionName>(offset, end) -> arrayPos, length {
1244
335
          if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset>() }
1245
335
          length := calldataload(offset)
1246
335
          if gt(length, 0xffffffffffffffff) { <revertStringLength>() }
1247
335
          arrayPos := add(offset, 0x20)
1248
335
          if gt(add(arrayPos, mul(length, <stride>)), end) { <revertStringPos>() }
1249
335
        }
1250
335
      )");
1251
335
      w("revertStringOffset", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset"));
1252
335
      w("revertStringLength", revertReasonIfDebugFunction("ABI decoding: invalid calldata array length"));
1253
335
    }
1254
76
    else
1255
76
    {
1256
76
      w = Whiskers(R"(
1257
76
        // <readableTypeName>
1258
76
        function <functionName>(offset, end) -> arrayPos {
1259
76
          arrayPos := offset
1260
76
          if gt(add(arrayPos, mul(<length>, <stride>)), end) { <revertStringPos>() }
1261
76
        }
1262
76
      )");
1263
76
      w("length", toCompactHexWithPrefix(_type.length()));
1264
76
    }
1265
411
    w("revertStringPos", revertReasonIfDebugFunction("ABI decoding: invalid calldata array stride"));
1266
411
    w("functionName", functionName);
1267
411
    w("readableTypeName", _type.toString(true));
1268
411
    w("stride", toCompactHexWithPrefix(_type.calldataStride()));
1269
1270
    // TODO add test
1271
411
    return w.render();
1272
411
  });
1273
433
}
1274
1275
std::string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
1276
799
{
1277
799
  solAssert(_type.dataStoredIn(DataLocation::Memory), "");
1278
799
  solAssert(_type.isByteArrayOrString(), "");
1279
1280
799
  std::string functionName =
1281
799
    "abi_decode_available_length_" +
1282
799
    _type.identifier() +
1283
799
    (_fromMemory ? "_fromMemory" : "");
1284
1285
799
  return createFunction(functionName, [&]() {
1286
799
    Whiskers templ(R"(
1287
799
      function <functionName>(src, length, end) -> array {
1288
799
        array := <allocate>(<allocationSize>(length))
1289
799
        mstore(array, length)
1290
799
        let dst := add(array, 0x20)
1291
799
        if gt(add(src, length), end) { <revertStringLength>() }
1292
799
        <copyToMemFun>(src, dst, length)
1293
799
      }
1294
799
    )");
1295
799
    templ("revertStringLength", revertReasonIfDebugFunction("ABI decoding: invalid byte array length"));
1296
799
    templ("functionName", functionName);
1297
799
    templ("allocate", m_utils.allocationFunction());
1298
799
    templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
1299
799
    templ("copyToMemFun", m_utils.copyToMemoryFunction(!_fromMemory, /*cleanup*/true));
1300
799
    return templ.render();
1301
799
  });
1302
799
}
1303
1304
std::string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
1305
437
{
1306
437
  solAssert(_type.dataStoredIn(DataLocation::CallData), "");
1307
437
  std::string functionName =
1308
437
    "abi_decode_" +
1309
437
    _type.identifier();
1310
1311
437
  return createFunction(functionName, [&]() {
1312
427
    Whiskers w{R"(
1313
427
        // <readableTypeName>
1314
427
        function <functionName>(offset, end) -> value {
1315
427
          if slt(sub(end, offset), <minimumSize>) { <revertString>() }
1316
427
          value := offset
1317
427
        }
1318
427
    )"};
1319
    // TODO add test
1320
427
    w("revertString", revertReasonIfDebugFunction("ABI decoding: struct calldata too short"));
1321
427
    w("functionName", functionName);
1322
427
    w("readableTypeName", _type.toString(true));
1323
427
    w("minimumSize", std::to_string(_type.isDynamicallyEncoded() ? _type.calldataEncodedTailSize() : _type.calldataEncodedSize(true)));
1324
427
    return w.render();
1325
427
  });
1326
437
}
1327
1328
std::string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory)
1329
8.71k
{
1330
8.71k
  solAssert(!_type.dataStoredIn(DataLocation::CallData), "");
1331
8.71k
  std::string functionName =
1332
8.71k
    "abi_decode_" +
1333
8.71k
    _type.identifier() +
1334
8.71k
    (_fromMemory ? "_fromMemory" : "");
1335
1336
8.71k
  return createFunction(functionName, [&]() {
1337
8.46k
    Whiskers templ(R"(
1338
8.46k
      // <readableTypeName>
1339
8.46k
      function <functionName>(headStart, end) -> value {
1340
8.46k
        if slt(sub(end, headStart), <minimumSize>) { <revertString>() }
1341
8.46k
        value := <allocate>(<memorySize>)
1342
8.46k
        <#members>
1343
8.46k
        {
1344
8.46k
          // <memberName>
1345
8.46k
          <decode>
1346
8.46k
        }
1347
8.46k
        </members>
1348
8.46k
      }
1349
8.46k
    )");
1350
    // TODO add test
1351
8.46k
    templ("revertString", revertReasonIfDebugFunction("ABI decoding: struct data too short"));
1352
8.46k
    templ("functionName", functionName);
1353
8.46k
    templ("readableTypeName", _type.toString(true));
1354
8.46k
    templ("allocate", m_utils.allocationFunction());
1355
8.46k
    solAssert(_type.memoryDataSize() < u256("0xffffffffffffffff"), "");
1356
8.46k
    templ("memorySize", toCompactHexWithPrefix(_type.memoryDataSize()));
1357
8.46k
    size_t headPos = 0;
1358
8.46k
    std::vector<std::map<std::string, std::string>> members;
1359
8.46k
    for (auto const& member: _type.members(nullptr))
1360
19.2k
    {
1361
19.2k
      solAssert(member.type, "");
1362
19.2k
      solAssert(!member.type->containsNestedMapping(), "");
1363
19.2k
      auto decodingType = member.type->decodingType();
1364
19.2k
      solAssert(decodingType, "");
1365
19.2k
      Whiskers memberTempl(R"(
1366
19.2k
        <?dynamic>
1367
19.2k
          let offset := <load>(add(headStart, <pos>))
1368
19.2k
          if gt(offset, 0xffffffffffffffff) { <revertString>() }
1369
19.2k
        <!dynamic>
1370
19.2k
          let offset := <pos>
1371
19.2k
        </dynamic>
1372
19.2k
        mstore(add(value, <memoryOffset>), <abiDecode>(add(headStart, offset), end))
1373
19.2k
      )");
1374
19.2k
      memberTempl("dynamic", decodingType->isDynamicallyEncoded());
1375
      // TODO add test
1376
19.2k
      memberTempl("revertString", revertReasonIfDebugFunction("ABI decoding: invalid struct offset"));
1377
19.2k
      memberTempl("load", _fromMemory ? "mload" : "calldataload");
1378
19.2k
      memberTempl("pos", std::to_string(headPos));
1379
19.2k
      memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name)));
1380
19.2k
      memberTempl("abiDecode", abiDecodingFunction(*member.type, _fromMemory, false));
1381
1382
19.2k
      members.emplace_back();
1383
19.2k
      members.back()["decode"] = memberTempl.render();
1384
19.2k
      members.back()["memberName"] = member.name;
1385
19.2k
      headPos += decodingType->calldataHeadSize();
1386
19.2k
    }
1387
8.46k
    templ("members", members);
1388
8.46k
    templ("minimumSize", toCompactHexWithPrefix(headPos));
1389
8.46k
    return templ.render();
1390
8.46k
  });
1391
8.71k
}
1392
1393
std::string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack)
1394
172
{
1395
172
  solAssert(_type.kind() == FunctionType::Kind::External, "");
1396
1397
172
  std::string functionName =
1398
172
    "abi_decode_" +
1399
172
    _type.identifier() +
1400
172
    (_fromMemory ? "_fromMemory" : "") +
1401
172
    (_forUseOnStack ? "_onStack" : "");
1402
1403
172
  return createFunction(functionName, [&]() {
1404
160
    if (_forUseOnStack)
1405
75
    {
1406
75
      return Whiskers(R"(
1407
75
        function <functionName>(offset, end) -> addr, function_selector {
1408
75
          addr, function_selector := <splitExtFun>(<decodeFun>(offset, end))
1409
75
        }
1410
75
      )")
1411
75
      ("functionName", functionName)
1412
75
      ("decodeFun", abiDecodingFunctionFunctionType(_type, _fromMemory, false))
1413
75
      ("splitExtFun", m_utils.splitExternalFunctionIdFunction())
1414
75
      .render();
1415
75
    }
1416
85
    else
1417
85
    {
1418
85
      return Whiskers(R"(
1419
85
        function <functionName>(offset, end) -> fun {
1420
85
          fun := <load>(offset)
1421
85
          <validateExtFun>(fun)
1422
85
        }
1423
85
      )")
1424
85
      ("functionName", functionName)
1425
85
      ("load", _fromMemory ? "mload" : "calldataload")
1426
85
      ("validateExtFun", m_utils.validatorFunction(_type, true))
1427
85
      .render();
1428
85
    }
1429
160
  });
1430
172
}
1431
1432
std::string ABIFunctions::calldataAccessFunction(Type const& _type)
1433
17.3k
{
1434
17.3k
  solAssert(_type.isValueType() || _type.dataStoredIn(DataLocation::CallData), "");
1435
17.3k
  std::string functionName = "calldata_access_" + _type.identifier();
1436
17.3k
  return createFunction(functionName, [&]() {
1437
9.17k
    if (_type.isDynamicallyEncoded())
1438
2.16k
    {
1439
2.16k
      unsigned int tailSize = _type.calldataEncodedTailSize();
1440
2.16k
      solAssert(tailSize > 1, "");
1441
2.16k
      Whiskers w(R"(
1442
2.16k
        function <functionName>(base_ref, ptr) -> <return> {
1443
2.16k
          let rel_offset_of_tail := calldataload(ptr)
1444
2.16k
          if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <revertStringOffset>() }
1445
2.16k
          value := add(rel_offset_of_tail, base_ref)
1446
2.16k
          <handleLength>
1447
2.16k
        }
1448
2.16k
      )");
1449
2.16k
      if (_type.isDynamicallySized())
1450
716
      {
1451
716
        auto const* arrayType = dynamic_cast<ArrayType const*>(&_type);
1452
716
        solAssert(!!arrayType, "");
1453
716
        w("handleLength", Whiskers(R"(
1454
716
          length := calldataload(value)
1455
716
          value := add(value, 0x20)
1456
716
          if gt(length, 0xffffffffffffffff) { <revertStringLength>() }
1457
716
          if sgt(value, sub(calldatasize(), mul(length, <calldataStride>))) { <revertStringStride>() }
1458
716
        )")
1459
716
        ("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride()))
1460
        // TODO add test
1461
716
        ("revertStringLength", revertReasonIfDebugFunction("Invalid calldata access length"))
1462
        // TODO add test
1463
716
        ("revertStringStride", revertReasonIfDebugFunction("Invalid calldata access stride"))
1464
716
        .render());
1465
716
        w("return", "value, length");
1466
716
      }
1467
1.44k
      else
1468
1.44k
      {
1469
1.44k
        w("handleLength", "");
1470
1.44k
        w("return", "value");
1471
1.44k
      }
1472
2.16k
      w("neededLength", toCompactHexWithPrefix(tailSize));
1473
2.16k
      w("functionName", functionName);
1474
2.16k
      w("revertStringOffset", revertReasonIfDebugFunction("Invalid calldata access offset"));
1475
2.16k
      return w.render();
1476
2.16k
    }
1477
7.01k
    else if (_type.isValueType())
1478
1.50k
    {
1479
1.50k
      std::string decodingFunction;
1480
1.50k
      if (auto const* functionType = dynamic_cast<FunctionType const*>(&_type))
1481
0
        decodingFunction = abiDecodingFunctionFunctionType(*functionType, false, false);
1482
1.50k
      else
1483
1.50k
        decodingFunction = abiDecodingFunctionValueType(_type, false);
1484
      // Note that the second argument to the decoding function should be discarded after inlining.
1485
1.50k
      return Whiskers(R"(
1486
1.50k
        function <functionName>(baseRef, ptr) -> value {
1487
1.50k
          value := <decodingFunction>(ptr, add(ptr, 32))
1488
1.50k
        }
1489
1.50k
      )")
1490
1.50k
      ("functionName", functionName)
1491
1.50k
      ("decodingFunction", decodingFunction)
1492
1.50k
      .render();
1493
1.50k
    }
1494
5.51k
    else
1495
5.51k
    {
1496
5.51k
      solAssert(
1497
5.51k
        _type.category() == Type::Category::Array ||
1498
5.51k
        _type.category() == Type::Category::Struct,
1499
5.51k
        ""
1500
5.51k
      );
1501
5.51k
      return Whiskers(R"(
1502
5.51k
        function <functionName>(baseRef, ptr) -> value {
1503
5.51k
          value := ptr
1504
5.51k
        }
1505
5.51k
      )")
1506
5.51k
      ("functionName", functionName)
1507
5.51k
      .render();
1508
5.51k
    }
1509
9.17k
  });
1510
17.3k
}
1511
1512
std::string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options)
1513
8.71k
{
1514
8.71k
  std::string functionName = "array_storeLengthForEncoding_" + _type.identifier() + _options.toFunctionNameSuffix();
1515
8.71k
  return createFunction(functionName, [&]() {
1516
7.05k
    if (_type.isDynamicallySized() && !_options.dynamicInplace)
1517
5.60k
      return Whiskers(R"(
1518
5.60k
        function <functionName>(pos, length) -> updated_pos {
1519
5.60k
          mstore(pos, length)
1520
5.60k
          updated_pos := add(pos, 0x20)
1521
5.60k
        }
1522
5.60k
      )")
1523
5.60k
      ("functionName", functionName)
1524
5.60k
      .render();
1525
1.44k
    else
1526
1.44k
      return Whiskers(R"(
1527
1.44k
        function <functionName>(pos, length) -> updated_pos {
1528
1.44k
          updated_pos := pos
1529
1.44k
        }
1530
1.44k
      )")
1531
1.44k
      ("functionName", functionName)
1532
1.44k
      .render();
1533
7.05k
  });
1534
8.71k
}
1535
1536
std::string ABIFunctions::createFunction(std::string const& _name, std::function<std::string ()> const& _creator)
1537
171k
{
1538
171k
  return m_functionCollector.createFunction(_name, _creator);
1539
171k
}
1540
1541
size_t ABIFunctions::headSize(TypePointers const& _targetTypes)
1542
22.3k
{
1543
22.3k
  size_t headSize = 0;
1544
22.3k
  for (auto const& t: _targetTypes)
1545
24.9k
    headSize += t->calldataHeadSize();
1546
1547
22.3k
  return headSize;
1548
22.3k
}
1549
1550
size_t ABIFunctions::numVariablesForType(Type const& _type, EncodingOptions const& _options)
1551
57.8k
{
1552
57.8k
  if (_type.category() == Type::Category::Function && !_options.encodeFunctionFromStack)
1553
11
    return 1;
1554
57.8k
  else
1555
57.8k
    return _type.sizeOnStack();
1556
57.8k
}
1557
1558
std::string ABIFunctions::revertReasonIfDebugFunction(std::string const& _message)
1559
61.2k
{
1560
61.2k
  return m_utils.revertReasonIfDebugFunction(_message);
1561
61.2k
}