Coverage Report

Created: 2023-09-15 06:19

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