Coverage Report

Created: 2023-09-30 06:14

/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
50.1k
  {
134
50.1k
    return id_;
135
50.1k
  }
136
137
  /** Returns a new id that was not used after last clean(). */
138
  std::size_t next()
139
49.2k
  {
140
49.2k
    return id_++;
141
49.2k
  }
142
143
  /**
144
   * Cleans history. Should be used to make fuzzer starts
145
   * independent.
146
   */
147
  void clean()
148
903
  {
149
903
    id_ = 0;
150
903
  }
151
152
private:
153
  std::size_t id_ = 0;
154
};
155
156
/** A singleton for counter id provider. */
157
CounterIdProvider&
158
GetCounterIdProvider()
159
100k
{
160
100k
  static CounterIdProvider provider;
161
100k
  return provider;
162
100k
}
163
164
std::string
165
GetCounterName(std::size_t id)
166
98.4k
{
167
98.4k
  return kCounterNamePrefix + std::to_string(id);
168
98.4k
}
169
170
/** Returns `<counter_name> = <counter_name> + 1`. */
171
std::string
172
GetCounterIncrement(const std::string &counter_name)
173
49.2k
{
174
49.2k
  std::string retval = counter_name;
175
49.2k
  retval += " = ";
176
49.2k
  retval += counter_name;
177
49.2k
  retval += " + 1\n";
178
49.2k
  return retval;
179
49.2k
}
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
49.2k
{
188
49.2k
  std::string retval = "if ";
189
49.2k
  retval += counter_name;
190
49.2k
  retval += " > ";
191
49.2k
  retval += std::to_string(kMaxCounterValue);
192
49.2k
  retval += " then ";
193
49.2k
  retval += then_block;
194
49.2k
  retval += " end\n";
195
49.2k
  return retval;
196
49.2k
}
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
49.2k
  {
212
49.2k
    block_stack_.push(type);
213
49.2k
    if (type == BlockType::kReturnable) {
214
14.4k
      ++returnable_counter_;
215
14.4k
    }
216
49.2k
  }
217
218
  void step_out()
219
49.2k
  {
220
49.2k
    assert(!block_stack_.empty());
221
49.2k
    if (block_stack_.top() == BlockType::kReturnable) {
222
14.4k
      assert(returnable_counter_ > 0);
223
0
      --returnable_counter_;
224
14.4k
    }
225
0
    block_stack_.pop();
226
49.2k
  }
227
228
  std::string get_next_block_setup()
229
49.2k
  {
230
49.2k
    std::size_t id = GetCounterIdProvider().next();
231
49.2k
    std::string counter_name = GetCounterName(id);
232
233
49.2k
    return GetCondition(counter_name, get_exit_statement_()) +
234
49.2k
           GetCounterIncrement(counter_name);
235
49.2k
  }
236
237
  bool break_is_possible()
238
1.43k
  {
239
1.43k
    return !block_stack_.empty() &&
240
1.43k
           block_stack_.top() == BlockType::kBreakable;
241
1.43k
  }
242
243
  bool return_is_possible()
244
5.28k
  {
245
5.28k
    return returnable_counter_ > 0;
246
5.28k
  }
247
248
private:
249
250
  std::string get_exit_statement_()
