Coverage Report

Created: 2025-08-29 06:37

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