1 /*************************************************************************/
2 /* test_gdscript.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #include "test_gdscript.h"
32
33 #include "core/os/file_access.h"
34 #include "core/os/main_loop.h"
35 #include "core/os/os.h"
36
37 #ifdef GDSCRIPT_ENABLED
38
39 #include "modules/gdscript/gdscript.h"
40 #include "modules/gdscript/gdscript_compiler.h"
41 #include "modules/gdscript/gdscript_parser.h"
42 #include "modules/gdscript/gdscript_tokenizer.h"
43
44 namespace TestGDScript {
45
_print_indent(int p_ident,const String & p_text)46 static void _print_indent(int p_ident, const String &p_text) {
47
48 String txt;
49 for (int i = 0; i < p_ident; i++) {
50 txt += '\t';
51 }
52
53 print_line(txt + p_text);
54 }
55
_parser_extends(const GDScriptParser::ClassNode * p_class)56 static String _parser_extends(const GDScriptParser::ClassNode *p_class) {
57
58 String txt = "extends ";
59 if (String(p_class->extends_file) != "") {
60 txt += "\"" + p_class->extends_file + "\"";
61 if (p_class->extends_class.size())
62 txt += ".";
63 }
64
65 for (int i = 0; i < p_class->extends_class.size(); i++) {
66
67 if (i != 0)
68 txt += ".";
69
70 txt += p_class->extends_class[i];
71 }
72
73 return txt;
74 }
75
_parser_expr(const GDScriptParser::Node * p_expr)76 static String _parser_expr(const GDScriptParser::Node *p_expr) {
77
78 String txt;
79 switch (p_expr->type) {
80
81 case GDScriptParser::Node::TYPE_IDENTIFIER: {
82
83 const GDScriptParser::IdentifierNode *id_node = static_cast<const GDScriptParser::IdentifierNode *>(p_expr);
84 txt = id_node->name;
85 } break;
86 case GDScriptParser::Node::TYPE_CONSTANT: {
87 const GDScriptParser::ConstantNode *c_node = static_cast<const GDScriptParser::ConstantNode *>(p_expr);
88 if (c_node->value.get_type() == Variant::STRING)
89 txt = "\"" + String(c_node->value) + "\"";
90 else
91 txt = c_node->value;
92
93 } break;
94 case GDScriptParser::Node::TYPE_SELF: {
95 txt = "self";
96 } break;
97 case GDScriptParser::Node::TYPE_ARRAY: {
98 const GDScriptParser::ArrayNode *arr_node = static_cast<const GDScriptParser::ArrayNode *>(p_expr);
99 txt += "[";
100 for (int i = 0; i < arr_node->elements.size(); i++) {
101
102 if (i > 0)
103 txt += ", ";
104 txt += _parser_expr(arr_node->elements[i]);
105 }
106 txt += "]";
107 } break;
108 case GDScriptParser::Node::TYPE_DICTIONARY: {
109 const GDScriptParser::DictionaryNode *dict_node = static_cast<const GDScriptParser::DictionaryNode *>(p_expr);
110 txt += "{";
111 for (int i = 0; i < dict_node->elements.size(); i++) {
112
113 if (i > 0)
114 txt += ", ";
115
116 const GDScriptParser::DictionaryNode::Pair &p = dict_node->elements[i];
117 txt += _parser_expr(p.key);
118 txt += ":";
119 txt += _parser_expr(p.value);
120 }
121 txt += "}";
122 } break;
123 case GDScriptParser::Node::TYPE_OPERATOR: {
124
125 const GDScriptParser::OperatorNode *c_node = static_cast<const GDScriptParser::OperatorNode *>(p_expr);
126 switch (c_node->op) {
127
128 case GDScriptParser::OperatorNode::OP_PARENT_CALL:
129 txt += ".";
130 FALLTHROUGH;
131 case GDScriptParser::OperatorNode::OP_CALL: {
132
133 ERR_FAIL_COND_V(c_node->arguments.size() < 1, "");
134 String func_name;
135 const GDScriptParser::Node *nfunc = c_node->arguments[0];
136 int arg_ofs = 0;
137 if (nfunc->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
138
139 const GDScriptParser::BuiltInFunctionNode *bif_node = static_cast<const GDScriptParser::BuiltInFunctionNode *>(nfunc);
140 func_name = GDScriptFunctions::get_func_name(bif_node->function);
141 arg_ofs = 1;
142 } else if (nfunc->type == GDScriptParser::Node::TYPE_TYPE) {
143
144 const GDScriptParser::TypeNode *t_node = static_cast<const GDScriptParser::TypeNode *>(nfunc);
145 func_name = Variant::get_type_name(t_node->vtype);
146 arg_ofs = 1;
147 } else {
148
149 ERR_FAIL_COND_V(c_node->arguments.size() < 2, "");
150 nfunc = c_node->arguments[1];
151 ERR_FAIL_COND_V(nfunc->type != GDScriptParser::Node::TYPE_IDENTIFIER, "");
152
153 if (c_node->arguments[0]->type != GDScriptParser::Node::TYPE_SELF)
154 func_name = _parser_expr(c_node->arguments[0]) + ".";
155
156 func_name += _parser_expr(nfunc);
157 arg_ofs = 2;
158 }
159
160 txt += func_name + "(";
161
162 for (int i = arg_ofs; i < c_node->arguments.size(); i++) {
163
164 const GDScriptParser::Node *arg = c_node->arguments[i];
165 if (i > arg_ofs)
166 txt += ", ";
167 txt += _parser_expr(arg);
168 }
169
170 txt += ")";
171
172 } break;
173 case GDScriptParser::OperatorNode::OP_INDEX: {
174
175 ERR_FAIL_COND_V(c_node->arguments.size() != 2, "");
176
177 //index with []
178 txt = _parser_expr(c_node->arguments[0]) + "[" + _parser_expr(c_node->arguments[1]) + "]";
179
180 } break;
181 case GDScriptParser::OperatorNode::OP_INDEX_NAMED: {
182
183 ERR_FAIL_COND_V(c_node->arguments.size() != 2, "");
184
185 txt = _parser_expr(c_node->arguments[0]) + "." + _parser_expr(c_node->arguments[1]);
186
187 } break;
188 case GDScriptParser::OperatorNode::OP_NEG: {
189 txt = "-" + _parser_expr(c_node->arguments[0]);
190 } break;
191 case GDScriptParser::OperatorNode::OP_NOT: {
192 txt = "not " + _parser_expr(c_node->arguments[0]);
193 } break;
194 case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
195 txt = "~" + _parser_expr(c_node->arguments[0]);
196 } break;
197 case GDScriptParser::OperatorNode::OP_IN: {
198 txt = _parser_expr(c_node->arguments[0]) + " in " + _parser_expr(c_node->arguments[1]);
199 } break;
200 case GDScriptParser::OperatorNode::OP_EQUAL: {
201 txt = _parser_expr(c_node->arguments[0]) + "==" + _parser_expr(c_node->arguments[1]);
202 } break;
203 case GDScriptParser::OperatorNode::OP_NOT_EQUAL: {
204 txt = _parser_expr(c_node->arguments[0]) + "!=" + _parser_expr(c_node->arguments[1]);
205 } break;
206 case GDScriptParser::OperatorNode::OP_LESS: {
207 txt = _parser_expr(c_node->arguments[0]) + "<" + _parser_expr(c_node->arguments[1]);
208 } break;
209 case GDScriptParser::OperatorNode::OP_LESS_EQUAL: {
210 txt = _parser_expr(c_node->arguments[0]) + "<=" + _parser_expr(c_node->arguments[1]);
211 } break;
212 case GDScriptParser::OperatorNode::OP_GREATER: {
213 txt = _parser_expr(c_node->arguments[0]) + ">" + _parser_expr(c_node->arguments[1]);
214 } break;
215 case GDScriptParser::OperatorNode::OP_GREATER_EQUAL: {
216 txt = _parser_expr(c_node->arguments[0]) + ">=" + _parser_expr(c_node->arguments[1]);
217 } break;
218 case GDScriptParser::OperatorNode::OP_AND: {
219 txt = _parser_expr(c_node->arguments[0]) + " and " + _parser_expr(c_node->arguments[1]);
220 } break;
221 case GDScriptParser::OperatorNode::OP_OR: {
222 txt = _parser_expr(c_node->arguments[0]) + " or " + _parser_expr(c_node->arguments[1]);
223 } break;
224 case GDScriptParser::OperatorNode::OP_ADD: {
225 txt = _parser_expr(c_node->arguments[0]) + "+" + _parser_expr(c_node->arguments[1]);
226 } break;
227 case GDScriptParser::OperatorNode::OP_SUB: {
228 txt = _parser_expr(c_node->arguments[0]) + "-" + _parser_expr(c_node->arguments[1]);
229 } break;
230 case GDScriptParser::OperatorNode::OP_MUL: {
231 txt = _parser_expr(c_node->arguments[0]) + "*" + _parser_expr(c_node->arguments[1]);
232 } break;
233 case GDScriptParser::OperatorNode::OP_DIV: {
234 txt = _parser_expr(c_node->arguments[0]) + "/" + _parser_expr(c_node->arguments[1]);
235 } break;
236 case GDScriptParser::OperatorNode::OP_MOD: {
237 txt = _parser_expr(c_node->arguments[0]) + "%" + _parser_expr(c_node->arguments[1]);
238 } break;
239 case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: {
240 txt = _parser_expr(c_node->arguments[0]) + "<<" + _parser_expr(c_node->arguments[1]);
241 } break;
242 case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: {
243 txt = _parser_expr(c_node->arguments[0]) + ">>" + _parser_expr(c_node->arguments[1]);
244 } break;
245 case GDScriptParser::OperatorNode::OP_ASSIGN: {
246 txt = _parser_expr(c_node->arguments[0]) + "=" + _parser_expr(c_node->arguments[1]);
247 } break;
248 case GDScriptParser::OperatorNode::OP_ASSIGN_ADD: {
249 txt = _parser_expr(c_node->arguments[0]) + "+=" + _parser_expr(c_node->arguments[1]);
250 } break;
251 case GDScriptParser::OperatorNode::OP_ASSIGN_SUB: {
252 txt = _parser_expr(c_node->arguments[0]) + "-=" + _parser_expr(c_node->arguments[1]);
253 } break;
254 case GDScriptParser::OperatorNode::OP_ASSIGN_MUL: {
255 txt = _parser_expr(c_node->arguments[0]) + "*=" + _parser_expr(c_node->arguments[1]);
256 } break;
257 case GDScriptParser::OperatorNode::OP_ASSIGN_DIV: {
258 txt = _parser_expr(c_node->arguments[0]) + "/=" + _parser_expr(c_node->arguments[1]);
259 } break;
260 case GDScriptParser::OperatorNode::OP_ASSIGN_MOD: {
261 txt = _parser_expr(c_node->arguments[0]) + "%=" + _parser_expr(c_node->arguments[1]);
262 } break;
263 case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT: {
264 txt = _parser_expr(c_node->arguments[0]) + "<<=" + _parser_expr(c_node->arguments[1]);
265 } break;
266 case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: {
267 txt = _parser_expr(c_node->arguments[0]) + ">>=" + _parser_expr(c_node->arguments[1]);
268 } break;
269 case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_AND: {
270 txt = _parser_expr(c_node->arguments[0]) + "&=" + _parser_expr(c_node->arguments[1]);
271 } break;
272 case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_OR: {
273 txt = _parser_expr(c_node->arguments[0]) + "|=" + _parser_expr(c_node->arguments[1]);
274 } break;
275 case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_XOR: {
276 txt = _parser_expr(c_node->arguments[0]) + "^=" + _parser_expr(c_node->arguments[1]);
277 } break;
278 case GDScriptParser::OperatorNode::OP_BIT_AND: {
279 txt = _parser_expr(c_node->arguments[0]) + "&" + _parser_expr(c_node->arguments[1]);
280 } break;
281 case GDScriptParser::OperatorNode::OP_BIT_OR: {
282 txt = _parser_expr(c_node->arguments[0]) + "|" + _parser_expr(c_node->arguments[1]);
283 } break;
284 case GDScriptParser::OperatorNode::OP_BIT_XOR: {
285 txt = _parser_expr(c_node->arguments[0]) + "^" + _parser_expr(c_node->arguments[1]);
286 } break;
287 default: {
288 }
289 }
290
291 } break;
292 case GDScriptParser::Node::TYPE_CAST: {
293 const GDScriptParser::CastNode *cast_node = static_cast<const GDScriptParser::CastNode *>(p_expr);
294 txt = _parser_expr(cast_node->source_node) + " as " + cast_node->cast_type.to_string();
295
296 } break;
297 case GDScriptParser::Node::TYPE_NEWLINE: {
298
299 //skippie
300 } break;
301 default: {
302
303 ERR_FAIL_V_MSG("", "Parser bug at " + itos(p_expr->line) + ", invalid expression type: " + itos(p_expr->type));
304 }
305 }
306
307 return txt;
308 }
309
_parser_show_block(const GDScriptParser::BlockNode * p_block,int p_indent)310 static void _parser_show_block(const GDScriptParser::BlockNode *p_block, int p_indent) {
311
312 for (int i = 0; i < p_block->statements.size(); i++) {
313
314 const GDScriptParser::Node *statement = p_block->statements[i];
315
316 switch (statement->type) {
317
318 case GDScriptParser::Node::TYPE_CONTROL_FLOW: {
319
320 const GDScriptParser::ControlFlowNode *cf_node = static_cast<const GDScriptParser::ControlFlowNode *>(statement);
321 switch (cf_node->cf_type) {
322
323 case GDScriptParser::ControlFlowNode::CF_IF: {
324
325 ERR_FAIL_COND(cf_node->arguments.size() != 1);
326 String txt;
327 txt += "if ";
328 txt += _parser_expr(cf_node->arguments[0]);
329 txt += ":";
330 _print_indent(p_indent, txt);
331 ERR_FAIL_COND(!cf_node->body);
332 _parser_show_block(cf_node->body, p_indent + 1);
333 if (cf_node->body_else) {
334 _print_indent(p_indent, "else:");
335 _parser_show_block(cf_node->body_else, p_indent + 1);
336 }
337
338 } break;
339 case GDScriptParser::ControlFlowNode::CF_FOR: {
340 ERR_FAIL_COND(cf_node->arguments.size() != 2);
341 String txt;
342 txt += "for ";
343 txt += _parser_expr(cf_node->arguments[0]);
344 txt += " in ";
345 txt += _parser_expr(cf_node->arguments[1]);
346 txt += ":";
347 _print_indent(p_indent, txt);
348 ERR_FAIL_COND(!cf_node->body);
349 _parser_show_block(cf_node->body, p_indent + 1);
350
351 } break;
352 case GDScriptParser::ControlFlowNode::CF_WHILE: {
353
354 ERR_FAIL_COND(cf_node->arguments.size() != 1);
355 String txt;
356 txt += "while ";
357 txt += _parser_expr(cf_node->arguments[0]);
358 txt += ":";
359 _print_indent(p_indent, txt);
360 ERR_FAIL_COND(!cf_node->body);
361 _parser_show_block(cf_node->body, p_indent + 1);
362
363 } break;
364 case GDScriptParser::ControlFlowNode::CF_MATCH: {
365 // FIXME: Implement
366 } break;
367 case GDScriptParser::ControlFlowNode::CF_CONTINUE: {
368
369 _print_indent(p_indent, "continue");
370 } break;
371 case GDScriptParser::ControlFlowNode::CF_BREAK: {
372
373 _print_indent(p_indent, "break");
374 } break;
375 case GDScriptParser::ControlFlowNode::CF_RETURN: {
376
377 if (cf_node->arguments.size())
378 _print_indent(p_indent, "return " + _parser_expr(cf_node->arguments[0]));
379 else
380 _print_indent(p_indent, "return ");
381 } break;
382 }
383
384 } break;
385 case GDScriptParser::Node::TYPE_LOCAL_VAR: {
386
387 const GDScriptParser::LocalVarNode *lv_node = static_cast<const GDScriptParser::LocalVarNode *>(statement);
388 _print_indent(p_indent, "var " + String(lv_node->name));
389 } break;
390 default: {
391 //expression i guess
392 _print_indent(p_indent, _parser_expr(statement));
393 }
394 }
395 }
396 }
397
_parser_show_function(const GDScriptParser::FunctionNode * p_func,int p_indent,GDScriptParser::BlockNode * p_initializer=NULL)398 static void _parser_show_function(const GDScriptParser::FunctionNode *p_func, int p_indent, GDScriptParser::BlockNode *p_initializer = NULL) {
399
400 String txt;
401 if (p_func->_static)
402 txt = "static ";
403 txt += "func ";
404 if (p_func->name == "") // initializer
405 txt += "[built-in-initializer]";
406 else
407 txt += String(p_func->name);
408 txt += "(";
409
410 for (int i = 0; i < p_func->arguments.size(); i++) {
411
412 if (i != 0)
413 txt += ", ";
414 txt += "var " + String(p_func->arguments[i]);
415 if (i >= (p_func->arguments.size() - p_func->default_values.size())) {
416 int defarg = i - (p_func->arguments.size() - p_func->default_values.size());
417 txt += "=";
418 txt += _parser_expr(p_func->default_values[defarg]);
419 }
420 }
421
422 txt += ")";
423
424 //todo constructor check!
425
426 txt += ":";
427
428 _print_indent(p_indent, txt);
429 if (p_initializer)
430 _parser_show_block(p_initializer, p_indent + 1);
431 _parser_show_block(p_func->body, p_indent + 1);
432 }
433
_parser_show_class(const GDScriptParser::ClassNode * p_class,int p_indent,const Vector<String> & p_code)434 static void _parser_show_class(const GDScriptParser::ClassNode *p_class, int p_indent, const Vector<String> &p_code) {
435
436 if (p_indent == 0 && (String(p_class->extends_file) != "" || p_class->extends_class.size())) {
437
438 _print_indent(p_indent, _parser_extends(p_class));
439 print_line("\n");
440 }
441
442 for (int i = 0; i < p_class->subclasses.size(); i++) {
443
444 const GDScriptParser::ClassNode *subclass = p_class->subclasses[i];
445 String line = "class " + subclass->name;
446 if (String(subclass->extends_file) != "" || subclass->extends_class.size())
447 line += " " + _parser_extends(subclass);
448 line += ":";
449 _print_indent(p_indent, line);
450 _parser_show_class(subclass, p_indent + 1, p_code);
451 print_line("\n");
452 }
453
454 for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
455 const GDScriptParser::ClassNode::Constant &constant = E->get();
456 _print_indent(p_indent, "const " + String(E->key()) + "=" + _parser_expr(constant.expression));
457 }
458
459 for (int i = 0; i < p_class->variables.size(); i++) {
460
461 const GDScriptParser::ClassNode::Member &m = p_class->variables[i];
462
463 _print_indent(p_indent, "var " + String(m.identifier));
464 }
465
466 print_line("\n");
467
468 for (int i = 0; i < p_class->static_functions.size(); i++) {
469
470 _parser_show_function(p_class->static_functions[i], p_indent);
471 print_line("\n");
472 }
473
474 for (int i = 0; i < p_class->functions.size(); i++) {
475
476 if (String(p_class->functions[i]->name) == "_init") {
477 _parser_show_function(p_class->functions[i], p_indent, p_class->initializer);
478 } else
479 _parser_show_function(p_class->functions[i], p_indent);
480 print_line("\n");
481 }
482 //_parser_show_function(p_class->initializer,p_indent);
483 print_line("\n");
484 }
485
_disassemble_addr(const Ref<GDScript> & p_script,const GDScriptFunction & func,int p_addr)486 static String _disassemble_addr(const Ref<GDScript> &p_script, const GDScriptFunction &func, int p_addr) {
487
488 int addr = p_addr & GDScriptFunction::ADDR_MASK;
489
490 switch (p_addr >> GDScriptFunction::ADDR_BITS) {
491
492 case GDScriptFunction::ADDR_TYPE_SELF: {
493 return "self";
494 } break;
495 case GDScriptFunction::ADDR_TYPE_CLASS: {
496 return "class";
497 } break;
498 case GDScriptFunction::ADDR_TYPE_MEMBER: {
499
500 return "member(" + p_script->debug_get_member_by_index(addr) + ")";
501 } break;
502 case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
503
504 return "class_const(" + func.get_global_name(addr) + ")";
505 } break;
506 case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
507
508 Variant v = func.get_constant(addr);
509 String txt;
510 if (v.get_type() == Variant::STRING || v.get_type() == Variant::NODE_PATH)
511 txt = "\"" + String(v) + "\"";
512 else
513 txt = v;
514 return "const(" + txt + ")";
515 } break;
516 case GDScriptFunction::ADDR_TYPE_STACK: {
517
518 return "stack(" + itos(addr) + ")";
519 } break;
520 case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
521
522 return "var_stack(" + itos(addr) + ")";
523 } break;
524 case GDScriptFunction::ADDR_TYPE_GLOBAL: {
525
526 return "global(" + func.get_global_name(addr) + ")";
527 } break;
528 case GDScriptFunction::ADDR_TYPE_NIL: {
529 return "nil";
530 } break;
531 }
532
533 return "<err>";
534 }
535
_disassemble_class(const Ref<GDScript> & p_class,const Vector<String> & p_code)536 static void _disassemble_class(const Ref<GDScript> &p_class, const Vector<String> &p_code) {
537
538 const Map<StringName, GDScriptFunction *> &mf = p_class->debug_get_member_functions();
539
540 for (const Map<StringName, GDScriptFunction *>::Element *E = mf.front(); E; E = E->next()) {
541
542 const GDScriptFunction &func = *E->get();
543 const int *code = func.get_code();
544 int codelen = func.get_code_size();
545 String defargs;
546 if (func.get_default_argument_count()) {
547 defargs = "defarg at: ";
548 for (int i = 0; i < func.get_default_argument_count(); i++) {
549
550 if (i > 0)
551 defargs += ",";
552 defargs += itos(func.get_default_argument_addr(i));
553 }
554 defargs += " ";
555 }
556 print_line("== function " + String(func.get_name()) + "() :: stack size: " + itos(func.get_max_stack_size()) + " " + defargs + "==");
557
558 #define DADDR(m_ip) (_disassemble_addr(p_class, func, code[ip + m_ip]))
559
560 for (int ip = 0; ip < codelen;) {
561
562 int incr = 0;
563 String txt = itos(ip) + " ";
564
565 switch (code[ip]) {
566
567 case GDScriptFunction::OPCODE_OPERATOR: {
568
569 int op = code[ip + 1];
570 txt += " op ";
571
572 String opname = Variant::get_operator_name(Variant::Operator(op));
573
574 txt += DADDR(4);
575 txt += " = ";
576 txt += DADDR(2);
577 txt += " " + opname + " ";
578 txt += DADDR(3);
579 incr += 5;
580
581 } break;
582 case GDScriptFunction::OPCODE_SET: {
583
584 txt += "set ";
585 txt += DADDR(1);
586 txt += "[";
587 txt += DADDR(2);
588 txt += "]=";
589 txt += DADDR(3);
590 incr += 4;
591
592 } break;
593 case GDScriptFunction::OPCODE_GET: {
594
595 txt += " get ";
596 txt += DADDR(3);
597 txt += "=";
598 txt += DADDR(1);
599 txt += "[";
600 txt += DADDR(2);
601 txt += "]";
602 incr += 4;
603
604 } break;
605 case GDScriptFunction::OPCODE_SET_NAMED: {
606
607 txt += " set_named ";
608 txt += DADDR(1);
609 txt += "[\"";
610 txt += func.get_global_name(code[ip + 2]);
611 txt += "\"]=";
612 txt += DADDR(3);
613 incr += 4;
614
615 } break;
616 case GDScriptFunction::OPCODE_GET_NAMED: {
617
618 txt += " get_named ";
619 txt += DADDR(3);
620 txt += "=";
621 txt += DADDR(1);
622 txt += "[\"";
623 txt += func.get_global_name(code[ip + 2]);
624 txt += "\"]";
625 incr += 4;
626
627 } break;
628 case GDScriptFunction::OPCODE_SET_MEMBER: {
629
630 txt += " set_member ";
631 txt += "[\"";
632 txt += func.get_global_name(code[ip + 1]);
633 txt += "\"]=";
634 txt += DADDR(2);
635 incr += 3;
636
637 } break;
638 case GDScriptFunction::OPCODE_GET_MEMBER: {
639
640 txt += " get_member ";
641 txt += DADDR(2);
642 txt += "=";
643 txt += "[\"";
644 txt += func.get_global_name(code[ip + 1]);
645 txt += "\"]";
646 incr += 3;
647
648 } break;
649 case GDScriptFunction::OPCODE_ASSIGN: {
650
651 txt += " assign ";
652 txt += DADDR(1);
653 txt += "=";
654 txt += DADDR(2);
655 incr += 3;
656
657 } break;
658 case GDScriptFunction::OPCODE_ASSIGN_TRUE: {
659
660 txt += " assign ";
661 txt += DADDR(1);
662 txt += "= true";
663 incr += 2;
664
665 } break;
666 case GDScriptFunction::OPCODE_ASSIGN_FALSE: {
667
668 txt += " assign ";
669 txt += DADDR(1);
670 txt += "= false";
671 incr += 2;
672
673 } break;
674 case GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN: {
675
676 txt += " assign typed builtin (";
677 txt += Variant::get_type_name((Variant::Type)code[ip + 1]);
678 txt += ") ";
679 txt += DADDR(2);
680 txt += " = ";
681 txt += DADDR(3);
682 incr += 4;
683
684 } break;
685 case GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE: {
686 Variant className = func.get_constant(code[ip + 1]);
687 GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(className.operator Object *());
688
689 txt += " assign typed native (";
690 txt += nc->get_name().operator String();
691 txt += ") ";
692 txt += DADDR(2);
693 txt += " = ";
694 txt += DADDR(3);
695 incr += 4;
696
697 } break;
698 case GDScriptFunction::OPCODE_CAST_TO_SCRIPT: {
699
700 txt += " cast ";
701 txt += DADDR(3);
702 txt += "=";
703 txt += DADDR(1);
704 txt += " as ";
705 txt += DADDR(2);
706 incr += 4;
707
708 } break;
709 case GDScriptFunction::OPCODE_CONSTRUCT: {
710
711 Variant::Type t = Variant::Type(code[ip + 1]);
712 int argc = code[ip + 2];
713
714 txt += " construct ";
715 txt += DADDR(3 + argc);
716 txt += " = ";
717
718 txt += Variant::get_type_name(t) + "(";
719 for (int i = 0; i < argc; i++) {
720
721 if (i > 0)
722 txt += ", ";
723 txt += DADDR(i + 3);
724 }
725 txt += ")";
726
727 incr = 4 + argc;
728
729 } break;
730 case GDScriptFunction::OPCODE_CONSTRUCT_ARRAY: {
731
732 int argc = code[ip + 1];
733 txt += " make_array ";
734 txt += DADDR(2 + argc);
735 txt += " = [ ";
736
737 for (int i = 0; i < argc; i++) {
738 if (i > 0)
739 txt += ", ";
740 txt += DADDR(2 + i);
741 }
742
743 txt += "]";
744
745 incr += 3 + argc;
746
747 } break;
748 case GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY: {
749
750 int argc = code[ip + 1];
751 txt += " make_dict ";
752 txt += DADDR(2 + argc * 2);
753 txt += " = { ";
754
755 for (int i = 0; i < argc; i++) {
756 if (i > 0)
757 txt += ", ";
758 txt += DADDR(2 + i * 2 + 0);
759 txt += ":";
760 txt += DADDR(2 + i * 2 + 1);
761 }
762
763 txt += "}";
764
765 incr += 3 + argc * 2;
766
767 } break;
768
769 case GDScriptFunction::OPCODE_CALL:
770 case GDScriptFunction::OPCODE_CALL_RETURN: {
771
772 bool ret = code[ip] == GDScriptFunction::OPCODE_CALL_RETURN;
773
774 if (ret)
775 txt += " call-ret ";
776 else
777 txt += " call ";
778
779 int argc = code[ip + 1];
780 if (ret) {
781 txt += DADDR(4 + argc) + "=";
782 }
783
784 txt += DADDR(2) + ".";
785 txt += String(func.get_global_name(code[ip + 3]));
786 txt += "(";
787
788 for (int i = 0; i < argc; i++) {
789 if (i > 0)
790 txt += ", ";
791 txt += DADDR(4 + i);
792 }
793 txt += ")";
794
795 incr = 5 + argc;
796
797 } break;
798 case GDScriptFunction::OPCODE_CALL_BUILT_IN: {
799
800 txt += " call-built-in ";
801
802 int argc = code[ip + 2];
803 txt += DADDR(3 + argc) + "=";
804
805 txt += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(code[ip + 1]));
806 txt += "(";
807
808 for (int i = 0; i < argc; i++) {
809 if (i > 0)
810 txt += ", ";
811 txt += DADDR(3 + i);
812 }
813 txt += ")";
814
815 incr = 4 + argc;
816
817 } break;
818 case GDScriptFunction::OPCODE_CALL_SELF_BASE: {
819
820 txt += " call-self-base ";
821
822 int argc = code[ip + 2];
823 txt += DADDR(3 + argc) + "=";
824
825 txt += func.get_global_name(code[ip + 1]);
826 txt += "(";
827
828 for (int i = 0; i < argc; i++) {
829 if (i > 0)
830 txt += ", ";
831 txt += DADDR(3 + i);
832 }
833 txt += ")";
834
835 incr = 4 + argc;
836
837 } break;
838 case GDScriptFunction::OPCODE_YIELD: {
839
840 txt += " yield ";
841 incr = 1;
842
843 } break;
844 case GDScriptFunction::OPCODE_YIELD_SIGNAL: {
845
846 txt += " yield_signal ";
847 txt += DADDR(1);
848 txt += ",";
849 txt += DADDR(2);
850 incr = 3;
851 } break;
852 case GDScriptFunction::OPCODE_YIELD_RESUME: {
853
854 txt += " yield resume: ";
855 txt += DADDR(1);
856 incr = 2;
857 } break;
858 case GDScriptFunction::OPCODE_JUMP: {
859
860 txt += " jump ";
861 txt += itos(code[ip + 1]);
862
863 incr = 2;
864
865 } break;
866 case GDScriptFunction::OPCODE_JUMP_IF: {
867
868 txt += " jump-if ";
869 txt += DADDR(1);
870 txt += " to ";
871 txt += itos(code[ip + 2]);
872
873 incr = 3;
874 } break;
875 case GDScriptFunction::OPCODE_JUMP_IF_NOT: {
876
877 txt += " jump-if-not ";
878 txt += DADDR(1);
879 txt += " to ";
880 txt += itos(code[ip + 2]);
881
882 incr = 3;
883 } break;
884 case GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT: {
885
886 txt += " jump-to-default-argument ";
887 incr = 1;
888 } break;
889 case GDScriptFunction::OPCODE_RETURN: {
890
891 txt += " return ";
892 txt += DADDR(1);
893
894 incr = 2;
895
896 } break;
897 case GDScriptFunction::OPCODE_ITERATE_BEGIN: {
898
899 txt += " for-init " + DADDR(4) + " in " + DADDR(2) + " counter " + DADDR(1) + " end " + itos(code[ip + 3]);
900 incr += 5;
901
902 } break;
903 case GDScriptFunction::OPCODE_ITERATE: {
904
905 txt += " for-loop " + DADDR(4) + " in " + DADDR(2) + " counter " + DADDR(1) + " end " + itos(code[ip + 3]);
906 incr += 5;
907
908 } break;
909 case GDScriptFunction::OPCODE_LINE: {
910
911 int line = code[ip + 1] - 1;
912 if (line >= 0 && line < p_code.size())
913 txt = "\n" + itos(line + 1) + ": " + p_code[line] + "\n";
914 else
915 txt = "";
916 incr += 2;
917 } break;
918 case GDScriptFunction::OPCODE_END: {
919
920 txt += " end";
921 incr += 1;
922 } break;
923 case GDScriptFunction::OPCODE_ASSERT: {
924
925 txt += " assert ";
926 txt += DADDR(1);
927 incr += 2;
928
929 } break;
930 }
931
932 if (incr == 0) {
933
934 ERR_BREAK_MSG(true, "Unhandled opcode: " + itos(code[ip]));
935 }
936
937 ip += incr;
938 if (txt != "")
939 print_line(txt);
940 }
941 }
942 }
943
test(TestType p_type)944 MainLoop *test(TestType p_type) {
945
946 List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
947
948 if (cmdlargs.empty()) {
949 return NULL;
950 }
951
952 String test = cmdlargs.back()->get();
953 if (!test.ends_with(".gd") && !test.ends_with(".gdc")) {
954 print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test);
955 return NULL;
956 }
957
958 FileAccess *fa = FileAccess::open(test, FileAccess::READ);
959 ERR_FAIL_COND_V_MSG(!fa, NULL, "Could not open file: " + test);
960
961 Vector<uint8_t> buf;
962 int flen = fa->get_len();
963 buf.resize(fa->get_len() + 1);
964 fa->get_buffer(buf.ptrw(), flen);
965 buf.write[flen] = 0;
966
967 String code;
968 code.parse_utf8((const char *)&buf[0]);
969
970 Vector<String> lines;
971 int last = 0;
972
973 for (int i = 0; i <= code.length(); i++) {
974
975 if (code[i] == '\n' || code[i] == 0) {
976
977 lines.push_back(code.substr(last, i - last));
978 last = i + 1;
979 }
980 }
981
982 if (p_type == TEST_TOKENIZER) {
983
984 GDScriptTokenizerText tk;
985 tk.set_code(code);
986 int line = -1;
987 while (tk.get_token() != GDScriptTokenizer::TK_EOF) {
988
989 String text;
990 if (tk.get_token() == GDScriptTokenizer::TK_IDENTIFIER)
991 text = "'" + tk.get_token_identifier() + "' (identifier)";
992 else if (tk.get_token() == GDScriptTokenizer::TK_CONSTANT) {
993 const Variant &c = tk.get_token_constant();
994 if (c.get_type() == Variant::STRING)
995 text = "\"" + String(c) + "\"";
996 else
997 text = c;
998
999 text = text + " (" + Variant::get_type_name(c.get_type()) + " constant)";
1000 } else if (tk.get_token() == GDScriptTokenizer::TK_ERROR)
1001 text = "ERROR: " + tk.get_token_error();
1002 else if (tk.get_token() == GDScriptTokenizer::TK_NEWLINE)
1003 text = "newline (" + itos(tk.get_token_line()) + ") + indent: " + itos(tk.get_token_line_indent());
1004 else if (tk.get_token() == GDScriptTokenizer::TK_BUILT_IN_FUNC)
1005 text = "'" + String(GDScriptFunctions::get_func_name(tk.get_token_built_in_func())) + "' (built-in function)";
1006 else
1007 text = tk.get_token_name(tk.get_token());
1008
1009 if (tk.get_token_line() != line) {
1010 int from = line + 1;
1011 line = tk.get_token_line();
1012
1013 for (int i = from; i <= line; i++) {
1014 int l = i - 1;
1015 if (l >= 0 && l < lines.size()) {
1016 print_line("\n" + itos(i) + ": " + lines[l] + "\n");
1017 }
1018 }
1019 }
1020 print_line("\t(" + itos(tk.get_token_column()) + "): " + text);
1021 tk.advance();
1022 }
1023 }
1024
1025 if (p_type == TEST_PARSER) {
1026
1027 GDScriptParser parser;
1028 Error err = parser.parse(code);
1029 if (err) {
1030 print_line("Parse Error:\n" + itos(parser.get_error_line()) + ":" + itos(parser.get_error_column()) + ":" + parser.get_error());
1031 memdelete(fa);
1032 return NULL;
1033 }
1034
1035 const GDScriptParser::Node *root = parser.get_parse_tree();
1036 ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, NULL);
1037 const GDScriptParser::ClassNode *cnode = static_cast<const GDScriptParser::ClassNode *>(root);
1038
1039 _parser_show_class(cnode, 0, lines);
1040 }
1041
1042 if (p_type == TEST_COMPILER) {
1043
1044 GDScriptParser parser;
1045
1046 Error err = parser.parse(code);
1047 if (err) {
1048 print_line("Parse Error:\n" + itos(parser.get_error_line()) + ":" + itos(parser.get_error_column()) + ":" + parser.get_error());
1049 memdelete(fa);
1050 return NULL;
1051 }
1052
1053 Ref<GDScript> gds;
1054 gds.instance();
1055
1056 GDScriptCompiler gdc;
1057 err = gdc.compile(&parser, gds.ptr());
1058 if (err) {
1059
1060 print_line("Compile Error:\n" + itos(gdc.get_error_line()) + ":" + itos(gdc.get_error_column()) + ":" + gdc.get_error());
1061 return NULL;
1062 }
1063
1064 Ref<GDScript> current = gds;
1065
1066 while (current.is_valid()) {
1067
1068 print_line("** CLASS **");
1069 _disassemble_class(current, lines);
1070
1071 current = current->get_base();
1072 }
1073
1074 } else if (p_type == TEST_BYTECODE) {
1075
1076 Vector<uint8_t> buf2 = GDScriptTokenizerBuffer::parse_code_string(code);
1077 String dst = test.get_basename() + ".gdc";
1078 FileAccess *fw = FileAccess::open(dst, FileAccess::WRITE);
1079 fw->store_buffer(buf2.ptr(), buf2.size());
1080 memdelete(fw);
1081 }
1082
1083 memdelete(fa);
1084
1085 return NULL;
1086 }
1087 } // namespace TestGDScript
1088
1089 #else
1090
1091 namespace TestGDScript {
1092
test(TestType p_type)1093 MainLoop *test(TestType p_type) {
1094
1095 return NULL;
1096 }
1097 } // namespace TestGDScript
1098
1099 #endif
1100