Coverage Report

Created: 2022-08-24 06:55

/src/solidity/test/tools/ossfuzz/protoToYul.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
#include <test/tools/ossfuzz/protoToYul.h>
20
#include <test/tools/ossfuzz/yulOptimizerFuzzDictionary.h>
21
22
#include <libyul/Exceptions.h>
23
24
#include <libsolutil/StringUtils.h>
25
26
#include <range/v3/algorithm/all_of.hpp>
27
28
#include <boost/algorithm/string.hpp>
29
#include <boost/algorithm/string/split.hpp>
30
31
#include <range/v3/action/remove_if.hpp>
32
33
#include <algorithm>
34
35
using namespace std;
36
using namespace solidity::yul::test::yul_fuzzer;
37
using namespace solidity::yul::test;
38
using namespace solidity::langutil;
39
using namespace solidity::util;
40
using namespace solidity;
41
42
string ProtoConverter::dictionaryToken(HexPrefix _p)
43
657k
{
44
657k
  std::string token;
45
  // If dictionary constant is requested while converting
46
  // for loop condition, then return zero so that we don't
47
  // generate infinite for loops.
48
657k
  if (m_inForCond)
49
26.7k
    token = "0";
50
630k
  else
51
630k
  {
52
630k
    unsigned indexVar = m_inputSize * m_inputSize + counter();
53
630k
    token = hexDictionary[indexVar % hexDictionary.size()];
54
630k
    yulAssert(token.size() <= 64, "Proto Fuzzer: Dictionary token too large");
55
630k
  }
56
57
657k
  return _p == HexPrefix::Add ? "0x" + token : token;
58
657k
}
59
60
string ProtoConverter::createHex(string const& _hexBytes)
61
38.3k
{
62
38.3k
  string tmp{_hexBytes};
63
38.3k
  if (!tmp.empty())
64
35.1k
  {
65
6.61M
    ranges::actions::remove_if(tmp, [=](char c) -> bool {
66
6.61M
      return !std::isxdigit(c);
67
6.61M
    });
68
35.1k
    tmp = tmp.substr(0, 64);
69
35.1k
  }
70
  // We need this awkward if case because hex literals cannot be empty.
71
  // Use a dictionary token.
72
38.3k
  if (tmp.empty())
73
23.7k
    tmp = dictionaryToken(HexPrefix::DontAdd);
74
  // Hex literals must have even number of digits
75
38.3k
  if (tmp.size() % 2)
76
19.2k
    tmp.insert(0, "0");
77
78
38.3k
  yulAssert(tmp.size() <= 64, "Proto Fuzzer: Dictionary token too large");
79
38.3k
  return tmp;
80
38.3k
}
81
82
string ProtoConverter::createAlphaNum(string const& _strBytes)
83
12.6k
{
84
12.6k
  string tmp{_strBytes};
85
12.6k
  if (!tmp.empty())
86
10.3k
  {
87
288k
    ranges::actions::remove_if(tmp, [=](char c) -> bool {
88
288k
      return !(std::isalpha(c) || std::isdigit(c));
89
288k
    });
90
10.3k
    tmp = tmp.substr(0, 32);
91
10.3k
  }
92
12.6k
  return tmp;
93
12.6k
}
94
95
EVMVersion ProtoConverter::evmVersionMapping(Program_Version const& _ver)
96
36.7k
{
97
36.7k
  switch (_ver)
98
36.7k
  {
99
11.0k
  case Program::HOMESTEAD:
100
11.0k
    return EVMVersion::homestead();
101
1.08k
  case Program::TANGERINE:
102
1.08k
    return EVMVersion::tangerineWhistle();
103
1.91k
  case Program::SPURIOUS:
104
1.91k
    return EVMVersion::spuriousDragon();
105
1.05k
  case Program::BYZANTIUM:
106
1.05k
    return EVMVersion::byzantium();
107
707
  case Program::CONSTANTINOPLE:
108
707
    return EVMVersion::constantinople();
109
1.21k
  case Program::PETERSBURG:
110
1.21k
    return EVMVersion::petersburg();
111
3.99k
  case Program::ISTANBUL:
112
3.99k
    return EVMVersion::istanbul();
113
15.7k
  case Program::BERLIN:
114
15.7k
    return EVMVersion::berlin();
115
36.7k
  }
116
36.7k
}
117
118
string ProtoConverter::visit(Literal const& _x)
119
132k
{
120
132k
  switch (_x.literal_oneof_case())
121
132k
  {
122
45.3k
  case Literal::kIntval:
123
45.3k
    return to_string(_x.intval());
124
31.7k
  case Literal::kHexval:
125
31.7k
    return "0x" + createHex(_x.hexval());
126
12.6k
  case Literal::kStrval:
127
12.6k
    return "\"" + createAlphaNum(_x.strval()) + "\"";
128
6.92k
  case Literal::kBoolval:
129
6.92k
    return _x.boolval() ? "true" : "false";
130
36.3k
  case Literal::LITERAL_ONEOF_NOT_SET:
131
36.3k
    return dictionaryToken();
132
132k
  }
133
132k
}
134
135
void ProtoConverter::consolidateVarDeclsInFunctionDef()
136
56.8k
{
137
56.8k
  m_currentFuncVars.clear();
138
56.8k
  yulAssert(!m_funcVars.empty(), "Proto fuzzer: Invalid operation");
139
140
56.8k
  auto const& scopes = m_funcVars.back();
141
56.8k
  for (auto const& s: scopes)
142
154k
    for (auto const& var: s)
143
261k
      m_currentFuncVars.push_back(&var);
144
56.8k
  yulAssert(!m_funcForLoopInitVars.empty(), "Proto fuzzer: Invalid operation");
145
56.8k
  auto const& forinitscopes = m_funcForLoopInitVars.back();
146
56.8k
  for (auto const& s: forinitscopes)
147
10.6k
    for (auto const& var: s)
148
6.62k
      m_currentFuncVars.push_back(&var);
149
56.8k
}
150
151
void ProtoConverter::consolidateGlobalVarDecls()
152
41.6k
{
153
41.6k
  m_currentGlobalVars.clear();
154
  // Place pointers to all global variables that are in scope
155
  // into a single vector
156
41.6k
  for (auto const& scope: m_globalVars)
157
81.7k
    for (auto const& var: scope)
158
153k
      m_currentGlobalVars.push_back(&var);
159
  // Place pointers to all variables declared in for-init blocks
160
  // that are still live into the same vector
161
41.6k
  for (auto const& init: m_globalForLoopInitVars)
162
5.08k
    for (auto const& var: init)
163
10.7k
      m_currentGlobalVars.push_back(&var);
164
41.6k
}
165
166
bool ProtoConverter::varDeclAvailable()
167
98.4k
{
168
98.4k
  if (m_inFunctionDef)
169
56.8k
  {
170
56.8k
    consolidateVarDeclsInFunctionDef();
171
56.8k
    return !m_currentFuncVars.empty();
172
56.8k
  }
173
41.6k
  else
174
41.6k
  {
175
41.6k
    consolidateGlobalVarDecls();
176
41.6k
    return !m_currentGlobalVars.empty();
177
41.6k
  }
178
98.4k
}
179
180
void ProtoConverter::visit(VarRef const& _x)
181
78.8k
{
182
78.8k
  if (m_inFunctionDef)
183
56.1k
  {
184
    // Ensure that there is at least one variable declaration to reference in function scope.
185
56.1k
    yulAssert(!m_currentFuncVars.empty(), "Proto fuzzer: No variables to reference.");
186
56.1k
    m_output << *m_currentFuncVars[static_cast<size_t>(_x.varnum()) % m_currentFuncVars.size()];
187
56.1k
  }
188
22.6k
  else
189
22.6k
  {
190
    // Ensure that there is at least one variable declaration to reference in nested scopes.
191
22.6k
    yulAssert(!m_currentGlobalVars.empty(), "Proto fuzzer: No global variables to reference.");
192
22.6k
    m_output << *m_currentGlobalVars[static_cast<size_t>(_x.varnum()) % m_currentGlobalVars.size()];
193
22.6k
  }
194
78.8k
}
195
196
void ProtoConverter::visit(Expression const& _x)
197
936k
{
198
936k
  switch (_x.expr_oneof_case())
199
936k
  {
200
76.6k
  case Expression::kVarref:
201
    // If the expression requires a variable reference that we cannot provide
202
    // (because there are no variables in scope), we silently output a literal
203
    // expression from the optimizer dictionary.
204
76.6k
    if (!varDeclAvailable())
205
15.7k
      m_output << dictionaryToken();
206
60.9k
    else
207
60.9k
      visit(_x.varref());
208
76.6k
    break;
209
71.8k
  case Expression::kCons:
210
    // If literal expression describes for-loop condition
211
    // then force it to zero, so we don't generate infinite
212
    // for loops
213
71.8k
    if (m_inForCond)
214
1.16k
      m_output << "0";
215
70.6k
    else
216
70.6k
      m_output << visit(_x.cons());
217
71.8k
    break;
218
92.3k
  case Expression::kBinop:
219
92.3k
    visit(_x.binop());
220
92.3k
    break;
221
49.4k
  case Expression::kUnop:
222
49.4k
    visit(_x.unop());
223
49.4k
    break;
224
20.7k
  case Expression::kTop:
225
20.7k
    visit(_x.top());
226
20.7k
    break;
227
29.3k
  case Expression::kNop:
228
29.3k
    visit(_x.nop());
229
29.3k
    break;
230
26.8k
  case Expression::kFuncExpr:
231
26.8k
    if (auto v = functionExists(NumFunctionReturns::Single); v.has_value())
232
21.9k
    {
233
21.9k
      string functionName = v.value();
234
21.9k
      visit(_x.func_expr(), functionName, true);
235
21.9k
    }
236
4.97k
    else
237
4.97k
      m_output << dictionaryToken();
238
26.8k
    break;
239
15.3k
  case Expression::kLowcall:
240
15.3k
    visit(_x.lowcall());
241
15.3k
    break;
242
9.02k
  case Expression::kCreate:
243
    // Create and create2 return address of created contract which
244
    // may lead to state change via sstore of the returned address.
245
9.02k
    if (!m_filterStatefulInstructions)
246
8.12k
      visit(_x.create());
247
898
    else
248
898
      m_output << dictionaryToken();
249
9.02k
    break;
250
17.1k
  case Expression::kUnopdata:
251
    // Filter datasize and dataoffset because these instructions may return
252
    // a value that is a function of optimisation. Therefore, when run on
253
    // an EVM client, the execution traces for unoptimised vs optimised
254
    // programs may differ. This ends up as a false-positive bug report.
255
17.1k
    if (m_isObject && !m_filterStatefulInstructions)
256
2.66k
      visit(_x.unopdata());
257
14.5k
    else
258
14.5k
      m_output << dictionaryToken();
259
17.1k
    break;
260
527k
  case Expression::EXPR_ONEOF_NOT_SET:
261
527k
    m_output << dictionaryToken();
262
527k
    break;
263
936k
  }
264
936k
}
265
266
void ProtoConverter::visit(BinaryOp const& _x)
267
92.3k
{
268
92.3k
  BinaryOp_BOp op = _x.op();
269
270
92.3k
  if ((op == BinaryOp::SHL || op == BinaryOp::SHR || op == BinaryOp::SAR) &&
271
92.3k
    !m_evmVersion.hasBitwiseShifting())
272
576
  {
273
576
    m_output << dictionaryToken();
274
576
    return;
275
576
  }
276
277
91.7k
  switch (op)
278
91.7k
  {
279
6.41k
  case BinaryOp::ADD:
280
6.41k
    m_output << "add";
281
6.41k
    break;
282
2.94k
  case BinaryOp::SUB:
283
2.94k
    m_output << "sub";
284
2.94k
    break;
285
7.72k
  case BinaryOp::MUL:
286
7.72k
    m_output << "mul";
287
7.72k
    break;
288
14.3k
  case BinaryOp::DIV:
289
14.3k
    m_output << "div";
290
14.3k
    break;
291
5.93k
  case BinaryOp::MOD:
292
5.93k
    m_output << "mod";
293
5.93k
    break;
294
2.90k
  case BinaryOp::XOR:
295
2.90k
    m_output << "xor";
296
2.90k
    break;
297
2.60k
  case BinaryOp::AND:
298
2.60k
    m_output << "and";
299
2.60k
    break;
300
5.00k
  case BinaryOp::OR:
301
5.00k
    m_output << "or";
302
5.00k
    break;
303
1.18k
  case BinaryOp::EQ:
304
1.18k
    m_output << "eq";
305
1.18k
    break;
306
1.68k
  case BinaryOp::LT:
307
1.68k
    m_output << "lt";
308
1.68k
    break;
309
2.56k
  case BinaryOp::GT:
310
2.56k
    m_output << "gt";
311
2.56k
    break;
312
2.19k
  case BinaryOp::SHR:
313
2.19k
    yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version");
314
2.19k
    m_output << "shr";
315
2.19k
    break;
316
4.40k
  case BinaryOp::SHL:
317
4.40k
    yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version");
318
4.40k
    m_output << "shl";
319
4.40k
    break;
320
656
  case BinaryOp::SAR:
321
656
    yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version");
322
656
    m_output << "sar";
323
656
    break;
324
6.01k
  case BinaryOp::SDIV:
325
6.01k
    m_output << "sdiv";
326
6.01k
    break;
327
10.3k
  case BinaryOp::SMOD:
328
10.3k
    m_output << "smod";
329
10.3k
    break;
330
3.79k
  case BinaryOp::EXP:
331
3.79k
    m_output << "exp";
332
3.79k
    break;
333
1.26k
  case BinaryOp::SLT:
334
1.26k
    m_output << "slt";
335
1.26k
    break;
336
1.61k
  case BinaryOp::SGT:
337
1.61k
    m_output << "sgt";
338
1.61k
    break;
339
1.20k
  case BinaryOp::BYTE:
340
1.20k
    m_output << "byte";
341
1.20k
    break;
342
4.89k
  case BinaryOp::SI:
343
4.89k
    m_output << "signextend";
344
4.89k
    break;
345
2.07k
  case BinaryOp::KECCAK:
346
2.07k
    m_output << "keccak256";
347
2.07k
    break;
348
91.7k
  }
349
91.7k
  m_output << "(";
350
91.7k
  visit(_x.left());
351
91.7k
  m_output << ",";
352
91.7k
  visit(_x.right());
353
91.7k
  m_output << ")";
354
91.7k
}
355
356
void ProtoConverter::scopeVariables(vector<string> const& _varNames)
357
22.3k
{
358
  // If we are inside a for-init block, there are two places
359
  // where the visited vardecl may have been defined:
360
  // - directly inside the for-init block
361
  // - inside a block within the for-init block
362
  // In the latter case, we don't scope extend. The flag
363
  // m_forInitScopeExtEnabled (= true) indicates whether we are directly
364
  // inside a for-init block e.g., for { let x } or (= false) inside a
365
  // nested for-init block e.g., for { { let x } }
366
22.3k
  bool forInitScopeExtendVariable = m_inForInitScope && m_forInitScopeExtEnabled;
367
368
  // There are four cases that are tackled here
369
  // Case 1. We are inside a function definition and the variable declaration's
370
  // scope needs to be extended.
371
  // Case 2. We are inside a function definition but scope extension is disabled
372
  // Case 3. We are inside global scope and scope extension is required
373
  // Case 4. We are inside global scope but scope extension is disabled
374
22.3k
  if (m_inFunctionDef)
375
7.83k
  {
376
    // Variables declared directly in for-init block
377
    // are tracked separately because their scope
378
    // extends beyond the block they are defined in
379
    // to the rest of the for-loop statement.
380
    // Case 1
381
7.83k
    if (forInitScopeExtendVariable)
382
815
    {
383
815
      yulAssert(
384
815
        !m_funcForLoopInitVars.empty() && !m_funcForLoopInitVars.back().empty(),
385
815
        "Proto fuzzer: Invalid operation"
386
815
      );
387
815
      for (auto const& varName: _varNames)
388
2.16k
        m_funcForLoopInitVars.back().back().push_back(varName);
389
815
    }
390
    // Case 2
391
7.02k
    else
392
7.02k
    {
393
7.02k
      yulAssert(
394
7.02k
        !m_funcVars.empty() && !m_funcVars.back().empty(),
395
7.02k
        "Proto fuzzer: Invalid operation"
396
7.02k
      );
397
7.02k
      for (auto const& varName: _varNames)
398
14.3k
        m_funcVars.back().back().push_back(varName);
399
7.02k
    }
400
7.83k
  }
401
  // If m_inFunctionDef is false, we are in global scope
402
14.4k
  else
403
14.4k
  {
404
    // Case 3
405
14.4k
    if (forInitScopeExtendVariable)
406
856
    {
407
856
      yulAssert(!m_globalForLoopInitVars.empty(), "Proto fuzzer: Invalid operation");
408
409
856
      for (auto const& varName: _varNames)
410
2.41k
        m_globalForLoopInitVars.back().push_back(varName);
411
856
    }
412
    // Case 4
413
13.6k
    else
414
13.6k
    {
415
13.6k
      yulAssert(!m_globalVars.empty(), "Proto fuzzer: Invalid operation");
416
417
13.6k
      for (auto const& varName: _varNames)
418
27.9k
        m_globalVars.back().push_back(varName);
419
13.6k
    }
420
14.4k
  }
421
22.3k
}
422
423
void ProtoConverter::visit(VarDecl const& _x)
424
9.75k
{
425
9.75k
  string varName = newVarName();
426
9.75k
  m_output << "let " << varName << " := ";
427
9.75k
  visit(_x.expr());
428
9.75k
  m_output << "\n";
429
9.75k
  scopeVariables({varName});
430
9.75k
}
431
432
void ProtoConverter::visit(MultiVarDecl const& _x)
433
12.5k
{
434
12.5k
  m_output << "let ";
435
12.5k
  vector<string> varNames;
436
  // We support up to 4 variables in a single
437
  // declaration statement.
438
12.5k
  unsigned numVars = _x.num_vars() % 3 + 2;
439
12.5k
  string delimiter;
440
49.6k
  for (unsigned i = 0; i < numVars; i++)
441
37.1k
  {
442
37.1k
    string varName = newVarName();
443
37.1k
    varNames.push_back(varName);
444
37.1k
    m_output << delimiter << varName;
445
37.1k
    if (i == 0)
446
12.5k
      delimiter = ", ";
447
37.1k
  }
448
12.5k
  m_output << "\n";
449
12.5k
  scopeVariables(varNames);
450
12.5k
}
451
452
void ProtoConverter::visit(TypedVarDecl const& _x)
453
0
{
454
0
  string varName = newVarName();
455
0
  m_output << "let " << varName;
456
0
  switch (_x.type())
457
0
  {
458
0
  case TypedVarDecl::BOOL:
459
0
    m_output << ": bool := ";
460
0
    visit(_x.expr());
461
0
    m_output << " : bool\n";
462
0
    break;
463
0
  case TypedVarDecl::S8:
464
0
    m_output << ": s8 := ";
465
0
    visit(_x.expr());
466
0
    m_output << " : s8\n";
467
0
    break;
468
0
  case TypedVarDecl::S32:
469
0
    m_output << ": s32 := ";
470
0
    visit(_x.expr());
471
0
    m_output << " : s32\n";
472
0
    break;
473
0
  case TypedVarDecl::S64:
474
0
    m_output << ": s64 := ";
475
0
    visit(_x.expr());
476
0
    m_output << " : s64\n";
477
0
    break;
478
0
  case TypedVarDecl::S128:
479
0
    m_output << ": s128 := ";
480
0
    visit(_x.expr());
481
0
    m_output << " : s128\n";
482
0
    break;
483
0
  case TypedVarDecl::S256:
484
0
    m_output << ": s256 := ";
485
0
    visit(_x.expr());
486
0
    m_output << " : s256\n";
487
0
    break;
488
0
  case TypedVarDecl::U8:
489
0
    m_output << ": u8 := ";
490
0
    visit(_x.expr());
491
0
    m_output << " : u8\n";
492
0
    break;
493
0
  case TypedVarDecl::U32:
494
0
    m_output << ": u32 := ";
495
0
    visit(_x.expr());
496
0
    m_output << " : u32\n";
497
0
    break;
498
0
  case TypedVarDecl::U64:
499
0
    m_output << ": u64 := ";
500
0
    visit(_x.expr());
501
0
    m_output << " : u64\n";
502
0
    break;
503
0
  case TypedVarDecl::U128:
504
0
    m_output << ": u128 := ";
505
0
    visit(_x.expr());
506
0
    m_output << " : u128\n";
507
0
    break;
508
0
  case TypedVarDecl::U256:
509
0
    m_output << ": u256 := ";
510
0
    visit(_x.expr());
511
0
    m_output << " : u256\n";
512
0
    break;
513
0
  }
514
  // If we are inside a for-init block, there are two places
515
  // where the visited vardecl may have been defined:
516
  // - directly inside the for-init block
517
  // - inside a block within the for-init block
518
  // In the latter case, we don't scope extend.
519
0
  if (m_inFunctionDef)
520
0
  {
521
    // Variables declared directly in for-init block
522
    // are tracked separately because their scope
523
    // extends beyond the block they are defined in
524
    // to the rest of the for-loop statement.
525
0
    if (m_inForInitScope && m_forInitScopeExtEnabled)
526
0
    {
527
0
      yulAssert(
528
0
        !m_funcForLoopInitVars.empty() && !m_funcForLoopInitVars.back().empty(),
529
0
        "Proto fuzzer: Invalid operation"
530
0
      );
531
0
      m_funcForLoopInitVars.back().back().push_back(varName);
532
0
    }
533
0
    else
534
0
    {
535
0
      yulAssert(
536
0
        !m_funcVars.empty() && !m_funcVars.back().empty(),
537
0
        "Proto fuzzer: Invalid operation"
538
0
      );
539
0
      m_funcVars.back().back().push_back(varName);
540
0
    }
541
0
  }
542
0
  else
543
0
  {
544
0
    if (m_inForInitScope && m_forInitScopeExtEnabled)
545
0
    {
546
0
      yulAssert(
547
0
        !m_globalForLoopInitVars.empty(),
548
0
        "Proto fuzzer: Invalid operation"
549
0
      );
550
0
      m_globalForLoopInitVars.back().push_back(varName);
551
0
    }
552
0
    else
553
0
    {
554
0
      yulAssert(
555
0
        !m_globalVars.empty(),
556
0
        "Proto fuzzer: Invalid operation"
557
0
      );
558
0
      m_globalVars.back().push_back(varName);
559
0
    }
560
0
  }
561
0
}
562
563
void ProtoConverter::visit(UnaryOp const& _x)
564
49.4k
{
565
49.4k
  UnaryOp_UOp op = _x.op();
566
567
  // Replace calls to extcodehash on unsupported EVMs with a dictionary
568
  // token.
569
49.4k
  if (op == UnaryOp::EXTCODEHASH && !m_evmVersion.hasExtCodeHash())
570
448
  {
571
448
    m_output << dictionaryToken();
572
448
    return;
573
448
  }
574
575
  // The following instructions may lead to change of EVM state and are hence
576
  // excluded to avoid false positives.
577
48.9k
  if (
578
48.9k
    m_filterStatefulInstructions &&
579
48.9k
    (
580
5.58k
      op == UnaryOp::EXTCODEHASH ||
581
5.58k
      op == UnaryOp::EXTCODESIZE ||
582
5.58k
      op == UnaryOp::BALANCE ||
583
5.58k
      op == UnaryOp::BLOCKHASH
584
5.58k
    )
585
48.9k
  )
586
271
  {
587
271
    m_output << dictionaryToken();
588
271
    return;
589
271
  }
590
591
48.7k
  switch (op)
592
48.7k
  {
593
34.4k
  case UnaryOp::NOT:
594
34.4k
    m_output << "not";
595
34.4k
    break;
596
3.93k
  case UnaryOp::MLOAD:
597
3.93k
    m_output << "mload";
598
3.93k
    break;
599
3.31k
  case UnaryOp::SLOAD:
600
3.31k
    m_output << "sload";
601
3.31k
    break;
602
1.92k
  case UnaryOp::ISZERO:
603
1.92k
    m_output << "iszero";
604
1.92k
    break;
605
1.24k
  case UnaryOp::CALLDATALOAD:
606
1.24k
    m_output << "calldataload";
607
1.24k
    break;
608
697
  case UnaryOp::EXTCODESIZE:
609
697
    m_output << "extcodesize";
610
697
    break;
611
688
  case UnaryOp::EXTCODEHASH:
612
688
    m_output << "extcodehash";
613
688
    break;
614
915
  case UnaryOp::BALANCE:
615
915
    m_output << "balance";
616
915
    break;
617
1.49k
  case UnaryOp::BLOCKHASH:
618
1.49k
    m_output << "blockhash";
619
1.49k
    break;
620
48.7k
  }
621
48.7k
  m_output << "(";
622
48.7k
  visit(_x.operand());
623
48.7k
  m_output << ")";
624
48.7k
}
625
626
void ProtoConverter::visit(TernaryOp const& _x)
627
20.7k
{
628
20.7k
  switch (_x.op())
629
20.7k
  {
630
13.2k
  case TernaryOp::ADDM:
631
13.2k
    m_output << "addmod";
632
13.2k
    break;
633
7.49k
  case TernaryOp::MULM:
634
7.49k
    m_output << "mulmod";
635
7.49k
    break;
636
20.7k
  }
637
20.7k
  m_output << "(";
638
20.7k
  visit(_x.arg1());
639
20.7k
  m_output << ", ";
640
20.7k
  visit(_x.arg2());
641
20.7k
  m_output << ", ";
642
20.7k
  visit(_x.arg3());
643
20.7k
  m_output << ")";
644
20.7k
}
645
646
void ProtoConverter::visit(NullaryOp const& _x)
647
29.3k
{
648
29.3k
  auto op = _x.op();
649
  // The following instructions may lead to a change in EVM state and are
650
  // excluded to avoid false positive reports.
651
29.3k
  if (
652
29.3k
    m_filterStatefulInstructions &&
653
29.3k
    (
654
4.91k
      op == NullaryOp::GAS ||
655
4.91k
      op == NullaryOp::CODESIZE ||
656
4.91k
      op == NullaryOp::ADDRESS ||
657
4.91k
      op == NullaryOp::TIMESTAMP ||
658
4.91k
      op == NullaryOp::NUMBER ||
659
4.91k
      op == NullaryOp::DIFFICULTY
660
4.91k
    )
661
29.3k
  )
662
1.07k
  {
663
1.07k
    m_output << dictionaryToken();
664
1.07k
    return;
665
1.07k
  }
666
667
28.2k
  switch (op)
668
28.2k
  {
669
5.14k
  case NullaryOp::MSIZE:
670
5.14k
    m_output << "msize()";
671
5.14k
    break;
672
2.17k
  case NullaryOp::GAS:
673
2.17k
    m_output << "gas()";
674
2.17k
    break;
675
1.13k
  case NullaryOp::CALLDATASIZE:
676
1.13k
    m_output << "calldatasize()";
677
1.13k
    break;
678
2.66k
  case NullaryOp::CODESIZE:
679
2.66k
    m_output << "codesize()";
680
2.66k
    break;
681
1.07k
  case NullaryOp::RETURNDATASIZE:
682
    // If evm supports returndatasize, we generate it. Otherwise,
683
    // we output a dictionary token.
684
1.07k
    if (m_evmVersion.supportsReturndata())
685
721
      m_output << "returndatasize()";
686
357
    else
687
357
      m_output << dictionaryToken();
688
1.07k
    break;
689
1.64k
  case NullaryOp::ADDRESS:
690
1.64k
    m_output << "address()";
691
1.64k
    break;
692
2.26k
  case NullaryOp::ORIGIN:
693
2.26k
    m_output << "origin()";
694
2.26k
    break;
695
1.22k
  case NullaryOp::CALLER:
696
1.22k
    m_output << "caller()";
697
1.22k
    break;
698
1.29k
  case NullaryOp::CALLVALUE:
699
1.29k
    m_output << "callvalue()";
700
1.29k
    break;
701
1.48k
  case NullaryOp::GASPRICE:
702
1.48k
    m_output << "gasprice()";
703
1.48k
    break;
704
1.20k
  case NullaryOp::COINBASE:
705
1.20k
    m_output << "coinbase()";
706
1.20k
    break;
707
683
  case NullaryOp::TIMESTAMP:
708
683
    m_output << "timestamp()";
709
683
    break;
710
1.56k
  case NullaryOp::NUMBER:
711
1.56k
    m_output << "number()";
712
1.56k
    break;
713
809
  case NullaryOp::DIFFICULTY:
714
809
    m_output << "difficulty()";
715
809
    break;
716
1.08k
  case NullaryOp::GASLIMIT:
717
1.08k
    m_output << "gaslimit()";
718
1.08k
    break;
719
1.36k
  case NullaryOp::SELFBALANCE:
720
    // Replace calls to selfbalance() on unsupported EVMs with a dictionary
721
    // token.
722
1.36k
    if (m_evmVersion.hasSelfBalance())
723
565
      m_output << "selfbalance()";
724
802
    else
725
802
      m_output << dictionaryToken();
726
1.36k
    break;
727
1.44k
  case NullaryOp::CHAINID:
728
    // Replace calls to chainid() on unsupported EVMs with a dictionary
729
    // token.
730
1.44k
    if (m_evmVersion.hasChainID())
731
935
      m_output << "chainid()";
732
509
    else
733
509
      m_output << dictionaryToken();
734
1.44k
    break;
735
28.2k
  }
736
28.2k
}
737
738
void ProtoConverter::visit(CopyFunc const& _x)
739
9.45k
{
740
9.45k
  CopyFunc_CopyType type = _x.ct();
741
742
  // datacopy() is valid only if we are inside
743
  // a Yul object.
744
9.45k
  if (type == CopyFunc::DATA && !m_isObject)
745
1.30k
    return;
746
747
  // We don't generate code if the copy function is returndatacopy
748
  // and the underlying evm does not support it.
749
8.14k
  if (type == CopyFunc::RETURNDATA && !m_evmVersion.supportsReturndata())
750
119
    return;
751
752
  // Code copy may change state if e.g., some byte of code
753
  // is stored to storage via a sequence of mload and sstore.
754
8.03k
  if (m_filterStatefulInstructions && type == CopyFunc::CODE)
755
65
    return;
756
757
7.96k
  switch (type)
758
7.96k
  {
759
3.04k
  case CopyFunc::CALLDATA:
760
3.04k
    m_output << "calldatacopy";
761
3.04k
    break;
762
3.71k
  case CopyFunc::CODE:
763
3.71k
    m_output << "codecopy";
764
3.71k
    break;
765
713
  case CopyFunc::RETURNDATA:
766
713
    yulAssert(m_evmVersion.supportsReturndata(), "Proto fuzzer: Invalid evm version");
767
713
    m_output << "returndatacopy";
768
713
    break;
769
493
  case CopyFunc::DATA:
770
493
    m_output << "datacopy";
771
493
    break;
772
7.96k
  }
773
7.96k
  m_output << "(";
774
7.96k
  visit(_x.target());
775
7.96k
  m_output << ", ";
776
7.96k
  visit(_x.source());
777
7.96k
  m_output << ", ";
778
7.96k
  visit(_x.size());
779
7.96k
  m_output << ")\n";
780
7.96k
}
781
782
void ProtoConverter::visit(ExtCodeCopy const& _x)
783
8.57k
{
784
8.57k
  m_output << "extcodecopy";
785
8.57k
  m_output << "(";
786
8.57k
  visit(_x.addr());
787
8.57k
  m_output << ", ";
788
8.57k
  visit(_x.target());
789
8.57k
  m_output << ", ";
790
8.57k
  visit(_x.source());
791
8.57k
  m_output << ", ";
792
8.57k
  visit(_x.size());
793
8.57k
  m_output << ")\n";
794
8.57k
}
795
796
void ProtoConverter::visit(LogFunc const& _x)
797
6.10k
{
798
6.10k
  switch (_x.num_topics())
799
6.10k
  {
800
2.21k
  case LogFunc::ZERO:
801
2.21k
    m_output << "log0";
802
2.21k
    m_output << "(";
803
2.21k
    visit(_x.pos());
804
2.21k
    m_output << ", ";
805
2.21k
    visit(_x.size());
806
2.21k
    m_output << ")\n";
807
2.21k
    break;
808
1.08k
  case LogFunc::ONE:
809
1.08k
    m_output << "log1";
810
1.08k
    m_output << "(";
811
1.08k
    visit(_x.pos());
812
1.08k
    m_output << ", ";
813
1.08k
    visit(_x.size());
814
1.08k
    m_output << ", ";
815
1.08k
    visit(_x.t1());
816
1.08k
    m_output << ")\n";
817
1.08k
    break;
818
727
  case LogFunc::TWO:
819
727
    m_output << "log2";
820
727
    m_output << "(";
821
727
    visit(_x.pos());
822
727
    m_output << ", ";
823
727
    visit(_x.size());
824
727
    m_output << ", ";
825
727
    visit(_x.t1());
826
727
    m_output << ", ";
827
727
    visit(_x.t2());
828
727
    m_output << ")\n";
829
727
    break;
830
726
  case LogFunc::THREE:
831
726
    m_output << "log3";
832
726
    m_output << "(";
833
726
    visit(_x.pos());
834
726
    m_output << ", ";
835
726
    visit(_x.size());
836
726
    m_output << ", ";
837
726
    visit(_x.t1());
838
726
    m_output << ", ";
839
726
    visit(_x.t2());
840
726
    m_output << ", ";
841
726
    visit(_x.t3());
842
726
    m_output << ")\n";
843
726
    break;
844
1.34k
  case LogFunc::FOUR:
845
1.34k
    m_output << "log4";
846
1.34k
    m_output << "(";
847
1.34k
    visit(_x.pos());
848
1.34k
    m_output << ", ";
849
1.34k
    visit(_x.size());
850
1.34k
    m_output << ", ";
851
1.34k
    visit(_x.t1());
852
1.34k
    m_output << ", ";
853
1.34k
    visit(_x.t2());
854
1.34k
    m_output << ", ";
855
1.34k
    visit(_x.t3());
856
1.34k
    m_output << ", ";
857
1.34k
    visit(_x.t4());
858
1.34k
    m_output << ")\n";
859
1.34k
    break;
860
6.10k
  }
861
6.10k
}
862
863
void ProtoConverter::visit(AssignmentStatement const& _x)
864
17.9k
{
865
17.9k
  visit(_x.ref_id());
866
17.9k
  m_output << " := ";
867
17.9k
  visit(_x.expr());
868
17.9k
  m_output << "\n";
869
17.9k
}
870
871
void ProtoConverter::visitFunctionInputParams(FunctionCall const& _x, unsigned _numInputParams)
872
92.3k
{
873
  // We reverse the order of function input visits since it helps keep this switch case concise.
874
92.3k
  switch (_numInputParams)
875
92.3k
  {
876
31.0k
  case 4:
877
31.0k
    visit(_x.in_param4());
878
31.0k
    m_output << ", ";
879
31.0k
    [[fallthrough]];
880
44.9k
  case 3:
881
44.9k
    visit(_x.in_param3());
882
44.9k
    m_output << ", ";
883
44.9k
    [[fallthrough]];
884
53.1k
  case 2:
885
53.1k
    visit(_x.in_param2());
886
53.1k
    m_output << ", ";
887
53.1k
    [[fallthrough]];
888
84.8k
  case 1:
889
84.8k
    visit(_x.in_param1());
890
84.8k
    [[fallthrough]];
891
92.3k
  case 0:
892
92.3k
    break;
893
0
  default:
894
0
    yulAssert(false, "Proto fuzzer: Function call with too many input parameters.");
895
0
    break;
896
92.3k
  }
897
92.3k
}
898
899
void ProtoConverter::convertFunctionCall(
900
  FunctionCall const& _x,
901
  string const& _name,
902
  unsigned _numInParams,
903
  bool _newLine
904
)
905
92.3k
{
906
92.3k
  m_output << _name << "(";
907
92.3k
  visitFunctionInputParams(_x, _numInParams);
908
92.3k
  m_output << ")";
909
92.3k
  if (_newLine)
910
92.3k
    m_output << "\n";
911
92.3k
}
912
913
vector<string> ProtoConverter::createVarDecls(unsigned _start, unsigned _end, bool _isAssignment)
914
92.3k
{
915
92.3k
  m_output << "let ";
916
92.3k
  vector<string> varsVec = createVars(_start, _end);
917
92.3k
  if (_isAssignment)
918
92.3k
    m_output << " := ";
919
0
  else
920
0
    m_output << "\n";
921
92.3k
  return varsVec;
922
92.3k
}
923
924
optional<string> ProtoConverter::functionExists(NumFunctionReturns _numReturns)
925
26.8k
{
926
26.8k
  for (auto const& item: m_functionSigMap)
927
43.7k
    if (_numReturns == NumFunctionReturns::None || _numReturns == NumFunctionReturns::Single)
928
43.7k
    {
929
43.7k
      if (item.second.second == static_cast<unsigned>(_numReturns))
930
21.9k
        return item.first;
931
43.7k
    }
932
0
    else
933
0
    {
934
0
      if (item.second.second >= static_cast<unsigned>(_numReturns))
935
0
        return item.first;
936
0
    }
937
4.97k
  return nullopt;
938
26.8k
}
939
940
void ProtoConverter::visit(FunctionCall const& _x, string const& _functionName, bool _expression)
941
92.3k
{
942
92.3k
  yulAssert(m_functionSigMap.count(_functionName), "Proto fuzzer: Invalid function.");
943
92.3k
  auto ret = m_functionSigMap.at(_functionName);
944
92.3k
  unsigned numInParams = ret.first;
945
92.3k
  unsigned numOutParams = ret.second;
946
947
92.3k
  if (numOutParams == 0)
948
31.2k
  {
949
31.2k
    convertFunctionCall(_x, _functionName, numInParams);
950
31.2k
    return;
951
31.2k
  }
952
61.0k
  else
953
61.0k
  {
954
61.0k
    yulAssert(numOutParams > 0, "");
955
61.0k
    vector<string> varsVec;
956
61.0k
    if (!_expression)
957
39.1k
    {
958
      // Obtain variable name suffix
959
39.1k
      unsigned startIdx = counter();
960
39.1k
      varsVec = createVarDecls(
961
39.1k
        startIdx,
962
39.1k
        startIdx + numOutParams,
963
39.1k
        /*isAssignment=*/true
964
39.1k
      );
965
39.1k
    }
966
61.0k
    convertFunctionCall(_x, _functionName, numInParams);
967
    // Add newly minted vars in the multidecl statement to current scope
968
61.0k
    if (!_expression)
969
39.1k
      addVarsToScope(varsVec);
970
61.0k
  }
971
92.3k
}
972
973
void ProtoConverter::visit(LowLevelCall const& _x)
974
15.3k
{
975
15.3k
  LowLevelCall_Type type = _x.callty();
976
977
  // Generate staticcall if it is supported by the underlying evm
978
15.3k
  if (type == LowLevelCall::STATICCALL && !m_evmVersion.hasStaticCall())
979
244
  {
980
    // Since staticcall is supposed to return 0 on success and 1 on
981
    // failure, we can use counter value to emulate it
982
244
    m_output << ((counter() % 2) ? "0" : "1");
983
244
    return;
984
244
  }
985
986
15.0k
  switch (type)
987
15.0k
  {
988
11.2k
  case LowLevelCall::CALL:
989
11.2k
    m_output << "call(";
990
11.2k
    break;
991
1.76k
  case LowLevelCall::CALLCODE:
992
1.76k
    m_output << "callcode(";
993
1.76k
    break;
994
878
  case LowLevelCall::DELEGATECALL:
995
878
    m_output << "delegatecall(";
996
878
    break;
997
1.20k
  case LowLevelCall::STATICCALL:
998
1.20k
    yulAssert(m_evmVersion.hasStaticCall(), "Proto fuzzer: Invalid evm version");
999
1.20k
    m_output << "staticcall(";
1000
1.20k
    break;
1001
15.0k
  }
1002
15.0k
  visit(_x.gas());
1003
15.0k
  m_output << ", ";
1004
15.0k
  visit(_x.addr());
1005
15.0k
  m_output << ", ";
1006
15.0k
  if (type == LowLevelCall::CALL || type == LowLevelCall::CALLCODE)
1007
13.0k
  {
1008
13.0k
    visit(_x.wei());
1009
13.0k
    m_output << ", ";
1010
13.0k
  }
1011
15.0k
  visit(_x.in());
1012
15.0k
  m_output << ", ";
1013
15.0k
  visit(_x.insize());
1014
15.0k
  m_output << ", ";
1015
15.0k
  visit(_x.out());
1016
15.0k
  m_output << ", ";
1017
15.0k
  visit(_x.outsize());
1018
15.0k
  m_output << ")";
1019
15.0k
}
1020
1021
void ProtoConverter::visit(Create const& _x)
1022
8.12k
{
1023
8.12k
  Create_Type type = _x.createty();
1024
1025
  // Replace a call to create2 on unsupported EVMs with a dictionary
1026
  // token.
1027
8.12k
  if (type == Create::CREATE2 && !m_evmVersion.hasCreate2())
1028
568
  {
1029
568
    m_output << dictionaryToken();
1030
568
    return;
1031
568
  }
1032
1033
7.55k
  switch (type)
1034
7.55k
  {
1035
5.67k
  case Create::CREATE:
1036
5.67k
    m_output << "create(";
1037
5.67k
    break;
1038
1.88k
  case Create::CREATE2:
1039
1.88k
    m_output << "create2(";
1040
1.88k
    break;
1041
7.55k
  }
1042
7.55k
  visit(_x.wei());
1043
7.55k
  m_output << ", ";
1044
7.55k
  visit(_x.position());
1045
7.55k
  m_output << ", ";
1046
7.55k
  visit(_x.size());
1047
7.55k
  if (type == Create::CREATE2)
1048
1.88k
  {
1049
1.88k
    m_output << ", ";
1050
1.88k
    visit(_x.value());
1051
1.88k
  }
1052
7.55k
  m_output << ")";
1053
7.55k
}
1054
1055
void ProtoConverter::visit(IfStmt const& _x)
1056
28.3k
{
1057
28.3k
  m_output << "if ";
1058
28.3k
  visit(_x.cond());
1059
28.3k
  m_output << " ";
1060
28.3k
  visit(_x.if_body());
1061
28.3k
}
1062
1063
void ProtoConverter::visit(StoreFunc const& _x)
1064
51.9k
{
1065
51.9k
  switch (_x.st())
1066
51.9k
  {
1067
21.7k
  case StoreFunc::MSTORE:
1068
21.7k
    m_output << "mstore(";
1069
21.7k
    break;
1070
20.8k
  case StoreFunc::SSTORE:
1071
20.8k
    m_output << "sstore(";
1072
20.8k
    break;
1073
9.32k
  case StoreFunc::MSTORE8:
1074
9.32k
    m_output << "mstore8(";
1075
9.32k
    break;
1076
51.9k
  }
1077
51.9k
  visit(_x.loc());
1078
51.9k
  m_output << ", ";
1079
51.9k
  visit(_x.val());
1080
51.9k
  m_output << ")\n";
1081
51.9k
}
1082
1083
void ProtoConverter::visit(ForStmt const& _x)
1084
20.0k
{
1085
20.0k
  if (++m_numForLoops > s_maxForLoops)
1086
14.9k
    return;
1087
5.14k
  bool wasInForBody = m_inForBodyScope;
1088
5.14k
  bool wasInForInit = m_inForInitScope;
1089
5.14k
  bool wasForInitScopeExtEnabled = m_forInitScopeExtEnabled;
1090
5.14k
  m_inForBodyScope = false;
1091
5.14k
  m_inForInitScope = true;
1092
5.14k
  m_forInitScopeExtEnabled = true;
1093
5.14k
  m_inForCond = false;
1094
5.14k
  m_output << "for ";
1095
5.14k
  visit(_x.for_init());
1096
5.14k
  m_inForInitScope = false;
1097
5.14k
  m_forInitScopeExtEnabled = wasForInitScopeExtEnabled;
1098
5.14k
  m_inForCond = true;
1099
5.14k
  visit(_x.for_cond());
1100
5.14k
  m_inForCond = false;
1101
5.14k
  visit(_x.for_post());
1102
5.14k
  m_inForBodyScope = true;
1103
5.14k
  visit(_x.for_body());
1104
5.14k
  m_inForBodyScope = wasInForBody;
1105
5.14k
  m_inForInitScope = wasInForInit;
1106
5.14k
  if (m_inFunctionDef)
1107
2.93k
  {
1108
2.93k
    yulAssert(
1109
2.93k
      !m_funcForLoopInitVars.empty() && !m_funcForLoopInitVars.back().empty(),
1110
2.93k
      "Proto fuzzer: Invalid data structure"
1111
2.93k
    );
1112
    // Remove variables in for-init
1113
2.93k
    m_funcForLoopInitVars.back().pop_back();
1114
2.93k
  }
1115
2.20k
  else
1116
2.20k
  {
1117
2.20k
    yulAssert(!m_globalForLoopInitVars.empty(), "Proto fuzzer: Invalid data structure");
1118
2.20k
    m_globalForLoopInitVars.pop_back();
1119
2.20k
  }
1120
5.14k
}
1121
1122
void ProtoConverter::visit(BoundedForStmt const& _x)
1123
37.0k
{
1124
37.0k
  if (++m_numForLoops > s_maxForLoops)
1125
23.9k
    return;
1126
1127
  // Boilerplate for loop that limits the number of iterations to a maximum of 4.
1128
13.1k
  std::string loopVarName("i_" + std::to_string(m_numNestedForLoops++));
1129
13.1k
  m_output << "for { let " << loopVarName << " := 0 } "
1130
13.1k
         << "lt(" << loopVarName << ", 0x60) "
1131
13.1k
         << "{ " << loopVarName << " := add(" << loopVarName << ", 0x20) } ";
1132
  // Store previous for body scope
1133
13.1k
  bool wasInForBody = m_inForBodyScope;
1134
13.1k
  bool wasInForInit = m_inForInitScope;
1135
13.1k
  m_inForBodyScope = true;
1136
13.1k
  m_inForInitScope = false;
1137
13.1k
  visit(_x.for_body());
1138
  // Restore previous for body scope and init
1139
13.1k
  m_inForBodyScope = wasInForBody;
1140
13.1k
  m_inForInitScope = wasInForInit;
1141
13.1k
}
1142
1143
void ProtoConverter::visit(CaseStmt const& _x)
1144
62.2k
{
1145
62.2k
  string literal = visit(_x.case_lit());
1146
  // u256 value of literal
1147
62.2k
  u256 literalVal;
1148
1149
  // Convert string to u256 before looking for duplicate case literals
1150
62.2k
  if (_x.case_lit().has_strval())
1151
4.36k
  {
1152
    // Since string literals returned by the Literal visitor are enclosed within
1153
    // double quotes (like this "\"<string>\""), their size is at least two in the worst case
1154
    // that <string> is empty. Here we assert this invariant.
1155
4.36k
    yulAssert(literal.size() >= 2, "Proto fuzzer: String literal too short");
1156
    // This variable stores the <string> part i.e., literal minus the first and last
1157
    // double quote characters. This is used to compute the keccak256 hash of the
1158
    // string literal. The hashing is done to check whether we are about to create
1159
    // a case statement containing a case literal that has already been used in a
1160
    // previous case statement. If the hash (u256 value) matches a previous hash,
1161
    // then we simply don't create a new case statement.
1162
4.36k
    string noDoubleQuoteStr;
1163
4.36k
    if (literal.size() > 2)
1164
3.32k
    {
1165
      // Ensure that all characters in the string literal except the first
1166
      // and the last (double quote characters) are alphanumeric.
1167
3.32k
      yulAssert(
1168
3.32k
        ranges::all_of(
1169
3.32k
          literal.begin() + 1,
1170
3.32k
          literal.end() - 2,
1171
3.32k
          [=](char c) { return isalpha(c) || isdigit(c); }),
1172
3.32k
        "Proto fuzzer: Invalid string literal encountered"
1173
3.32k
      );
1174
1175
      // Make a copy because literal will need to be used later
1176
3.32k
      noDoubleQuoteStr = literal.substr(1, literal.size() - 2);
1177
3.32k
    }
1178
    // Hash the result to check for duplicate case literal strings
1179
4.36k
    literalVal = u256(h256(noDoubleQuoteStr, h256::FromBinary, h256::AlignLeft));
1180
1181
    // Make sure that an empty string literal evaluates to zero. This is to detect creation of
1182
    // duplicate case literals like so
1183
    // switch (x)
1184
    // {
1185
    //    case "": { x := 0 }
1186
    //    case 0: { x:= 1 } // Case statement with duplicate literal is invalid
1187
    // } // This snippet will not be parsed successfully.
1188
4.36k
    if (noDoubleQuoteStr.empty())
1189
4.36k
      yulAssert(literalVal == 0, "Proto fuzzer: Empty string does not evaluate to zero");
1190
4.36k
  }
1191
57.9k
  else if (_x.case_lit().has_boolval())
1192
1.82k
    literalVal = _x.case_lit().boolval() ? u256(1) : u256(0);
1193
56.1k
  else
1194
56.1k
    literalVal = u256(literal);
1195
1196
  // Check if set insertion fails (case literal present) or succeeds (case literal
1197
  // absent).
1198
62.2k
  bool isUnique = m_switchLiteralSetPerScope.top().insert(literalVal).second;
1199
1200
  // It is fine to bail out if we encounter a duplicate case literal because
1201
  // we can be assured that the switch statement is well-formed i.e., contains
1202
  // at least one case statement or a default block.
1203
62.2k
  if (isUnique)
1204
56.7k
  {
1205
56.7k
    m_output << "case " << literal << " ";
1206
56.7k
    visit(_x.case_block());
1207
56.7k
  }
1208
62.2k
}
1209
1210
void ProtoConverter::visit(SwitchStmt const& _x)
1211
55.2k
{
1212
55.2k
  if (_x.case_stmt_size() > 0 || _x.has_default_block())
1213
43.0k
  {
1214
43.0k
    std::set<u256> s;
1215
43.0k
    m_switchLiteralSetPerScope.push(s);
1216
43.0k
    m_output << "switch ";
1217
43.0k
    visit(_x.switch_expr());
1218
43.0k
    m_output << "\n";
1219
1220
43.0k
    for (auto const& caseStmt: _x.case_stmt())
1221
62.2k
      visit(caseStmt);
1222
1223
43.0k
    m_switchLiteralSetPerScope.pop();
1224
1225
43.0k
    if (_x.has_default_block())
1226
39.4k
    {
1227
39.4k
      m_output << "default ";
1228
39.4k
      visit(_x.default_block());
1229
39.4k
    }
1230
43.0k
  }
1231
55.2k
}
1232
1233
void ProtoConverter::visit(StopInvalidStmt const& _x)
1234
2.66k
{
1235
2.66k
  switch (_x.stmt())
1236
2.66k
  {
1237
1.58k
  case StopInvalidStmt::STOP:
1238
1.58k
    m_output << "stop()\n";
1239
1.58k
    break;
1240
1.08k
  case StopInvalidStmt::INVALID:
1241
1.08k
    m_output << "invalid()\n";
1242
1.08k
    break;
1243
2.66k
  }
1244
2.66k
}
1245
1246
void ProtoConverter::visit(RetRevStmt const& _x)
1247
1.28k
{
1248
1.28k
  switch (_x.stmt())
1249
1.28k
  {
1250
542
  case RetRevStmt::RETURN:
1251
542
    m_output << "return";
1252
542
    break;
1253
741
  case RetRevStmt::REVERT:
1254
741
    m_output << "revert";
1255
741
    break;
1256
1.28k
  }
1257
1.28k
  m_output << "(";
1258
1.28k
  visit(_x.pos());
1259
1.28k
  m_output << ", ";
1260
1.28k
  visit(_x.size());
1261
1.28k
  m_output << ")\n";
1262
1.28k
}
1263
1264
void ProtoConverter::visit(SelfDestructStmt const& _x)
1265
1.49k
{
1266
1.49k
  m_output << "selfdestruct";
1267
1.49k
  m_output << "(";
1268
1.49k
  visit(_x.addr());
1269
1.49k
  m_output << ")\n";
1270
1.49k
}
1271
1272
void ProtoConverter::visit(TerminatingStmt const& _x)
1273
41.9k
{
1274
41.9k
  switch (_x.term_oneof_case())
1275
41.9k
  {
1276
2.66k
  case TerminatingStmt::kStopInvalid:
1277
2.66k
    visit(_x.stop_invalid());
1278
2.66k
    break;
1279
1.28k
  case TerminatingStmt::kRetRev:
1280
1.28k
    visit(_x.ret_rev());
1281
1.28k
    break;
1282
1.49k
  case TerminatingStmt::kSelfDes:
1283
1.49k
    visit(_x.self_des());
1284
1.49k
    break;
1285
36.4k
  case TerminatingStmt::TERM_ONEOF_NOT_SET:
1286
36.4k
    break;
1287
41.9k
  }
1288
41.9k
}
1289
1290
void ProtoConverter::visit(UnaryOpData const& _x)
1291
2.66k
{
1292
2.66k
  switch (_x.op())
1293
2.66k
  {
1294
1.73k
  case UnaryOpData::SIZE:
1295
1.73k
    m_output << Whiskers(R"(datasize("<id>"))")
1296
1.73k
      ("id", getObjectIdentifier(static_cast<unsigned>(_x.identifier())))
1297
1.73k
      .render();
1298
1.73k
    break;
1299
935
  case UnaryOpData::OFFSET:
1300
935
    m_output << Whiskers(R"(dataoffset("<id>"))")
1301
935
      ("id", getObjectIdentifier(static_cast<unsigned>(_x.identifier())))
1302
935
      .render();
1303
935
    break;
1304
2.66k
  }
1305
2.66k
}
1306
1307
void ProtoConverter::visit(Statement const& _x)
1308
801k
{
1309
801k
  switch (_x.stmt_oneof_case())
1310
801k
  {
1311
9.75k
  case Statement::kDecl:
1312
9.75k
    visit(_x.decl());
1313
9.75k
    break;
1314
21.7k
  case Statement::kAssignment:
1315
    // Create an assignment statement only if there is at least one variable
1316
    // declaration that is in scope.
1317
21.7k
    if (varDeclAvailable())
1318
17.9k
      visit(_x.assignment());
1319
21.7k
    break;
1320
41.8k
  case Statement::kIfstmt:
1321
41.8k
    if (_x.ifstmt().if_body().statements_size() > 0)
1322
28.3k
      visit(_x.ifstmt());
1323
41.8k
    break;
1324
51.9k
  case Statement::kStorageFunc:
1325
51.9k
    visit(_x.storage_func());
1326
51.9k
    break;
1327
13.1k
  case Statement::kBlockstmt:
1328
13.1k
    if (_x.blockstmt().statements_size() > 0)
1329
11.3k
      visit(_x.blockstmt());
1330
13.1k
    break;
1331
39.3k
  case Statement::kForstmt:
1332
39.3k
    if (_x.forstmt().for_body().statements_size() > 0 && !m_filterUnboundedLoops)
1333
20.0k
      visit(_x.forstmt());
1334
39.3k
    break;
1335
49.6k
  case Statement::kBoundedforstmt:
1336
49.6k
    if (_x.boundedforstmt().for_body().statements_size() > 0)
1337
37.0k
      visit(_x.boundedforstmt());
1338
49.6k
    break;
1339
55.2k
  case Statement::kSwitchstmt:
1340
55.2k
    visit(_x.switchstmt());
1341
55.2k
    break;
1342
45.0k
  case Statement::kBreakstmt:
1343
45.0k
    if (m_inForBodyScope)
1344
4.89k
      m_output << "break\n";
1345
45.0k
    break;
1346
43.5k
  case Statement::kContstmt:
1347
43.5k
    if (m_inForBodyScope)
1348
6.48k
      m_output << "continue\n";
1349
43.5k
    break;
1350
6.44k
  case Statement::kLogFunc:
1351
    // Log is a stateful statement since it writes to storage.
1352
6.44k
    if (!m_filterStatefulInstructions)
1353
6.10k
      visit(_x.log_func());
1354
6.44k
    break;
1355
9.45k
  case Statement::kCopyFunc:
1356
9.45k
    visit(_x.copy_func());
1357
9.45k
    break;
1358
9.28k
  case Statement::kExtcodeCopy:
1359
    // Extcodecopy may change state if external code is copied via a
1360
    // sequence of mload/sstore.
1361
9.28k
    if (!m_filterStatefulInstructions)
1362
8.57k
      visit(_x.extcode_copy());
1363
9.28k
    break;
1364
41.9k
  case Statement::kTerminatestmt:
1365
41.9k
    visit(_x.terminatestmt());
1366
41.9k
    break;
1367
85.1k
  case Statement::kFunctioncall:
1368
85.1k
    if (!m_functionSigMap.empty())
1369
70.3k
    {
1370
70.3k
      unsigned index = counter() % m_functionSigMap.size();
1371
70.3k
      auto iter = m_functionSigMap.begin();
1372
70.3k
      advance(iter, index);
1373
70.3k
      visit(_x.functioncall(), iter->first);
1374
70.3k
    }
1375
85.1k
    break;
1376
83.4k
  case Statement::kFuncdef:
1377
83.4k
    if (_x.funcdef().block().statements_size() > 0)
1378
79.5k
      if (!m_inForInitScope)
1379
79.3k
        visit(_x.funcdef());
1380
83.4k
    break;
1381
6.78k
  case Statement::kPop:
1382
6.78k
    visit(_x.pop());
1383
6.78k
    break;
1384
44.0k
  case Statement::kLeave:
1385
44.0k
    if (m_inFunctionDef)
1386
18.7k
      visit(_x.leave());
1387
44.0k
    break;
1388
12.5k
  case Statement::kMultidecl:
1389
12.5k
    visit(_x.multidecl());
1390
12.5k
    break;
1391
131k
  case Statement::STMT_ONEOF_NOT_SET:
1392
131k
    break;
1393
801k
  }
1394
801k
}
1395
1396
void ProtoConverter::openBlockScope()
1397
298k
{
1398
298k
  m_scopeFuncs.emplace_back();
1399
1400
  // Create new block scope inside current function scope
1401
298k
  if (m_inFunctionDef)
1402
139k
  {
1403
139k
    yulAssert(
1404
139k
      !m_funcVars.empty(),
1405
139k
      "Proto fuzzer: Invalid data structure"
1406
139k
    );
1407
139k
    m_funcVars.back().push_back(vector<string>{});
1408
139k
    if (m_inForInitScope && m_forInitScopeExtEnabled)
1409
2.93k
    {
1410
2.93k
      yulAssert(
1411
2.93k
        !m_funcForLoopInitVars.empty(),
1412
2.93k
        "Proto fuzzer: Invalid data structure"
1413
2.93k
      );
1414
2.93k
      m_funcForLoopInitVars.back().push_back(vector<string>{});
1415
2.93k
    }
1416
139k
  }
1417
158k
  else
1418
158k
  {
1419
158k
    m_globalVars.emplace_back();
1420
158k
    if (m_inForInitScope && m_forInitScopeExtEnabled)
1421
2.20k
      m_globalForLoopInitVars.emplace_back();
1422
158k
  }
1423
298k
}
1424
1425
void ProtoConverter::openFunctionScope(vector<string> const& _funcParams)
1426
79.3k
{
1427
79.3k
  m_funcVars.push_back(vector<vector<string>>({_funcParams}));
1428
79.3k
  m_funcForLoopInitVars.push_back(vector<vector<string>>({}));
1429
79.3k
}
1430
1431
void ProtoConverter::updateFunctionMaps(string const& _var)
1432
79.3k
{
1433
79.3k
  size_t erased = m_functionSigMap.erase(_var);
1434
1435
79.3k
  for (auto const& i: m_functionDefMap)
1436
471k
    if (i.second == _var)
1437
79.3k
    {
1438
79.3k
      erased += m_functionDefMap.erase(i.first);
1439
79.3k
      break;
1440
79.3k
    }
1441
1442
79.3k
  yulAssert(erased == 2, "Proto fuzzer: Function maps not updated");
1443
79.3k
}
1444
1445
void ProtoConverter::closeBlockScope()
1446
298k
{
1447
  // Remove functions declared in the block that is going
1448
  // out of scope from the global function map.
1449
298k
  for (auto const& f: m_scopeFuncs.back())
1450
79.3k
  {
1451
79.3k
    size_t numFuncsRemoved = m_functions.size();
1452
79.3k
    m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end());
1453
79.3k
    numFuncsRemoved -= m_functions.size();
1454
79.3k
    yulAssert(
1455
79.3k
      numFuncsRemoved == 1,
1456
79.3k
      "Proto fuzzer: Nothing or too much went out of scope"
1457
79.3k
    );
1458
79.3k
    updateFunctionMaps(f);
1459
79.3k
  }