251
49.2k
  {
252
49.2k
    assert(!block_stack_.empty());
253
0
    switch (block_stack_.top()) {
254
34.7k
    case BlockType::kBreakable:
255
34.7k
      return "break";
256
14.4k
    case BlockType::kReturnable:
257
14.4k
      return "return";
258
49.2k
    }
259
49.2k
    unreachable();
260
49.2k
  }
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
154k
{
280
154k
  static Context context;
281
154k
  return context;
282
154k
}
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
34.7k
{
292
34.7k
  std::string retval = GetContext().get_next_block_setup();
293
34.7k
  retval += ChunkToString(block.chunk());
294
34.7k
  return retval;
295
34.7k
}
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
26.9k
{
305
26.9k
  std::string retval = "do\n";
306
26.9k
  retval += BlockToStringCycleProtected(block.block());
307
26.9k
  retval += "end\n";
308
26.9k
  return retval;
309
26.9k
}
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
14.4k
{
319
14.4k
  std::string body_str = "( ";
320
14.4k
  if (body.has_parlist()) {
321
3.17k
    body_str += ParListToString(body.parlist());
322
3.17k
  }
323
14.4k
  body_str += " )\n\t";
324
325
14.4k
  body_str += GetContext().get_next_block_setup();
326
327
14.4k
  body_str += BlockToString(body.block());
328
14.4k
  body_str += "end\n";
329
14.4k
  return body_str;
330
14.4k
}
331
332
std::string
333
ClearIdentifier(const std::string &identifier)
334
168k
{
335
168k
  std::string cleared;
336
337
168k
  bool has_first_not_digit = false;
338
434k
  for (char c : identifier) {
339
434k
    if (has_first_not_digit && (std::iswalnum(c) || c == '_')) {
340
206k
      cleared += c;
341
227k
    } else if (std::isalpha(c) || c == '_') {
342
8.10k
      has_first_not_digit = true;
343
8.10k
      cleared += c;
344
8.10k
    }
345
434k
  }
346
168k
  return cleared;
347
168k
}
348
349
inline std::string
350
clamp(std::string s, size_t maxSize = kMaxStrLength)
351
168k
{
352
168k
  if (s.size() > maxSize)
353
1.27k
    s.resize(maxSize);
354
168k
  return s;
355
168k
}
356
357
inline double
358
clamp(double number, double upper, double lower)
359
11.4k
{
360
11.4k
  return number <= lower ? lower :
361
11.4k
         number >= upper ? upper : number;
362
11.4k
}
363
364
inline std::string
365
ConvertToStringDefault(const std::string &s)
366
168k
{
367
168k
  std::string ident = ClearIdentifier(s);
368
168k
  ident = clamp(ident);
369
168k
  if (ident.empty())
370
160k
    return std::string(kDefaultIdent);
371
8.10k
  return ident;
372
168k
}
373
374
PROTO_TOSTRING(Block, block)
375
22.1k
{
376
22.1k
  return ChunkToString(block.chunk());
377
22.1k
}
378
379
PROTO_TOSTRING(Chunk, chunk)
380
56.9k
{
381
56.9k
  std::string chunk_str;
382
124k
  for (int i = 0; i < chunk.stat_size(); ++i)
383
67.3k
    chunk_str += StatementToString(chunk.stat(i)) + "\n";
384
385
56.9k
  if (chunk.has_laststat())
386
6.71k
    chunk_str += LastStatementToString(chunk.laststat()) + "\n";
387
388
56.9k
  return chunk_str;
389
56.9k
}
390
391
/**
392
 * LastStatement and nested types.
393
 */
394
PROTO_TOSTRING(LastStatement, laststat)
395
6.71k
{
396
6.71k
  std::string laststat_str;
397
6.71k
  using LastStatType = LastStatement::LastOneofCase;
398
6.71k
  switch (laststat.last_oneof_case()) {
399
2.95k
  case LastStatType::kExplist:
400
2.95k
    laststat_str = ReturnOptionalExpressionListToString(
401
2.95k
      laststat.explist());
402
2.95k
    break;
403
1.43k
  case LastStatType::kBreak:
404
1.43k
    if (GetContext().break_is_possible()) {
405
821
      laststat_str = "break";
406
821
    }
407
1.43k
    break;
408
2.32k
  default:
409
    /* Chosen as default in order to decrease number of 'break's. */
410
2.32k
    laststat_str = ReturnOptionalExpressionListToString(
411
2.32k
      laststat.explist());
412
2.32k
    break;
413
6.71k
  }
414
415
6.71k
  if (!laststat_str.empty() && laststat.has_semicolon())
416
1.17k
    laststat_str += "; ";
417
418
6.71k
  return laststat_str;
419
6.71k
}
420
421
NESTED_PROTO_TOSTRING(ReturnOptionalExpressionList, explist, LastStatement)
422
5.28k
{
423
5.28k
  if (!GetContext().return_is_possible()) {
424
1.64k
    return "";
425
1.64k
  }
426
427
3.63k
  std::string explist_str = "return";
428
3.63k
  if (explist.has_explist()) {
429
2.10k
    explist_str += " " + ExpressionListToString(explist.explist());
430
2.10k
    explist_str += " ";
431
2.10k
  }
432
3.63k
  return explist_str;
433
5.28k
}
434
435
/**
436
 * Statement and statement options.
437
 */
