/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 | | using namespace lua_grammar; |
12 | | |
13 | | #define PROTO_TOSTRING(TYPE, VAR_NAME) \ |
14 | | std::string TYPE##ToString(const TYPE & (VAR_NAME)) |
15 | | |
16 | | /* PROTO_TOSTRING version for nested (depth=2) protobuf messages. */ |
17 | | #define NESTED_PROTO_TOSTRING(TYPE, VAR_NAME, PARENT_MESSAGE) \ |
18 | | std::string TYPE##ToString \ |
19 | | (const PARENT_MESSAGE::TYPE & (VAR_NAME)) |
20 | | |
21 | | namespace luajit_fuzzer { |
22 | | namespace { |
23 | | |
24 | | const std::string kCounterNamePrefix = "counter_"; |
25 | | |
26 | | PROTO_TOSTRING(Block, block); |
27 | | PROTO_TOSTRING(Chunk, chunk); |
28 | | |
29 | | PROTO_TOSTRING(Statement, stat); |
30 | | |
31 | | /** LastStatement and nested types. */ |
32 | | PROTO_TOSTRING(LastStatement, laststat); |
33 | | NESTED_PROTO_TOSTRING(ReturnOptionalExpressionList, explist, LastStatement); |
34 | | |
35 | | /** |
36 | | * Statement options. |
37 | | */ |
38 | | |
39 | | /** AssignmentList and nested types. */ |
40 | | PROTO_TOSTRING(AssignmentList, assignmentlist); |
41 | | NESTED_PROTO_TOSTRING(VariableList, varlist, AssignmentList); |
42 | | |
43 | | /** FunctionCall and nested types. */ |
44 | | PROTO_TOSTRING(FunctionCall, call); |
45 | | NESTED_PROTO_TOSTRING(Args, args, FunctionCall); |
46 | | NESTED_PROTO_TOSTRING(PrefixArgs, prefixargs, FunctionCall); |
47 | | NESTED_PROTO_TOSTRING(PrefixNamedArgs, prefixnamedargs, FunctionCall); |
48 | | |
49 | | /** DoBlock, WhileCycle and RepeatCycle clauses. */ |
50 | | PROTO_TOSTRING(DoBlock, block); |
51 | | PROTO_TOSTRING(WhileCycle, whilecycle); |
52 | | PROTO_TOSTRING(RepeatCycle, repeatcycle); |
53 | | |
54 | | /** IfStatement and nested types. */ |
55 | | PROTO_TOSTRING(IfStatement, statement); |
56 | | NESTED_PROTO_TOSTRING(ElseIfBlock, elseifblock, IfStatement); |
57 | | |
58 | | /** ForCycleName and ForCycleList clauses. */ |
59 | | PROTO_TOSTRING(ForCycleName, forcyclename); |
60 | | PROTO_TOSTRING(ForCycleList, forcyclelist); |
61 | | |
62 | | /** Function and nested types. */ |
63 | | PROTO_TOSTRING(Function, func); |
64 | | NESTED_PROTO_TOSTRING(FuncName, funcname, Function); |
65 | | |
66 | | PROTO_TOSTRING(NameList, namelist); |
67 | | NESTED_PROTO_TOSTRING(NameListWithEllipsis, namelist, FuncBody); |
68 | | NESTED_PROTO_TOSTRING(ParList, parlist, FuncBody); |
69 | | |
70 | | /** LocalFunc and LocalNames clauses. */ |
71 | | PROTO_TOSTRING(LocalFunc, localfunc); |
72 | | PROTO_TOSTRING(LocalNames, localnames); |
73 | | |
74 | | /** |
75 | | * Expressions and variables. |
76 | | */ |
77 | | |
78 | | /** Expressions clauses. */ |
79 | | PROTO_TOSTRING(ExpressionList, explist); |
80 | | PROTO_TOSTRING(OptionalExpressionList, explist); |
81 | | PROTO_TOSTRING(PrefixExpression, prefExpr); |
82 | | |
83 | | /* Variable and nested types. */ |
84 | | PROTO_TOSTRING(Variable, var); |
85 | | NESTED_PROTO_TOSTRING(IndexWithExpression, indexexpr, Variable); |
86 | | NESTED_PROTO_TOSTRING(IndexWithName, indexname, Variable); |
87 | | |
88 | | /** Expression and nested types. */ |
89 | | PROTO_TOSTRING(Expression, expr); |
90 | | NESTED_PROTO_TOSTRING(AnonFunc, function, Expression); |
91 | | NESTED_PROTO_TOSTRING(ExpBinaryOpExp, binary, Expression); |
92 | | NESTED_PROTO_TOSTRING(UnaryOpExp, unary, Expression); |
93 | | |
94 | | /** |
95 | | * Tables and fields. |
96 | | */ |
97 | | PROTO_TOSTRING(TableConstructor, table); |
98 | | PROTO_TOSTRING(FieldList, fieldlist); |
99 | | NESTED_PROTO_TOSTRING(FieldWithFieldSep, field, FieldList); |
100 | | |
101 | | /** Field and nested types. */ |
102 | | PROTO_TOSTRING(Field, field); |
103 | | NESTED_PROTO_TOSTRING(ExpressionAssignment, assignment, Field); |
104 | | NESTED_PROTO_TOSTRING(NameAssignment, assignment, Field); |
105 | | PROTO_TOSTRING(FieldSep, sep); |
106 | | |
107 | | /** Operators. */ |
108 | | PROTO_TOSTRING(BinaryOperator, op); |
109 | | PROTO_TOSTRING(UnaryOperator, op); |
110 | | |
111 | | /** Identifier (Name). */ |
112 | | PROTO_TOSTRING(Name, name); |
113 | | |
114 | | /** |
115 | | * Class that controls id creation for counters. Basically, a |
116 | | * variable wrapper that guarantees variable to be incremented. |
117 | | */ |
118 | | class CounterIdProvider { |
119 | | public: |
120 | | /** Returns number of id provided. */ |
121 | | std::size_t count() |
122 | 80.3k | { |
123 | 80.3k | return id_; |
124 | 80.3k | } |
125 | | |
126 | | /** Returns a new id that was not used after last clean(). */ |
127 | | std::size_t next() |
128 | 77.7k | { |
129 | 77.7k | return id_++; |
130 | 77.7k | } |
131 | | |
132 | | /** |
133 | | * Cleans history. Should be used to make fuzzer starts |
134 | | * independent. |
135 | | */ |
136 | | void clean() |
137 | 2.58k | { |
138 | 2.58k | id_ = 0; |
139 | 2.58k | } |
140 | | |
141 | | private: |
142 | | std::size_t id_ = 0; |
143 | | }; |
144 | | |
145 | | /** A singleton for counter id provider. */ |
146 | | CounterIdProvider& |
147 | | GetCounterIdProvider() |
148 | 160k | { |
149 | 160k | static CounterIdProvider provider; |
150 | 160k | return provider; |
151 | 160k | } |
152 | | |
153 | | std::string |
154 | | GetCounterName(std::size_t id) |
155 | 155k | { |
156 | 155k | return kCounterNamePrefix + std::to_string(id); |
157 | 155k | } |
158 | | |
159 | | /** Returns `<counter_name> = <counter_name> + 1`. */ |
160 | | std::string |
161 | | GetCounterIncrement(const std::string &counter_name) |
162 | 77.7k | { |
163 | 77.7k | std::string retval = counter_name; |
164 | 77.7k | retval += " = "; |
165 | 77.7k | retval += counter_name; |
166 | 77.7k | retval += " + 1\n"; |
167 | 77.7k | return retval; |
168 | 77.7k | } |
169 | | |
170 | | /** |
171 | | * Returns `if <counter_name> > kMaxCounterValue then |
172 | | * <then_block> end`. |
173 | | */ |
174 | | std::string |
175 | | GetCondition(const std::string &counter_name, const std::string &then_block) |
176 | 77.7k | { |
177 | 77.7k | std::string retval = "if "; |
178 | 77.7k | retval += counter_name; |
179 | 77.7k | retval += " > "; |
180 | 77.7k | retval += std::to_string(kMaxCounterValue); |
181 | 77.7k | retval += " then "; |
182 | 77.7k | retval += then_block; |
183 | 77.7k | retval += " end\n"; |
184 | 77.7k | return retval; |
185 | 77.7k | } |
186 | | |
187 | | /** |
188 | | * Block may be placed not only in a cycle, so specially for cycles |
189 | | * there is a function that will add a break condition and a |
190 | | * counter increment. |
191 | | */ |
192 | | std::string |
193 | | BlockToStringCycleProtected(const Block &block, const std::string &counter_name) |
194 | 53.5k | { |
195 | 53.5k | std::string retval = GetCondition(counter_name, "break"); |
196 | 53.5k | retval += GetCounterIncrement(counter_name); |
197 | 53.5k | retval += ChunkToString(block.chunk()); |
198 | 53.5k | return retval; |
199 | 53.5k | } |
200 | | |
201 | | /** |
202 | | * DoBlock may be placed not only in a cycle, so specially for |
203 | | * cycles there is a function that will call |
204 | | * BlockToStringCycleProtected(). |
205 | | */ |
206 | | std::string |
207 | | DoBlockToStringCycleProtected(const DoBlock &block, |
208 | | const std::string &counter_name) |
209 | 47.2k | { |
210 | 47.2k | std::string retval = "do\n"; |
211 | 47.2k | retval += BlockToStringCycleProtected(block.block(), counter_name); |
212 | 47.2k | retval += "end\n"; |
213 | 47.2k | return retval; |
214 | 47.2k | } |
215 | | |
216 | | /** |
217 | | * FuncBody may contain recursive calls, so for all function bodies, |
218 | | * there is a function that adds a return condition and a counter |
219 | | * increment. |
220 | | */ |
221 | | std::string |
222 | | FuncBodyToStringReqProtected(const FuncBody &body, |
223 | | const std::string &counter_name) |
224 | 24.2k | { |
225 | 24.2k | std::string body_str = "( "; |
226 | 24.2k | if (body.has_parlist()) { |
227 | 3.51k | body_str += ParListToString(body.parlist()); |
228 | 3.51k | } |
229 | 24.2k | body_str += " )\n\t"; |
230 | | |
231 | 24.2k | body_str += GetCondition(counter_name, "return"); |
232 | 24.2k | body_str += GetCounterIncrement(counter_name); |
233 | | |
234 | 24.2k | body_str += BlockToString(body.block()); |
235 | 24.2k | body_str += "end\n"; |
236 | 24.2k | return body_str; |
237 | 24.2k | } |
238 | | |
239 | | std::string |
240 | | ClearIdentifier(const std::string &identifier) |
241 | 286k | { |
242 | 286k | std::string cleared; |
243 | | |
244 | 286k | bool has_first_not_digit = false; |
245 | 582k | for (char c : identifier) { |
246 | 582k | if (has_first_not_digit && (std::iswalnum(c) || c == '_')) { |
247 | 296k | cleared += c; |
248 | 296k | } else if (std::isalpha(c) || c == '_') { |
249 | 16.4k | has_first_not_digit = true; |
250 | 16.4k | cleared += c; |
251 | 16.4k | } |
252 | 582k | } |
253 | 286k | return cleared; |
254 | 286k | } |
255 | | |
256 | | inline std::string |
257 | | clamp(std::string s, size_t maxSize = kMaxStrLength) |
258 | 286k | { |
259 | 286k | if (s.size() > maxSize) |
260 | 1.43k | s.resize(maxSize); |
261 | 286k | return s; |
262 | 286k | } |
263 | | |
264 | | inline double |
265 | | clamp(double number, double upper, double lower) |
266 | 25.8k | { |
267 | 25.8k | return number <= lower ? lower : |
268 | 25.8k | number >= upper ? upper : number; |
269 | 25.8k | } |
270 | | |
271 | | inline std::string |
272 | | ConvertToStringDefault(const std::string &s) |
273 | 286k | { |
274 | 286k | std::string ident = ClearIdentifier(s); |
275 | 286k | ident = clamp(ident); |
276 | 286k | if (ident.empty()) |
277 | 270k | return std::string(kDefaultIdent); |
278 | 16.4k | return ident; |
279 | 286k | } |
280 | | |
281 | | PROTO_TOSTRING(Block, block) |
282 | 36.7k | { |
283 | 36.7k | return ChunkToString(block.chunk()); |
284 | 36.7k | } |
285 | | |
286 | | PROTO_TOSTRING(Chunk, chunk) |
287 | 90.2k | { |
288 | 90.2k | std::string chunk_str; |
289 | 195k | for (int i = 0; i < chunk.stat_size(); ++i) |
290 | 104k | chunk_str += StatementToString(chunk.stat(i)) + "\n"; |
291 | | |
292 | 90.2k | if (chunk.has_laststat()) |
293 | 9.08k | chunk_str += LastStatementToString(chunk.laststat()) + "\n"; |
294 | | |
295 | 90.2k | return chunk_str; |
296 | 90.2k | } |
297 | | |
298 | | /** |
299 | | * LastStatement and nested types. |
300 | | */ |
301 | | PROTO_TOSTRING(LastStatement, laststat) |
302 | 9.08k | { |
303 | 9.08k | std::string laststat_str; |
304 | 9.08k | using LastStatType = LastStatement::LastOneofCase; |
305 | 9.08k | switch (laststat.last_oneof_case()) { |
306 | 4.58k | case LastStatType::kExplist: |
307 | 4.58k | laststat_str = ReturnOptionalExpressionListToString( |
308 | 4.58k | laststat.explist()); |
309 | 4.58k | break; |
310 | 1.98k | case LastStatType::kBreak: |
311 | 1.98k | laststat_str = "break"; |
312 | 1.98k | break; |
313 | 2.51k | default: |
314 | | /* Chosen as default in order to decrease number of 'break's. */ |
315 | 2.51k | laststat_str = ReturnOptionalExpressionListToString( |
316 | 2.51k | laststat.explist()); |
317 | 2.51k | break; |
318 | 9.08k | } |
319 | | |
320 | 9.08k | if (laststat.has_semicolon()) |
321 | 2.58k | laststat_str += "; "; |
322 | | |
323 | 9.08k | return laststat_str; |
324 | 9.08k | } |
325 | | |
326 | | NESTED_PROTO_TOSTRING(ReturnOptionalExpressionList, explist, LastStatement) |
327 | 7.10k | { |
328 | 7.10k | std::string explist_str = "return"; |
329 | 7.10k | if (explist.has_explist()) { |
330 | 4.31k | explist_str += " " + ExpressionListToString(explist.explist()); |
331 | 4.31k | explist_str += " "; |
332 | 4.31k | } |
333 | 7.10k | return explist_str; |
334 | 7.10k | } |
335 | | |
336 | | /** |
337 | | * Statement and statement options. |
338 | | */ |
339 | | PROTO_TOSTRING(Statement, stat) |
340 | 104k | { |
341 | 104k | std::string stat_str; |
342 | 104k | using StatType = Statement::StatOneofCase; |
343 | 104k | switch (stat.stat_oneof_case()) { |
344 | 8.49k | case StatType::kList: |
345 | 8.49k | stat_str = AssignmentListToString(stat.list()); |
346 | 8.49k | break; |
347 | 7.34k | case StatType::kCall: |
348 | 7.34k | stat_str = FunctionCallToString(stat.call()); |
349 | 7.34k | break; |
350 | 1.63k | case StatType::kBlock: |
351 | 1.63k | stat_str = DoBlockToString(stat.block()); |
352 | 1.63k | break; |
353 | 8.70k | case StatType::kWhilecycle: |
354 | 8.70k | stat_str = WhileCycleToString(stat.whilecycle()); |
355 | 8.70k | break; |
356 | 6.25k | case StatType::kRepeatcycle: |
357 | 6.25k | stat_str = RepeatCycleToString(stat.repeatcycle()); |
358 | 6.25k | break; |
359 | 4.52k | case StatType::kIfstat: |
360 | 4.52k | stat_str = IfStatementToString(stat.ifstat()); |
361 | 4.52k | break; |
362 | 2.02k | case StatType::kForcyclename: |
363 | 2.02k | stat_str = ForCycleNameToString(stat.forcyclename()); |
364 | 2.02k | break; |
365 | 36.5k | case StatType::kForcyclelist: |
366 | 36.5k | stat_str = ForCycleListToString(stat.forcyclelist()); |
367 | 36.5k | break; |
368 | 3.78k | case StatType::kFunc: |
369 | 3.78k | stat_str = FunctionToString(stat.func()); |
370 | 3.78k | break; |
371 | 2.48k | case StatType::kLocalfunc: |
372 | 2.48k | stat_str = LocalFuncToString(stat.localfunc()); |
373 | 2.48k | break; |
374 | 2.58k | case StatType::kLocalnames: |
375 | 2.58k | stat_str = LocalNamesToString(stat.localnames()); |
376 | 2.58k | break; |
377 | 20.5k | default: |
378 | | /** |
379 | | * Chosen arbitrarily more for simplicity. |
380 | | * TODO: Choose "more interesting" defaults. |
381 | | */ |
382 | 20.5k | stat_str = AssignmentListToString(stat.list()); |
383 | 20.5k | break; |
384 | 104k | } |
385 | | |
386 | 104k | if (stat.has_semicolon()) |
387 | 53.9k | stat_str += "; "; |
388 | | |
389 | 104k | return stat_str; |
390 | 104k | } |
391 | | |
392 | | /** |
393 | | * AssignmentList and nested types. |
394 | | */ |
395 | | PROTO_TOSTRING(AssignmentList, assignmentlist) |
396 | 29.0k | { |
397 | 29.0k | std::string list_str = VariableListToString(assignmentlist.varlist()); |
398 | 29.0k | list_str += " = " + ExpressionListToString(assignmentlist.explist()); |
399 | 29.0k | return list_str; |
400 | 29.0k | } |
401 | | |
402 | | NESTED_PROTO_TOSTRING(VariableList, varlist, AssignmentList) |
403 | 29.0k | { |
404 | 29.0k | std::string varlist_str = VariableToString(varlist.var()); |
405 | 43.6k | for (int i = 0; i < varlist.vars_size(); ++i) { |
406 | 14.5k | varlist_str += ", " + VariableToString(varlist.vars(i)); |
407 | 14.5k | varlist_str += " "; |
408 | 14.5k | } |
409 | 29.0k | return varlist_str; |
410 | 29.0k | } |
411 | | |
412 | | /** |
413 | | * FunctionCall and nested types. |
414 | | */ |
415 | | PROTO_TOSTRING(FunctionCall, call) |
416 | 19.8k | { |
417 | 19.8k | using FuncCallType = FunctionCall::CallOneofCase; |
418 | 19.8k | switch (call.call_oneof_case()) { |
419 | 8.63k | case FuncCallType::kPrefArgs: |
420 | 8.63k | return PrefixArgsToString(call.prefargs()); |
421 | 3.86k | case FuncCallType::kNamedArgs: |
422 | 3.86k | return PrefixNamedArgsToString(call.namedargs()); |
423 | 7.34k | default: |
424 | | /* Chosen for more variability of generated programs. */ |
425 | 7.34k | return PrefixNamedArgsToString(call.namedargs()); |
426 | 19.8k | } |
427 | 19.8k | } |
428 | | |
429 | | NESTED_PROTO_TOSTRING(Args, args, FunctionCall) |
430 | 19.8k | { |
431 | 19.8k | using ArgsType = FunctionCall::Args::ArgsOneofCase; |
432 | 19.8k | switch (args.args_oneof_case()) { |
433 | 6.72k | case ArgsType::kExplist: |
434 | 6.72k | return "(" + OptionalExpressionListToString(args.explist()) + |
435 | 6.72k | ")"; |
436 | 771 | case ArgsType::kTableconstructor: |
437 | 771 | return TableConstructorToString(args.tableconstructor()); |
438 | 958 | case ArgsType::kStr: |
439 | 958 | return "'" + ConvertToStringDefault(args.str()) + "'"; |
440 | 11.3k | default: |
441 | | /* For more variability. */ |
442 | 11.3k | return TableConstructorToString(args.tableconstructor()); |
443 | 19.8k | } |
444 | 19.8k | } |
445 | | |
446 | | NESTED_PROTO_TOSTRING(PrefixArgs, prefixargs, FunctionCall) |
447 | 8.63k | { |
448 | 8.63k | std::string prefixargs_str = PrefixExpressionToString( |
449 | 8.63k | prefixargs.prefixexp()); |
450 | 8.63k | prefixargs_str += " " + ArgsToString(prefixargs.args()); |
451 | 8.63k | return prefixargs_str; |
452 | 8.63k | } |
453 | | |
454 | | NESTED_PROTO_TOSTRING(PrefixNamedArgs, prefixnamedargs, FunctionCall) |
455 | 11.2k | { |
456 | 11.2k | std::string predixnamedargs_str = PrefixExpressionToString( |
457 | 11.2k | prefixnamedargs.prefixexp()); |
458 | 11.2k | predixnamedargs_str += ":" + NameToString(prefixnamedargs.name()); |
459 | 11.2k | predixnamedargs_str += " " + ArgsToString(prefixnamedargs.args()); |
460 | 11.2k | return predixnamedargs_str; |
461 | 11.2k | } |
462 | | |
463 | | /** |
464 | | * DoBlock clause. |
465 | | */ |
466 | | PROTO_TOSTRING(DoBlock, block) |
467 | 1.63k | { |
468 | 1.63k | return "do\n" + BlockToString(block.block()) + "end\n"; |
469 | 1.63k | } |
470 | | |
471 | | /** |
472 | | * WhileCycle clause. |
473 | | */ |
474 | | PROTO_TOSTRING(WhileCycle, whilecycle) |
475 | 8.70k | { |
476 | 8.70k | const auto id = GetCounterIdProvider().next(); |
477 | 8.70k | auto counter_name = GetCounterName(id); |
478 | | |
479 | 8.70k | std::string whilecycle_str = "while "; |
480 | 8.70k | whilecycle_str += ExpressionToString(whilecycle.condition()); |
481 | 8.70k | whilecycle_str += " "; |
482 | 8.70k | whilecycle_str += DoBlockToStringCycleProtected(whilecycle.doblock(), |
483 | 8.70k | counter_name); |
484 | | |
485 | 8.70k | return whilecycle_str; |
486 | 8.70k | } |
487 | | |
488 | | /** |
489 | | * RepeatCycle clause. |
490 | | */ |
491 | | PROTO_TOSTRING(RepeatCycle, repeatcycle) |
492 | 6.25k | { |
493 | 6.25k | const auto id = GetCounterIdProvider().next(); |
494 | 6.25k | auto counter_name = GetCounterName(id); |
495 | | |
496 | 6.25k | std::string repeatcycle_str = "repeat\n"; |
497 | 6.25k | repeatcycle_str += BlockToStringCycleProtected(repeatcycle.block(), |
498 | 6.25k | counter_name); |
499 | 6.25k | repeatcycle_str += "until "; |
500 | 6.25k | repeatcycle_str += ExpressionToString(repeatcycle.condition()); |
501 | | |
502 | 6.25k | return repeatcycle_str; |
503 | 6.25k | } |
504 | | |
505 | | /** |
506 | | * IfStatement and nested types. |
507 | | */ |
508 | | PROTO_TOSTRING(IfStatement, statement) |
509 | 4.52k | { |
510 | 4.52k | std::string statement_str = "if " + |
511 | 4.52k | ExpressionToString(statement.condition()); |
512 | 4.52k | statement_str += " then\n\t" + BlockToString(statement.first()); |
513 | | |
514 | 6.13k | for (int i = 0; i < statement.clauses_size(); ++i) |
515 | 1.60k | statement_str += ElseIfBlockToString(statement.clauses(i)); |
516 | | |
517 | 4.52k | if (statement.has_last()) |
518 | 2.17k | statement_str += "else\n\t" + BlockToString(statement.last()); |
519 | | |
520 | 4.52k | statement_str += "end\n"; |
521 | 4.52k | return statement_str; |
522 | 4.52k | } |
523 | | |
524 | | NESTED_PROTO_TOSTRING(ElseIfBlock, elseifblock, IfStatement) |
525 | 1.60k | { |
526 | 1.60k | std::string elseifblock_str = "else if "; |
527 | 1.60k | elseifblock_str += ExpressionToString(elseifblock.condition()); |
528 | 1.60k | elseifblock_str += " then\n\t"; |
529 | 1.60k | elseifblock_str += BlockToString(elseifblock.block()); |
530 | 1.60k | return elseifblock_str; |
531 | 1.60k | } |
532 | | |
533 | | /** |
534 | | * ForCycleName clause. |
535 | | * TODO: In 'for i = start, stop, step' construction start, stop, step |
536 | | * should be numbers. So results of the corresponding expressions |
537 | | * should be number. |
538 | | */ |
539 | | PROTO_TOSTRING(ForCycleName, forcyclename) |
540 | 2.02k | { |
541 | 2.02k | const auto id = GetCounterIdProvider().next(); |
542 | 2.02k | auto counter_name = GetCounterName(id); |
543 | | |
544 | 2.02k | std::string forcyclename_str = "for "; |
545 | 2.02k | forcyclename_str += NameToString(forcyclename.name()); |
546 | 2.02k | forcyclename_str += " = "; |
547 | 2.02k | forcyclename_str += ExpressionToString(forcyclename.startexp()); |
548 | 2.02k | forcyclename_str += ", "; |
549 | 2.02k | forcyclename_str += ExpressionToString(forcyclename.stopexp()); |
550 | | |
551 | 2.02k | if (forcyclename.has_stepexp()) |
552 | 1.02k | forcyclename_str += ", " + ExpressionToString( |
553 | 1.02k | forcyclename.stepexp()); |
554 | | |
555 | 2.02k | forcyclename_str += " "; |
556 | 2.02k | forcyclename_str += DoBlockToStringCycleProtected( |
557 | 2.02k | forcyclename.doblock(), counter_name); |
558 | 2.02k | return forcyclename_str; |
559 | 2.02k | } |
560 | | |
561 | | /** |
562 | | * ForCycleList clause. |
563 | | */ |
564 | | PROTO_TOSTRING(ForCycleList, forcyclelist) |
565 | 36.5k | { |
566 | 36.5k | const auto id = GetCounterIdProvider().next(); |
567 | 36.5k | auto counter_name = GetCounterName(id); |
568 | | |
569 | 36.5k | std::string forcyclelist_str = "for "; |
570 | 36.5k | forcyclelist_str += NameListToString(forcyclelist.names()); |
571 | 36.5k | forcyclelist_str += " in "; |
572 | 36.5k | forcyclelist_str += ExpressionListToString(forcyclelist.expressions()); |
573 | 36.5k | forcyclelist_str += " "; |
574 | 36.5k | forcyclelist_str += DoBlockToStringCycleProtected( |
575 | 36.5k | forcyclelist.doblock(), counter_name); |
576 | 36.5k | return forcyclelist_str; |
577 | 36.5k | } |
578 | | |
579 | | /** |
580 | | * Function and nested types. |
581 | | */ |
582 | | PROTO_TOSTRING(Function, func) |
583 | 3.78k | { |
584 | 3.78k | const auto id = GetCounterIdProvider().next(); |
585 | 3.78k | auto counter_name = GetCounterName(id); |
586 | | |
587 | 3.78k | std::string func_str = "function "; |
588 | 3.78k | func_str += FuncNameToString(func.name()); |
589 | 3.78k | func_str += FuncBodyToStringReqProtected(func.body(), counter_name); |
590 | | |
591 | 3.78k | return func_str; |
592 | 3.78k | } |
593 | | |
594 | | NESTED_PROTO_TOSTRING(FuncName, funcname, Function) |
595 | 3.78k | { |
596 | 3.78k | std::string funcname_str = NameToString(funcname.firstname()); |
597 | | |
598 | 4.87k | for (int i = 0; i < funcname.names_size(); ++i) |
599 | 1.09k | funcname_str += "." + NameToString(funcname.names(i)); |
600 | | |
601 | 3.78k | if (funcname.has_lastname()) |
602 | 231 | funcname_str += ":" + NameToString(funcname.lastname()); |
603 | | |
604 | 3.78k | return funcname_str; |
605 | 3.78k | } |
606 | | |
607 | | PROTO_TOSTRING(NameList, namelist) |
608 | 41.0k | { |
609 | 41.0k | std::string namelist_str = NameToString(namelist.firstname()); |
610 | 54.8k | for (int i = 0; i < namelist.names_size(); ++i) |
611 | 13.8k | namelist_str += ", " + NameToString(namelist.names(i)); |
612 | 41.0k | return namelist_str; |
613 | 41.0k | } |
614 | | |
615 | | NESTED_PROTO_TOSTRING(NameListWithEllipsis, namelist, FuncBody) |
616 | 1.93k | { |
617 | 1.93k | std::string namelist_str = NameListToString(namelist.namelist()); |
618 | 1.93k | if (namelist.has_ellipsis()) |
619 | 374 | namelist_str += ", ..."; |
620 | 1.93k | return namelist_str; |
621 | 1.93k | } |
622 | | |
623 | | NESTED_PROTO_TOSTRING(ParList, parlist, FuncBody) |
624 | 3.51k | { |
625 | 3.51k | using ParListType = FuncBody::ParList::ParlistOneofCase; |
626 | 3.51k | switch (parlist.parlist_oneof_case()) { |
627 | 742 | case ParListType::kNamelist: |
628 | 742 | return NameListWithEllipsisToString(parlist.namelist()); |
629 | 1.58k | case ParListType::kEllipsis: |
630 | 1.58k | return "..."; |
631 | 1.19k | default: |
632 | | /* Chosen as default in order to decrease number of ellipses. */ |
633 | 1.19k | return NameListWithEllipsisToString(parlist.namelist()); |
634 | 3.51k | } |
635 | 3.51k | } |
636 | | |
637 | | /** |
638 | | * LocalFunc clause. |
639 | | */ |
640 | | PROTO_TOSTRING(LocalFunc, localfunc) |
641 | 2.48k | { |
642 | 2.48k | const auto id = GetCounterIdProvider().next(); |
643 | 2.48k | auto counter_name = GetCounterName(id); |
644 | | |
645 | 2.48k | std::string localfunc_str = "local function "; |
646 | 2.48k | localfunc_str += NameToString(localfunc.name()); |
647 | 2.48k | localfunc_str += " "; |
648 | 2.48k | localfunc_str += FuncBodyToStringReqProtected(localfunc.funcbody(), |
649 | 2.48k | counter_name); |
650 | | |
651 | 2.48k | return localfunc_str; |
652 | 2.48k | } |
653 | | |
654 | | /** |
655 | | * LocalNames clause. |
656 | | */ |
657 | | PROTO_TOSTRING(LocalNames, localnames) |
658 | 2.58k | { |
659 | 2.58k | std::string localnames_str = "local "; |
660 | 2.58k | localnames_str += NameListToString(localnames.namelist()); |
661 | | |
662 | 2.58k | if (localnames.has_explist()) |
663 | 954 | localnames_str += " = " + ExpressionListToString( |
664 | 954 | localnames.explist()); |
665 | 2.58k | return localnames_str; |
666 | 2.58k | } |
667 | | |
668 | | /** |
669 | | * Expressions and variables. |
670 | | */ |
671 | | |
672 | | /** |
673 | | * Expressions clauses. |
674 | | */ |
675 | | PROTO_TOSTRING(ExpressionList, explist) |
676 | 77.4k | { |
677 | 77.4k | std::string explist_str; |
678 | 124k | for (int i = 0; i < explist.expressions_size(); ++i) |
679 | 46.8k | explist_str += ExpressionToString(explist.expressions(i)) + |
680 | 46.8k | ", "; |
681 | 77.4k | explist_str += ExpressionToString(explist.explast()) + " "; |
682 | 77.4k | return explist_str; |
683 | 77.4k | } |
684 | | |
685 | | PROTO_TOSTRING(OptionalExpressionList, explist) |
686 | 6.72k | { |
687 | 6.72k | if (explist.has_explist()) |
688 | 6.57k | return ExpressionListToString(explist.explist()); |
689 | 150 | return ""; |
690 | 6.72k | } |
691 | | |
692 | | PROTO_TOSTRING(PrefixExpression, prefixexp) |
693 | 61.2k | { |
694 | 61.2k | using PrefExprType = PrefixExpression::PrefixOneofCase; |
695 | 61.2k | switch (prefixexp.prefix_oneof_case()) { |
696 | 8.38k | case PrefExprType::kVar: |
697 | 8.38k | return VariableToString(prefixexp.var()); |
698 | 12.5k | case PrefExprType::kFunctioncall: |
699 | 12.5k | return FunctionCallToString(prefixexp.functioncall()); |
700 | 6.86k | case PrefExprType::kExp: |
701 | 6.86k | return "(" + ExpressionToString(prefixexp.exp()) + ")"; |
702 | 33.4k | default: |
703 | | /* |
704 | | * Can be generated too nested expressions with other options, |
705 | | * though they can be enabled for more variable fuzzing. |
706 | | */ |
707 | 33.4k | return VariableToString(prefixexp.var()); |
708 | 61.2k | } |
709 | 61.2k | } |
710 | | |
711 | | /** |
712 | | * Variable and nested types. |
713 | | */ |
714 | | PROTO_TOSTRING(Variable, var) |
715 | 85.4k | { |
716 | 85.4k | using VarType = Variable::VarOneofCase; |
717 | 85.4k | switch (var.var_oneof_case()) { |
718 | 2.13k | case VarType::kName: |
719 | 2.13k | return NameToString(var.name()); |
720 | 6.96k | case VarType::kIndexexpr: |
721 | 6.96k | return IndexWithExpressionToString(var.indexexpr()); |
722 | 7.94k | case VarType::kIndexname: |
723 | 7.94k | return IndexWithNameToString(var.indexname()); |
724 | 68.4k | default: |
725 | | /* |
726 | | * Can be generated too nested expressions with other options, |
727 | | * though they can be enabled for more variable fuzzing. |
728 | | */ |
729 | 68.4k | return NameToString(var.name()); |
730 | 85.4k | } |
731 | 85.4k | } |
732 | | |
733 | | NESTED_PROTO_TOSTRING(IndexWithExpression, indexexpr, Variable) |
734 | 6.96k | { |
735 | 6.96k | std::string indexexpr_str = PrefixExpressionToString( |
736 | 6.96k | indexexpr.prefixexp()); |
737 | 6.96k | indexexpr_str += "[" + ExpressionToString(indexexpr.exp()) + "]"; |
738 | 6.96k | return indexexpr_str; |
739 | 6.96k | } |
740 | | |
741 | | NESTED_PROTO_TOSTRING(IndexWithName, indexname, Variable) |
742 | 7.94k | { |
743 | 7.94k | std::string indexname_str = PrefixExpressionToString( |
744 | 7.94k | indexname.prefixexp()); |
745 | 7.94k | indexname_str += "." + ConvertToStringDefault(indexname.name()); |
746 | 7.94k | return indexname_str; |
747 | 7.94k | } |
748 | | |
749 | | /** |
750 | | * Expression and nested types. |
751 | | */ |
752 | | PROTO_TOSTRING(Expression, expr) |
753 | 269k | { |
754 | 269k | using ExprType = Expression::ExprOneofCase; |
755 | 269k | switch (expr.expr_oneof_case()) { |
756 | 6.10k | case ExprType::kNil: |
757 | 6.10k | return "nil"; |
758 | 3.04k | case ExprType::kFalse: |
759 | 3.04k | return "false"; |
760 | 4.45k | case ExprType::kTrue: |
761 | 4.45k | return "true"; |
762 | 25.8k | case ExprType::kNumber: { |
763 | | /* Clamp number between given boundaries. */ |
764 | 25.8k | double number = clamp(expr.number(), kMaxNumber, kMinNumber); |
765 | 25.8k | return std::to_string(number); |
766 | 0 | } |
767 | 6.10k | case ExprType::kStr: |
768 | 6.10k | return "'" + ConvertToStringDefault(expr.str()) + "'"; |
769 | 4.91k | case ExprType::kEllipsis: |
770 | 4.91k | return " ... "; |
771 | 17.9k | case ExprType::kFunction: |
772 | 17.9k | return AnonFuncToString(expr.function()); |
773 | 26.4k | case ExprType::kPrefixexp: |
774 | 26.4k | return PrefixExpressionToString(expr.prefixexp()); |
775 | 11.0k | case ExprType::kTableconstructor: |
776 | 11.0k | return TableConstructorToString(expr.tableconstructor()); |
777 | 36.4k | case ExprType::kBinary: |
778 | 36.4k | return ExpBinaryOpExpToString(expr.binary()); |
779 | 8.96k | case ExprType::kUnary: |
780 | 8.96k | return UnaryOpExpToString(expr.unary()); |
781 | 118k | default: |
782 | | /** |
783 | | * Arbitrary choice. |
784 | | * TODO: Choose "more interesting" defaults. |
785 | | */ |
786 | 118k | return "'" + ConvertToStringDefault(expr.str()) + "'"; |
787 | 269k | } |
788 | 269k | } |
789 | | |
790 | | NESTED_PROTO_TOSTRING(AnonFunc, func, Expression) |
791 | 17.9k | { |
792 | 17.9k | const auto id = GetCounterIdProvider().next(); |
793 | 17.9k | auto counter_name = GetCounterName(id); |
794 | | |
795 | 17.9k | std::string retval = "function "; |
796 | 17.9k | retval += FuncBodyToStringReqProtected(func.body(), counter_name); |
797 | | |
798 | 17.9k | return retval; |
799 | 17.9k | } |
800 | | |
801 | | NESTED_PROTO_TOSTRING(ExpBinaryOpExp, binary, Expression) |
802 | 36.4k | { |
803 | 36.4k | std::string binary_str = ExpressionToString(binary.leftexp()); |
804 | 36.4k | binary_str += " " + BinaryOperatorToString(binary.binop()) + " "; |
805 | 36.4k | binary_str += ExpressionToString(binary.rightexp()); |
806 | 36.4k | return binary_str; |
807 | 36.4k | } |
808 | | |
809 | | NESTED_PROTO_TOSTRING(UnaryOpExp, unary, Expression) |
810 | 8.96k | { |
811 | 8.96k | std::string unary_str = UnaryOperatorToString(unary.unop()); |
812 | 8.96k | unary_str += ExpressionToString(unary.exp()); |
813 | 8.96k | return unary_str; |
814 | 8.96k | } |
815 | | |
816 | | /** |
817 | | * Tables and fields. |
818 | | */ |
819 | | PROTO_TOSTRING(TableConstructor, table) |
820 | 23.2k | { |
821 | 23.2k | std::string table_str = "{ "; |
822 | 23.2k | if (table.has_fieldlist()) |
823 | 9.38k | table_str += FieldListToString(table.fieldlist()); |
824 | 23.2k | table_str += " }"; |
825 | 23.2k | return table_str; |
826 | 23.2k | } |
827 | | |
828 | | PROTO_TOSTRING(FieldList, fieldlist) |
829 | 9.38k | { |
830 | 9.38k | std::string fieldlist_str = FieldToString(fieldlist.firstfield()); |
831 | 18.3k | for (int i = 0; i < fieldlist.fields_size(); ++i) |
832 | 8.93k | fieldlist_str += FieldWithFieldSepToString(fieldlist.fields(i)); |
833 | 9.38k | if (fieldlist.has_lastsep()) |
834 | 1.90k | fieldlist_str += FieldSepToString(fieldlist.lastsep()); |
835 | 9.38k | return fieldlist_str; |
836 | 9.38k | } |
837 | | |
838 | | NESTED_PROTO_TOSTRING(FieldWithFieldSep, field, FieldList) |
839 | 8.93k | { |
840 | 8.93k | std::string field_str = FieldSepToString(field.sep()); |
841 | 8.93k | field_str += " " + FieldToString(field.field()); |
842 | 8.93k | return field_str; |
843 | 8.93k | } |
844 | | |
845 | | /** |
846 | | * Field and nested types. |
847 | | */ |
848 | | PROTO_TOSTRING(Field, field) |
849 | 18.3k | { |
850 | 18.3k | using FieldType = Field::FieldOneofCase; |
851 | 18.3k | switch (field.field_oneof_case()) { |
852 | 5.47k | case FieldType::kExprassign: |
853 | 5.47k | return ExpressionAssignmentToString(field.exprassign()); |
854 | 783 | case FieldType::kNamedassign: |
855 | 783 | return NameAssignmentToString(field.namedassign()); |
856 | 5.70k | case FieldType::kExpression: |
857 | 5.70k | return ExpressionToString(field.expression()); |
858 | 6.36k | default: |
859 | | /* More common case of using fields. */ |
860 | 6.36k | return NameAssignmentToString(field.namedassign()); |
861 | 18.3k | } |
862 | 18.3k | } |
863 | | |
864 | | NESTED_PROTO_TOSTRING(ExpressionAssignment, assignment, Field) |
865 | 5.47k | { |
866 | 5.47k | std::string assignment_str = "[ " + |
867 | 5.47k | ExpressionToString(assignment.key()) + " ]"; |
868 | 5.47k | assignment_str += " = " + ExpressionToString(assignment.value()); |
869 | 5.47k | return assignment_str; |
870 | 5.47k | } |
871 | | |
872 | | NESTED_PROTO_TOSTRING(NameAssignment, assignment, Field) |
873 | 7.14k | { |
874 | 7.14k | std::string assignment_str = NameToString(assignment.name()); |
875 | 7.14k | assignment_str += " = " + ExpressionToString(assignment.value()); |
876 | 7.14k | return assignment_str; |
877 | 7.14k | } |
878 | | |
879 | | PROTO_TOSTRING(FieldSep, sep) |
880 | 10.8k | { |
881 | 10.8k | using FieldSepType = FieldSep::SepOneofCase; |
882 | 10.8k | switch (sep.sep_oneof_case()) { |
883 | 2.26k | case FieldSepType::kComma: |
884 | 2.26k | return ","; |
885 | 1.38k | case FieldSepType::kSemicolon: |
886 | 1.38k | return ";"; |
887 | 7.18k | default: |
888 | 7.18k | return ","; |
889 | 10.8k | } |
890 | 10.8k | } |
891 | | |
892 | | /** |
893 | | * Operators. |
894 | | */ |
895 | | PROTO_TOSTRING(BinaryOperator, op) |
896 | 36.4k | { |
897 | 36.4k | using BinopType = BinaryOperator::BinaryOneofCase; |
898 | 36.4k | switch (op.binary_oneof_case()) { |
899 | 1.17k | case BinopType::kAdd: |
900 | 1.17k | return "+"; |
901 | 2.33k | case BinopType::kSub: |
902 | 2.33k | return "-"; |
903 | 8.97k | case BinopType::kMult: |
904 | 8.97k | return "*"; |
905 | 1.26k | case BinopType::kDiv: |
906 | 1.26k | return "/"; |
907 | 873 | case BinopType::kExp: |
908 | 873 | return "^"; |
909 | 1.44k | case BinopType::kMod: |
910 | 1.44k | return "%"; |
911 | | |
912 | 3.35k | case BinopType::kConcat: |
913 | 3.35k | return ".."; |
914 | | |
915 | 905 | case BinopType::kLess: |
916 | 905 | return "<"; |
917 | 752 | case BinopType::kLessEqual: |
918 | 752 | return "<="; |
919 | 762 | case BinopType::kGreater: |
920 | 762 | return ">"; |
921 | 927 | case BinopType::kGreaterEqual: |
922 | 927 | return ">="; |
923 | 784 | case BinopType::kEqual: |
924 | 784 | return "=="; |
925 | 622 | case BinopType::kNotEqual: |
926 | 622 | return "~="; |
927 | 2.08k | case BinopType::kAnd: |
928 | 2.08k | return "and"; |
929 | 4.08k | case BinopType::kOr: |
930 | 4.08k | return "or"; |
931 | 6.07k | default: |
932 | | /* Works in most cases. */ |
933 | 6.07k | return "=="; |
934 | 36.4k | } |
935 | 36.4k | } |
936 | | |
937 | | PROTO_TOSTRING(UnaryOperator, op) |
938 | 8.96k | { |
939 | 8.96k | using UnaryopType = UnaryOperator::UnaryOneofCase; |
940 | 8.96k | switch (op.unary_oneof_case()) { |
941 | 1.32k | case UnaryopType::kNegate: |
942 | 1.32k | return "-"; |
943 | 435 | case UnaryopType::kNot: |
944 | 435 | return "not "; |
945 | 4.72k | case UnaryopType::kLength: |
946 | 4.72k | return "#"; |
947 | 2.48k | default: |
948 | | /* Works in most cases. */ |
949 | 2.48k | return "not "; |
950 | 8.96k | } |
951 | 8.96k | } |
952 | | |
953 | | /** |
954 | | * Identifier (Name). |
955 | | */ |
956 | | PROTO_TOSTRING(Name, name) |
957 | 153k | { |
958 | 153k | std::string ident = ConvertToStringDefault(name.name()); |
959 | 153k | return ident + std::to_string(name.num() % kMaxIdentifiers); |
960 | 153k | } |
961 | | |
962 | | } /* namespace */ |
963 | | |
964 | | std::string |
965 | | MainBlockToString(const Block &block) |
966 | 2.58k | { |
967 | 2.58k | GetCounterIdProvider().clean(); |
968 | | |
969 | 2.58k | std::string block_str = BlockToString(block); |
970 | 2.58k | std::string retval; |
971 | | |
972 | 80.3k | for (size_t i = 0; i < GetCounterIdProvider().count(); ++i) { |
973 | 77.7k | retval += GetCounterName(i); |
974 | 77.7k | retval += " = 0\n"; |
975 | 77.7k | } |
976 | 2.58k | retval += block_str; |
977 | | |
978 | 2.58k | return retval; |
979 | 2.58k | } |
980 | | |
981 | | } /* namespace luajit_fuzzer */ |