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