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