Coverage Report

Created: 2026-08-14 07:35

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