438
PROTO_TOSTRING(Statement, stat)
439
67.3k
{
440
67.3k
  std::string stat_str;
441
67.3k
  using StatType = Statement::StatOneofCase;
442
67.3k
  switch (stat.stat_oneof_case()) {
443
3.58k
  case StatType::kList:
444
3.58k
    stat_str = AssignmentListToString(stat.list());
445
3.58k
    break;
446
6.16k
  case StatType::kCall:
447
6.16k
    stat_str = FunctionCallToString(stat.call());
448
6.16k
    break;
449
1.17k
  case StatType::kBlock:
450
1.17k
    stat_str = DoBlockToString(stat.block());
451
1.17k
    break;
452
2.64k
  case StatType::kWhilecycle:
453
2.64k
    stat_str = WhileCycleToString(stat.whilecycle());
454
2.64k
    break;
455
7.76k
  case StatType::kRepeatcycle:
456
7.76k
    stat_str = RepeatCycleToString(stat.repeatcycle());
457
7.76k
    break;
458
2.72k
  case StatType::kIfstat:
459
2.72k
    stat_str = IfStatementToString(stat.ifstat());
460
2.72k
    break;
461
1.34k
  case StatType::kForcyclename:
462
1.34k
    stat_str = ForCycleNameToString(stat.forcyclename());
463
1.34k
    break;
464
22.9k
  case StatType::kForcyclelist:
465
22.9k
    stat_str = ForCycleListToString(stat.forcyclelist());
466
22.9k
    break;
467
2.26k
  case StatType::kFunc:
468
2.26k
    stat_str = FunctionToString(stat.func());
469
2.26k
    break;
470
1.90k
  case StatType::kLocalfunc:
471
1.90k
    stat_str = LocalFuncToString(stat.localfunc());
472
1.90k
    break;
473
2.03k
  case StatType::kLocalnames:
474
2.03k
    stat_str = LocalNamesToString(stat.localnames());
475
2.03k
    break;
476
12.7k
  default:
477
    /**
478
     * Chosen arbitrarily more for simplicity.
479
     * TODO: Choose "more interesting" defaults.
480
     */
481
12.7k
    stat_str = AssignmentListToString(stat.list());
482
12.7k
    break;
483
67.3k
  }
484
485
67.3k
  if (stat.has_semicolon())
486
34.0k
    stat_str += "; ";
487
488
67.3k
  return stat_str;
489
67.3k
}
490
491
/**
492
 * AssignmentList and nested types.
493
 */
494
PROTO_TOSTRING(AssignmentList, assignmentlist)
495
16.3k
{
496
16.3k
  std::string list_str = VariableListToString(assignmentlist.varlist());
497
16.3k
  list_str += " = " + ExpressionListToString(assignmentlist.explist());
498
16.3k
  return list_str;
499
16.3k
}
500
501
NESTED_PROTO_TOSTRING(VariableList, varlist, AssignmentList)
502
16.3k
{
503
16.3k
  std::string varlist_str = VariableToString(varlist.var());
504
26.1k
  for (int i = 0; i < varlist.vars_size(); ++i) {
505
9.82k
    varlist_str += ", " + VariableToString(varlist.vars(i));
506
9.82k
    varlist_str += " ";
507
9.82k
  }
508
16.3k
  return varlist_str;
509
16.3k
}
510
511
/**
512
 * FunctionCall and nested types.
513
 */
514
PROTO_TOSTRING(FunctionCall, call)
515
11.9k
{
516
11.9k
  using FuncCallType = FunctionCall::CallOneofCase;
517
11.9k
  switch (call.call_oneof_case()) {
518
2.65k
  case FuncCallType::kPrefArgs:
519
2.65k
    return PrefixArgsToString(call.prefargs());
520
2.56k
  case FuncCallType::kNamedArgs:
521
2.56k
    return PrefixNamedArgsToString(call.namedargs());
522
6.75k
  default:
523
    /* Chosen for more variability of generated programs. */
524
6.75k
    return PrefixNamedArgsToString(call.namedargs());
525
11.9k
  }
526
11.9k
}
527
528
NESTED_PROTO_TOSTRING(Args, args, FunctionCall)
529
11.9k
{
530
11.9k
  using ArgsType = FunctionCall::Args::ArgsOneofCase;
531
11.9k
  switch (args.args_oneof_case()) {
532
2.19k
  case ArgsType::kExplist:
533
2.19k
    return "(" + OptionalExpressionListToString(args.explist()) +
534
2.19k
           ")";
535
325
  case ArgsType::kTableconstructor:
536
325
    return TableConstructorToString(args.tableconstructor());
537
183
  case ArgsType::kStr:
538
183
    return "'" + ConvertToStringDefault(args.str()) + "'";
539
9.26k
  default:
540
    /* For more variability. */
541
9.26k
    return TableConstructorToString(args.tableconstructor());
542
11.9k
  }
543
11.9k
}
544
545
NESTED_PROTO_TOSTRING(PrefixArgs, prefixargs, FunctionCall)
546
2.65k
{
547
2.65k
  std::string prefixargs_str = PrefixExpressionToString(
548
2.65k
    prefixargs.prefixexp());
549
2.65k
  prefixargs_str += " " + ArgsToString(prefixargs.args());
550
2.65k
  return prefixargs_str;
551
2.65k
}
552
553
NESTED_PROTO_TOSTRING(PrefixNamedArgs, prefixnamedargs, FunctionCall)
554
9.31k
{
555
9.31k
  std::string predixnamedargs_str = PrefixExpressionToString(
556
9.31k
    prefixnamedargs.prefixexp());
557
9.31k
  predixnamedargs_str += ":" + NameToString(prefixnamedargs.name());
558
9.31k
  predixnamedargs_str += " " + ArgsToString(prefixnamedargs.args());
559
9.31k
  return predixnamedargs_str;
560
9.31k
}
561
562
/**
563
 * DoBlock clause.
564
 */
