Coverage Report

Created: 2023-09-11 06:55

/src/testdir/tests/luaL_loadbuffer_proto/serializer.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: BSD-2-Clause
3
 *
4
 * Copyright 2022, Tarantool AUTHORS, please see AUTHORS file.
5
 */
6
#include "serializer.h"
7
8
#include <stack>
9
#include <string>
10
11
/**
12
* If control flow reaches the point of the unreachable(), the program is
13
* undefined. It is useful in situations where the compiler cannot deduce
14
* the unreachability of the code.
15
*/
16
#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
17
0
#  define unreachable() (assert(0), __builtin_unreachable())
18
#else
19
#  define unreachable() (assert(0))
20
#endif
21
22
using namespace lua_grammar;
23
24
#define PROTO_TOSTRING(TYPE, VAR_NAME) \
25
  std::string TYPE##ToString(const TYPE & (VAR_NAME))
26
27
/* PROTO_TOSTRING version for nested (depth=2) protobuf messages. */
28
#define NESTED_PROTO_TOSTRING(TYPE, VAR_NAME, PARENT_MESSAGE) \
29
  std::string TYPE##ToString \
30
  (const PARENT_MESSAGE::TYPE & (VAR_NAME))
31
32
namespace luajit_fuzzer {
33
namespace {
34
35
const std::string kCounterNamePrefix = "counter_";
36
37
PROTO_TOSTRING(Block, block);
38
PROTO_TOSTRING(Chunk, chunk);
39
40
PROTO_TOSTRING(Statement, stat);
41
42
/** LastStatement and nested types. */
43
PROTO_TOSTRING(LastStatement, laststat);
44
NESTED_PROTO_TOSTRING(ReturnOptionalExpressionList, explist, LastStatement);
45
46
/**
47
 * Statement options.
48
 */
49
50
/** AssignmentList and nested types. */
51
PROTO_TOSTRING(AssignmentList, assignmentlist);
52
NESTED_PROTO_TOSTRING(VariableList, varlist, AssignmentList);
53
54
/** FunctionCall and nested types. */
55
PROTO_TOSTRING(FunctionCall, call);
56
NESTED_PROTO_TOSTRING(Args, args, FunctionCall);
57
NESTED_PROTO_TOSTRING(PrefixArgs, prefixargs, FunctionCall);
58
NESTED_PROTO_TOSTRING(PrefixNamedArgs, prefixnamedargs, FunctionCall);
59
60
/** DoBlock, WhileCycle and RepeatCycle clauses. */
61
PROTO_TOSTRING(DoBlock, block);
62
PROTO_TOSTRING(WhileCycle, whilecycle);
63
PROTO_TOSTRING(RepeatCycle, repeatcycle);
64
65
/** IfStatement and nested types. */
66
PROTO_TOSTRING(IfStatement, statement);
67
NESTED_PROTO_TOSTRING(ElseIfBlock, elseifblock, IfStatement);
68
69
/** ForCycleName and ForCycleList clauses. */
70
PROTO_TOSTRING(ForCycleName, forcyclename);
71
PROTO_TOSTRING(ForCycleList, forcyclelist);
72
73
/** Function and nested types. */
74
PROTO_TOSTRING(Function, func);
75
NESTED_PROTO_TOSTRING(FuncName, funcname, Function);
76
77
PROTO_TOSTRING(NameList, namelist);
78
NESTED_PROTO_TOSTRING(NameListWithEllipsis, namelist, FuncBody);
79
NESTED_PROTO_TOSTRING(ParList, parlist, FuncBody);
80
81
/** LocalFunc and LocalNames clauses. */
82
PROTO_TOSTRING(LocalFunc, localfunc);
83
PROTO_TOSTRING(LocalNames, localnames);
84
85
/**
86
 * Expressions and variables.
87
 */
88
89
/** Expressions clauses. */
90
PROTO_TOSTRING(ExpressionList, explist);
91
PROTO_TOSTRING(OptionalExpressionList, explist);
92
PROTO_TOSTRING(PrefixExpression, prefExpr);
93
94
/* Variable and nested types. */
95
PROTO_TOSTRING(Variable, var);
96
NESTED_PROTO_TOSTRING(IndexWithExpression, indexexpr, Variable);
97
NESTED_PROTO_TOSTRING(IndexWithName, indexname, Variable);
98
99
/** Expression and nested types. */
100
PROTO_TOSTRING(Expression, expr);
101
NESTED_PROTO_TOSTRING(AnonFunc, function, Expression);
102
NESTED_PROTO_TOSTRING(ExpBinaryOpExp, binary, Expression);
103
NESTED_PROTO_TOSTRING(UnaryOpExp, unary, Expression);
104
105
/**
106
 * Tables and fields.
107
 */
108
PROTO_TOSTRING(TableConstructor, table);
109
PROTO_TOSTRING(FieldList, fieldlist);
110
NESTED_PROTO_TOSTRING(FieldWithFieldSep, field, FieldList);
111
112
/** Field and nested types. */
113
PROTO_TOSTRING(Field, field);
114
NESTED_PROTO_TOSTRING(ExpressionAssignment, assignment, Field);
115
NESTED_PROTO_TOSTRING(NameAssignment, assignment, Field);
116
PROTO_TOSTRING(FieldSep, sep);
117
118
/** Operators. */
119
PROTO_TOSTRING(BinaryOperator, op);
120
PROTO_TOSTRING(UnaryOperator, op);
121
122
/** Identifier (Name). */
123
PROTO_TOSTRING(Name, name);
124
125
/**
126
 * Class that controls id creation for counters. Basically, a
127
 * variable wrapper that guarantees variable to be incremented.
128
 */
129
class CounterIdProvider {
130
public:
131
  /** Returns number of id provided. */
132
  std::size_t count()
133
61.3k
  {
134
61.3k
    return id_;
135
61.3k
  }
136
137
  /** Returns a new id that was not used after last clean(). */
138
  std::size_t next()
139
60.0k
  {
140
60.0k
    return id_++;
141
60.0k
  }
142
143
  /**
144
   * Cleans history. Should be used to make fuzzer starts
145
   * independent.
146
   */
147
  void clean()
148
1.30k
  {
149
1.30k
    id_ = 0;
150
1.30k
  }
151
152
private:
153
  std::size_t id_ = 0;
154
};
155
156
/** A singleton for counter id provider. */
157
CounterIdProvider&
158
GetCounterIdProvider()
159
122k
{
160
122k
  static CounterIdProvider provider;
161
122k
  return provider;
162
122k
}
163
164
std::string
165
GetCounterName(std::size_t id)
166
120k
{
167
120k
  return kCounterNamePrefix + std::to_string(id);
168
120k
}
169
170
/** Returns `<counter_name> = <counter_name> + 1`. */
171
std::string
172
GetCounterIncrement(const std::string &counter_name)
173
60.0k
{
174
60.0k
  std::string retval = counter_name;
175
60.0k
  retval += " = ";
176
60.0k
  retval += counter_name;
177
60.0k
  retval += " + 1\n";
178
60.0k
  return retval;
179
60.0k
}
180
181
/**
182
 * Returns `if <counter_name> > kMaxCounterValue then
183
 * <then_block> end`.
184
 */
185
std::string
186
GetCondition(const std::string &counter_name, const std::string &then_block)
187
60.0k
{
188
60.0k
  std::string retval = "if ";
189
60.0k
  retval += counter_name;
190
60.0k
  retval += " > ";
191
60.0k
  retval += std::to_string(kMaxCounterValue);
192
60.0k
  retval += " then ";
193
60.0k
  retval += then_block;
194
60.0k
  retval += " end\n";
195
60.0k
  return retval;
196
60.0k
}
197
198
/**
199
 * Class that registers and provides context during code
200
 * generation.
201
 * Used to generate correct Lua code.
202
 */
203
class Context {
204
public:
205
  enum class BlockType {
206
    kReturnable,
207
    kBreakable,
208
  };
209
210
  void step_in(BlockType type)
211
60.0k
  {
212
60.0k
    block_stack_.push(type);
213
60.0k
    if (type == BlockType::kReturnable) {
214
18.7k
      ++returnable_counter_;
215
18.7k
    }
216
60.0k
  }
217
218
  void step_out()
219
60.0k
  {
220
60.0k
    assert(!block_stack_.empty());
221
60.0k
    if (block_stack_.top() == BlockType::kReturnable) {
222
18.7k
      assert(returnable_counter_ > 0);
223
0
      --returnable_counter_;
224
18.7k
    }
225
0
    block_stack_.pop();
226
60.0k
  }
227
228
  std::string get_next_block_setup()
229
60.0k
  {
230
60.0k
    std::size_t id = GetCounterIdProvider().next();
231
60.0k
    std::string counter_name = GetCounterName(id);
232
233
60.0k
    return GetCondition(counter_name, get_exit_statement_()) +
234
60.0k
           GetCounterIncrement(counter_name);
235
60.0k
  }
236
237
  bool break_is_possible()
238
1.94k
  {
239
1.94k
    return !block_stack_.empty() &&
240
1.94k
           block_stack_.top() == BlockType::kBreakable;
241
1.94k
  }
242
243
  bool return_is_possible()
244
5.74k
  {
245
5.74k
    return returnable_counter_ > 0;
246
5.74k
  }
247
248
private:
249
250
  std::string get_exit_statement_()
251
60.0k
  {
252
60.0k
    assert(!block_stack_.empty());
253
0
    switch (block_stack_.top()) {
254
41.2k
    case BlockType::kBreakable:
255
41.2k
      return "break";
256
18.7k
    case BlockType::kReturnable:
257
18.7k
      return "return";
258
60.0k
    }
259
60.0k
    unreachable();
260
60.0k
  }
261
262
  std::stack<BlockType> block_stack_;
263
  /*
264
   * The returnable block can be exited with return from
265
   * the breakable block within it, but the breakable block
266
   * cannot be exited with break from the returnable block within
267
   * it.
268
   * Valid code:
269
   * `function foo() while true do return end end`
270
   * Erroneous code:
271
   * `while true do function foo() break end end`
272
   * This counter is used to check if `return` is possible.
273
   */
274
  uint64_t returnable_counter_ = 0;
275
};
276
277
Context&
278
GetContext()
279
187k
{
280
187k
  static Context context;
281
187k
  return context;
282
187k
}
283
284
/**
285
 * Block may be placed not only in a cycle, so specially for cycles
286
 * there is a function that will add a break condition and a
287
 * counter increment.
288
 */
289
std::string
290
BlockToStringCycleProtected(const Block &block)
291
41.2k
{
292
41.2k
  std::string retval = GetContext().get_next_block_setup();
293
41.2k
  retval += ChunkToString(block.chunk());
294
41.2k
  return retval;
295
41.2k
}
296
297
/**
298
 * DoBlock may be placed not only in a cycle, so specially for
299
 * cycles there is a function that will call
300
 * BlockToStringCycleProtected().
301
 */
302
std::string
303
DoBlockToStringCycleProtected(const DoBlock &block)
304
33.3k
{
305
33.3k
  std::string retval = "do\n";
306
33.3k
  retval += BlockToStringCycleProtected(block.block());
307
33.3k
  retval += "end\n";
308
33.3k
  return retval;
309
33.3k
}
310
311
/**
312
 * FuncBody may contain recursive calls, so for all function bodies,
313
 * there is a function that adds a return condition and a counter
314
 * increment.
315
 */
316
std::string
317
FuncBodyToStringReqProtected(const FuncBody &body)
318
18.7k
{
319
18.7k
  std::string body_str = "( ";
320
18.7k
  if (body.has_parlist()) {
321
3.31k
    body_str += ParListToString(body.parlist());
322
3.31k
  }
323
18.7k
  body_str += " )\n\t";
324
325
18.7k
  body_str += GetContext().get_next_block_setup();
326
327
18.7k
  body_str += BlockToString(body.block());
328
18.7k
  body_str += "end\n";
329
18.7k
  return body_str;
330
18.7k
}
331
332
std::string
333
ClearIdentifier(const std::string &identifier)
334
208k
{
335
208k
  std::string cleared;
336
337
208k
  bool has_first_not_digit = false;
338
478k
  for (char c : identifier) {
339
478k
    if (has_first_not_digit && (std::iswalnum(c) || c == '_')) {
340
197k
      cleared += c;
341
281k
    } else if (std::isalpha(c) || c == '_') {
342
14.2k
      has_first_not_digit = true;
343
14.2k
      cleared += c;
344
14.2k
    }
345
478k
  }
346
208k
  return cleared;
347
208k
}
348
349
inline std::string
350
clamp(std::string s, size_t maxSize = kMaxStrLength)
351
208k
{
352
208k
  if (s.size() > maxSize)
353
1.31k
    s.resize(maxSize);
354
208k
  return s;
355
208k
}
356
357
inline double
358
clamp(double number, double upper, double lower)
359
16.1k
{
360
16.1k
  return number <= lower ? lower :
361
16.1k
         number >= upper ? upper : number;
362
16.1k
}
363
364
inline std::string
365
ConvertToStringDefault(const std::string &s)
366
208k
{
367
208k
  std::string ident = ClearIdentifier(s);
368
208k
  ident = clamp(ident);
369
208k
  if (ident.empty())
370
194k
    return std::string(kDefaultIdent);
371
14.2k
  return ident;
372
208k
}
373
374
PROTO_TOSTRING(Block, block)
375
27.2k
{
376
27.2k
  return ChunkToString(block.chunk());
377
27.2k
}
378
379
PROTO_TOSTRING(Chunk, chunk)
380
68.4k
{
381
68.4k
  std::string chunk_str;
382
148k
  for (int i = 0; i < chunk.stat_size(); ++i)
383
80.3k
    chunk_str += StatementToString(chunk.stat(i)) + "\n";
384
385
68.4k
  if (chunk.has_laststat())
386
7.69k
    chunk_str += LastStatementToString(chunk.laststat()) + "\n";
387
388
68.4k
  return chunk_str;
389
68.4k
}
390
391
/**
392
 * LastStatement and nested types.
393
 */
394
PROTO_TOSTRING(LastStatement, laststat)
395
7.69k
{
396
7.69k
  std::string laststat_str;
397
7.69k
  using LastStatType = LastStatement::LastOneofCase;
398
7.69k
  switch (laststat.last_oneof_case()) {
399
2.89k
  case LastStatType::kExplist:
400
2.89k
    laststat_str = ReturnOptionalExpressionListToString(
401
2.89k
      laststat.explist());
402
2.89k
    break;
403
1.94k
  case LastStatType::kBreak:
404
1.94k
    if (GetContext().break_is_possible()) {
405
1.54k
      laststat_str = "break";
406
1.54k
    }
407
1.94k
    break;
408
2.85k
  default:
409
    /* Chosen as default in order to decrease number of 'break's. */
410
2.85k
    laststat_str = ReturnOptionalExpressionListToString(
411
2.85k
      laststat.explist());
412
2.85k
    break;
413
7.69k
  }
414
415
7.69k
  if (!laststat_str.empty() && laststat.has_semicolon())
416
1.42k
    laststat_str += "; ";
417
418
7.69k
  return laststat_str;
419
7.69k
}
420
421
NESTED_PROTO_TOSTRING(ReturnOptionalExpressionList, explist, LastStatement)
422
5.74k
{
423
5.74k
  if (!GetContext().return_is_possible()) {
424
2.18k
    return "";
425
2.18k
  }
426
427
3.55k
  std::string explist_str = "return";
428
3.55k
  if (explist.has_explist()) {
429
2.00k
    explist_str += " " + ExpressionListToString(explist.explist());
430
2.00k
    explist_str += " ";
431
2.00k
  }
432
3.55k
  return explist_str;
433
5.74k
}
434
435
/**
436
 * Statement and statement options.
437
 */
438
PROTO_TOSTRING(Statement, stat)
439
80.3k
{
440
80.3k
  std::string stat_str;
441
80.3k
  using StatType = Statement::StatOneofCase;
442
80.3k
  switch (stat.stat_oneof_case()) {
443
5.36k
  case StatType::kList:
444
5.36k
    stat_str = AssignmentListToString(stat.list());
445
5.36k
    break;
446
8.24k
  case StatType::kCall:
447
8.24k
    stat_str = FunctionCallToString(stat.call());
448
8.24k
    break;
449
1.43k
  case StatType::kBlock:
450
1.43k
    stat_str = DoBlockToString(stat.block());
451
1.43k
    break;
452
5.75k
  case StatType::kWhilecycle:
453
5.75k
    stat_str = WhileCycleToString(stat.whilecycle());
454
5.75k
    break;
455
7.95k
  case StatType::kRepeatcycle:
456
7.95k
    stat_str = RepeatCycleToString(stat.repeatcycle());
457
7.95k
    break;
458
2.90k
  case StatType::kIfstat:
459
2.90k
    stat_str = IfStatementToString(stat.ifstat());
460
2.90k
    break;
461
1.54k
  case StatType::kForcyclename:
462
1.54k
    stat_str = ForCycleNameToString(stat.forcyclename());
463
1.54k
    break;
464
26.0k
  case StatType::kForcyclelist:
465
26.0k
    stat_str = ForCycleListToString(stat.forcyclelist());
466
26.0k
    break;
467
2.78k
  case StatType::kFunc:
468
2.78k
    stat_str = FunctionToString(stat.func());
469
2.78k
    break;
470
2.34k
  case StatType::kLocalfunc:
471
2.34k
    stat_str = LocalFuncToString(stat.localfunc());
472
2.34k
    break;
473
2.22k
  case StatType::kLocalnames:
474
2.22k
    stat_str = LocalNamesToString(stat.localnames());
475
2.22k
    break;
476
13.7k
  default:
477
    /**
478
     * Chosen arbitrarily more for simplicity.
479
     * TODO: Choose "more interesting" defaults.
480
     */
481
13.7k
    stat_str = AssignmentListToString(stat.list());
482
13.7k
    break;
483
80.3k
  }
484
485
80.3k
  if (stat.has_semicolon())
486
39.2k
    stat_str += "; ";
487
488
80.3k
  return stat_str;
489
80.3k
}
490
491
/**
492
 * AssignmentList and nested types.
493
 */
494
PROTO_TOSTRING(AssignmentList, assignmentlist)
495
19.1k
{
496
19.1k
  std::string list_str = VariableListToString(assignmentlist.varlist());
497
19.1k
  list_str += " = " + ExpressionListToString(assignmentlist.explist());
498
19.1k
  return list_str;
499
19.1k
}
500
501
NESTED_PROTO_TOSTRING(VariableList, varlist, AssignmentList)
502
19.1k
{
503
19.1k
  std::string varlist_str = VariableToString(varlist.var());
504
28.5k
  for (int i = 0; i < varlist.vars_size(); ++i) {
505
9.41k
    varlist_str += ", " + VariableToString(varlist.vars(i));
506
9.41k
    varlist_str += " ";
507
9.41k
  }
508
19.1k
  return varlist_str;
509
19.1k
}
510
511
/**
512
 * FunctionCall and nested types.
513
 */
514
PROTO_TOSTRING(FunctionCall, call)
515
17.5k
{
516
17.5k
  using FuncCallType = FunctionCall::CallOneofCase;
517
17.5k
  switch (call.call_oneof_case()) {
518
6.68k
  case FuncCallType::kPrefArgs:
519
6.68k
    return PrefixArgsToString(call.prefargs());
520
3.06k
  case FuncCallType::kNamedArgs:
521
3.06k
    return PrefixNamedArgsToString(call.namedargs());
522
7.78k
  default:
523
    /* Chosen for more variability of generated programs. */
524
7.78k
    return PrefixNamedArgsToString(call.namedargs());
525
17.5k
  }
526
17.5k
}
527
528
NESTED_PROTO_TOSTRING(Args, args, FunctionCall)
529
17.5k
{
530
17.5k
  using ArgsType = FunctionCall::Args::ArgsOneofCase;
531
17.5k
  switch (args.args_oneof_case()) {
532
6.19k
  case ArgsType::kExplist:
533
6.19k
    return "(" + OptionalExpressionListToString(args.explist()) +
534
6.19k
           ")";
535
284
  case ArgsType::kTableconstructor:
536
284
    return TableConstructorToString(args.tableconstructor());
537
279
  case ArgsType::kStr:
538
279
    return "'" + ConvertToStringDefault(args.str()) + "'";
539
10.7k
  default:
540
    /* For more variability. */
541
10.7k
    return TableConstructorToString(args.tableconstructor());
542
17.5k
  }
543
17.5k
}
544
545
NESTED_PROTO_TOSTRING(PrefixArgs, prefixargs, FunctionCall)
546
6.68k
{
547
6.68k
  std::string prefixargs_str = PrefixExpressionToString(
548
6.68k
    prefixargs.prefixexp());
549
6.68k
  prefixargs_str += " " + ArgsToString(prefixargs.args());
550
6.68k
  return prefixargs_str;
551
6.68k
}
552
553
NESTED_PROTO_TOSTRING(PrefixNamedArgs, prefixnamedargs, FunctionCall)
554
10.8k
{
555
10.8k
  std::string predixnamedargs_str = PrefixExpressionToString(
556
10.8k
    prefixnamedargs.prefixexp());
557
10.8k
  predixnamedargs_str += ":" + NameToString(prefixnamedargs.name());
558
10.8k
  predixnamedargs_str += " " + ArgsToString(prefixnamedargs.args());
559
10.8k
  return predixnamedargs_str;
560
10.8k
}
561
562
/**
563
 * DoBlock clause.
564
 */
565
PROTO_TOSTRING(DoBlock, block)
566
1.43k
{
567
1.43k
  return "do\n" + BlockToString(block.block()) + "end\n";
568
1.43k
}
569
570
/**
571
 * WhileCycle clause.
572
 */
573
PROTO_TOSTRING(WhileCycle, whilecycle)
574
5.75k
{
575
5.75k
  GetContext().step_in(Context::BlockType::kBreakable);
576
577
5.75k
  std::string whilecycle_str = "while ";
578
5.75k
  whilecycle_str += ExpressionToString(whilecycle.condition());
579
5.75k
  whilecycle_str += " ";
580
5.75k
  whilecycle_str += DoBlockToStringCycleProtected(whilecycle.doblock());
581
582
5.75k
  GetContext().step_out();
583
5.75k
  return whilecycle_str;
584
5.75k
}
585
586
/**
587
 * RepeatCycle clause.
588
 */
589
PROTO_TOSTRING(RepeatCycle, repeatcycle)
590
7.95k
{
591
7.95k
  GetContext().step_in(Context::BlockType::kBreakable);
592
593
7.95k
  std::string repeatcycle_str = "repeat\n";
594
7.95k
  repeatcycle_str += BlockToStringCycleProtected(repeatcycle.block());
595
7.95k
  repeatcycle_str += "until ";
596
7.95k
  repeatcycle_str += ExpressionToString(repeatcycle.condition());
597
598
7.95k
  GetContext().step_out();
599
7.95k
  return repeatcycle_str;
600
7.95k
}
601
602
/**
603
 * IfStatement and nested types.
604
 */
605
PROTO_TOSTRING(IfStatement, statement)
606
2.90k
{
607
2.90k
  std::string statement_str = "if " +
608
2.90k
    ExpressionToString(statement.condition());
609
2.90k
  statement_str += " then\n\t" + BlockToString(statement.first());
610
611
4.62k
  for (int i = 0; i < statement.clauses_size(); ++i)
612
1.71k
    statement_str += ElseIfBlockToString(statement.clauses(i));
613
614
2.90k
  if (statement.has_last())
615
1.07k
    statement_str += "else\n\t" + BlockToString(statement.last());
616
617
2.90k
  statement_str += "end\n";
618
2.90k
  return statement_str;
619
2.90k
}
620
621
NESTED_PROTO_TOSTRING(ElseIfBlock, elseifblock, IfStatement)
622
1.71k
{
623
1.71k
  std::string elseifblock_str = "elseif ";
624
1.71k
  elseifblock_str += ExpressionToString(elseifblock.condition());
625
1.71k
  elseifblock_str += " then\n\t";
626
1.71k
  elseifblock_str += BlockToString(elseifblock.block());
627
1.71k
  return elseifblock_str;
628
1.71k
}
629
630
/**
631
 * ForCycleName clause.
632
 * TODO: In 'for i = start, stop, step' construction start, stop, step
633
 * should be numbers. So results of the corresponding expressions
634
 * should be number.
635
 */
636
PROTO_TOSTRING(ForCycleName, forcyclename)
637
1.54k
{
638
1.54k
  GetContext().step_in(Context::BlockType::kBreakable);
639
640
1.54k
  std::string forcyclename_str = "for ";
641
1.54k
  forcyclename_str += NameToString(forcyclename.name());
642
1.54k
  forcyclename_str += " = ";
643
1.54k
  forcyclename_str += ExpressionToString(forcyclename.startexp());
644
1.54k
  forcyclename_str += ", ";
645
1.54k
  forcyclename_str += ExpressionToString(forcyclename.stopexp());
646
647
1.54k
  if (forcyclename.has_stepexp())
648
811
    forcyclename_str += ", " + ExpressionToString(
649
811
      forcyclename.stepexp());
650
651
1.54k
  forcyclename_str += " ";
652
1.54k
  forcyclename_str += DoBlockToStringCycleProtected(
653
1.54k
    forcyclename.doblock());
654
655
1.54k
  GetContext().step_out();
656
1.54k
  return forcyclename_str;
657
1.54k
}
658
659
/**
660
 * ForCycleList clause.
661
 */
662
PROTO_TOSTRING(ForCycleList, forcyclelist)
663
26.0k
{
664
26.0k
  GetContext().step_in(Context::BlockType::kBreakable);
665
666
26.0k
  std::string forcyclelist_str = "for ";
667
26.0k
  forcyclelist_str += NameListToString(forcyclelist.names());
668
26.0k
  forcyclelist_str += " in ";
669
26.0k
  forcyclelist_str += ExpressionListToString(forcyclelist.expressions());
670
26.0k
  forcyclelist_str += " ";
671
26.0k
  forcyclelist_str += DoBlockToStringCycleProtected(
672
26.0k
    forcyclelist.doblock());
673
674
26.0k
  GetContext().step_out();
675
26.0k
  return forcyclelist_str;
676
26.0k
}
677
678
/**
679
 * Function and nested types.
680
 */
681
PROTO_TOSTRING(Function, func)
682
2.78k
{
683
2.78k
  GetContext().step_in(Context::BlockType::kReturnable);
684
685
2.78k
  std::string func_str = "function ";
686
2.78k
  func_str += FuncNameToString(func.name());
687
2.78k
  func_str += FuncBodyToStringReqProtected(func.body());
688
689
2.78k
  GetContext().step_out();
690
2.78k
  return func_str;
691
2.78k
}
692
693
NESTED_PROTO_TOSTRING(FuncName, funcname, Function)
694
2.78k
{
695
2.78k
  std::string funcname_str = NameToString(funcname.firstname());
696
697
3.79k
  for (int i = 0; i < funcname.names_size(); ++i)
698
1.01k
    funcname_str += "." + NameToString(funcname.names(i));
699
700
2.78k
  if (funcname.has_lastname())
701
120
    funcname_str += ":" + NameToString(funcname.lastname());
702
703
2.78k
  return funcname_str;
704
2.78k
}
705
706
PROTO_TOSTRING(NameList, namelist)
707
30.3k
{
708
30.3k
  std::string namelist_str = NameToString(namelist.firstname());
709
39.2k
  for (int i = 0; i < namelist.names_size(); ++i)
710
8.89k
    namelist_str += ", " + NameToString(namelist.names(i));
711
30.3k
  return namelist_str;
712
30.3k
}
713
714
NESTED_PROTO_TOSTRING(NameListWithEllipsis, namelist, FuncBody)
715
2.13k
{
716
2.13k
  std::string namelist_str = NameListToString(namelist.namelist());
717
2.13k
  if (namelist.has_ellipsis())
718
363
    namelist_str += ", ...";
719
2.13k
  return namelist_str;
720
2.13k
}
721
722
NESTED_PROTO_TOSTRING(ParList, parlist, FuncBody)
723
3.31k
{
724
3.31k
  using ParListType = FuncBody::ParList::ParlistOneofCase;
725
3.31k
  switch (parlist.parlist_oneof_case()) {
726
719
  case ParListType::kNamelist:
727
719
    return NameListWithEllipsisToString(parlist.namelist());
728
1.18k
  case ParListType::kEllipsis:
729
1.18k
    return "...";
730
1.41k
  default:
731
    /* Chosen as default in order to decrease number of ellipses. */
732
1.41k
    return NameListWithEllipsisToString(parlist.namelist());
733
3.31k
  }
734
3.31k
}
735
736
/**
737
 * LocalFunc clause.
738
 */
739
PROTO_TOSTRING(LocalFunc, localfunc)
740
2.34k
{
741
2.34k
  GetContext().step_in(Context::BlockType::kReturnable);
742
743
2.34k
  std::string localfunc_str = "local function ";
744
2.34k
  localfunc_str += NameToString(localfunc.name());
745
2.34k
  localfunc_str += " ";
746
2.34k
  localfunc_str += FuncBodyToStringReqProtected(localfunc.funcbody());
747
748
2.34k
  GetContext().step_out();
749
2.34k
  return localfunc_str;
750
2.34k
}
751
752
/**
753
 * LocalNames clause.
754
 */
755
PROTO_TOSTRING(LocalNames, localnames)
756
2.22k
{
757
2.22k
  std::string localnames_str = "local ";
758
2.22k
  localnames_str += NameListToString(localnames.namelist());
759
760
2.22k
  if (localnames.has_explist())
761
1.09k
    localnames_str += " = " + ExpressionListToString(
762
1.09k
      localnames.explist());
763
2.22k
  return localnames_str;
764
2.22k
}
765
766
/**
767
 * Expressions and variables.
768
 */
769
770
/**
771
 * Expressions clauses.
772
 */
773
PROTO_TOSTRING(ExpressionList, explist)
774
54.1k
{
775
54.1k
  std::string explist_str;
776
87.2k
  for (int i = 0; i < explist.expressions_size(); ++i)
777
33.1k
    explist_str += ExpressionToString(explist.expressions(i)) +
778
33.1k
        ", ";
779
54.1k
  explist_str += ExpressionToString(explist.explast()) + " ";
780
54.1k
  return explist_str;
781
54.1k
}
782
783
PROTO_TOSTRING(OptionalExpressionList, explist)
784
6.19k
{
785
6.19k
  if (explist.has_explist())
786
5.95k
    return ExpressionListToString(explist.explist());
787
240
  return "";
788
6.19k
}
789
790
PROTO_TOSTRING(PrefixExpression, prefixexp)
791
49.8k
{
792
49.8k
  using PrefExprType = PrefixExpression::PrefixOneofCase;
793
49.8k
  switch (prefixexp.prefix_oneof_case()) {
794
6.96k
  case PrefExprType::kVar:
795
6.96k
    return VariableToString(prefixexp.var());
796
9.28k
  case PrefExprType::kFunctioncall:
797
9.28k
    return FunctionCallToString(prefixexp.functioncall());
798
6.24k
  case PrefExprType::kExp:
799
6.24k
    return "(" + ExpressionToString(prefixexp.exp()) + ")";
800
27.3k
  default:
801
    /*
802
     * Can be generated too nested expressions with other options,
803
     * though they can be enabled for more variable fuzzing.
804
     */
805
27.3k
    return VariableToString(prefixexp.var());
806
49.8k
  }
807
49.8k
}
808
809
/**
810
 * Variable and nested types.
811
 */
812
PROTO_TOSTRING(Variable, var)
813
62.8k
{
814
62.8k
  using VarType = Variable::VarOneofCase;
815
62.8k
  switch (var.var_oneof_case()) {
816
1.25k
  case VarType::kName:
817
1.25k
    return NameToString(var.name());
818
4.88k
  case VarType::kIndexexpr:
819
4.88k
    return IndexWithExpressionToString(var.indexexpr());
820
6.43k
  case VarType::kIndexname:
821
6.43k
    return IndexWithNameToString(var.indexname());
822
50.2k
  default:
823
    /*
824
     * Can be generated too nested expressions with other options,
825
     * though they can be enabled for more variable fuzzing.
826
     */
827
50.2k
    return NameToString(var.name());
828
62.8k
  }
829
62.8k
}
830
831
NESTED_PROTO_TOSTRING(IndexWithExpression, indexexpr, Variable)
832
4.88k
{
833
4.88k
  std::string indexexpr_str = PrefixExpressionToString(
834
4.88k
    indexexpr.prefixexp());
835
4.88k
  indexexpr_str += "[" + ExpressionToString(indexexpr.exp()) + "]";
836
4.88k
  return indexexpr_str;
837
4.88k
}
838
839
NESTED_PROTO_TOSTRING(IndexWithName, indexname, Variable)
840
6.43k
{
841
6.43k
  std::string indexname_str = PrefixExpressionToString(
842
6.43k
    indexname.prefixexp());
843
6.43k
  indexname_str += "." + ConvertToStringDefault(indexname.name());
844
6.43k
  return indexname_str;
845
6.43k
}
846
847
/**
848
 * Expression and nested types.
849
 */
850
PROTO_TOSTRING(Expression, expr)
851
187k
{
852
187k
  using ExprType = Expression::ExprOneofCase;
853
187k
  switch (expr.expr_oneof_case()) {
854
4.51k
  case ExprType::kNil:
855
4.51k
    return "nil";
856
2.03k
  case ExprType::kFalse:
857
2.03k
    return "false";
858
2.26k
  case ExprType::kTrue:
859
2.26k
    return "true";
860
16.1k
  case ExprType::kNumber: {
861
    /* Clamp number between given boundaries. */
862
16.1k
    double number = clamp(expr.number(), kMaxNumber, kMinNumber);
863
16.1k
    return std::to_string(number);
864
0
  }
865
5.71k
  case ExprType::kStr:
866
5.71k
    return "'" + ConvertToStringDefault(expr.str()) + "'";
867
4.68k
  case ExprType::kEllipsis:
868
4.68k
    return " ... ";
869
13.6k
  case ExprType::kFunction:
870
13.6k
    return AnonFuncToString(expr.function());
871
21.0k
  case ExprType::kPrefixexp:
872
21.0k
    return PrefixExpressionToString(expr.prefixexp());
873
7.52k
  case ExprType::kTableconstructor:
874
7.52k
    return TableConstructorToString(expr.tableconstructor());
875
23.1k
  case ExprType::kBinary:
876
23.1k
    return ExpBinaryOpExpToString(expr.binary());
877
5.28k
  case ExprType::kUnary:
878
5.28k
    return UnaryOpExpToString(expr.unary());
879
81.4k
  default:
880
    /**
881
     * Arbitrary choice.
882
     * TODO: Choose "more interesting" defaults.
883
     */
884
81.4k
    return "'" + ConvertToStringDefault(expr.str()) + "'";
885
187k
  }
886
187k
}
887
888
NESTED_PROTO_TOSTRING(AnonFunc, func, Expression)
889
13.6k
{
890
13.6k
  GetContext().step_in(Context::BlockType::kReturnable);
891
892
13.6k
  std::string retval = "function ";
893
13.6k
  retval += FuncBodyToStringReqProtected(func.body());
894
895
13.6k
  GetContext().step_out();
896
13.6k
  return retval;
897
13.6k
}
898
899
NESTED_PROTO_TOSTRING(ExpBinaryOpExp, binary, Expression)
900
23.1k
{
901
23.1k
  std::string binary_str = ExpressionToString(binary.leftexp());
902
23.1k
  binary_str += " " + BinaryOperatorToString(binary.binop()) + " ";
903
23.1k
  binary_str += ExpressionToString(binary.rightexp());
904
23.1k
  return binary_str;
905
23.1k
}
906
907
NESTED_PROTO_TOSTRING(UnaryOpExp, unary, Expression)
908
5.28k
{
909
5.28k
  std::string unary_str = UnaryOperatorToString(unary.unop());
910
5.28k
  unary_str += ExpressionToString(unary.exp());
911
5.28k
  return unary_str;
912
5.28k
}
913
914
/**
915
 * Tables and fields.
916
 */
917
PROTO_TOSTRING(TableConstructor, table)
918
18.5k
{
919
18.5k
  std::string table_str = "{ ";
920
18.5k
  if (table.has_fieldlist())
921
6.22k
    table_str += FieldListToString(table.fieldlist());
922
18.5k
  table_str += " }";
923
18.5k
  return table_str;
924
18.5k
}
925
926
PROTO_TOSTRING(FieldList, fieldlist)
927
6.22k
{
928
6.22k
  std::string fieldlist_str = FieldToString(fieldlist.firstfield());
929
11.8k
  for (int i = 0; i < fieldlist.fields_size(); ++i)
930
5.66k
    fieldlist_str += FieldWithFieldSepToString(fieldlist.fields(i));
931
6.22k
  if (fieldlist.has_lastsep())
932
1.32k
    fieldlist_str += FieldSepToString(fieldlist.lastsep());
933
6.22k
  return fieldlist_str;
934
6.22k
}
935
936
NESTED_PROTO_TOSTRING(FieldWithFieldSep, field, FieldList)
937
5.66k
{
938
5.66k
  std::string field_str = FieldSepToString(field.sep());
939
5.66k
  field_str += " " + FieldToString(field.field());
940
5.66k
  return field_str;
941
5.66k
}
942
943
/**
944
 * Field and nested types.
945
 */
946
PROTO_TOSTRING(Field, field)
947
11.8k
{
948
11.8k
  using FieldType = Field::FieldOneofCase;
949
11.8k
  switch (field.field_oneof_case()) {
950
3.39k
  case FieldType::kExprassign:
951
3.39k
    return ExpressionAssignmentToString(field.exprassign());
952
523
  case FieldType::kNamedassign:
953
523
    return NameAssignmentToString(field.namedassign());
954
3.39k
  case FieldType::kExpression:
955
3.39k
    return ExpressionToString(field.expression());
956
4.57k
  default:
957
    /* More common case of using fields. */
958
4.57k
    return NameAssignmentToString(field.namedassign());
959
11.8k
  }
960
11.8k
}
961
962
NESTED_PROTO_TOSTRING(ExpressionAssignment, assignment, Field)
963
3.39k
{
964
3.39k
  std::string assignment_str = "[ " +
965
3.39k
    ExpressionToString(assignment.key()) + " ]";
966
3.39k
  assignment_str += " = " + ExpressionToString(assignment.value());
967
3.39k
  return assignment_str;
968
3.39k
}
969
970
NESTED_PROTO_TOSTRING(NameAssignment, assignment, Field)
971
5.09k
{
972
5.09k
  std::string assignment_str = NameToString(assignment.name());
973
5.09k
  assignment_str += " = " + ExpressionToString(assignment.value());
974
5.09k
  return assignment_str;
975
5.09k
}
976
977
PROTO_TOSTRING(FieldSep, sep)
978
6.99k
{
979
6.99k
  using FieldSepType = FieldSep::SepOneofCase;
980
6.99k
  switch (sep.sep_oneof_case()) {
981
1.45k
  case FieldSepType::kComma:
982
1.45k
    return ",";
983
1.52k
  case FieldSepType::kSemicolon:
984
1.52k
    return ";";
985
4.01k
  default:
986
4.01k
    return ",";
987
6.99k
  }
988
6.99k
}
989
990
/**
991
 * Operators.
992
 */
993
PROTO_TOSTRING(BinaryOperator, op)
994
23.1k
{
995
23.1k
  using BinopType = BinaryOperator::BinaryOneofCase;
996
23.1k
  switch (op.binary_oneof_case()) {
997
910
  case BinopType::kAdd:
998
910
    return "+";
999
2.12k
  case BinopType::kSub:
1000
2.12k
    return "-";
1001
4.09k
  case BinopType::kMult:
1002
4.09k
    return "*";
1003
730
  case BinopType::kDiv:
1004
730
    return "/";
1005
1.00k
  case BinopType::kExp:
1006
1.00k
    return "^";
1007
447
  case BinopType::kMod:
1008
447
    return "%";
1009
1010
1.29k
  case BinopType::kConcat:
1011
1.29k
    return "..";
1012
1013
680
  case BinopType::kLess:
1014
680
    return "<";
1015
658
  case BinopType::kLessEqual:
1016
658
    return "<=";
1017
793
  case BinopType::kGreater:
1018
793
    return ">";
1019
509
  case BinopType::kGreaterEqual:
1020
509
    return ">=";
1021
315
  case BinopType::kEqual:
1022
315
    return "==";
1023
720
  case BinopType::kNotEqual:
1024
720
    return "~=";
1025
2.45k
  case BinopType::kAnd:
1026
2.45k
    return "and";
1027
2.72k
  case BinopType::kOr:
1028
2.72k
    return "or";
1029
3.64k
  default:
1030
    /* Works in most cases. */
1031
3.64k
    return "==";
1032
23.1k
  }
1033
23.1k
}
1034
1035
PROTO_TOSTRING(UnaryOperator, op)
1036
5.28k
{
1037
5.28k
  using UnaryopType = UnaryOperator::UnaryOneofCase;
1038
5.28k
  switch (op.unary_oneof_case()) {
1039
852
  case UnaryopType::kNegate:
1040
852
    return "-";
1041
216
  case UnaryopType::kNot:
1042
216
    return "not ";
1043
2.55k
  case UnaryopType::kLength:
1044
2.55k
    return "#";
1045
1.66k
  default:
1046
    /* Works in most cases. */
1047
1.66k
    return "not ";
1048
5.28k
  }
1049
5.28k
}
1050
1051
/**
1052
 * Identifier (Name).
1053
 */
1054
PROTO_TOSTRING(Name, name)
1055
114k
{
1056
114k
  std::string ident = ConvertToStringDefault(name.name());
1057
114k
  return ident + std::to_string(name.num() % kMaxIdentifiers);
1058
114k
}
1059
1060
} /* namespace */
1061
1062
std::string
1063
MainBlockToString(const Block &block)
1064
1.30k
{
1065
1.30k
  GetCounterIdProvider().clean();
1066
1067
1.30k
  std::string block_str = BlockToString(block);
1068
1.30k
  std::string retval;
1069
1070
61.3k
  for (size_t i = 0; i < GetCounterIdProvider().count(); ++i) {
1071
60.0k
    retval += GetCounterName(i);
1072
60.0k
    retval += " = 0\n";
1073
60.0k
  }
1074
1.30k
  retval += block_str;
1075
1076
1.30k
  return retval;
1077
1.30k
}
1078
1079
} /* namespace luajit_fuzzer */