1460
  // Pop back the vector of scoped functions.
1461
298k
  if (!m_scopeFuncs.empty())
1462
298k
    m_scopeFuncs.pop_back();
1463
1464
  // If block belongs to function body, then remove
1465
  // local variables in function body that are going out of scope.
1466
298k
  if (m_inFunctionDef)
1467
139k
  {
1468
139k
    yulAssert(!m_funcVars.empty(), "Proto fuzzer: Invalid data structure");
1469
139k
    if (!m_funcVars.back().empty())
1470
139k
      m_funcVars.back().pop_back();
1471
139k
  }
1472
  // Remove variables declared in vanilla block from current
1473
  // global scope.
1474
158k
  else
1475
158k
  {
1476
158k
    yulAssert(!m_globalVars.empty(), "Proto fuzzer: Invalid data structure");
1477
158k
    m_globalVars.pop_back();
1478
158k
  }
1479
298k
}
1480
1481
void ProtoConverter::closeFunctionScope()
1482
79.3k
{
1483
79.3k
  yulAssert(!m_funcVars.empty(), "Proto fuzzer: Invalid data structure");
1484
79.3k
  m_funcVars.pop_back();
1485
79.3k
  yulAssert(!m_funcForLoopInitVars.empty(), "Proto fuzzer: Invalid data structure");
1486
79.3k
  m_funcForLoopInitVars.pop_back();
1487
79.3k
}
1488
1489
void ProtoConverter::addVarsToScope(vector<string> const& _vars)
1490
92.3k
{
1491
  // If we are in function definition, add the new vars to current function scope
1492
92.3k
  if (m_inFunctionDef)
1493
52.4k
  {
1494
    // If we are directly in for-init block, add the newly created vars to the
1495
    // stack of for-init variables.
1496
52.4k
    if (m_inForInitScope && m_forInitScopeExtEnabled)
1497
209
    {
1498
209
      yulAssert(
1499
209
        !m_funcForLoopInitVars.empty() && !m_funcForLoopInitVars.back().empty(),
1500
209
        "Proto fuzzer: Invalid data structure"
1501
209
      );
1502
209
      m_funcForLoopInitVars.back().back().insert(
1503
209
        m_funcForLoopInitVars.back().back().end(),
1504
209
        _vars.begin(),
1505
209
        _vars.end()
1506
209
      );
1507
209
    }
1508
52.2k
    else
1509
52.2k
    {
1510
52.2k
      yulAssert(
1511
52.2k
        !m_funcVars.empty() && !m_funcVars.back().empty(),
1512
52.2k
        "Proto fuzzer: Invalid data structure"
1513
52.2k
      );
1514
52.2k
      m_funcVars.back().back().insert(
1515
52.2k
        m_funcVars.back().back().end(),
1516
52.2k
        _vars.begin(),
1517
52.2k
        _vars.end()
1518
52.2k
      );
1519
52.2k
    }
1520
52.4k
  }
1521
  // If we are in a vanilla block, add the new vars to current global scope
1522
39.9k
  else
1523
39.9k
  {
1524
39.9k
    if (m_inForInitScope && m_forInitScopeExtEnabled)
1525
29
    {
1526
29
      yulAssert(
1527
29
        !m_globalForLoopInitVars.empty(),
1528
29
        "Proto fuzzer: Invalid data structure"
1529
29
      );
1530
29
      m_globalForLoopInitVars.back().insert(
1531
29
        m_globalForLoopInitVars.back().end(),
1532
29
        _vars.begin(),
1533
29
        _vars.end()
1534
29
      );
1535
29
    }
1536
39.8k
    else
1537
39.8k
    {
1538
39.8k
      yulAssert(
1539
39.8k
        !m_globalVars.empty(),
1540
39.8k
        "Proto fuzzer: Invalid data structure"
1541
39.8k
      );
1542
39.8k
      m_globalVars.back().insert(
1543
39.8k
        m_globalVars.back().end(),
1544
39.8k
        _vars.begin(),
1545
39.8k
        _vars.end()
1546
39.8k
      );
1547
39.8k
    }
1548
39.9k
  }
1549
92.3k
}
1550
1551
void ProtoConverter::visit(Block const& _x)
1552
298k
{
1553
298k
  openBlockScope();
1554
1555
  // Register function declarations in this scope unless this
1556
  // scope belongs to for-init (in which function declarations
1557
  // are forbidden).
1558
298k
  for (auto const& statement: _x.statements())
1559
801k
    if (statement.has_funcdef() && statement.funcdef().block().statements_size() > 0 && !m_inForInitScope)
1560
79.3k
      registerFunction(&statement.funcdef());
1561
1562
298k
  if (_x.statements_size() > 0)
1563
252k
  {
1564
252k
    m_output << "{\n";
1565
252k
    bool wasForInitScopeExtEnabled = m_forInitScopeExtEnabled;
1566
252k
    for (auto const& st: _x.statements())
1567
801k
    {
1568
      // If statement is block or introduces one and we are in for-init block
1569
      // then temporarily disable scope extension if it is not already disabled.
1570
801k
      if (
1571
801k
        (st.has_blockstmt() || st.has_switchstmt() || st.has_ifstmt()) &&
1572
801k
        m_inForInitScope &&
1573
801k
        m_forInitScopeExtEnabled
1574
801k
      )
1575
1.25k
        m_forInitScopeExtEnabled = false;
1576
801k
      visit(st);
1577
801k
      m_forInitScopeExtEnabled = wasForInitScopeExtEnabled;
1578
801k
    }
1579
252k
    m_output << "}\n";
1580
252k
  }
1581
46.0k
  else
1582
46.0k
    m_output << "{}\n";
1583
298k
  closeBlockScope();
1584
298k
}
1585
1586
vector<string> ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx)
1587
219k
{
1588
219k
  yulAssert(_endIdx > _startIdx, "Proto fuzzer: Variable indices not in range");
1589
219k
  string varsStr = suffixedVariableNameList("x_", _startIdx, _endIdx);
1590
219k
  m_output << varsStr;
1591
219k
  vector<string> varsVec;
1592
219k
  boost::split(
1593
219k
    varsVec,
1594
219k
    varsStr,
1595
219k
    boost::algorithm::is_any_of(", "),
1596
219k
    boost::algorithm::token_compress_on
1597
219k
  );
1598
1599
219k
  yulAssert(
1600
219k
    varsVec.size() == (_endIdx - _startIdx),
1601
219k
    "Proto fuzzer: Variable count mismatch during function definition"
1602
219k
  );
1603
219k
  m_counter += varsVec.size();
1604
219k
  return varsVec;
1605
219k
}
1606
1607
void ProtoConverter::registerFunction(FunctionDef const* _x)
1608
79.3k
{
1609
79.3k
  unsigned numInParams = _x->num_input_params() % s_modInputParams;
1610
79.3k
  unsigned numOutParams = _x->num_output_params() % s_modOutputParams;
1611
79.3k
  NumFunctionReturns numReturns;
1612
79.3k
  if (numOutParams == 0)
1613
15.9k
    numReturns = NumFunctionReturns::None;
1614
63.4k
  else if (numOutParams == 1)
1615
14.0k
    numReturns = NumFunctionReturns::Single;
1616
49.3k
  else
1617
49.3k
    numReturns = NumFunctionReturns::Multiple;
1618
1619
  // Generate function name
1620
79.3k
  string funcName = functionName(numReturns);
1621
1622
  // Register function
1623
79.3k
  auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams)));