565
PROTO_TOSTRING(DoBlock, block)
566
1.17k
{
567
1.17k
  return "do\n" + BlockToString(block.block()) + "end\n";
568
1.17k
}
569
570
/**
571
 * WhileCycle clause.
572
 */
573
PROTO_TOSTRING(WhileCycle, whilecycle)
574
2.64k
{
575
2.64k
  GetContext().step_in(Context::BlockType::kBreakable);
576
577
2.64k
  std::string whilecycle_str = "while ";
578
2.64k
  whilecycle_str += ExpressionToString(whilecycle.condition());
579
2.64k
  whilecycle_str += " ";
580
2.64k
  whilecycle_str += DoBlockToStringCycleProtected(whilecycle.doblock());
581
582
2.64k
  GetContext().step_out();
583
2.64k
  return whilecycle_str;
584
2.64k
}
585
586
/**
587
 * RepeatCycle clause.
588
 */
589
PROTO_TOSTRING(RepeatCycle, repeatcycle)
590
7.76k
{
591
7.76k
  GetContext().step_in(Context::BlockType::kBreakable);
592
593
7.76k
  std::string repeatcycle_str = "repeat\n";
594
7.76k
  repeatcycle_str += BlockToStringCycleProtected(repeatcycle.block());
595
7.76k
  repeatcycle_str += "until ";
596
7.76k
  repeatcycle_str += ExpressionToString(repeatcycle.condition());
597
598
7.76k
  GetContext().step_out();
599
7.76k
  return repeatcycle_str;
600
7.76k
}
601
602
/**
603
 * IfStatement and nested types.
604
 */
605
PROTO_TOSTRING(IfStatement, statement)
606
2.72k
{
607
2.72k
  std::string statement_str = "if " +
608
2.72k
    ExpressionToString(statement.condition());
609
2.72k
  statement_str += " then\n\t" + BlockToString(statement.first());
610
611
4.56k
  for (int i = 0; i < statement.clauses_size(); ++i)
612
1.83k
    statement_str += ElseIfBlockToString(statement.clauses(i));
613
614
2.72k
  if (statement.has_last())
615
1.06k
    statement_str += "else\n\t" + BlockToString(statement.last());
616
617
2.72k
  statement_str += "end\n";
618
2.72k
  return statement_str;
619
2.72k
}
620
621
NESTED_PROTO_TOSTRING(ElseIfBlock, elseifblock, IfStatement)
622
1.83k
{
623
1.83k
  std::string elseifblock_str = "elseif ";
624
1.83k
  elseifblock_str += ExpressionToString(elseifblock.condition());
625
1.83k
  elseifblock_str += " then\n\t";
626
1.83k
  elseifblock_str += BlockToString(elseifblock.block());
627
1.83k
  return elseifblock_str;
628
1.83k
}
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.34k
{
638
1.34k
  GetContext().step_in(Context::BlockType::kBreakable);
639
640
1.34k
  std::string forcyclename_str = "for ";
641
1.34k
  forcyclename_str += NameToString(forcyclename.name());
642
1.34k
  forcyclename_str += " = ";
643
1.34k
  forcyclename_str += ExpressionToString(forcyclename.startexp());
644
1.34k
  forcyclename_str += ", ";
645
1.34k
  forcyclename_str += ExpressionToString(forcyclename.stopexp());
646
647
1.34k
  if (forcyclename.has_stepexp())
648
472
    forcyclename_str += ", " + ExpressionToString(
649
472
      forcyclename.stepexp());
650
651
1.34k
  forcyclename_str += " ";
652
1.34k
  forcyclename_str += DoBlockToStringCycleProtected(
653
1.34k
    forcyclename.doblock());
654
655
1.34k
  GetContext().step_out();
656
1.34k
  return forcyclename_str;
657
1.34k
}
658
659
/**
660
 * ForCycleList clause.
661
 */
