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