Coverage Report

Created: 2026-02-26 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/testdir/tests/capi/luaL_loadbuffer_proto/serializer.cc
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: BSD-2-Clause
3
 *
4
 * Copyright 2023-2026, Sergey Bronnikov.
5
 * Copyright 2022, Tarantool AUTHORS, please see AUTHORS file.
6
 */
7
#include "serializer.h"
8
9
#include <stack>
10
#include <string>
11
12
/**
13
* If control flow reaches the point of the unreachable(), the program is
14
* undefined. It is useful in situations where the compiler cannot deduce
15
* the unreachability of the code.
16
*/
17
#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
18
0
#  define unreachable() (assert(0), __builtin_unreachable())
19
#else
20
#  define unreachable() (assert(0))
21
#endif
22
23
using namespace lua_grammar;
24
25
extern char preamble_lua[];
26
27
#define PROTO_TOSTRING(TYPE, VAR_NAME) \
28
  std::string TYPE##ToString(const TYPE & (VAR_NAME))
29
30
/* PROTO_TOSTRING version for nested (depth=2) protobuf messages. */
31
#define NESTED_PROTO_TOSTRING(TYPE, VAR_NAME, PARENT_MESSAGE) \
32
  std::string TYPE##ToString \
33
  (const PARENT_MESSAGE::TYPE & (VAR_NAME))