662
PROTO_TOSTRING(ForCycleList, forcyclelist)
663
22.9k
{
664
22.9k
  GetContext().step_in(Context::BlockType::kBreakable);
665
666
22.9k
  std::string forcyclelist_str = "for ";
667
22.9k
  forcyclelist_str += NameListToString(forcyclelist.names());
668
22.9k
  forcyclelist_str += " in ";
669
22.9k
  forcyclelist_str += ExpressionListToString(forcyclelist.expressions());
670
22.9k
  forcyclelist_str += " ";
671
22.9k
  forcyclelist_str += DoBlockToStringCycleProtected(
672
22.9k
    forcyclelist.doblock());
673
674
22.9k
  GetContext().step_out();
675
22.9k
  return forcyclelist_str;
676
22.9k
}
677
678
/**
679
 * Function and nested types.
680
 */
681
PROTO_TOSTRING(Function, func)
682
2.26k
{
683
2.26k
  GetContext().step_in(Context::BlockType::kReturnable);
684
685
2.26k
  std::string func_str = "function ";
686
2.26k
  func_str += FuncNameToString(func.name());
687
2.26k
  func_str += FuncBodyToStringReqProtected(func.body());
688
689
2.26k
  GetContext().step_out();
690
2.26k
  return func_str;
691
2.26k
}
692
693
NESTED_PROTO_TOSTRING(FuncName, funcname, Function)
694
2.26k
{
695
2.26k
  std::string funcname_str = NameToString(funcname.firstname());
696
697
2.73k
  for (int i = 0; i < funcname.names_size(); ++i)
698
471
    funcname_str += "." + NameToString(funcname.names(i));
699
700
2.26k
  if (funcname.has_lastname())
701
126
    funcname_str += ":" + NameToString(funcname.lastname());
702
703
2.26k
  return funcname_str;
704
2.26k
}
705
706
PROTO_TOSTRING(NameList, namelist)
707
27.2k
{
708
27.2k
  std::string namelist_str = NameToString(namelist.firstname());
709
37.2k
  for (int i = 0; i < namelist.names_size(); ++i)
710
9.99k
    namelist_str += ", " + NameToString(namelist.names(i));
711
27.2k
  return namelist_str;
712
27.2k
}
713
714
NESTED_PROTO_TOSTRING(NameListWithEllipsis, namelist, FuncBody)
715
2.24k
{
716
2.24k
  std::string namelist_str = NameListToString(namelist.namelist());
717
2.24k
  if (namelist.has_ellipsis())
718
219
    namelist_str += ", ...";
719
2.24k
  return namelist_str;
720
2.24k
}
721
722
NESTED_PROTO_TOSTRING(ParList, parlist, FuncBody)
723
3.17k
{
724
3.17k
  using ParListType = FuncBody::ParList::ParlistOneofCase;
725
3.17k
  switch (parlist.parlist_oneof_case()) {
726
392
  case ParListType::kNamelist:
727
392
    return NameListWithEllipsisToString(parlist.namelist());
728
928
  case ParListType::kEllipsis:
729
928
    return "...";
730
1.85k
  default:
731
    /* Chosen as default in order to decrease number of ellipses. */
732
1.85k
    return NameListWithEllipsisToString(parlist.namelist());
733
3.17k
  }
734
3.17k
}
735
736
/**
737
 * LocalFunc clause.
738
 */
739
PROTO_TOSTRING(LocalFunc, localfunc)
740
1.90k
{
741
1.90k
  GetContext().step_in(Context::BlockType::kReturnable);
742
743
1.90k
  std::string localfunc_str = "local function ";
744
1.90k
  localfunc_str += NameToString(localfunc.name());
745
1.90k
  localfunc_str += " ";
746
1.90k
  localfunc_str += FuncBodyToStringReqProtected(localfunc.funcbody());
747
748
1.90k
  GetContext().step_out();
749
1.90k
  return localfunc_str;
750
1.90k
}
751
752
/**
753
 * LocalNames clause.
754
 */
