Coverage Report

Created: 2025-12-28 06:16

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