34
35
namespace luajit_fuzzer {
36
namespace {
37
38
/*
39
 * The following keywords are reserved and cannot be used as names,
40
 * see Lua 5.1 Reference Manual, 2.1 – Lexical Conventions.
41
 */
42
const std::set<std::string> KReservedLuaKeywords {
43
  "and",
44
  "break",
45
  "do",
46
  "else",
47
  "elseif",
48
  "end",
49
  "false",
50
  "for",
51
  "function",
52
  "if",
53
  "in",
54
  "local",
55
  "nil",
56
  "not",
57
  "or",
58
  "repeat",
59
  "return",
60
  "then",
61
  "true",
62
  "until",
63
  "while",
64
};
65
66
const std::string kCounterNamePrefix = "counter_";
67
const std::string kNumberWrapperName = "always_number";
68
const std::string kBinOpWrapperName = "only_numbers_cmp";
69
const std::string kNotNaNAndNilWrapperName = "not_nan_and_nil";
70
71
PROTO_TOSTRING(Block, block);
72
PROTO_TOSTRING(Chunk, chunk);
73
74
PROTO_TOSTRING(Statement, stat);
75
76
/** LastStatement and nested types. */
77
PROTO_TOSTRING(LastStatement, laststat);
78
NESTED_PROTO_TOSTRING(ReturnOptionalExpressionList, explist, LastStatement);
79
80
/**
81
 * Statement options.
82
 */
83
84
/** AssignmentList and nested types. */
85
PROTO_TOSTRING(AssignmentList, assignmentlist);
86
NESTED_PROTO_TOSTRING(VariableList, varlist, AssignmentList);
87
88
/** FunctionCall and nested types. */
89
PROTO_TOSTRING(FunctionCall, call);
90
NESTED_PROTO_TOSTRING(Args, args, FunctionCall);
91
NESTED_PROTO_TOSTRING(PrefixArgs, prefixargs, FunctionCall);
92
NESTED_PROTO_TOSTRING(PrefixNamedArgs, prefixnamedargs, FunctionCall);
93
94
/** DoBlock, WhileCycle and RepeatCycle clauses. */
95
PROTO_TOSTRING(DoBlock, block);
96
PROTO_TOSTRING(WhileCycle, whilecycle);
97
PROTO_TOSTRING(RepeatCycle, repeatcycle);
98
99
/** IfStatement and nested types. */
100
PROTO_TOSTRING(IfStatement, statement);
101
NESTED_PROTO_TOSTRING(ElseIfBlock, elseifblock, IfStatement);
102
103
/** ForCycleName and ForCycleList clauses. */
104
PROTO_TOSTRING(ForCycleName, forcyclename);
105
PROTO_TOSTRING(ForCycleList, forcyclelist);
106
107
/** Function and nested types. */
108
PROTO_TOSTRING(Function, func);
109
NESTED_PROTO_TOSTRING(FuncName, funcname, Function);
110
111
PROTO_TOSTRING(NameList, namelist);
112
NESTED_PROTO_TOSTRING(NameListWithEllipsis, namelist, FuncBody);
113
NESTED_PROTO_TOSTRING(ParList, parlist, FuncBody);
114
115
/** LocalFunc and LocalNames clauses. */
116
PROTO_TOSTRING(LocalFunc, localfunc);
117
PROTO_TOSTRING(LocalNames, localnames);
118
119
/**
120
 * Expressions and variables.
121
 */
122
123
/** Expressions clauses. */
124
PROTO_TOSTRING(ExpressionList, explist);
125
PROTO_TOSTRING(OptionalExpressionList, explist);
126
PROTO_TOSTRING(PrefixExpression, prefExpr);
127
128
/* Variable and nested types. */
129
PROTO_TOSTRING(Variable, var);
130
NESTED_PROTO_TOSTRING(IndexWithExpression, indexexpr, Variable);
131
NESTED_PROTO_TOSTRING(IndexWithName, indexname, Variable);
132
133
/** Expression and nested types. */
134
PROTO_TOSTRING(Expression, expr);
135
NESTED_PROTO_TOSTRING(AnonFunc, function, Expression);
136
NESTED_PROTO_TOSTRING(ExpBinaryOpExp, binary, Expression);
137
NESTED_PROTO_TOSTRING(UnaryOpExp, unary, Expression);
138
139
/**
140
 * Tables and fields.
141
 */
142
PROTO_TOSTRING(TableConstructor, table);
143
PROTO_TOSTRING(FieldList, fieldlist);
144
NESTED_PROTO_TOSTRING(FieldWithFieldSep, field, FieldList);
145
146
/** Field and nested types. */
147
PROTO_TOSTRING(Field, field);
148
NESTED_PROTO_TOSTRING(ExpressionAssignment, assignment, Field);
149
NESTED_PROTO_TOSTRING(NameAssignment, assignment, Field);
150
PROTO_TOSTRING(FieldSep, sep);
151
152
/** Operators. */
153
PROTO_TOSTRING(BinaryOperator, op);
154
PROTO_TOSTRING(UnaryOperator, op);
155
156
/** Identifier (Name). */
157
PROTO_TOSTRING(Name, name);
158
159
std::string
160
NumberWrappedExpressionToString(const Expression &expr)
161
10.9k
{
162
10.9k
  std::string retval;
163
10.9k
  retval += kNumberWrapperName;
164
10.9k
  retval += "(";
165
10.9k
  retval += ExpressionToString(expr);
166
10.9k
  retval += ")";
167
168
10.9k
  return retval;
169
10.9k
}
170
171
std::string
172
AllowedIndexExpressionToString(const Expression &expr)
173
4.78k
{
174
4.78k
  std::string retval;
175
4.78k
  retval += kNotNaNAndNilWrapperName;
176
4.78k
  retval += "(";
177
4.78k
  retval += ExpressionToString(expr);
178
4.78k
  retval += ")";
179
4.78k
  return retval;
180
4.78k
}
181
182
/**
183
 * Class that controls id creation for counters. Basically, a
184
 * variable wrapper that guarantees variable to be incremented.
185
 */
186
class CounterIdProvider {
187
public:
188
  /** Returns number of id provided. */
189
  std::size_t count()
190
124k
  {
191
124k
    return id_;
192
124k
  }
193
194
  /** Returns a new id that was not used after last clean(). */
195
  std::size_t next()
196
122k
  {
197
122k
    return id_++;
198
122k
  }
199
200
  /**
201
   * Cleans history. Should be used to make fuzzer starts
202
   * independent.
203
   */
204
  void clean()
205
1.81k
  {
206
1.81k
    id_ = 0;
207
1.81k
  }
208
209
private:
210
  std::size_t id_ = 0;
211
};
212
213
/** A singleton for counter id provider. */
214
CounterIdProvider&
215
GetCounterIdProvider()
216
248k
{
217
248k
  static CounterIdProvider provider;
218
248k
  return provider;
219
248k
}
220
221
std::string
222
GetCounterName(std::size_t id)
223
245k
{
224
245k
  return kCounterNamePrefix + std::to_string(id);
225
245k
}
226
227
/** Returns `<counter_name> = <counter_name> + 1`. */
228
std::string
229
GetCounterIncrement(const std::string &counter_name)
230
122k
{
231
122k
  std::string retval = counter_name;
232
122k
  retval += " = ";
233
122k
  retval += counter_name;
234
122k
  retval += " + 1;\n";
235
122k
  return retval;
236
122k
}
237
238
/**
239
 * Returns `if <counter_name> > kMaxCounterValue then
240
 * <then_block> end`.
241
 */
242
std::string
243
GetCondition(const std::string &counter_name, const std::string &then_block)
244
122k
{
245
122k
  std::string retval = "if ";
246
122k
  retval += counter_name;
247
122k
  retval += " > ";
248
122k
  retval += std::to_string(kMaxCounterValue);
249
122k
  retval += " then ";
250
122k
  retval += then_block;
251
122k
  retval += " end\n";
252
122k
  return retval;
253
122k
}
254
255
/**
256
 * Class that registers and provides context during code
257
 * generation.
258
 * Used to generate correct Lua code.
259
 */
260
class Context {
261
public:
262
  enum class BlockType {
263
    kReturnable,
264
    kBreakable,
265
    kReturnableWithVararg,
266
  };
267
268
  void step_in(BlockType type)
269
122k
  {
270
122k
    block_stack_.push(type);
271
122k
    if (block_type_is_returnable_(type)) {
272
47.3k
      returnable_stack_.push(type);
273
47.3k
    }
274
122k
  }
275
276
  void step_out()
277
122k
  {
278
122k
    assert(!block_stack_.empty());
279
122k
    if (block_type_is_returnable_(block_stack_.top())) {
280
47.3k
      assert(!returnable_stack_.empty());
281
47.3k
      returnable_stack_.pop();
282
47.3k
    }
283
122k
    block_stack_.pop();
284
122k
  }
285
286
  std::string get_next_block_setup()
287
122k
  {
288
122k
    std::size_t id = GetCounterIdProvider().next();
289
122k
    std::string counter_name = GetCounterName(id);
290
291
122k
    return GetCondition(counter_name, get_exit_statement_()) +
292
122k
           GetCounterIncrement(counter_name);
293
122k
  }
294
295
  bool break_is_possible()
296
2.98k
  {
297
2.98k
    return !block_stack_.empty() &&
298
2.47k
           block_stack_.top() == BlockType::kBreakable;
299
2.98k
  }
300
301
  bool return_is_possible()
302
12.3k
  {
303
12.3k
    return !returnable_stack_.empty();
304
12.3k
  }
305
306
  bool vararg_is_possible()
307
5.20k
  {
308
5.20k
    return (returnable_stack_.empty() ||
309
1.58k
      (!returnable_stack_.empty() &&
310
1.58k
       returnable_stack_.top() ==
311
1.58k
        BlockType::kReturnableWithVararg));
312
5.20k
  }
313
314
private:
315
316
  bool block_type_is_returnable_(BlockType type)
317
245k
  {
318
245k
    switch (type) {
319
150k
    case BlockType::kBreakable:
320
150k
      return false;
321
91.0k
    case BlockType::kReturnable:
322
94.6k
    case BlockType::kReturnableWithVararg:
323
94.6k
      return true;
324
245k
    }
325
0
    unreachable();
326
0
  }
327
328
  std::string get_exit_statement_()
329
122k
  {
330
122k
    assert(!block_stack_.empty());
331
122k
    switch (block_stack_.top()) {
332
75.3k
    case BlockType::kBreakable:
333
75.3k
      return "break";
334
45.5k
    case BlockType::kReturnable:
335
47.3k
    case BlockType::kReturnableWithVararg:
336
47.3k
      return "return";
337
122k
    }
338
0
    unreachable();
339
0
  }
340
341
  std::stack<BlockType> block_stack_;
342
  /*
343
   * The returnable block can be exited with return from
344
   * the breakable block within it, but the breakable block
345
   * cannot be exited with break from the returnable block within
346
   * it.
347
   * Valid code:
348
   * `function foo() while true do return end end`
349
   * Erroneous code:
350
   * `while true do function foo() break end end`
351
   * This stack is used to check if `return` is possible.
352
   */
353
  std::stack<BlockType> returnable_stack_;
354
};
355
356
Context&
357
GetContext()
358
388k
{
359
388k
  static Context context;
360
388k
  return context;
361
388k
}
362
363
/**
364
 * Block may be placed not only in a cycle, so specially for cycles
365
 * there is a function that will add a break condition and a
366
 * counter increment.
367
 */
368
std::string
369
BlockToStringCycleProtected(const Block &block)
370
75.3k
{
371
75.3k
  std::string retval = GetContext().get_next_block_setup();
372
75.3k
  retval += ChunkToString(block.chunk());
373
75.3k
  return retval;
374
75.3k
}
375
376
/**
377
 * DoBlock may be placed not only in a cycle, so specially for
378
 * cycles there is a function that will call
379
 * BlockToStringCycleProtected().
380
 */
381
std::string
382
DoBlockToStringCycleProtected(const DoBlock &block)
383
24.6k
{
384
24.6k
  std::string retval = "do\n";
385
24.6k
  retval += BlockToStringCycleProtected(block.block());
386
24.6k
  retval += "end\n";
387
24.6k
  return retval;
388
24.6k
}
389
390
/**
391
 * FuncBody may contain recursive calls, so for all function bodies,
392
 * there is a function that adds a return condition and a counter
393
 * increment.
394
 */
395
std::string
396
FuncBodyToStringReqProtected(const FuncBody &body)
397
47.3k
{
398
47.3k
  std::string body_str = "( ";
399
47.3k
  if (body.has_parlist()) {
400
8.86k
    body_str += ParListToString(body.parlist());
401
8.86k
  }
402
47.3k
  body_str += " )\n\t";
403
404
47.3k
  body_str += GetContext().get_next_block_setup();
405
406
47.3k
  body_str += BlockToString(body.block());
407
47.3k
  body_str += "end\n";
408
47.3k
  return body_str;
409
47.3k
}
410
411
bool
412
FuncBodyHasVararg(const FuncBody &body)
413
47.3k
{
414
47.3k
  if (!body.has_parlist()) {
415
38.4k
    return false;
416
38.4k
  }
417
8.86k
  const FuncBody::ParList &parlist = body.parlist();
418
8.86k
  switch (parlist.parlist_oneof_case()) {
419
973
  case FuncBody::ParList::ParlistOneofCase::kNamelist:
420
973
    return parlist.namelist().has_ellipsis();
421
1.19k
  case FuncBody::ParList::ParlistOneofCase::kEllipsis:
422
1.19k
    return true;
423
6.69k
  default:
424
6.69k
    return parlist.namelist().has_ellipsis();
425
8.86k
  }
426
8.86k
}
427
428
Context::BlockType
429
GetFuncBodyType(const FuncBody &body)
430
47.3k
{
431
47.3k
  return FuncBodyHasVararg(body) ?
432
1.79k
    Context::BlockType::kReturnableWithVararg :
433
47.3k
    Context::BlockType::kReturnable;
434
47.3k
}
435
436
std::string
437
ClearIdentifier(const std::string &identifier)
438
248k
{
439
248k
  std::string cleared;
440
441
248k
  bool has_first_not_digit = false;
442
248k
  for (char c : identifier) {
443
119k
    if (has_first_not_digit && (std::iswalnum(c) || c == '_')) {
444
66.8k
      cleared += c;
445
66.8k
    } else if (std::isalpha(c) || c == '_') {
446
10.3k
      has_first_not_digit = true;
447
10.3k
      cleared += c;
448
41.9k
    } else {
449
41.9k
      cleared += '_';
450
41.9k
    }
451
119k
  }
452
248k
  return cleared;
453
248k
}
454
455
/** Drop any special symbols to avoid parser errors. */
456
std::string
457
ClearString(const std::string &str)
458
168k
{
459
168k
  std::string escaped;
460
168k
  int nbackslash = 0;
461
168k
  for (char c : str) {
462
93.1k
    if (c == '\\') {
463
2.05k
      nbackslash++;
464
91.0k
    } else {
465
91.0k
      if (!std::isprint(c))
466
9.18k
        continue;
467
81.8k
      nbackslash = 0;
468
81.8k
      if (c == '\'' || c == '\n')
469
7.00k
        escaped += '\\';
470
81.8k
    }
471
83.9k
    escaped += c;
472
83.9k
  }
473
  /* Avoid escaping the ending quote. */
474
168k
  if (nbackslash & 1)
475
95
    escaped += '\\';
476
168k
  return escaped;
477
168k
}
478
479
inline std::string
480
clamp(std::string s, size_t maxSize = kMaxStrLength)
481
416k
{
482
416k
  if (s.size() > maxSize)
483
4.58k
    s.resize(maxSize);
484
416k
  return s;
485
416k
}
486
487
inline double
488
clamp(double number, double upper, double lower)
489
15.4k
{
490
15.4k
  return number <= lower ? lower :
491
15.4k
         number >= upper ? upper : number;
492
15.4k
}
493
494
/**
495
 * Function to sanitize strings or identifiers. For identifiers,
496
 * sanitization is more stringent.
497
 */
498
inline std::string
499
ConvertToStringDefault(const std::string &s, bool is_identifier = false)
500
416k
{
501
416k
  std::string str = clamp(s);
502
416k
  if (is_identifier)
503
248k
    str = ClearIdentifier(str);
504
168k
  else
505
168k
    str = ClearString(str);
506
416k
  if (is_identifier && str.empty())
507
232k
    str = std::string(kDefaultIdent);
508
416k
  return str;
509
416k
}
510
511
PROTO_TOSTRING(Block, block)
512
70.6k
{
513
70.6k
  return ChunkToString(block.chunk());
514
70.6k
}
515
516
PROTO_TOSTRING(Chunk, chunk)
517
146k
{
518
146k
  std::string chunk_str;
519
348k
  for (int i = 0; i < chunk.stat_size(); ++i)
520
202k
    chunk_str += StatementToString(chunk.stat(i)) + "\n";
521
522
146k
  if (chunk.has_laststat())
523
15.3k
    chunk_str += LastStatementToString(chunk.laststat()) + "\n";
524
525
146k
  return chunk_str;
526
146k
}
527
528
/**
529
 * LastStatement and nested types.
530
 */
531
PROTO_TOSTRING(LastStatement, laststat)
532
15.3k
{
533
15.3k
  std::string laststat_str;
534
15.3k
  using LastStatType = LastStatement::LastOneofCase;
535
15.3k
  switch (laststat.last_oneof_case()) {
536
4.86k
  case LastStatType::kExplist:
537
4.86k
    laststat_str = ReturnOptionalExpressionListToString(
538
4.86k
      laststat.explist());
539
4.86k
    break;
540
2.98k
  case LastStatType::kBreak:
541
2.98k
    if (GetContext().break_is_possible()) {
542
1.42k
      laststat_str = "break";
543
1.42k
    }
544
2.98k
    break;
545
7.46k
  default:
546
    /* Chosen as default in order to decrease number of 'break's. */
547
7.46k
    laststat_str = ReturnOptionalExpressionListToString(
548
7.46k
      laststat.explist());
549
7.46k
    break;
550
15.3k
  }
551
552
  /*
553
   * Add a semicolon when last statement is not empty
554
   * to avoid errors like:
555
   *
556
   * <preamble.lua>
557
   * (nil):Name0()
558
   * (nil)() -- ambiguous syntax (function call x new statement) near '('
559
   */
560
15.3k
  if (!laststat_str.empty())
561
9.25k
    laststat_str += "; ";
562
563
15.3k
  return laststat_str;
564
15.3k
}
565
566
NESTED_PROTO_TOSTRING(ReturnOptionalExpressionList, explist, LastStatement)
567
12.3k
{
568
12.3k
  if (!GetContext().return_is_possible()) {
569
4.50k
    return "";
570
4.50k
  }
571
572
7.82k
  std::string explist_str = "return";
573
7.82k
  if (explist.has_explist()) {
574
3.76k
    explist_str += " " + ExpressionListToString(explist.explist());
575
3.76k
    explist_str += " ";
576
3.76k
  }
577
7.82k
  return explist_str;
578
12.3k
}
579
580
/**
581
 * Statement and statement options.
582
 */
583
PROTO_TOSTRING(Statement, stat)
584
202k
{
585
202k
  std::string stat_str;
586
202k
  using StatType = Statement::StatOneofCase;
587
202k
  switch (stat.stat_oneof_case()) {
588
11.6k
  case StatType::kList:
589
11.6k
    stat_str = AssignmentListToString(stat.list());
590
11.6k
    break;
591
47.7k
  case StatType::kCall:
592
47.7k
    stat_str = FunctionCallToString(stat.call());
593
47.7k
    break;
594
2.04k
  case StatType::kBlock:
595
2.04k
    stat_str = DoBlockToString(stat.block());
596
2.04k
    break;
597
4.89k
  case StatType::kWhilecycle:
598
4.89k
    stat_str = WhileCycleToString(stat.whilecycle());
599
4.89k
    break;
600
50.6k
  case StatType::kRepeatcycle:
601
50.6k
    stat_str = RepeatCycleToString(stat.repeatcycle());
602
50.6k
    break;
603
8.97k
  case StatType::kIfstat:
604
8.97k
    stat_str = IfStatementToString(stat.ifstat());
605
8.97k
    break;
606
4.39k
  case StatType::kForcyclename:
607
4.39k
    stat_str = ForCycleNameToString(stat.forcyclename());
608
4.39k
    break;
609
15.3k
  case StatType::kForcyclelist:
610
15.3k
    stat_str = ForCycleListToString(stat.forcyclelist());
611
15.3k
    break;
612
4.82k
  case StatType::kFunc:
613
4.82k
    stat_str = FunctionToString(stat.func());
614
4.82k
    break;
615
10.6k
  case StatType::kLocalfunc:
616
10.6k
    stat_str = LocalFuncToString(stat.localfunc());
617
10.6k
    break;
618
9.55k
  case StatType::kLocalnames:
619
9.55k
    stat_str = LocalNamesToString(stat.localnames());
620
9.55k
    break;
621
32.1k
  default:
622
    /**
623
     * Chosen arbitrarily more for simplicity.
624
     * TODO: Choose "more interesting" defaults.
625
     */
626
32.1k
    stat_str = AssignmentListToString(stat.list());
627
32.1k
    break;
628
202k
  }
629
630
  /*
631
   * Always add a semicolon regardless of grammar
632
   * to avoid errors like:
633
   *
634
   * <preamble.lua>
635
   * (nil):Name0()
636
   * (nil)() -- ambiguous syntax (function call x new statement) near '('
637
   */
638
202k
  stat_str += "; ";
639
640
202k
  return stat_str;
641
202k
}
642
643
/**
644
 * AssignmentList and nested types.
645
 */
646
PROTO_TOSTRING(AssignmentList, assignmentlist)
647
43.8k
{
648
43.8k
  std::string list_str = VariableListToString(assignmentlist.varlist());
649
43.8k
  list_str += " = " + ExpressionListToString(assignmentlist.explist());
650
43.8k
  return list_str;
651
43.8k
}
652
653
NESTED_PROTO_TOSTRING(VariableList, varlist, AssignmentList)
654
43.8k
{
655
43.8k
  std::string varlist_str = VariableToString(varlist.var());
656
57.7k
  for (int i = 0; i < varlist.vars_size(); ++i) {
657
13.8k
    varlist_str += ", " + VariableToString(varlist.vars(i));
658
13.8k
    varlist_str += " ";
659
13.8k
  }
660
43.8k
  return varlist_str;
661
43.8k
}
662
663
/**
664
 * FunctionCall and nested types.
665
 */
666
PROTO_TOSTRING(FunctionCall, call)
667
60.0k
{
668
60.0k
  using FuncCallType = FunctionCall::CallOneofCase;
669
60.0k
  switch (call.call_oneof_case()) {
670
8.43k
  case FuncCallType::kPrefArgs:
671
8.43k
    return PrefixArgsToString(call.prefargs());
672
5.33k
  case FuncCallType::kNamedArgs:
673
5.33k
    return PrefixNamedArgsToString(call.namedargs());
674
46.2k
  default:
675
    /* Chosen for more variability of generated programs. */
676
46.2k
    return PrefixNamedArgsToString(call.namedargs());
677
60.0k
  }
678
60.0k
}
679
680
NESTED_PROTO_TOSTRING(Args, args, FunctionCall)
681
60.0k
{
682
60.0k
  using ArgsType = FunctionCall::Args::ArgsOneofCase;
683
60.0k
  switch (args.args_oneof_case()) {
684
4.02k
  case ArgsType::kExplist:
685
4.02k
    return "(" + OptionalExpressionListToString(args.explist()) +
686
4.02k
           ")";
687
1.06k
  case ArgsType::kTableconstructor:
688
1.06k
    return TableConstructorToString(args.tableconstructor());
689
2.11k
  case ArgsType::kStr:
690
2.11k
    return "'" + ConvertToStringDefault(args.str()) + "'";
691
52.8k
  default:
692
    /* For more variability. */
693
52.8k
    return TableConstructorToString(args.tableconstructor());
694
60.0k
  }
695
60.0k
}
696
697
NESTED_PROTO_TOSTRING(PrefixArgs, prefixargs, FunctionCall)
698
8.43k
{
699
8.43k
  std::string prefixargs_str = PrefixExpressionToString(
700
8.43k
    prefixargs.prefixexp());
701
8.43k
  prefixargs_str += " " + ArgsToString(prefixargs.args());
702
8.43k
  return prefixargs_str;
703
8.43k
}
704
705
NESTED_PROTO_TOSTRING(PrefixNamedArgs, prefixnamedargs, FunctionCall)
706
51.6k
{
707
51.6k
  std::string predixnamedargs_str = PrefixExpressionToString(
708
51.6k
    prefixnamedargs.prefixexp());
709
51.6k
  predixnamedargs_str += ":" + NameToString(prefixnamedargs.name());
710
51.6k
  predixnamedargs_str += " " + ArgsToString(prefixnamedargs.args());
711
51.6k
  return predixnamedargs_str;
712
51.6k
}
713
714
/**
715
 * DoBlock clause.
716
 */
717
PROTO_TOSTRING(DoBlock, block)
718
2.04k
{
719
2.04k
  return "do\n" + BlockToString(block.block()) + "end\n";
720
2.04k
}
721
722
/**
723
 * WhileCycle clause.
724
 */
725
PROTO_TOSTRING(WhileCycle, whilecycle)
726
4.89k
{
727
4.89k
  GetContext().step_in(Context::BlockType::kBreakable);
728
729
4.89k
  std::string whilecycle_str = "while ";
730
4.89k
  whilecycle_str += ExpressionToString(whilecycle.condition());
731
4.89k
  whilecycle_str += " ";
732
4.89k
  whilecycle_str += DoBlockToStringCycleProtected(whilecycle.doblock());
733
734
4.89k
  GetContext().step_out();
735
4.89k
  return whilecycle_str;
736
4.89k
}
737
738
/**
739
 * RepeatCycle clause.
740
 */
741
PROTO_TOSTRING(RepeatCycle, repeatcycle)
742
50.6k
{
743
50.6k
  GetContext().step_in(Context::BlockType::kBreakable);
744
745
50.6k
  std::string repeatcycle_str = "repeat\n";
746
50.6k
  repeatcycle_str += BlockToStringCycleProtected(repeatcycle.block());
747
50.6k
  repeatcycle_str += "until ";
748
50.6k
  repeatcycle_str += ExpressionToString(repeatcycle.condition());
749
750
50.6k
  GetContext().step_out();
751
50.6k
  return repeatcycle_str;
752
50.6k
}
753
754
/**
755
 * IfStatement and nested types.
756
 */
757
PROTO_TOSTRING(IfStatement, statement)
758
8.97k
{
759
8.97k
  std::string statement_str = "if " +
760
8.97k
    ExpressionToString(statement.condition());
761
8.97k
  statement_str += " then\n\t" + BlockToString(statement.first());
762
763
15.1k
  for (int i = 0; i < statement.clauses_size(); ++i)
764
6.21k
    statement_str += ElseIfBlockToString(statement.clauses(i));
765
766
8.97k
  if (statement.has_last())
767
4.33k
    statement_str += "else\n\t" + BlockToString(statement.last());
768
769
8.97k
  statement_str += "end\n";
770
8.97k
  return statement_str;
771
8.97k
}
772
773
NESTED_PROTO_TOSTRING(ElseIfBlock, elseifblock, IfStatement)
774
6.21k
{
775
6.21k
  std::string elseifblock_str = "elseif ";
776
6.21k
  elseifblock_str += ExpressionToString(elseifblock.condition());
777
6.21k
  elseifblock_str += " then\n\t";
778
6.21k
  elseifblock_str += BlockToString(elseifblock.block());
779
6.21k
  return elseifblock_str;
780
6.21k
}
781
782
/**
783
 * ForCycleName clause.
784
 * TODO: In 'for i = start, stop, step' construction start, stop, step
785
 * should be numbers. So results of the corresponding expressions
786
 * should be number.
787
 */
788
PROTO_TOSTRING(ForCycleName, forcyclename)
789
4.39k
{
790
4.39k
  GetContext().step_in(Context::BlockType::kBreakable);
791
792
4.39k
  std::string forcyclename_str = "for ";
793
4.39k
  forcyclename_str += NameToString(forcyclename.name());
794
4.39k
  forcyclename_str += " = ";
795
4.39k
  forcyclename_str += NumberWrappedExpressionToString(
796
4.39k
    forcyclename.startexp());
797
4.39k
  forcyclename_str += ", ";
798
4.39k
  forcyclename_str += NumberWrappedExpressionToString(
799
4.39k
    forcyclename.stopexp());
800
801
4.39k
  if (forcyclename.has_stepexp())
802
2.21k
    forcyclename_str += ", " + NumberWrappedExpressionToString(
803
2.21k
      forcyclename.stepexp());
804
805
4.39k
  forcyclename_str += " ";
806
4.39k
  forcyclename_str += DoBlockToStringCycleProtected(
807
4.39k
    forcyclename.doblock());
808
809
4.39k
  GetContext().step_out();
810
4.39k
  return forcyclename_str;
811
4.39k
}
812
813
/**
814
 * ForCycleList clause.
815
 */
816
PROTO_TOSTRING(ForCycleList, forcyclelist)
817
15.3k
{
818
15.3k
  GetContext().step_in(Context::BlockType::kBreakable);
819
820
15.3k
  std::string forcyclelist_str = "for ";
821
15.3k
  forcyclelist_str += NameListToString(forcyclelist.names());
822
15.3k
  forcyclelist_str += " in ";
823
15.3k
  forcyclelist_str += ExpressionListToString(forcyclelist.expressions());
824
15.3k
  forcyclelist_str += " ";
825
15.3k
  forcyclelist_str += DoBlockToStringCycleProtected(
826
15.3k
    forcyclelist.doblock());
827
828
15.3k
  GetContext().step_out();
829
15.3k
  return forcyclelist_str;
830
15.3k
}
831
832
/**
833
 * Function and nested types.
834
 */
835
PROTO_TOSTRING(Function, func)
836
4.82k
{
837
4.82k
  GetContext().step_in(GetFuncBodyType(func.body()));
838
839
4.82k
  std::string func_str = "function ";
840
4.82k
  func_str += FuncNameToString(func.name());
841
4.82k
  func_str += FuncBodyToStringReqProtected(func.body());
842
843
4.82k
  GetContext().step_out();
844
4.82k
  return func_str;
845
4.82k
}
846
847
NESTED_PROTO_TOSTRING(FuncName, funcname, Function)
848
4.82k
{
849
4.82k
  std::string funcname_str = NameToString(funcname.firstname());
850
851
5.80k
  for (int i = 0; i < funcname.names_size(); ++i)
852
977
    funcname_str += "." + NameToString(funcname.names(i));
853
854
4.82k
  if (funcname.has_lastname())
855
453
    funcname_str += ":" + NameToString(funcname.lastname());
856
857
4.82k
  return funcname_str;
858
4.82k
}
859
860
PROTO_TOSTRING(NameList, namelist)
861
32.5k
{
862
32.5k
  std::string namelist_str = NameToString(namelist.firstname());
863
40.4k
  for (int i = 0; i < namelist.names_size(); ++i)
864
7.88k
    namelist_str += ", " + NameToString(namelist.names(i));
865
32.5k
  return namelist_str;
866
32.5k
}
867
868
NESTED_PROTO_TOSTRING(NameListWithEllipsis, namelist, FuncBody)
869
7.67k
{
870
7.67k
  std::string namelist_str = NameListToString(namelist.namelist());
871
7.67k
  if (namelist.has_ellipsis())
872
598
    namelist_str += ", ...";
873
7.67k
  return namelist_str;
874
7.67k
}
875
876
NESTED_PROTO_TOSTRING(ParList, parlist, FuncBody)
877
8.86k
{
878
8.86k
  using ParListType = FuncBody::ParList::ParlistOneofCase;
879
8.86k
  switch (parlist.parlist_oneof_case()) {
880
973
  case ParListType::kNamelist:
881
973
    return NameListWithEllipsisToString(parlist.namelist());
882
1.19k
  case ParListType::kEllipsis:
883
1.19k
    return "...";
884
6.69k
  default:
885
    /* Chosen as default in order to decrease number of ellipses. */
886
6.69k
    return NameListWithEllipsisToString(parlist.namelist());
887
8.86k
  }
888
8.86k
}
889
890
/**
891
 * LocalFunc clause.
892
 */
893
PROTO_TOSTRING(LocalFunc, localfunc)
894
10.6k
{
895
10.6k
  GetContext().step_in(GetFuncBodyType(localfunc.funcbody()));
896
897
10.6k
  std::string localfunc_str = "local function ";
898
10.6k
  localfunc_str += NameToString(localfunc.name());
899
10.6k
  localfunc_str += " ";
900
10.6k
  localfunc_str += FuncBodyToStringReqProtected(localfunc.funcbody());
901
902
10.6k
  GetContext().step_out();
903
10.6k
  return localfunc_str;
904
10.6k
}
905
906
/**
907
 * LocalNames clause.
908
 */
909
PROTO_TOSTRING(LocalNames, localnames)
910
9.55k
{
911
9.55k
  std::string localnames_str = "local ";
912
9.55k
  localnames_str += NameListToString(localnames.namelist());
913
914
9.55k
  if (localnames.has_explist())
915
7.70k
    localnames_str += " = " + ExpressionListToString(
916
7.70k
      localnames.explist());
917
9.55k
  return localnames_str;
918
9.55k
}
919
920
/**
921
 * Expressions and variables.
922
 */
923
924
/**
925
 * Expressions clauses.
926
 */
927
PROTO_TOSTRING(ExpressionList, explist)
928
74.4k
{
929
74.4k
  std::string explist_str;
930
117k
  for (int i = 0; i < explist.expressions_size(); ++i)
931
42.8k
    explist_str += ExpressionToString(explist.expressions(i)) +
932
42.8k
        ", ";
933
74.4k
  explist_str += ExpressionToString(explist.explast()) + " ";
934
74.4k
  return explist_str;
935
74.4k
}
936
937
PROTO_TOSTRING(OptionalExpressionList, explist)
938
4.02k
{
939
4.02k
  if (explist.has_explist())
940
3.83k
    return ExpressionListToString(explist.explist());
941
190
  return "";
942
4.02k
}
943
944
PROTO_TOSTRING(PrefixExpression, prefixexp)
945
91.3k
{
946
91.3k
  using PrefExprType = PrefixExpression::PrefixOneofCase;
947
91.3k
  switch (prefixexp.prefix_oneof_case()) {
948
7.02k
  case PrefExprType::kVar:
949
7.02k
    return VariableToString(prefixexp.var());
950
12.2k
  case PrefExprType::kFunctioncall:
951
12.2k
    return FunctionCallToString(prefixexp.functioncall());
952
6.13k
  case PrefExprType::kExp:
953
6.13k
    return "(" + ExpressionToString(prefixexp.exp()) + ")";
954
65.9k
  default:
955
    /*
956
     * Can be generated too nested expressions with other options,
957
     * though they can be enabled for more variable fuzzing.
958
     */
959
65.9k
    return VariableToString(prefixexp.var());
960
91.3k
  }
961
91.3k
}
962
963
/**
964
 * Variable and nested types.
965
 */
966
PROTO_TOSTRING(Variable, var)
967
130k
{
968
130k
  using VarType = Variable::VarOneofCase;
969
130k
  switch (var.var_oneof_case()) {
970
6.81k
  case VarType::kName:
971
6.81k
    return NameToString(var.name());
972
5.23k
  case VarType::kIndexexpr:
973
5.23k
    return IndexWithExpressionToString(var.indexexpr());
974
3.23k
  case VarType::kIndexname:
975
3.23k
    return IndexWithNameToString(var.indexname());
976
115k
  default:
977
    /*
978
     * Can be generated too nested expressions with other options,
979
     * though they can be enabled for more variable fuzzing.
980
     */
981
115k
    return NameToString(var.name());
982
130k
  }
983
130k
}
984
985
NESTED_PROTO_TOSTRING(IndexWithExpression, indexexpr, Variable)
986
5.23k
{
987
5.23k
  std::string indexexpr_str = PrefixExpressionToString(
988
5.23k
    indexexpr.prefixexp());
989
5.23k
  indexexpr_str += "[" + ExpressionToString(indexexpr.exp()) + "]";
990
5.23k
  return indexexpr_str;
991
5.23k
}
992
993
NESTED_PROTO_TOSTRING(IndexWithName, indexname, Variable)
994
3.23k
{
995
3.23k
  std::string indexname_str = PrefixExpressionToString(
996
3.23k
    indexname.prefixexp());
997
3.23k
  std::string idx_str = ConvertToStringDefault(indexname.name(), true);
998
  /* Prevent using reserved keywords as indices. */
999
3.23k
  if (KReservedLuaKeywords.find(idx_str) != KReservedLuaKeywords.end()) {
1000
32
    idx_str += "_1";
1001
32
  }
1002
3.23k
  indexname_str += "." + idx_str;
1003
3.23k
  return indexname_str;
1004
3.23k
}
1005
1006
/**
1007
 * Expression and nested types.
1008
 */
1009
PROTO_TOSTRING(Expression, expr)
1010
322k
{
1011
322k
  using ExprType = Expression::ExprOneofCase;
1012
322k
  switch (expr.expr_oneof_case()) {
1013
16.8k
  case ExprType::kNil:
1014
16.8k
    return "nil";
1015
3.95k
  case ExprType::kFalse:
1016
3.95k
    return "false";
1017
4.40k
  case ExprType::kTrue:
1018
4.40k
    return "true";
1019
15.4k
  case ExprType::kNumber: {
1020
    /* Clamp number between given boundaries. */
1021
15.4k
    double number = clamp(expr.number(), kMaxNumber, kMinNumber);
1022
15.4k
    return std::to_string(number);
1023
0
  }
1024
7.24k
  case ExprType::kStr:
1025
7.24k
    return "'" + ConvertToStringDefault(expr.str()) + "'";
1026
5.20k
  case ExprType::kEllipsis:
1027
5.20k
    if (GetContext().vararg_is_possible()) {
1028
4.15k
      return " ... ";
1029
4.15k
    } else {
1030
1.04k
      return " nil";
1031
1.04k
    }
1032
31.8k
  case ExprType::kFunction:
1033
31.8k
    return AnonFuncToString(expr.function());
1034
22.8k
  case ExprType::kPrefixexp:
1035
22.8k
    return PrefixExpressionToString(expr.prefixexp());
1036
8.50k
  case ExprType::kTableconstructor:
1037
8.50k
    return TableConstructorToString(expr.tableconstructor());
1038
41.7k
  case ExprType::kBinary:
1039
41.7k
    return ExpBinaryOpExpToString(expr.binary());
1040
5.75k
  case ExprType::kUnary:
1041
5.75k
    return UnaryOpExpToString(expr.unary());
1042
158k
  default:
1043
    /**
1044
     * Arbitrary choice.
1045
     * TODO: Choose "more interesting" defaults.
1046
     */
1047
158k
    return "'" + ConvertToStringDefault(expr.str()) + "'";
1048
322k
  }
1049
322k
}
1050
1051
NESTED_PROTO_TOSTRING(AnonFunc, func, Expression)
1052
31.8k
{
1053
31.8k
  GetContext().step_in(GetFuncBodyType(func.body()));
1054
1055
31.8k
  std::string retval = "function ";
1056
31.8k
  retval += FuncBodyToStringReqProtected(func.body());
1057
1058
31.8k
  GetContext().step_out();
1059
31.8k
  return retval;
1060
31.8k
}
1061
1062
NESTED_PROTO_TOSTRING(ExpBinaryOpExp, binary, Expression)
1063
41.7k
{
1064
41.7k
  std::string leftexp_str = ExpressionToString(binary.leftexp());
1065
41.7k
  std::string binop_str = BinaryOperatorToString(binary.binop());
1066
41.7k
  std::string rightexp_str = ExpressionToString(binary.rightexp());
1067
1068
41.7k
  std::string binary_str;
1069
41.7k
  if (binop_str == "<" ||
1070
40.0k
      binop_str == ">" ||
1071
39.6k
      binop_str == "<=" ||
1072
39.5k
      binop_str == ">=") {
1073
2.58k
    binary_str = kBinOpWrapperName;
1074
2.58k
    binary_str += "(" + leftexp_str;
1075
2.58k
    binary_str += ", '" + binop_str + "', ";
1076
2.58k
    binary_str += rightexp_str + ")";
1077
2.58k
    return binary_str;
1078
2.58k
  }
1079
1080
39.1k
  binary_str = leftexp_str;
1081
39.1k
  binary_str += " " + binop_str + " ";
1082
39.1k
  binary_str += rightexp_str;
1083
1084
39.1k
  return binary_str;
1085
41.7k
}
1086
1087
NESTED_PROTO_TOSTRING(UnaryOpExp, unary, Expression)
1088
5.75k
{
1089
5.75k
  std::string unary_str = UnaryOperatorToString(unary.unop());
1090
  /*
1091
   * Add a whitespace before an expression with unary minus,
1092
   * otherwise double hyphen comments the following code
1093
   * and it breaks generated programs syntactically.
1094
   */
1095
5.75k
  unary_str += " " + ExpressionToString(unary.exp());
1096
5.75k
  return unary_str;
1097
5.75k
}
1098
1099
/**
1100
 * Tables and fields.
1101
 */
1102
PROTO_TOSTRING(TableConstructor, table)
1103
62.4k
{
1104
62.4k
  std::string table_str = " (setmetatable({ ";
1105
62.4k
  if (table.has_fieldlist())
1106
6.69k
    table_str += FieldListToString(table.fieldlist());
1107
62.4k
  table_str += " }, table_mt))()";
1108
62.4k
  return table_str;
1109
62.4k
}
1110
1111
PROTO_TOSTRING(FieldList, fieldlist)
1112
6.69k
{
1113
6.69k
  std::string fieldlist_str = FieldToString(fieldlist.firstfield());
1114
18.0k
  for (int i = 0; i < fieldlist.fields_size(); ++i)
1115
11.3k
    fieldlist_str += FieldWithFieldSepToString(fieldlist.fields(i));
1116
6.69k
  if (fieldlist.has_lastsep())
1117
1.46k
    fieldlist_str += FieldSepToString(fieldlist.lastsep());
1118
6.69k
  return fieldlist_str;
1119
6.69k
}
1120
1121
NESTED_PROTO_TOSTRING(FieldWithFieldSep, field, FieldList)
1122
11.3k
{
1123
11.3k
  std::string field_str = FieldSepToString(field.sep());
1124
11.3k
  field_str += " " + FieldToString(field.field());
1125
11.3k
  return field_str;
1126
11.3k
}
1127
1128
/**
1129
 * Field and nested types.
1130
 */
1131
PROTO_TOSTRING(Field, field)
1132
18.0k
{
1133
18.0k
  using FieldType = Field::FieldOneofCase;
1134
18.0k
  switch (field.field_oneof_case()) {
1135
4.78k
  case FieldType::kExprassign:
1136
4.78k
    return ExpressionAssignmentToString(field.exprassign());
1137
526
  case FieldType::kNamedassign:
1138
526
    return NameAssignmentToString(field.namedassign());
1139
3.77k
  case FieldType::kExpression:
1140
3.77k
    return ExpressionToString(field.expression());
1141
8.95k
  default:
1142
    /* More common case of using fields. */
1143
8.95k
    return NameAssignmentToString(field.namedassign());
1144
18.0k
  }
1145
18.0k
}
1146
1147
NESTED_PROTO_TOSTRING(ExpressionAssignment, assignment, Field)
1148
4.78k
{
1149
  /* Prevent error 'table index is nil' and 'table index is NaN'. */
1150
4.78k
  std::string assignment_str = "[ " +
1151
4.78k
    AllowedIndexExpressionToString(assignment.key()) + " ]";
1152
4.78k
  assignment_str += " = " + ExpressionToString(assignment.value());
1153
4.78k
  return assignment_str;
1154
4.78k
}
1155
1156
NESTED_PROTO_TOSTRING(NameAssignment, assignment, Field)
1157
9.48k
{
1158
9.48k
  std::string assignment_str = NameToString(assignment.name());
1159
9.48k
  assignment_str += " = " + ExpressionToString(assignment.value());
1160
9.48k
  return assignment_str;
1161
9.48k
}
1162
1163
PROTO_TOSTRING(FieldSep, sep)
1164
12.8k
{
1165
12.8k
  using FieldSepType = FieldSep::SepOneofCase;
1166
12.8k
  switch (sep.sep_oneof_case()) {
1167
3.68k
  case FieldSepType::kComma:
1168
3.68k
    return ",";
1169
1.24k
  case FieldSepType::kSemicolon:
1170
1.24k
    return ";";
1171
7.88k
  default:
1172
7.88k
    return ",";
1173
12.8k
  }
1174
12.8k
}
1175
1176
/**
1177
 * Operators.
1178
 */
1179
PROTO_TOSTRING(BinaryOperator, op)
1180
41.7k
{
1181
41.7k
  using BinopType = BinaryOperator::BinaryOneofCase;
1182
41.7k
  switch (op.binary_oneof_case()) {
1183
2.66k
  case BinopType::kAdd:
1184
2.66k
    return "+";
1185
1.68k
  case BinopType::kSub:
1186
1.68k
    return "-";
1187
3.22k
  case BinopType::kMult:
1188
3.22k
    return "*";
1189
1.12k
  case BinopType::kDiv:
1190
1.12k
    return "/";
1191
#if LUA_VERSION_NUM >= 503
1192
  case BinopType::kIDiv:
1193
    return "//";
1194
#endif
1195
773
  case BinopType::kExp:
1196
773
    return "^";
1197
1.40k
  case BinopType::kMod:
1198
1.40k
    return "%";
1199
1200
10.0k
  case BinopType::kConcat:
1201
10.0k
    return "..";
1202
1203
1.71k
  case BinopType::kLess:
1204
1.71k
    return "<";
1205
120
  case BinopType::kLessEqual:
1206
120
    return "<=";
1207
350
  case BinopType::kGreater:
1208
350
    return ">";
1209
402
  case BinopType::kGreaterEqual:
1210
402
    return ">=";
1211
216
  case BinopType::kEqual:
1212
216
    return "==";
1213
232
  case BinopType::kNotEqual:
1214
232
    return "~=";
1215
943
  case BinopType::kAnd:
1216
943
    return "and";
1217
6.06k
  case BinopType::kOr:
1218
6.06k
    return "or";
1219
1220
#if LUA_VERSION_NUM >= 503
1221
  case BinopType::kBAnd:
1222
    return "&";
1223
  case BinopType::kBOr:
1224
    return "|";
1225
  case BinopType::kBXor:
1226
    return "~";
1227
  case BinopType::kBShl:
1228
    return "<<";
1229
  case BinopType::kBShr:
1230
    return ">>";
1231
#endif
1232
10.7k
  default:
1233
    /* Works in most cases. */
1234
10.7k
    return "==";
1235
41.7k
  }
1236
41.7k
}
1237
1238
PROTO_TOSTRING(UnaryOperator, op)
1239
5.75k
{
1240
5.75k
  using UnaryopType = UnaryOperator::UnaryOneofCase;
1241
5.75k
  switch (op.unary_oneof_case()) {
1242
1.76k
  case UnaryopType::kNegate:
1243
1.76k
    return "-";
1244
398
  case UnaryopType::kNot:
1245
398
    return "not ";
1246
642
  case UnaryopType::kLength:
1247
642
    return "#";
1248
#if LUA_VERSION_NUM >= 503
1249
  case UnaryopType::kBNot:
1250
    return "~";
1251
#endif
1252
2.95k
  default:
1253
    /* Works in most cases. */
1254
2.95k
    return "not ";
1255
5.75k
  }
1256
5.75k
}
1257
1258
/**
1259
 * Identifier (Name).
1260
 */
1261
PROTO_TOSTRING(Name, name)
1262
245k
{
1263
245k
  std::string ident = ConvertToStringDefault(name.name(), true);
1264
  /* Prevent using reserved keywords as identifiers. */
1265
245k
  if (KReservedLuaKeywords.find(ident) != KReservedLuaKeywords.end()) {
1266
163
    ident += "_1";
1267
163
  }
1268
  /* Identifier has default name, add an index. */
1269
245k
  if (!ident.compare(kDefaultIdent)) {
1270
230k
    ident += std::to_string(name.num() % kMaxIdentifiers);
1271
230k
  }
1272
245k
  return ident;
1273
245k
}
1274
1275
} /* namespace */
1276
1277
std::string
1278
MainBlockToString(const Block &block)
1279
1.81k
{
1280
1.81k
  GetCounterIdProvider().clean();
1281
1282
1.81k
  std::string block_str = BlockToString(block);
1283
1.81k
  std::string retval = preamble_lua;
1284
1285
124k
  for (size_t i = 0; i < GetCounterIdProvider().count(); ++i) {
1286
122k
    retval += GetCounterName(i);
1287
122k
    retval += " = 0\n";
1288
122k
  }
1289
1.81k
  retval += block_str;
1290
1291
1.81k
  return retval;
1292
1.81k
}
1293
1294
} /* namespace luajit_fuzzer */