755
PROTO_TOSTRING(LocalNames, localnames)
756
2.03k
{
757
2.03k
  std::string localnames_str = "local ";
758
2.03k
  localnames_str += NameListToString(localnames.namelist());
759
760
2.03k
  if (localnames.has_explist())
761
845
    localnames_str += " = " + ExpressionListToString(
762
845
      localnames.explist());
763
2.03k
  return localnames_str;
764
2.03k
}
765
766
/**
767
 * Expressions and variables.
768
 */
769
770
/**
771
 * Expressions clauses.
772
 */
773
PROTO_TOSTRING(ExpressionList, explist)
774
44.2k
{
775
44.2k
  std::string explist_str;
776
64.1k
  for (int i = 0; i < explist.expressions_size(); ++i)
777
19.8k
    explist_str += ExpressionToString(explist.expressions(i)) +
778
19.8k
        ", ";
779
44.2k
  explist_str += ExpressionToString(explist.explast()) + " ";
780
44.2k
  return explist_str;
781
44.2k
}
782
783
PROTO_TOSTRING(OptionalExpressionList, explist)
784
2.19k
{
785
2.19k
  if (explist.has_explist())
786
1.99k
    return ExpressionListToString(explist.explist());
787
203
  return "";
788
2.19k
}
789
790
PROTO_TOSTRING(PrefixExpression, prefixexp)
791
31.4k
{
792
31.4k
  using PrefExprType = PrefixExpression::PrefixOneofCase;
793
31.4k
  switch (prefixexp.prefix_oneof_case()) {
794
2.78k
  case PrefExprType::kVar:
795
2.78k
    return VariableToString(prefixexp.var());
796
5.80k
  case PrefExprType::kFunctioncall:
797
5.80k
    return FunctionCallToString(prefixexp.functioncall());
798
3.69k
  case PrefExprType::kExp:
799
3.69k
    return "(" + ExpressionToString(prefixexp.exp()) + ")";
800
19.2k
  default:
801
    /*
802
     * Can be generated too nested expressions with other options,
803
     * though they can be enabled for more variable fuzzing.
804
     */
805
19.2k
    return VariableToString(prefixexp.var());
806
31.4k
  }
807
31.4k
}
808
809
/**
810
 * Variable and nested types.
811
 */
812
PROTO_TOSTRING(Variable, var)
813
48.1k
{
814
48.1k
  using VarType = Variable::VarOneofCase;
815
48.1k
  switch (var.var_oneof_case()) {
816
1.13k
  case VarType::kName:
817
1.13k
    return NameToString(var.name());
818
3.59k
  case VarType::kIndexexpr:
819
3.59k
    return IndexWithExpressionToString(var.indexexpr());
820
3.67k
  case VarType::kIndexname:
821
3.67k
    return IndexWithNameToString(var.indexname());
822
39.7k
  default:
823
    /*
824
     * Can be generated too nested expressions with other options,
825
     * though they can be enabled for more variable fuzzing.
826
     */
827
39.7k
    return NameToString(var.name());
828
48.1k
  }
829
48.1k
}
830
831
NESTED_PROTO_TOSTRING(IndexWithExpression, indexexpr, Variable)
832
3.59k
{
833
3.59k
  std::string indexexpr_str = PrefixExpressionToString(
834
3.59k
    indexexpr.prefixexp());
835
3.59k
  indexexpr_str += "[" + ExpressionToString(indexexpr.exp()) + "]";
836
3.59k
  return indexexpr_str;
837
3.59k
}
838
839
NESTED_PROTO_TOSTRING(IndexWithName, indexname, Variable)
840
3.67k
{
841
3.67k
  std::string indexname_str = PrefixExpressionToString(
842
3.67k
    indexname.prefixexp());
843
3.67k
  indexname_str += "." + ConvertToStringDefault(indexname.name());
844
3.67k
  return indexname_str;
845
3.67k
}
846
847
/**
848
 * Expression and nested types.
849
 */