1624
79.3k
  yulAssert(ret.second, "Proto fuzzer: Function already exists.");
1625
79.3k
  m_functions.push_back(funcName);
1626
79.3k
  m_scopeFuncs.back().push_back(funcName);
1627
79.3k
  m_functionDefMap.emplace(make_pair(_x, funcName));
1628
79.3k
}
1629
1630
void ProtoConverter::fillFunctionCallInput(unsigned _numInParams)
1631
54.4k
{
1632
211k
  for (unsigned i = 0; i < _numInParams; i++)
1633
157k
  {
1634
    // Throw a 4-sided dice to choose whether to populate function input
1635
    // argument from a pseudo-randomly chosen slot in one of the following
1636
    // locations: calldata, memory, storage, or Yul optimizer dictionary.
1637
157k
    unsigned diceValue = counter() % 4;
1638
    // Pseudo-randomly choose one of the first ten 32-byte
1639
    // aligned slots.
1640
157k
    string slot = to_string((counter() % 10) * 32);
1641
157k
    switch (diceValue)
1642
157k
    {
1643
49.7k
    case 0:
1644
49.7k
      m_output << "calldataload(" << slot << ")";
1645
49.7k
      break;
1646
15.5k
    case 1:
1647
15.5k
      m_output << "mload(" << slot << ")";
1648
15.5k
      break;
1649
62.9k
    case 2:
1650
62.9k
      m_output << "sload(" << slot << ")";
1651
62.9k
      break;
1652
29.0k
    default:
1653
      // Call to dictionaryToken() automatically picks a token
1654
      // at a pseudo-random location.
1655
29.0k
      m_output << dictionaryToken();
1656
29.0k
      break;
1657
157k
    }
1658
157k
    if (i < _numInParams - 1)
1659
102k
      m_output << ",";
1660
157k
  }
1661
54.4k
}
1662
1663
void ProtoConverter::saveFunctionCallOutput(vector<string> const& _varsVec)
1664
53.2k
{
1665
53.2k
  for (auto const& var: _varsVec)
1666
167k
  {
1667
    // Flip a dice to choose whether to save output values
1668
    // in storage or memory.
1669
167k
    bool coinFlip = counter() % 2 == 0;
1670
    // Pseudo-randomly choose one of the first ten 32-byte
1671
    // aligned slots.
1672
167k
    string slot = to_string((counter() % 10) * 32);
1673
167k
    if (coinFlip)
1674
151k
      m_output << "sstore(" << slot << ", " << var << ")\n";
1675
16.8k
    else
1676
16.8k
      m_output << "mstore(" << slot << ", " << var << ")\n";
1677
167k
  }
1678
53.2k
}
1679
1680
void ProtoConverter::createFunctionCall(
1681
  string const& _funcName,
1682
  unsigned _numInParams,
1683
  unsigned _numOutParams
1684
)
1685
64.9k
{
1686
64.9k
  vector<string> varsVec{};
1687
64.9k
  if (_numOutParams > 0)
1688
53.2k
  {
1689
53.2k
    unsigned startIdx = counter();
1690
    // Prints the following to output stream "let x_i,...,x_n := "
1691
53.2k
    varsVec = createVarDecls(
1692
53.2k
      startIdx,
1693
53.2k
      startIdx + _numOutParams,
1694
53.2k
      /*isAssignment=*/true
1695
53.2k
    );
1696
53.2k
  }
1697
1698
  // Call the function with the correct number of input parameters
1699
64.9k
  m_output << _funcName << "(";
1700
64.9k
  if (_numInParams > 0)
1701
54.4k
    fillFunctionCallInput(_numInParams);
1702
64.9k
  m_output << ")\n";
1703
1704
64.9k
  if (!varsVec.empty())
1705
53.2k
  {
1706
    // Save values returned by function so that they are reflected
1707
    // in the interpreter trace.
1708
53.2k
    saveFunctionCallOutput(varsVec);
1709
    // Add newly minted vars to current scope
1710
53.2k
    addVarsToScope(varsVec);
1711
53.2k
  }
1712
11.7k
  else
1713
64.9k
    yulAssert(_numOutParams == 0, "Proto fuzzer: Function return value not saved");
1714
64.9k
}
1715
1716
void ProtoConverter::createFunctionDefAndCall(
1717
  FunctionDef const& _x,
1718
  unsigned _numInParams,
1719
  unsigned _numOutParams
1720
)
1721
79.3k
{
1722
79.3k
  yulAssert(
1723
79.3k
    ((_numInParams <= s_modInputParams - 1) && (_numOutParams <= s_modOutputParams - 1)),
1724
79.3k
    "Proto fuzzer: Too many function I/O parameters requested."
1725
79.3k
  );
1726
1727
  // Obtain function name
1728
79.3k
  yulAssert(m_functionDefMap.count(&_x), "Proto fuzzer: Unregistered function");
1729
79.3k
  string funcName = m_functionDefMap.at(&_x);
1730
1731
79.3k
  vector<string> varsVec = {};
1732
79.3k
  m_output << "function " << funcName << "(";
1733
79.3k
  unsigned startIdx = counter();
1734
79.3k
  if (_numInParams > 0)
1735
63.9k
    varsVec = createVars(startIdx, startIdx + _numInParams);
1736
79.3k
  m_output << ")";
1737
1738
79.3k
  vector<string> outVarsVec = {};
1739
  // This creates -> x_n+1,...,x_r
1740
79.3k
  if (_numOutParams > 0)
1741
63.4k
  {
1742
63.4k
    m_output << " -> ";
1743
63.4k
    if (varsVec.empty())
1744
9.33k
    {
1745
9.33k
      yulAssert(_numInParams == 0, "Proto fuzzer: Input parameters not processed correctly");
1746
9.33k
      varsVec = createVars(startIdx, startIdx + _numOutParams);
1747
9.33k
    }
1748
54.0k
    else
1749
54.0k
    {
1750
54.0k
      outVarsVec = createVars(startIdx + _numInParams, startIdx + _numInParams + _numOutParams);
1751
54.0k
      varsVec.insert(varsVec.end(), outVarsVec.begin(), outVarsVec.end());
1752
54.0k
    }
1753
63.4k
  }
1754
79.3k
  yulAssert(varsVec.size() == _numInParams + _numOutParams, "Proto fuzzer: Function parameters not processed correctly");
1755
1756
79.3k
  m_output << "\n";
1757
1758
  // If function definition is in for-loop body, update
1759
79.3k
  bool wasInForBody = m_inForBodyScope;
1760
79.3k
  m_inForBodyScope = false;
1761
1762
79.3k
  bool wasInFunctionDef = m_inFunctionDef;
1763
79.3k
  m_inFunctionDef = true;
1764
1765
  // Create new function scope and add function input and return
1766
  // parameters to it.
1767
79.3k
  openFunctionScope(varsVec);
1768
  // Visit function body
1769
79.3k
  visit(_x.block());
1770
79.3k
  closeFunctionScope();
1771
1772
79.3k
  m_inForBodyScope = wasInForBody;
1773
79.3k
  m_inFunctionDef = wasInFunctionDef;
1774
1775
79.3k
  yulAssert(
1776
79.3k
    !m_inForInitScope,
1777
79.3k
    "Proto fuzzer: Trying to create function call inside a for-init block"
1778
79.3k
  );
1779
79.3k
  if (_x.force_call())
1780
64.9k
    createFunctionCall(funcName, _numInParams, _numOutParams);
1781
79.3k
}
1782
1783
void ProtoConverter::visit(FunctionDef const& _x)
1784
79.3k
{
1785
79.3k
  unsigned numInParams = _x.num_input_params() % s_modInputParams;
1786
79.3k
  unsigned numOutParams = _x.num_output_params() % s_modOutputParams;
1787
79.3k
  createFunctionDefAndCall(_x, numInParams, numOutParams);
1788
79.3k
}
1789
1790
void ProtoConverter::visit(PopStmt const& _x)
1791
6.78k
{
1792
6.78k
  m_output << "pop(";
1793
6.78k
  visit(_x.expr());
1794
6.78k
  m_output << ")\n";
1795
6.78k
}
1796
1797
void ProtoConverter::visit(LeaveStmt const&)
1798
18.7k
{
1799
18.7k
  m_output << "leave\n";
1800
18.7k
}
1801
1802
string ProtoConverter::getObjectIdentifier(unsigned _x)
1803
2.66k
{
1804
2.66k
  unsigned currentId = currentObjectId();
1805
2.66k
  string currentObjName = "object" + to_string(currentId);
1806
2.66k
  yulAssert(
1807
2.66k
    m_objectScope.count(currentObjName) && !m_objectScope.at(currentObjName).empty(),
1808
2.66k
    "Yul proto fuzzer: Error referencing object"
1809
2.66k
  );
1810
2.66k
  vector<string> objectIdsInScope = m_objectScope.at(currentObjName);
1811
2.66k
  return objectIdsInScope[_x % objectIdsInScope.size()];
1812
2.66k
}
1813
1814
void ProtoConverter::visit(Code const& _x)
1815
20.1k
{
1816
20.1k
  m_output << "code {\n";
1817
20.1k
  visit(_x.block());
1818
20.1k
  m_output << "}\n";
1819
20.1k
}
1820
1821
void ProtoConverter::visit(Data const& _x)
1822
6.61k
{
1823
  // TODO: Generate random data block identifier
1824
6.61k
  m_output << "data \"" << s_dataIdentifier << "\" hex\"" << createHex(_x.hex()) << "\"\n";
1825
6.61k
}
1826
1827
void ProtoConverter::visit(Object const& _x)
1828
20.1k
{
1829
  // object "object<n>" {
1830
  // ...
1831
  // }
1832
20.1k
  m_output << "object " << newObjectId() << " {\n";
1833
20.1k
  visit(_x.code());
1834
20.1k
  if (_x.has_data())
1835
6.61k
    visit(_x.data());
1836
20.1k
  for (auto const& subObj: _x.sub_obj())
1837
17.8k
    visit(subObj);
1838
20.1k
  m_output << "}\n";
1839
20.1k
}
1840
1841
void ProtoConverter::buildObjectScopeTree(Object const& _x)
1842
20.1k
{
1843
  // Identifies object being visited
1844
20.1k
  string objectName = newObjectId(false);
1845
20.1k
  vector<string> node{objectName};
1846
20.1k
  if (_x.has_data())
1847
6.61k
    node.emplace_back(s_dataIdentifier);
1848
20.1k
  for (auto const& subObj: _x.sub_obj())
1849
17.8k
  {
1850
    // Identifies sub object whose numeric suffix is
1851
    // m_objectId
1852
17.8k
    unsigned subObjectId = m_objectId;
1853
17.8k
    string subObjectName = "object" + to_string(subObjectId);
1854
17.8k
    node.push_back(subObjectName);
1855
17.8k
    buildObjectScopeTree(subObj);
1856
    // Add sub-object to object's ancestors
1857
17.8k
    yulAssert(m_objectScope.count(subObjectName), "Yul proto fuzzer: Invalid object hierarchy");
1858
17.8k
    for (string const& item: m_objectScope.at(subObjectName))
1859
245k
      if (item != subObjectName)
1860
227k
        node.emplace_back(subObjectName + "." + item);
1861
17.8k
  }
1862
20.1k
  m_objectScope.emplace(objectName, node);
1863
20.1k
}
1864
1865
void ProtoConverter::visit(Program const& _x)
1866
36.7k
{
1867
  // Initialize input size
1868
36.7k
  m_inputSize = static_cast<unsigned>(_x.ByteSizeLong());
1869
1870
  // Record EVM Version
1871
36.7k
  m_evmVersion = evmVersionMapping(_x.ver());
1872
1873
  // Program is either a Yul object or a block of
1874
  // statements.
1875
36.7k
  switch (_x.program_oneof_case())
1876
36.7k
  {
1877
34.4k
  case Program::kBlock:
1878
34.4k
    m_output << "{\n";
1879
34.4k
    visit(_x.block());
1880
34.4k
    m_output << "}\n";
1881
34.4k
    break;
1882
2.23k
  case Program::kObj:
1883
2.23k
    m_isObject = true;
1884
2.23k
    buildObjectScopeTree(_x.obj());
1885
    // Reset object id counter
1886
2.23k
    m_objectId = 0;
1887
2.23k
    visit(_x.obj());
1888
2.23k
    break;
1889
98
  case Program::PROGRAM_ONEOF_NOT_SET:
1890
    // {} is a trivial Yul program
1891
98
    m_output << "{}";
1892
98
    break;
1893
36.7k
  }
1894
36.7k
}
1895
1896
string ProtoConverter::programToString(Program const& _input)
1897
36.7k
{
1898
36.7k
  visit(_input);
1899
36.7k
  return m_output.str();
1900
36.7k
}
1901
1902
string ProtoConverter::functionTypeToString(NumFunctionReturns _type)
1903
79.3k
{
1904
79.3k
  switch (_type)
1905
79.3k
  {
1906
15.9k
  case NumFunctionReturns::None:
1907
15.9k
    return "n";
1908
14.0k
  case NumFunctionReturns::Single:
1909
14.0k
    return "s";
1910
49.3k
  case NumFunctionReturns::Multiple:
1911
49.3k
    return "m";
1912
79.3k
  }
1913
79.3k
}