850
PROTO_TOSTRING(Expression, expr)
851
137k
{
852
137k
  using ExprType = Expression::ExprOneofCase;
853
137k
  switch (expr.expr_oneof_case()) {
854
3.22k
  case ExprType::kNil:
855
3.22k
    return "nil";
856
1.54k
  case ExprType::kFalse:
857
1.54k
    return "false";
858
1.92k
  case ExprType::kTrue:
859
1.92k
    return "true";
860
11.4k
  case ExprType::kNumber: {
861
    /* Clamp number between given boundaries. */
862
11.4k
    double number = clamp(expr.number(), kMaxNumber, kMinNumber);
863
11.4k
    return std::to_string(number);
864
0
  }
865
2.46k
  case ExprType::kStr:
866
2.46k
    return "'" + ConvertToStringDefault(expr.str()) + "'";
867
2.36k
  case ExprType::kEllipsis:
868
2.36k
    return " ... ";
869
10.3k
  case ExprType::kFunction:
870
10.3k
    return AnonFuncToString(expr.function());
871
12.2k
  case ExprType::kPrefixexp:
872
12.2k
    return PrefixExpressionToString(expr.prefixexp());
873
4.45k
  case ExprType::kTableconstructor:
874
4.45k
    return TableConstructorToString(expr.tableconstructor());
875
18.2k
  case ExprType::kBinary:
876
18.2k
    return ExpBinaryOpExpToString(expr.binary());
877
4.31k
  case ExprType::kUnary:
878
4.31k
    return UnaryOpExpToString(expr.unary());
879
65.3k
  default:
880
    /**
881
     * Arbitrary choice.
882
     * TODO: Choose "more interesting" defaults.
883
     */
884
65.3k
    return "'" + ConvertToStringDefault(expr.str()) + "'";
885
137k
  }
886
137k
}
887
888
NESTED_PROTO_TOSTRING(AnonFunc, func, Expression)
889
10.3k
{
890
10.3k
  GetContext().step_in(Context::BlockType::kReturnable);
891
892
10.3k
  std::string retval = "function ";
893
10.3k
  retval += FuncBodyToStringReqProtected(func.body());
894
895
10.3k
  GetContext().step_out();
896
10.3k
  return retval;
897
10.3k
}
898
899
NESTED_PROTO_TOSTRING(ExpBinaryOpExp, binary, Expression)
900
18.2k
{
901
18.2k
  std::string binary_str = ExpressionToString(binary.leftexp());
902
18.2k
  binary_str += " " + BinaryOperatorToString(binary.binop()) + " ";
903
18.2k
  binary_str += ExpressionToString(binary.rightexp());
904
18.2k
  return binary_str;
905
18.2k
}
906
907
NESTED_PROTO_TOSTRING(UnaryOpExp, unary, Expression)
908
4.31k
{
909
4.31k
  std::string unary_str = UnaryOperatorToString(unary.unop());
910
4.31k
  unary_str += ExpressionToString(unary.exp());
911
4.31k
  return unary_str;
912
4.31k
}
913
914
/**
915
 * Tables and fields.
916
 */
917
PROTO_TOSTRING(TableConstructor, table)
918
14.0k
{
919
14.0k
  std::string table_str = "{ ";
920
14.0k
  if (table.has_fieldlist())
921
3.56k
    table_str += FieldListToString(table.fieldlist());
922
14.0k
  table_str += " }";
923
14.0k
  return table_str;
924
14.0k
}
925
926
PROTO_TOSTRING(FieldList, fieldlist)
927
3.56k
{
928
3.56k
  std::string fieldlist_str = FieldToString(fieldlist.firstfield());
929
6.13k
  for (int i = 0; i < fieldlist.fields_size(); ++i)
930
2.56k
    fieldlist_str += FieldWithFieldSepToString(fieldlist.fields(i));
931
3.56k
  if (fieldlist.has_lastsep())
932
835
    fieldlist_str += FieldSepToString(fieldlist.lastsep());
933
3.56k
  return fieldlist_str;
934
3.56k
}
935
936
NESTED_PROTO_TOSTRING(FieldWithFieldSep, field, FieldList)
937
2.56k
{
938
2.56k
  std::string field_str = FieldSepToString(field.sep());
939
2.56k
  field_str += " " + FieldToString(field.field());
940
2.56k
  return field_str;
941
2.56k
}
942
943
/**
944
 * Field and nested types.
945
 */
946
PROTO_TOSTRING(Field, field)
947
6.13k
{
948
6.13k
  using FieldType = Field::FieldOneofCase;
949
6.13k
  switch (field.field_oneof_case()) {
950
1.44k
  case FieldType::kExprassign:
951
1.44k
    return ExpressionAssignmentToString(field.exprassign());
952
183
  case FieldType::kNamedassign:
953
183
    return NameAssignmentToString(field.namedassign());
954
1.54k
  case FieldType::kExpression:
955
1.54k
    return ExpressionToString(field.expression());
956
2.96k
  default:
957
    /* More common case of using fields. */
958
2.96k
    return NameAssignmentToString(field.namedassign());
959
6.13k
  }
960
6.13k
}
961
962
NESTED_PROTO_TOSTRING(ExpressionAssignment, assignment, Field)
963
1.44k
{
964
1.44k
  std::string assignment_str = "[ " +
965
1.44k
    ExpressionToString(assignment.key()) + " ]";
966
1.44k
  assignment_str += " = " + ExpressionToString(assignment.value());
967
1.44k
  return assignment_str;
968
1.44k
}
969
970
NESTED_PROTO_TOSTRING(NameAssignment, assignment, Field)
971
3.14k
{
972
3.14k
  std::string assignment_str = NameToString(assignment.name());
973
3.14k
  assignment_str += " = " + ExpressionToString(assignment.value());
974
3.14k
  return assignment_str;
975
3.14k
}
976
977
PROTO_TOSTRING(FieldSep, sep)
978
3.39k
{
979
3.39k
  using FieldSepType = FieldSep::SepOneofCase;
980
3.39k
  switch (sep.sep_oneof_case()) {
981
802
  case FieldSepType::kComma:
982
802
    return ",";
983
687
  case FieldSepType::kSemicolon:
984
687
    return ";";
985
1.91k
  default:
986
1.91k
    return ",";
987
3.39k
  }
988
3.39k
}
989
990
/**
991
 * Operators.
992
 */
993
PROTO_TOSTRING(BinaryOperator, op)
994
18.2k
{
995
18.2k
  using BinopType = BinaryOperator::BinaryOneofCase;
996
18.2k
  switch (op.binary_oneof_case()) {
997
621
  case BinopType::kAdd:
998
621
    return "+";
999
1.44k
  case BinopType::kSub:
1000
1.44k
    return "-";
1001
3.44k
  case BinopType::kMult:
1002
3.44k
    return "*";
1003
927
  case BinopType::kDiv:
1004
927
    return "/";
1005
708
  case BinopType::kExp:
1006
708
    return "^";
1007
526
  case BinopType::kMod:
1008
526
    return "%";
1009
1010
847
  case BinopType::kConcat:
1011
847
    return "..";
1012
1013
688
  case BinopType::kLess:
1014
688
    return "<";
1015
580
  case BinopType::kLessEqual:
1016
580
    return "<=";
1017
478
  case BinopType::kGreater:
1018
478
    return ">";
1019
377
  case BinopType::kGreaterEqual:
1020
377
    return ">=";
1021
304
  case BinopType::kEqual:
1022
304
    return "==";
1023
423
  case BinopType::kNotEqual:
1024
423
    return "~=";
1025
1.38k
  case BinopType::kAnd:
1026
1.38k
    return "and";
1027
2.21k
  case BinopType::kOr:
1028
2.21k
    return "or";
1029
3.32k
  default:
1030
    /* Works in most cases. */
1031
3.32k
    return "==";
1032
18.2k
  }
1033
18.2k
}
1034
1035
PROTO_TOSTRING(UnaryOperator, op)
1036
4.31k
{
1037
4.31k
  using UnaryopType = UnaryOperator::UnaryOneofCase;
1038
4.31k
  switch (op.unary_oneof_case()) {
1039
877
  case UnaryopType::kNegate:
1040
877
    return "-";
1041
227
  case UnaryopType::kNot:
1042
227
    return "not ";
1043
1.27k
  case UnaryopType::kLength:
1044
1.27k
    return "#";
1045
1.93k
  default:
1046
    /* Works in most cases. */
1047
1.93k
    return "not ";
1048
4.31k
  }
1049
4.31k
}
1050
1051
/**
1052
 * Identifier (Name).
1053
 */
1054
PROTO_TOSTRING(Name, name)
1055
96.6k
{
1056
96.6k
  std::string ident = ConvertToStringDefault(name.name());
1057
96.6k
  return ident + std::to_string(name.num() % kMaxIdentifiers);
1058
96.6k
}
1059
1060
} /* namespace */
1061
1062
std::string
1063
MainBlockToString(const Block &block)
1064
903
{
1065
903
  GetCounterIdProvider().clean();
1066
1067
903
  std::string block_str = BlockToString(block);
1068
903
  std::string retval;
1069
1070
50.1k
  for (size_t i = 0; i < GetCounterIdProvider().count(); ++i) {
1071
49.2k
    retval += GetCounterName(i);
1072
49.2k
    retval += " = 0\n";
1073
49.2k
  }
1074
903
  retval += block_str;
1075
1076
903
  return retval;
1077
903
}
1078
1079
} /* namespace luajit_fuzzer */