1 /*************************************************************************/
2 /*  bindings_generator.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 "bindings_generator.h"
32 
33 #if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
34 
35 #include "core/engine.h"
36 #include "core/global_constants.h"
37 #include "core/io/compression.h"
38 #include "core/os/dir_access.h"
39 #include "core/os/file_access.h"
40 #include "core/os/os.h"
41 #include "core/ucaps.h"
42 
43 #include "../glue/cs_glue_version.gen.h"
44 #include "../godotsharp_defs.h"
45 #include "../mono_gd/gd_mono_marshal.h"
46 #include "../utils/path_utils.h"
47 #include "../utils/string_utils.h"
48 
49 #define CS_INDENT "    " // 4 whitespaces
50 
51 #define INDENT1 CS_INDENT
52 #define INDENT2 INDENT1 INDENT1
53 #define INDENT3 INDENT2 INDENT1
54 #define INDENT4 INDENT3 INDENT1
55 #define INDENT5 INDENT4 INDENT1
56 
57 #define MEMBER_BEGIN "\n" INDENT2
58 
59 #define OPEN_BLOCK "{\n"
60 #define CLOSE_BLOCK "}\n"
61 
62 #define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3
63 #define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4
64 #define OPEN_BLOCK_L4 INDENT4 OPEN_BLOCK INDENT5
65 #define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
66 #define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
67 #define CLOSE_BLOCK_L4 INDENT4 CLOSE_BLOCK
68 
69 #define CS_FIELD_MEMORYOWN "memoryOwn"
70 #define CS_PARAM_METHODBIND "method"
71 #define CS_PARAM_INSTANCE "ptr"
72 #define CS_SMETHOD_GETINSTANCE "GetPtr"
73 #define CS_METHOD_CALL "Call"
74 
75 #define GLUE_HEADER_FILE "glue_header.h"
76 #define ICALL_PREFIX "godot_icall_"
77 #define SINGLETON_ICALL_SUFFIX "_get_singleton"
78 #define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method"
79 
80 #define C_LOCAL_RET "ret"
81 #define C_LOCAL_VARARG_RET "vararg_ret"
82 #define C_LOCAL_PTRCALL_ARGS "call_args"
83 #define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT"
84 
85 #define C_NS_MONOUTILS "GDMonoUtils"
86 #define C_NS_MONOINTERNALS "GDMonoInternals"
87 #define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOINTERNALS "::tie_managed_to_unmanaged"
88 #define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS "::unmanaged_get_managed"
89 
90 #define C_NS_MONOMARSHAL "GDMonoMarshal"
91 #define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL "::mono_object_to_variant"
92 #define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL "::variant_to_mono_object"
93 #define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL "::mono_string_to_godot"
94 #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
95 #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
96 #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
97 
98 #define BINDINGS_GENERATOR_VERSION UINT32_C(11)
99 
100 const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
101 
fix_doc_description(const String & p_bbcode)102 static String fix_doc_description(const String &p_bbcode) {
103 
104 	// This seems to be the correct way to do this. It's the same EditorHelp does.
105 
106 	return p_bbcode.dedent()
107 			.replace("\t", "")
108 			.replace("\r", "")
109 			.strip_edges();
110 }
111 
snake_to_pascal_case(const String & p_identifier,bool p_input_is_upper=false)112 static String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper = false) {
113 
114 	String ret;
115 	Vector<String> parts = p_identifier.split("_", true);
116 
117 	for (int i = 0; i < parts.size(); i++) {
118 		String part = parts[i];
119 
120 		if (part.length()) {
121 			part[0] = _find_upper(part[0]);
122 			if (p_input_is_upper) {
123 				for (int j = 1; j < part.length(); j++)
124 					part[j] = _find_lower(part[j]);
125 			}
126 			ret += part;
127 		} else {
128 			if (i == 0 || i == (parts.size() - 1)) {
129 				// Preserve underscores at the beginning and end
130 				ret += "_";
131 			} else {
132 				// Preserve contiguous underscores
133 				if (parts[i - 1].length()) {
134 					ret += "__";
135 				} else {
136 					ret += "_";
137 				}
138 			}
139 		}
140 	}
141 
142 	return ret;
143 }
144 
snake_to_camel_case(const String & p_identifier,bool p_input_is_upper=false)145 static String snake_to_camel_case(const String &p_identifier, bool p_input_is_upper = false) {
146 
147 	String ret;
148 	Vector<String> parts = p_identifier.split("_", true);
149 
150 	for (int i = 0; i < parts.size(); i++) {
151 		String part = parts[i];
152 
153 		if (part.length()) {
154 			if (i != 0) {
155 				part[0] = _find_upper(part[0]);
156 			}
157 			if (p_input_is_upper) {
158 				for (int j = i != 0 ? 1 : 0; j < part.length(); j++)
159 					part[j] = _find_lower(part[j]);
160 			}
161 			ret += part;
162 		} else {
163 			if (i == 0 || i == (parts.size() - 1)) {
164 				// Preserve underscores at the beginning and end
165 				ret += "_";
166 			} else {
167 				// Preserve contiguous underscores
168 				if (parts[i - 1].length()) {
169 					ret += "__";
170 				} else {
171 					ret += "_";
172 				}
173 			}
174 		}
175 	}
176 
177 	return ret;
178 }
179 
bbcode_to_xml(const String & p_bbcode,const TypeInterface * p_itype)180 String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) {
181 
182 	// Based on the version in EditorHelp
183 
184 	if (p_bbcode.empty())
185 		return String();
186 
187 	DocData *doc = EditorHelp::get_doc_data();
188 
189 	String bbcode = p_bbcode;
190 
191 	StringBuilder xml_output;
192 
193 	xml_output.append("<para>");
194 
195 	List<String> tag_stack;
196 	bool code_tag = false;
197 
198 	int pos = 0;
199 	while (pos < bbcode.length()) {
200 		int brk_pos = bbcode.find("[", pos);
201 
202 		if (brk_pos < 0)
203 			brk_pos = bbcode.length();
204 
205 		if (brk_pos > pos) {
206 			String text = bbcode.substr(pos, brk_pos - pos);
207 			if (code_tag || tag_stack.size() > 0) {
208 				xml_output.append(text.xml_escape());
209 			} else {
210 				Vector<String> lines = text.split("\n");
211 				for (int i = 0; i < lines.size(); i++) {
212 					if (i != 0)
213 						xml_output.append("<para>");
214 
215 					xml_output.append(lines[i].xml_escape());
216 
217 					if (i != lines.size() - 1)
218 						xml_output.append("</para>\n");
219 				}
220 			}
221 		}
222 
223 		if (brk_pos == bbcode.length())
224 			break; // nothing else to add
225 
226 		int brk_end = bbcode.find("]", brk_pos + 1);
227 
228 		if (brk_end == -1) {
229 			String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos);
230 			if (code_tag || tag_stack.size() > 0) {
231 				xml_output.append(text.xml_escape());
232 			} else {
233 				Vector<String> lines = text.split("\n");
234 				for (int i = 0; i < lines.size(); i++) {
235 					if (i != 0)
236 						xml_output.append("<para>");
237 
238 					xml_output.append(lines[i].xml_escape());
239 
240 					if (i != lines.size() - 1)
241 						xml_output.append("</para>\n");
242 				}
243 			}
244 
245 			break;
246 		}
247 
248 		String tag = bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
249 
250 		if (tag.begins_with("/")) {
251 			bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
252 
253 			if (!tag_ok) {
254 				xml_output.append("[");
255 				pos = brk_pos + 1;
256 				continue;
257 			}
258 
259 			tag_stack.pop_front();
260 			pos = brk_end + 1;
261 			code_tag = false;
262 
263 			if (tag == "/url") {
264 				xml_output.append("</a>");
265 			} else if (tag == "/code") {
266 				xml_output.append("</c>");
267 			} else if (tag == "/codeblock") {
268 				xml_output.append("</code>");
269 			}
270 		} else if (code_tag) {
271 			xml_output.append("[");
272 			pos = brk_pos + 1;
273 		} else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ")) {
274 			String link_target = tag.substr(tag.find(" ") + 1, tag.length());
275 			String link_tag = tag.substr(0, tag.find(" "));
276 
277 			Vector<String> link_target_parts = link_target.split(".");
278 
279 			if (link_target_parts.size() <= 0 || link_target_parts.size() > 2) {
280 				ERR_PRINTS("Invalid reference format: '" + tag + "'.");
281 
282 				xml_output.append("<c>");
283 				xml_output.append(tag);
284 				xml_output.append("</c>");
285 
286 				pos = brk_end + 1;
287 				continue;
288 			}
289 
290 			const TypeInterface *target_itype;
291 			StringName target_cname;
292 
293 			if (link_target_parts.size() == 2) {
294 				target_itype = _get_type_or_null(TypeReference(link_target_parts[0]));
295 				if (!target_itype) {
296 					target_itype = _get_type_or_null(TypeReference("_" + link_target_parts[0]));
297 				}
298 				target_cname = link_target_parts[1];
299 			} else {
300 				target_itype = p_itype;
301 				target_cname = link_target_parts[0];
302 			}
303 
304 			if (link_tag == "method") {
305 				if (!target_itype || !target_itype->is_object_type) {
306 					if (OS::get_singleton()->is_stdout_verbose()) {
307 						if (target_itype) {
308 							OS::get_singleton()->print("Cannot resolve method reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data());
309 						} else {
310 							OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", link_target.utf8().get_data());
311 						}
312 					}
313 
314 					// TODO Map what we can
315 					xml_output.append("<c>");
316 					xml_output.append(link_target);
317 					xml_output.append("</c>");
318 				} else {
319 					const MethodInterface *target_imethod = target_itype->find_method_by_name(target_cname);
320 
321 					if (target_imethod) {
322 						xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
323 						xml_output.append(target_itype->proxy_name);
324 						xml_output.append(".");
325 						xml_output.append(target_imethod->proxy_name);
326 						xml_output.append("\"/>");
327 					}
328 				}
329 			} else if (link_tag == "member") {
330 				if (!target_itype || !target_itype->is_object_type) {
331 					if (OS::get_singleton()->is_stdout_verbose()) {
332 						if (target_itype) {
333 							OS::get_singleton()->print("Cannot resolve member reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data());
334 						} else {
335 							OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", link_target.utf8().get_data());
336 						}
337 					}
338 
339 					// TODO Map what we can
340 					xml_output.append("<c>");
341 					xml_output.append(link_target);
342 					xml_output.append("</c>");
343 				} else {
344 					const PropertyInterface *target_iprop = target_itype->find_property_by_name(target_cname);
345 
346 					if (target_iprop) {
347 						xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
348 						xml_output.append(target_itype->proxy_name);
349 						xml_output.append(".");
350 						xml_output.append(target_iprop->proxy_name);
351 						xml_output.append("\"/>");
352 					}
353 				}
354 			} else if (link_tag == "signal") {
355 				// We do not declare signals in any way in C#, so there is nothing to reference
356 				xml_output.append("<c>");
357 				xml_output.append(link_target);
358 				xml_output.append("</c>");
359 			} else if (link_tag == "enum") {
360 				StringName search_cname = !target_itype ? target_cname :
361 														  StringName(target_itype->name + "." + (String)target_cname);
362 
363 				const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(search_cname);
364 
365 				if (!enum_match && search_cname != target_cname) {
366 					enum_match = enum_types.find(target_cname);
367 				}
368 
369 				if (enum_match) {
370 					const TypeInterface &target_enum_itype = enum_match->value();
371 
372 					xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
373 					xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
374 					xml_output.append("\"/>");
375 				} else {
376 					ERR_PRINTS("Cannot resolve enum reference in documentation: '" + link_target + "'.");
377 
378 					xml_output.append("<c>");
379 					xml_output.append(link_target);
380 					xml_output.append("</c>");
381 				}
382 			} else if (link_tag == "const") {
383 				if (!target_itype || !target_itype->is_object_type) {
384 					if (OS::get_singleton()->is_stdout_verbose()) {
385 						if (target_itype) {
386 							OS::get_singleton()->print("Cannot resolve constant reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data());
387 						} else {
388 							OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", link_target.utf8().get_data());
389 						}
390 					}
391 
392 					// TODO Map what we can
393 					xml_output.append("<c>");
394 					xml_output.append(link_target);
395 					xml_output.append("</c>");
396 				} else if (!target_itype && target_cname == name_cache.type_at_GlobalScope) {
397 					String target_name = (String)target_cname;
398 
399 					// Try to find as a global constant
400 					const ConstantInterface *target_iconst = find_constant_by_name(target_name, global_constants);
401 
402 					if (target_iconst) {
403 						// Found global constant
404 						xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "." BINDINGS_GLOBAL_SCOPE_CLASS ".");
405 						xml_output.append(target_iconst->proxy_name);
406 						xml_output.append("\"/>");
407 					} else {
408 						// Try to find as global enum constant
409 						const EnumInterface *target_ienum = NULL;
410 
411 						for (const List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
412 							target_ienum = &E->get();
413 							target_iconst = find_constant_by_name(target_name, target_ienum->constants);
414 							if (target_iconst)
415 								break;
416 						}
417 
418 						if (target_iconst) {
419 							xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
420 							xml_output.append(target_ienum->cname);
421 							xml_output.append(".");
422 							xml_output.append(target_iconst->proxy_name);
423 							xml_output.append("\"/>");
424 						} else {
425 							ERR_PRINTS("Cannot resolve global constant reference in documentation: '" + link_target + "'.");
426 
427 							xml_output.append("<c>");
428 							xml_output.append(link_target);
429 							xml_output.append("</c>");
430 						}
431 					}
432 				} else {
433 					String target_name = (String)target_cname;
434 
435 					// Try to find the constant in the current class
436 					const ConstantInterface *target_iconst = find_constant_by_name(target_name, target_itype->constants);
437 
438 					if (target_iconst) {
439 						// Found constant in current class
440 						xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
441 						xml_output.append(target_itype->proxy_name);
442 						xml_output.append(".");
443 						xml_output.append(target_iconst->proxy_name);
444 						xml_output.append("\"/>");
445 					} else {
446 						// Try to find as enum constant in the current class
447 						const EnumInterface *target_ienum = NULL;
448 
449 						for (const List<EnumInterface>::Element *E = target_itype->enums.front(); E; E = E->next()) {
450 							target_ienum = &E->get();
451 							target_iconst = find_constant_by_name(target_name, target_ienum->constants);
452 							if (target_iconst)
453 								break;
454 						}
455 
456 						if (target_iconst) {
457 							xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
458 							xml_output.append(target_itype->proxy_name);
459 							xml_output.append(".");
460 							xml_output.append(target_ienum->cname);
461 							xml_output.append(".");
462 							xml_output.append(target_iconst->proxy_name);
463 							xml_output.append("\"/>");
464 						} else {
465 							ERR_PRINTS("Cannot resolve constant reference in documentation: '" + link_target + "'.");
466 
467 							xml_output.append("<c>");
468 							xml_output.append(link_target);
469 							xml_output.append("</c>");
470 						}
471 					}
472 				}
473 			}
474 
475 			pos = brk_end + 1;
476 		} else if (doc->class_list.has(tag)) {
477 			if (tag == "Array" || tag == "Dictionary") {
478 				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE_COLLECTIONS ".");
479 				xml_output.append(tag);
480 				xml_output.append("\"/>");
481 			} else if (tag == "bool" || tag == "int") {
482 				xml_output.append("<see cref=\"");
483 				xml_output.append(tag);
484 				xml_output.append("\"/>");
485 			} else if (tag == "float") {
486 				xml_output.append("<see cref=\""
487 #ifdef REAL_T_IS_DOUBLE
488 								  "double"
489 #else
490 								  "float"
491 #endif
492 								  "\"/>");
493 			} else if (tag == "Variant") {
494 				// We use System.Object for Variant, so there is no Variant type in C#
495 				xml_output.append("<c>Variant</c>");
496 			} else if (tag == "String") {
497 				xml_output.append("<see cref=\"string\"/>");
498 			} else if (tag == "Nil") {
499 				xml_output.append("<see langword=\"null\"/>");
500 			} else if (tag.begins_with("@")) {
501 				// @GlobalScope, @GDScript, etc
502 				xml_output.append("<c>");
503 				xml_output.append(tag);
504 				xml_output.append("</c>");
505 			} else if (tag == "PoolByteArray") {
506 				xml_output.append("<see cref=\"byte\"/>");
507 			} else if (tag == "PoolIntArray") {
508 				xml_output.append("<see cref=\"int\"/>");
509 			} else if (tag == "PoolRealArray") {
510 #ifdef REAL_T_IS_DOUBLE
511 				xml_output.append("<see cref=\"double\"/>");
512 #else
513 				xml_output.append("<see cref=\"float\"/>");
514 #endif
515 			} else if (tag == "PoolStringArray") {
516 				xml_output.append("<see cref=\"string\"/>");
517 			} else if (tag == "PoolVector2Array") {
518 				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>");
519 			} else if (tag == "PoolVector3Array") {
520 				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>");
521 			} else if (tag == "PoolColorArray") {
522 				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>");
523 			} else {
524 				const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
525 
526 				if (!target_itype) {
527 					target_itype = _get_type_or_null(TypeReference("_" + tag));
528 				}
529 
530 				if (target_itype) {
531 					xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
532 					xml_output.append(target_itype->proxy_name);
533 					xml_output.append("\"/>");
534 				} else {
535 					ERR_PRINTS("Cannot resolve type reference in documentation: '" + tag + "'.");
536 
537 					xml_output.append("<c>");
538 					xml_output.append(tag);
539 					xml_output.append("</c>");
540 				}
541 			}
542 
543 			pos = brk_end + 1;
544 		} else if (tag == "b") {
545 			// bold is not supported in xml comments
546 			pos = brk_end + 1;
547 			tag_stack.push_front(tag);
548 		} else if (tag == "i") {
549 			// italics is not supported in xml comments
550 			pos = brk_end + 1;
551 			tag_stack.push_front(tag);
552 		} else if (tag == "code") {
553 			xml_output.append("<c>");
554 
555 			code_tag = true;
556 			pos = brk_end + 1;
557 			tag_stack.push_front(tag);
558 		} else if (tag == "codeblock") {
559 			xml_output.append("<code>");
560 
561 			code_tag = true;
562 			pos = brk_end + 1;
563 			tag_stack.push_front(tag);
564 		} else if (tag == "center") {
565 			// center is alignment not supported in xml comments
566 			pos = brk_end + 1;
567 			tag_stack.push_front(tag);
568 		} else if (tag == "br") {
569 			xml_output.append("\n"); // FIXME: Should use <para> instead. Luckily this tag isn't used for now.
570 			pos = brk_end + 1;
571 		} else if (tag == "u") {
572 			// underline is not supported in xml comments
573 			pos = brk_end + 1;
574 			tag_stack.push_front(tag);
575 		} else if (tag == "s") {
576 			// strikethrough is not supported in xml comments
577 			pos = brk_end + 1;
578 			tag_stack.push_front(tag);
579 		} else if (tag == "url") {
580 			int end = bbcode.find("[", brk_end);
581 			if (end == -1)
582 				end = bbcode.length();
583 			String url = bbcode.substr(brk_end + 1, end - brk_end - 1);
584 			xml_output.append("<a href=\"");
585 			xml_output.append(url);
586 			xml_output.append("\">");
587 			xml_output.append(url);
588 
589 			pos = brk_end + 1;
590 			tag_stack.push_front(tag);
591 		} else if (tag.begins_with("url=")) {
592 			String url = tag.substr(4, tag.length());
593 			xml_output.append("<a href=\"");
594 			xml_output.append(url);
595 			xml_output.append("\">");
596 
597 			pos = brk_end + 1;
598 			tag_stack.push_front("url");
599 		} else if (tag == "img") {
600 			int end = bbcode.find("[", brk_end);
601 			if (end == -1)
602 				end = bbcode.length();
603 			String image = bbcode.substr(brk_end + 1, end - brk_end - 1);
604 
605 			// Not supported. Just append the bbcode.
606 			xml_output.append("[img]");
607 			xml_output.append(image);
608 			xml_output.append("[/img]");
609 
610 			pos = end;
611 			tag_stack.push_front(tag);
612 		} else if (tag.begins_with("color=")) {
613 			// Not supported.
614 			pos = brk_end + 1;
615 			tag_stack.push_front("color");
616 		} else if (tag.begins_with("font=")) {
617 			// Not supported.
618 			pos = brk_end + 1;
619 			tag_stack.push_front("font");
620 		} else {
621 			xml_output.append("["); // ignore
622 			pos = brk_pos + 1;
623 		}
624 	}
625 
626 	xml_output.append("</para>");
627 
628 	return xml_output.as_string();
629 }
630 
_determine_enum_prefix(const EnumInterface & p_ienum)631 int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
632 
633 	CRASH_COND(p_ienum.constants.empty());
634 
635 	const ConstantInterface &front_iconstant = p_ienum.constants.front()->get();
636 	Vector<String> front_parts = front_iconstant.name.split("_", /* p_allow_empty: */ true);
637 	int candidate_len = front_parts.size() - 1;
638 
639 	if (candidate_len == 0)
640 		return 0;
641 
642 	for (const List<ConstantInterface>::Element *E = p_ienum.constants.front()->next(); E; E = E->next()) {
643 		const ConstantInterface &iconstant = E->get();
644 
645 		Vector<String> parts = iconstant.name.split("_", /* p_allow_empty: */ true);
646 
647 		int i;
648 		for (i = 0; i < candidate_len && i < parts.size(); i++) {
649 			if (front_parts[i] != parts[i]) {
650 				// HARDCODED: Some Flag enums have the prefix 'FLAG_' for everything except 'FLAGS_DEFAULT' (same for 'METHOD_FLAG_' and'METHOD_FLAGS_DEFAULT').
651 				bool hardcoded_exc = (i == candidate_len - 1 && ((front_parts[i] == "FLAGS" && parts[i] == "FLAG") || (front_parts[i] == "FLAG" && parts[i] == "FLAGS")));
652 				if (!hardcoded_exc)
653 					break;
654 			}
655 		}
656 		candidate_len = i;
657 
658 		if (candidate_len == 0)
659 			return 0;
660 	}
661 
662 	return candidate_len;
663 }
664 
_apply_prefix_to_enum_constants(BindingsGenerator::EnumInterface & p_ienum,int p_prefix_length)665 void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumInterface &p_ienum, int p_prefix_length) {
666 
667 	if (p_prefix_length > 0) {
668 		for (List<ConstantInterface>::Element *E = p_ienum.constants.front(); E; E = E->next()) {
669 			int curr_prefix_length = p_prefix_length;
670 
671 			ConstantInterface &curr_const = E->get();
672 
673 			String constant_name = curr_const.name;
674 
675 			Vector<String> parts = constant_name.split("_", /* p_allow_empty: */ true);
676 
677 			if (parts.size() <= curr_prefix_length)
678 				continue;
679 
680 			if (parts[curr_prefix_length][0] >= '0' && parts[curr_prefix_length][0] <= '9') {
681 				// The name of enum constants may begin with a numeric digit when strip from the enum prefix,
682 				// so we make the prefix for this constant one word shorter in those cases.
683 				for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) {
684 					if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9')
685 						break;
686 				}
687 			}
688 
689 			constant_name = "";
690 			for (int i = curr_prefix_length; i < parts.size(); i++) {
691 				if (i > curr_prefix_length)
692 					constant_name += "_";
693 				constant_name += parts[i];
694 			}
695 
696 			curr_const.proxy_name = snake_to_pascal_case(constant_name, true);
697 		}
698 	}
699 }
700 
_generate_method_icalls(const TypeInterface & p_itype)701 void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
702 
703 	for (const List<MethodInterface>::Element *E = p_itype.methods.front(); E; E = E->next()) {
704 		const MethodInterface &imethod = E->get();
705 
706 		if (imethod.is_virtual)
707 			continue;
708 
709 		const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type);
710 
711 		String im_sig = "IntPtr " CS_PARAM_METHODBIND ", ";
712 		String im_unique_sig = imethod.return_type.cname.operator String() + ",IntPtr,IntPtr";
713 
714 		im_sig += "IntPtr " CS_PARAM_INSTANCE;
715 
716 		// Get arguments information
717 		int i = 0;
718 		for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
719 			const TypeInterface *arg_type = _get_type_or_placeholder(F->get().type);
720 
721 			im_sig += ", ";
722 			im_sig += arg_type->im_type_in;
723 			im_sig += " arg";
724 			im_sig += itos(i + 1);
725 
726 			im_unique_sig += ",";
727 			im_unique_sig += get_unique_sig(*arg_type);
728 
729 			i++;
730 		}
731 
732 		String im_type_out = return_type->im_type_out;
733 
734 		if (return_type->ret_as_byref_arg) {
735 			// Doesn't affect the unique signature
736 			im_type_out = "void";
737 
738 			im_sig += ", ";
739 			im_sig += return_type->im_type_out;
740 			im_sig += " argRet";
741 
742 			i++;
743 		}
744 
745 		// godot_icall_{argc}_{icallcount}
746 		String icall_method = ICALL_PREFIX;
747 		icall_method += itos(imethod.arguments.size());
748 		icall_method += "_";
749 		icall_method += itos(method_icalls.size());
750 
751 		InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig);
752 
753 		List<InternalCall>::Element *match = method_icalls.find(im_icall);
754 
755 		if (match) {
756 			if (p_itype.api_type != ClassDB::API_EDITOR)
757 				match->get().editor_only = false;
758 			method_icalls_map.insert(&E->get(), &match->get());
759 		} else {
760 			List<InternalCall>::Element *added = method_icalls.push_back(im_icall);
761 			method_icalls_map.insert(&E->get(), &added->get());
762 		}
763 	}
764 }
765 
_generate_global_constants(StringBuilder & p_output)766 void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
767 
768 	// Constants (in partial GD class)
769 
770 	p_output.append("\n#pragma warning disable CS1591 // Disable warning: "
771 					"'Missing XML comment for publicly visible type or member'\n");
772 
773 	p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
774 	p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
775 
776 	for (const List<ConstantInterface>::Element *E = global_constants.front(); E; E = E->next()) {
777 		const ConstantInterface &iconstant = E->get();
778 
779 		if (iconstant.const_doc && iconstant.const_doc->description.size()) {
780 			String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
781 			Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
782 
783 			if (summary_lines.size()) {
784 				p_output.append(MEMBER_BEGIN "/// <summary>\n");
785 
786 				for (int i = 0; i < summary_lines.size(); i++) {
787 					p_output.append(INDENT2 "/// ");
788 					p_output.append(summary_lines[i]);
789 					p_output.append("\n");
790 				}
791 
792 				p_output.append(INDENT2 "/// </summary>");
793 			}
794 		}
795 
796 		p_output.append(MEMBER_BEGIN "public const int ");
797 		p_output.append(iconstant.proxy_name);
798 		p_output.append(" = ");
799 		p_output.append(itos(iconstant.value));
800 		p_output.append(";");
801 	}
802 
803 	if (!global_constants.empty())
804 		p_output.append("\n");
805 
806 	p_output.append(INDENT1 CLOSE_BLOCK); // end of GD class
807 
808 	// Enums
809 
810 	for (List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
811 		const EnumInterface &ienum = E->get();
812 
813 		CRASH_COND(ienum.constants.empty());
814 
815 		String enum_proxy_name = ienum.cname.operator String();
816 
817 		bool enum_in_static_class = false;
818 
819 		if (enum_proxy_name.find(".") > 0) {
820 			enum_in_static_class = true;
821 			String enum_class_name = enum_proxy_name.get_slicec('.', 0);
822 			enum_proxy_name = enum_proxy_name.get_slicec('.', 1);
823 
824 			CRASH_COND(enum_class_name != "Variant"); // Hard-coded...
825 
826 			_log("Declaring global enum '%s' inside static class '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
827 
828 			p_output.append("\n" INDENT1 "public static partial class ");
829 			p_output.append(enum_class_name);
830 			p_output.append("\n" INDENT1 OPEN_BLOCK);
831 		}
832 
833 		p_output.append("\n" INDENT1 "public enum ");
834 		p_output.append(enum_proxy_name);
835 		p_output.append("\n" INDENT1 OPEN_BLOCK);
836 
837 		for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
838 			const ConstantInterface &iconstant = F->get();
839 
840 			if (iconstant.const_doc && iconstant.const_doc->description.size()) {
841 				String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
842 				Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
843 
844 				if (summary_lines.size()) {
845 					p_output.append(INDENT2 "/// <summary>\n");
846 
847 					for (int i = 0; i < summary_lines.size(); i++) {
848 						p_output.append(INDENT2 "/// ");
849 						p_output.append(summary_lines[i]);
850 						p_output.append("\n");
851 					}
852 
853 					p_output.append(INDENT2 "/// </summary>\n");
854 				}
855 			}
856 
857 			p_output.append(INDENT2);
858 			p_output.append(iconstant.proxy_name);
859 			p_output.append(" = ");
860 			p_output.append(itos(iconstant.value));
861 			p_output.append(F != ienum.constants.back() ? ",\n" : "\n");
862 		}
863 
864 		p_output.append(INDENT1 CLOSE_BLOCK);
865 
866 		if (enum_in_static_class)
867 			p_output.append(INDENT1 CLOSE_BLOCK);
868 	}
869 
870 	p_output.append(CLOSE_BLOCK); // end of namespace
871 
872 	p_output.append("\n#pragma warning restore CS1591\n");
873 }
874 
generate_cs_core_project(const String & p_proj_dir)875 Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
876 
877 	ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
878 
879 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
880 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
881 
882 	if (!DirAccess::exists(p_proj_dir)) {
883 		Error err = da->make_dir_recursive(p_proj_dir);
884 		ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create directory '" + p_proj_dir + "'.");
885 	}
886 
887 	da->change_dir(p_proj_dir);
888 	da->make_dir("Generated");
889 	da->make_dir("Generated/GodotObjects");
890 
891 	String base_gen_dir = path::join(p_proj_dir, "Generated");
892 	String godot_objects_gen_dir = path::join(base_gen_dir, "GodotObjects");
893 
894 	Vector<String> compile_items;
895 
896 	// Generate source file for global scope constants and enums
897 	{
898 		StringBuilder constants_source;
899 		_generate_global_constants(constants_source);
900 		String output_file = path::join(base_gen_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_constants.cs");
901 		Error save_err = _save_file(output_file, constants_source);
902 		if (save_err != OK)
903 			return save_err;
904 
905 		compile_items.push_back(output_file);
906 	}
907 
908 	for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
909 		const TypeInterface &itype = E.get();
910 
911 		if (itype.api_type == ClassDB::API_EDITOR)
912 			continue;
913 
914 		String output_file = path::join(godot_objects_gen_dir, itype.proxy_name + ".cs");
915 		Error err = _generate_cs_type(itype, output_file);
916 
917 		if (err == ERR_SKIP)
918 			continue;
919 
920 		if (err != OK)
921 			return err;
922 
923 		compile_items.push_back(output_file);
924 	}
925 
926 	// Generate sources from compressed files
927 
928 	StringBuilder cs_icalls_content;
929 
930 	cs_icalls_content.append("using System;\n"
931 							 "using System.Runtime.CompilerServices;\n"
932 							 "\n");
933 	cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
934 	cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
935 
936 	cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
937 	cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
938 	cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = ");
939 	cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
940 	cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = ");
941 	cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
942 
943 #define ADD_INTERNAL_CALL(m_icall)                                                               \
944 	if (!m_icall.editor_only) {                                                                  \
945 		cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
946 		cs_icalls_content.append(INDENT2 "internal extern static ");                             \
947 		cs_icalls_content.append(m_icall.im_type_out + " ");                                     \
948 		cs_icalls_content.append(m_icall.name + "(");                                            \
949 		cs_icalls_content.append(m_icall.im_sig + ");\n");                                       \
950 	}
951 
952 	for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next())
953 		ADD_INTERNAL_CALL(E->get());
954 	for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next())
955 		ADD_INTERNAL_CALL(E->get());
956 
957 #undef ADD_INTERNAL_CALL
958 
959 	cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
960 
961 	String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
962 
963 	Error err = _save_file(internal_methods_file, cs_icalls_content);
964 	if (err != OK)
965 		return err;
966 
967 	compile_items.push_back(internal_methods_file);
968 
969 	StringBuilder includes_props_content;
970 	includes_props_content.append("<Project>\n"
971 								  "  <ItemGroup>\n");
972 
973 	for (int i = 0; i < compile_items.size(); i++) {
974 		String include = path::relative_to(compile_items[i], p_proj_dir).replace("/", "\\");
975 		includes_props_content.append("    <Compile Include=\"" + include + "\" />\n");
976 	}
977 
978 	includes_props_content.append("  </ItemGroup>\n"
979 								  "</Project>\n");
980 
981 	String includes_props_file = path::join(base_gen_dir, "GeneratedIncludes.props");
982 
983 	err = _save_file(includes_props_file, includes_props_content);
984 	if (err != OK)
985 		return err;
986 
987 	return OK;
988 }
989 
generate_cs_editor_project(const String & p_proj_dir)990 Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
991 
992 	ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
993 
994 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
995 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
996 
997 	if (!DirAccess::exists(p_proj_dir)) {
998 		Error err = da->make_dir_recursive(p_proj_dir);
999 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
1000 	}
1001 
1002 	da->change_dir(p_proj_dir);
1003 	da->make_dir("Generated");
1004 	da->make_dir("Generated/GodotObjects");
1005 
1006 	String base_gen_dir = path::join(p_proj_dir, "Generated");
1007 	String godot_objects_gen_dir = path::join(base_gen_dir, "GodotObjects");
1008 
1009 	Vector<String> compile_items;
1010 
1011 	for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
1012 		const TypeInterface &itype = E.get();
1013 
1014 		if (itype.api_type != ClassDB::API_EDITOR)
1015 			continue;
1016 
1017 		String output_file = path::join(godot_objects_gen_dir, itype.proxy_name + ".cs");
1018 		Error err = _generate_cs_type(itype, output_file);
1019 
1020 		if (err == ERR_SKIP)
1021 			continue;
1022 
1023 		if (err != OK)
1024 			return err;
1025 
1026 		compile_items.push_back(output_file);
1027 	}
1028 
1029 	StringBuilder cs_icalls_content;
1030 
1031 	cs_icalls_content.append("using System;\n"
1032 							 "using System.Runtime.CompilerServices;\n"
1033 							 "\n");
1034 	cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
1035 	cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
1036 
1037 	cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = ");
1038 	cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
1039 	cs_icalls_content.append(INDENT2 "internal static uint bindings_version = ");
1040 	cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
1041 	cs_icalls_content.append(INDENT2 "internal static uint cs_glue_version = ");
1042 	cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
1043 	cs_icalls_content.append("\n");
1044 
1045 #define ADD_INTERNAL_CALL(m_icall)                                                          \
1046 	if (m_icall.editor_only) {                                                              \
1047 		cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
1048 		cs_icalls_content.append(INDENT2 "internal extern static ");                        \
1049 		cs_icalls_content.append(m_icall.im_type_out + " ");                                \
1050 		cs_icalls_content.append(m_icall.name + "(");                                       \
1051 		cs_icalls_content.append(m_icall.im_sig + ");\n");                                  \
1052 	}
1053 
1054 	for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
1055 		ADD_INTERNAL_CALL(E->get());
1056 	for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next())
1057 		ADD_INTERNAL_CALL(E->get());
1058 
1059 #undef ADD_INTERNAL_CALL
1060 
1061 	cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
1062 
1063 	String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
1064 
1065 	Error err = _save_file(internal_methods_file, cs_icalls_content);
1066 	if (err != OK)
1067 		return err;
1068 
1069 	compile_items.push_back(internal_methods_file);
1070 
1071 	StringBuilder includes_props_content;
1072 	includes_props_content.append("<Project>\n"
1073 								  "  <ItemGroup>\n");
1074 
1075 	for (int i = 0; i < compile_items.size(); i++) {
1076 		String include = path::relative_to(compile_items[i], p_proj_dir).replace("/", "\\");
1077 		includes_props_content.append("    <Compile Include=\"" + include + "\" />\n");
1078 	}
1079 
1080 	includes_props_content.append("  </ItemGroup>\n"
1081 								  "</Project>\n");
1082 
1083 	String includes_props_file = path::join(base_gen_dir, "GeneratedIncludes.props");
1084 
1085 	err = _save_file(includes_props_file, includes_props_content);
1086 	if (err != OK)
1087 		return err;
1088 
1089 	return OK;
1090 }
1091 
generate_cs_api(const String & p_output_dir)1092 Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
1093 
1094 	ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
1095 
1096 	String output_dir = path::abspath(path::realpath(p_output_dir));
1097 
1098 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
1099 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
1100 
1101 	if (!DirAccess::exists(output_dir)) {
1102 		Error err = da->make_dir_recursive(output_dir);
1103 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
1104 	}
1105 
1106 	Error proj_err;
1107 
1108 	// Generate GodotSharp source files
1109 
1110 	String core_proj_dir = output_dir.plus_file(CORE_API_ASSEMBLY_NAME);
1111 
1112 	proj_err = generate_cs_core_project(core_proj_dir);
1113 	if (proj_err != OK) {
1114 		ERR_PRINT("Generation of the Core API C# project failed.");
1115 		return proj_err;
1116 	}
1117 
1118 	// Generate GodotSharpEditor source files
1119 
1120 	String editor_proj_dir = output_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
1121 
1122 	proj_err = generate_cs_editor_project(editor_proj_dir);
1123 	if (proj_err != OK) {
1124 		ERR_PRINT("Generation of the Editor API C# project failed.");
1125 		return proj_err;
1126 	}
1127 
1128 	_log("The Godot API sources were successfully generated\n");
1129 
1130 	return OK;
1131 }
1132 
1133 // FIXME: There are some members that hide other inherited members.
1134 // - In the case of both members being the same kind, the new one must be declared
1135 // explicitly as 'new' to avoid the warning (and we must print a message about it).
1136 // - In the case of both members being of a different kind, then the new one must
1137 // be renamed to avoid the name collision (and we must print a warning about it).
1138 // - Csc warning e.g.:
1139 // ObjectType/LineEdit.cs(140,38): warning CS0108: 'LineEdit.FocusMode' hides inherited member 'Control.FocusMode'. Use the new keyword if hiding was intended.
_generate_cs_type(const TypeInterface & itype,const String & p_output_file)1140 Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const String &p_output_file) {
1141 
1142 	CRASH_COND(!itype.is_object_type);
1143 
1144 	bool is_derived_type = itype.base_name != StringName();
1145 
1146 	if (!is_derived_type) {
1147 		// Some Godot.Object assertions
1148 		CRASH_COND(itype.cname != name_cache.type_Object);
1149 		CRASH_COND(!itype.is_instantiable);
1150 		CRASH_COND(itype.api_type != ClassDB::API_CORE);
1151 		CRASH_COND(itype.is_reference);
1152 		CRASH_COND(itype.is_singleton);
1153 	}
1154 
1155 	List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
1156 
1157 	_log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
1158 
1159 	String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
1160 
1161 	StringBuilder output;
1162 
1163 	output.append("using System;\n"); // IntPtr
1164 	output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
1165 
1166 	output.append("\n"
1167 				  "#pragma warning disable CS1591 // Disable warning: "
1168 				  "'Missing XML comment for publicly visible type or member'\n"
1169 				  "#pragma warning disable CS1573 // Disable warning: "
1170 				  "'Parameter has no matching param tag in the XML comment'\n");
1171 
1172 	output.append("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
1173 
1174 	const DocData::ClassDoc *class_doc = itype.class_doc;
1175 
1176 	if (class_doc && class_doc->description.size()) {
1177 		String xml_summary = bbcode_to_xml(fix_doc_description(class_doc->description), &itype);
1178 		Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
1179 
1180 		if (summary_lines.size()) {
1181 			output.append(INDENT1 "/// <summary>\n");
1182 
1183 			for (int i = 0; i < summary_lines.size(); i++) {
1184 				output.append(INDENT1 "/// ");
1185 				output.append(summary_lines[i]);
1186 				output.append("\n");
1187 			}
1188 
1189 			output.append(INDENT1 "/// </summary>\n");
1190 		}
1191 	}
1192 
1193 	output.append(INDENT1 "public ");
1194 	if (itype.is_singleton) {
1195 		output.append("static partial class ");
1196 	} else {
1197 		output.append(itype.is_instantiable ? "partial class " : "abstract partial class ");
1198 	}
1199 	output.append(itype.proxy_name);
1200 
1201 	if (itype.is_singleton) {
1202 		output.append("\n");
1203 	} else if (is_derived_type) {
1204 		if (obj_types.has(itype.base_name)) {
1205 			output.append(" : ");
1206 			output.append(obj_types[itype.base_name].proxy_name);
1207 			output.append("\n");
1208 		} else {
1209 			ERR_PRINTS("Base type '" + itype.base_name.operator String() + "' does not exist, for class '" + itype.name + "'.");
1210 			return ERR_INVALID_DATA;
1211 		}
1212 	}
1213 
1214 	output.append(INDENT1 "{");
1215 
1216 	if (class_doc) {
1217 
1218 		// Add constants
1219 
1220 		for (const List<ConstantInterface>::Element *E = itype.constants.front(); E; E = E->next()) {
1221 			const ConstantInterface &iconstant = E->get();
1222 
1223 			if (iconstant.const_doc && iconstant.const_doc->description.size()) {
1224 				String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
1225 				Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
1226 
1227 				if (summary_lines.size()) {
1228 					output.append(MEMBER_BEGIN "/// <summary>\n");
1229 
1230 					for (int i = 0; i < summary_lines.size(); i++) {
1231 						output.append(INDENT2 "/// ");
1232 						output.append(summary_lines[i]);
1233 						output.append("\n");
1234 					}
1235 
1236 					output.append(INDENT2 "/// </summary>");
1237 				}
1238 			}
1239 
1240 			output.append(MEMBER_BEGIN "public const int ");
1241 			output.append(iconstant.proxy_name);
1242 			output.append(" = ");
1243 			output.append(itos(iconstant.value));
1244 			output.append(";");
1245 		}
1246 
1247 		if (itype.constants.size())
1248 			output.append("\n");
1249 
1250 		// Add enums
1251 
1252 		for (const List<EnumInterface>::Element *E = itype.enums.front(); E; E = E->next()) {
1253 			const EnumInterface &ienum = E->get();
1254 
1255 			ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG);
1256 
1257 			output.append(MEMBER_BEGIN "public enum ");
1258 			output.append(ienum.cname.operator String());
1259 			output.append(MEMBER_BEGIN OPEN_BLOCK);
1260 
1261 			for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
1262 				const ConstantInterface &iconstant = F->get();
1263 
1264 				if (iconstant.const_doc && iconstant.const_doc->description.size()) {
1265 					String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
1266 					Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
1267 
1268 					if (summary_lines.size()) {
1269 						output.append(INDENT3 "/// <summary>\n");
1270 
1271 						for (int i = 0; i < summary_lines.size(); i++) {
1272 							output.append(INDENT3 "/// ");
1273 							output.append(summary_lines[i]);
1274 							output.append("\n");
1275 						}
1276 
1277 						output.append(INDENT3 "/// </summary>\n");
1278 					}
1279 				}
1280 
1281 				output.append(INDENT3);
1282 				output.append(iconstant.proxy_name);
1283 				output.append(" = ");
1284 				output.append(itos(iconstant.value));
1285 				output.append(F != ienum.constants.back() ? ",\n" : "\n");
1286 			}
1287 
1288 			output.append(INDENT2 CLOSE_BLOCK);
1289 		}
1290 
1291 		// Add properties
1292 
1293 		for (const List<PropertyInterface>::Element *E = itype.properties.front(); E; E = E->next()) {
1294 			const PropertyInterface &iprop = E->get();
1295 			Error prop_err = _generate_cs_property(itype, iprop, output);
1296 			ERR_FAIL_COND_V_MSG(prop_err != OK, prop_err,
1297 					"Failed to generate property '" + iprop.cname.operator String() +
1298 							"' for class '" + itype.name + "'.");
1299 		}
1300 	}
1301 
1302 	// TODO: BINDINGS_NATIVE_NAME_FIELD should be StringName, once we support it in C#
1303 
1304 	if (itype.is_singleton) {
1305 		// Add the type name and the singleton pointer as static fields
1306 
1307 		output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n");
1308 		output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3
1309 								   "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5
1310 								   "singleton = Engine.GetSingleton(typeof(");
1311 		output.append(itype.proxy_name);
1312 		output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
1313 
1314 		output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
1315 		output.append(itype.name);
1316 		output.append("\";\n");
1317 
1318 		output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
1319 		output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
1320 		output.append("." ICALL_PREFIX);
1321 		output.append(itype.name);
1322 		output.append(SINGLETON_ICALL_SUFFIX "();\n");
1323 	} else if (is_derived_type) {
1324 		// Add member fields
1325 
1326 		output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
1327 		output.append(itype.name);
1328 		output.append("\";\n");
1329 
1330 		// Add default constructor
1331 		if (itype.is_instantiable) {
1332 			output.append(MEMBER_BEGIN "public ");
1333 			output.append(itype.proxy_name);
1334 			output.append("() : this(");
1335 			output.append(itype.memory_own ? "true" : "false");
1336 
1337 			// The default constructor may also be called by the engine when instancing existing native objects
1338 			// The engine will initialize the pointer field of the managed side before calling the constructor
1339 			// This is why we only allocate a new native object from the constructor if the pointer field is not set
1340 			output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
1341 			output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
1342 			output.append("." + ctor_method);
1343 			output.append("(this);\n" CLOSE_BLOCK_L2);
1344 		} else {
1345 			// Hide the constructor
1346 			output.append(MEMBER_BEGIN "internal ");
1347 			output.append(itype.proxy_name);
1348 			output.append("() {}\n");
1349 		}
1350 
1351 		// Add.. em.. trick constructor. Sort of.
1352 		output.append(MEMBER_BEGIN "internal ");
1353 		output.append(itype.proxy_name);
1354 		output.append("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
1355 	}
1356 
1357 	int method_bind_count = 0;
1358 	for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
1359 		const MethodInterface &imethod = E->get();
1360 		Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
1361 		ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
1362 				"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
1363 	}
1364 
1365 	if (itype.is_singleton) {
1366 		InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
1367 
1368 		if (!find_icall_by_name(singleton_icall.name, custom_icalls))
1369 			custom_icalls.push_back(singleton_icall);
1370 	}
1371 
1372 	if (is_derived_type && itype.is_instantiable) {
1373 		InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
1374 
1375 		if (!find_icall_by_name(ctor_icall.name, custom_icalls))
1376 			custom_icalls.push_back(ctor_icall);
1377 	}
1378 
1379 	output.append(INDENT1 CLOSE_BLOCK /* class */
1380 					CLOSE_BLOCK /* namespace */);
1381 
1382 	output.append("\n"
1383 				  "#pragma warning restore CS1591\n"
1384 				  "#pragma warning restore CS1573\n");
1385 
1386 	return _save_file(p_output_file, output);
1387 }
1388 
_generate_cs_property(const BindingsGenerator::TypeInterface & p_itype,const PropertyInterface & p_iprop,StringBuilder & p_output)1389 Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output) {
1390 
1391 	const MethodInterface *setter = p_itype.find_method_by_name(p_iprop.setter);
1392 
1393 	// Search it in base types too
1394 	const TypeInterface *current_type = &p_itype;
1395 	while (!setter && current_type->base_name != StringName()) {
1396 		OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
1397 		ERR_FAIL_COND_V(!base_match, ERR_BUG);
1398 		current_type = &base_match.get();
1399 		setter = current_type->find_method_by_name(p_iprop.setter);
1400 	}
1401 
1402 	const MethodInterface *getter = p_itype.find_method_by_name(p_iprop.getter);
1403 
1404 	// Search it in base types too
1405 	current_type = &p_itype;
1406 	while (!getter && current_type->base_name != StringName()) {
1407 		OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
1408 		ERR_FAIL_COND_V(!base_match, ERR_BUG);
1409 		current_type = &base_match.get();
1410 		getter = current_type->find_method_by_name(p_iprop.getter);
1411 	}
1412 
1413 	ERR_FAIL_COND_V(!setter && !getter, ERR_BUG);
1414 
1415 	if (setter) {
1416 		int setter_argc = p_iprop.index != -1 ? 2 : 1;
1417 		ERR_FAIL_COND_V(setter->arguments.size() != setter_argc, ERR_BUG);
1418 	}
1419 
1420 	if (getter) {
1421 		int getter_argc = p_iprop.index != -1 ? 1 : 0;
1422 		ERR_FAIL_COND_V(getter->arguments.size() != getter_argc, ERR_BUG);
1423 	}
1424 
1425 	if (getter && setter) {
1426 		ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG);
1427 	}
1428 
1429 	const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
1430 
1431 	const TypeInterface *prop_itype = _get_type_or_null(proptype_name);
1432 	ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found
1433 
1434 	if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) {
1435 		String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype);
1436 		Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
1437 
1438 		if (summary_lines.size()) {
1439 			p_output.append(MEMBER_BEGIN "/// <summary>\n");
1440 
1441 			for (int i = 0; i < summary_lines.size(); i++) {
1442 				p_output.append(INDENT2 "/// ");
1443 				p_output.append(summary_lines[i]);
1444 				p_output.append("\n");
1445 			}
1446 
1447 			p_output.append(INDENT2 "/// </summary>");
1448 		}
1449 	}
1450 
1451 	p_output.append(MEMBER_BEGIN "public ");
1452 
1453 	if (p_itype.is_singleton)
1454 		p_output.append("static ");
1455 
1456 	p_output.append(prop_itype->cs_type);
1457 	p_output.append(" ");
1458 	p_output.append(p_iprop.proxy_name);
1459 	p_output.append("\n" INDENT2 OPEN_BLOCK);
1460 
1461 	if (getter) {
1462 		p_output.append(INDENT3 "get\n"
1463 
1464 								// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
1465 								"#pragma warning disable CS0618 // Disable warning about obsolete method\n"
1466 
1467 				OPEN_BLOCK_L3);
1468 
1469 		p_output.append("return ");
1470 		p_output.append(getter->proxy_name + "(");
1471 		if (p_iprop.index != -1) {
1472 			const ArgumentInterface &idx_arg = getter->arguments.front()->get();
1473 			if (idx_arg.type.cname != name_cache.type_int) {
1474 				// Assume the index parameter is an enum
1475 				const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
1476 				CRASH_COND(idx_arg_type == NULL);
1477 				p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index));
1478 			} else {
1479 				p_output.append(itos(p_iprop.index));
1480 			}
1481 		}
1482 		p_output.append(");\n"
1483 
1484 				CLOSE_BLOCK_L3
1485 
1486 						// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
1487 						"#pragma warning restore CS0618\n");
1488 	}
1489 
1490 	if (setter) {
1491 		p_output.append(INDENT3 "set\n"
1492 
1493 								// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
1494 								"#pragma warning disable CS0618 // Disable warning about obsolete method\n"
1495 
1496 				OPEN_BLOCK_L3);
1497 
1498 		p_output.append(setter->proxy_name + "(");
1499 		if (p_iprop.index != -1) {
1500 			const ArgumentInterface &idx_arg = setter->arguments.front()->get();
1501 			if (idx_arg.type.cname != name_cache.type_int) {
1502 				// Assume the index parameter is an enum
1503 				const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
1504 				CRASH_COND(idx_arg_type == NULL);
1505 				p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", ");
1506 			} else {
1507 				p_output.append(itos(p_iprop.index) + ", ");
1508 			}
1509 		}
1510 		p_output.append("value);\n"
1511 
1512 				CLOSE_BLOCK_L3
1513 
1514 						// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
1515 						"#pragma warning restore CS0618\n");
1516 	}
1517 
1518 	p_output.append(CLOSE_BLOCK_L2);
1519 
1520 	return OK;
1521 }
1522 
_generate_cs_method(const BindingsGenerator::TypeInterface & p_itype,const BindingsGenerator::MethodInterface & p_imethod,int & p_method_bind_count,StringBuilder & p_output)1523 Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
1524 
1525 	const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
1526 
1527 	String method_bind_field = "method_bind_" + itos(p_method_bind_count);
1528 
1529 	String arguments_sig;
1530 	String cs_in_statements;
1531 
1532 	String icall_params = method_bind_field + ", ";
1533 	icall_params += sformat(p_itype.cs_in, "this");
1534 
1535 	StringBuilder default_args_doc;
1536 
1537 	// Retrieve information from the arguments
1538 	for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
1539 		const ArgumentInterface &iarg = F->get();
1540 		const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
1541 
1542 		// Add the current arguments to the signature
1543 		// If the argument has a default value which is not a constant, we will make it Nullable
1544 		{
1545 			if (F != p_imethod.arguments.front())
1546 				arguments_sig += ", ";
1547 
1548 			if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
1549 				arguments_sig += "Nullable<";
1550 
1551 			arguments_sig += arg_type->cs_type;
1552 
1553 			if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
1554 				arguments_sig += "> ";
1555 			else
1556 				arguments_sig += " ";
1557 
1558 			arguments_sig += iarg.name;
1559 
1560 			if (iarg.default_argument.size()) {
1561 				if (iarg.def_param_mode != ArgumentInterface::CONSTANT)
1562 					arguments_sig += " = null";
1563 				else
1564 					arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type);
1565 			}
1566 		}
1567 
1568 		icall_params += ", ";
1569 
1570 		if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
1571 			// The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
1572 			// Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
1573 			String arg_in = iarg.name;
1574 			arg_in += "_in";
1575 
1576 			cs_in_statements += arg_type->cs_type;
1577 			cs_in_statements += " ";
1578 			cs_in_statements += arg_in;
1579 			cs_in_statements += " = ";
1580 			cs_in_statements += iarg.name;
1581 
1582 			if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
1583 				cs_in_statements += ".HasValue ? ";
1584 			else
1585 				cs_in_statements += " != null ? ";
1586 
1587 			cs_in_statements += iarg.name;
1588 
1589 			if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
1590 				cs_in_statements += ".Value : ";
1591 			else
1592 				cs_in_statements += " : ";
1593 
1594 			String def_arg = sformat(iarg.default_argument, arg_type->cs_type);
1595 
1596 			cs_in_statements += def_arg;
1597 			cs_in_statements += ";\n" INDENT3;
1598 
1599 			icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
1600 
1601 			// Apparently the name attribute must not include the @
1602 			String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name;
1603 
1604 			default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>");
1605 		} else {
1606 			icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
1607 		}
1608 	}
1609 
1610 	// Generate method
1611 	{
1612 		if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
1613 			p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr ");
1614 			p_output.append(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
1615 			p_output.append(p_imethod.name);
1616 			p_output.append("\");\n");
1617 		}
1618 
1619 		if (p_imethod.method_doc && p_imethod.method_doc->description.size()) {
1620 			String xml_summary = bbcode_to_xml(fix_doc_description(p_imethod.method_doc->description), &p_itype);
1621 			Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
1622 
1623 			if (summary_lines.size()) {
1624 				p_output.append(MEMBER_BEGIN "/// <summary>\n");
1625 
1626 				for (int i = 0; i < summary_lines.size(); i++) {
1627 					p_output.append(INDENT2 "/// ");
1628 					p_output.append(summary_lines[i]);
1629 					p_output.append("\n");
1630 				}
1631 
1632 				p_output.append(INDENT2 "/// </summary>");
1633 			}
1634 		}
1635 
1636 		if (default_args_doc.get_string_length()) {
1637 			p_output.append(default_args_doc.as_string());
1638 		}
1639 
1640 		if (!p_imethod.is_internal) {
1641 			p_output.append(MEMBER_BEGIN "[GodotMethod(\"");
1642 			p_output.append(p_imethod.name);
1643 			p_output.append("\")]");
1644 		}
1645 
1646 		if (p_imethod.is_deprecated) {
1647 			if (p_imethod.deprecation_message.empty())
1648 				WARN_PRINTS("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'.");
1649 
1650 			p_output.append(MEMBER_BEGIN "[Obsolete(\"");
1651 			p_output.append(p_imethod.deprecation_message);
1652 			p_output.append("\")]");
1653 		}
1654 
1655 		p_output.append(MEMBER_BEGIN);
1656 		p_output.append(p_imethod.is_internal ? "internal " : "public ");
1657 
1658 		if (p_itype.is_singleton) {
1659 			p_output.append("static ");
1660 		} else if (p_imethod.is_virtual) {
1661 			p_output.append("virtual ");
1662 		}
1663 
1664 		p_output.append(return_type->cs_type + " ");
1665 		p_output.append(p_imethod.proxy_name + "(");
1666 		p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2);
1667 
1668 		if (p_imethod.is_virtual) {
1669 			// Godot virtual method must be overridden, therefore we return a default value by default.
1670 
1671 			if (return_type->cname == name_cache.type_void) {
1672 				p_output.append("return;\n" CLOSE_BLOCK_L2);
1673 			} else {
1674 				p_output.append("return default(");
1675 				p_output.append(return_type->cs_type);
1676 				p_output.append(");\n" CLOSE_BLOCK_L2);
1677 			}
1678 
1679 			return OK; // Won't increment method bind count
1680 		}
1681 
1682 		if (p_imethod.requires_object_call) {
1683 			// Fallback to Godot's object.Call(string, params)
1684 
1685 			p_output.append(CS_METHOD_CALL "(\"");
1686 			p_output.append(p_imethod.name);
1687 			p_output.append("\"");
1688 
1689 			for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
1690 				p_output.append(", ");
1691 				p_output.append(F->get().name);
1692 			}
1693 
1694 			p_output.append(");\n" CLOSE_BLOCK_L2);
1695 
1696 			return OK; // Won't increment method bind count
1697 		}
1698 
1699 		const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
1700 		ERR_FAIL_NULL_V(match, ERR_BUG);
1701 
1702 		const InternalCall *im_icall = match->value();
1703 
1704 		String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
1705 		im_call += ".";
1706 		im_call += im_icall->name;
1707 
1708 		if (p_imethod.arguments.size())
1709 			p_output.append(cs_in_statements);
1710 
1711 		if (return_type->cname == name_cache.type_void) {
1712 			p_output.append(im_call + "(" + icall_params + ");\n");
1713 		} else if (return_type->cs_out.empty()) {
1714 			p_output.append("return " + im_call + "(" + icall_params + ");\n");
1715 		} else {
1716 			p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out));
1717 			p_output.append("\n");
1718 		}
1719 
1720 		p_output.append(CLOSE_BLOCK_L2);
1721 	}
1722 
1723 	p_method_bind_count++;
1724 
1725 	return OK;
1726 }
1727 
generate_glue(const String & p_output_dir)1728 Error BindingsGenerator::generate_glue(const String &p_output_dir) {
1729 
1730 	ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
1731 
1732 	bool dir_exists = DirAccess::exists(p_output_dir);
1733 	ERR_FAIL_COND_V_MSG(!dir_exists, ERR_FILE_BAD_PATH, "The output directory does not exist.");
1734 
1735 	StringBuilder output;
1736 
1737 	output.append("/* THIS FILE IS GENERATED DO NOT EDIT */\n");
1738 	output.append("#include \"" GLUE_HEADER_FILE "\"\n");
1739 	output.append("\n#ifdef MONO_GLUE_ENABLED\n");
1740 
1741 	generated_icall_funcs.clear();
1742 
1743 	for (OrderedHashMap<StringName, TypeInterface>::Element type_elem = obj_types.front(); type_elem; type_elem = type_elem.next()) {
1744 		const TypeInterface &itype = type_elem.get();
1745 
1746 		bool is_derived_type = itype.base_name != StringName();
1747 
1748 		if (!is_derived_type) {
1749 			// Some Object assertions
1750 			CRASH_COND(itype.cname != name_cache.type_Object);
1751 			CRASH_COND(!itype.is_instantiable);
1752 			CRASH_COND(itype.api_type != ClassDB::API_CORE);
1753 			CRASH_COND(itype.is_reference);
1754 			CRASH_COND(itype.is_singleton);
1755 		}
1756 
1757 		List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
1758 
1759 		OS::get_singleton()->print("Generating %s...\n", itype.name.utf8().get_data());
1760 
1761 		String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
1762 
1763 		for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
1764 			const MethodInterface &imethod = E->get();
1765 			Error method_err = _generate_glue_method(itype, imethod, output);
1766 			ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
1767 					"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
1768 		}
1769 
1770 		if (itype.is_singleton) {
1771 			String singleton_icall_name = ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX;
1772 			InternalCall singleton_icall = InternalCall(itype.api_type, singleton_icall_name, "IntPtr");
1773 
1774 			if (!find_icall_by_name(singleton_icall.name, custom_icalls))
1775 				custom_icalls.push_back(singleton_icall);
1776 
1777 			output.append("Object* ");
1778 			output.append(singleton_icall_name);
1779 			output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\"");
1780 			output.append(itype.proxy_name);
1781 			output.append("\");\n" CLOSE_BLOCK "\n");
1782 		}
1783 
1784 		if (is_derived_type && itype.is_instantiable) {
1785 			InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
1786 
1787 			if (!find_icall_by_name(ctor_icall.name, custom_icalls))
1788 				custom_icalls.push_back(ctor_icall);
1789 
1790 			output.append("Object* ");
1791 			output.append(ctor_method);
1792 			output.append("(MonoObject* obj) " OPEN_BLOCK
1793 						  "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
1794 			output.append(itype.name);
1795 			output.append("\");\n"
1796 						  "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
1797 						  "\treturn instance;\n" CLOSE_BLOCK "\n");
1798 		}
1799 	}
1800 
1801 	output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n");
1802 
1803 	output.append("uint64_t get_core_api_hash() { return ");
1804 	output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n");
1805 
1806 	output.append("#ifdef TOOLS_ENABLED\n"
1807 				  "uint64_t get_editor_api_hash() { return ");
1808 	output.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n");
1809 	output.append("#endif // TOOLS_ENABLED\n");
1810 
1811 	output.append("uint32_t get_bindings_version() { return ");
1812 	output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
1813 
1814 	output.append("uint32_t get_cs_glue_version() { return ");
1815 	output.append(String::num_uint64(CS_GLUE_VERSION) + "; }\n");
1816 
1817 	output.append("\nvoid register_generated_icalls() " OPEN_BLOCK);
1818 	output.append("\tgodot_register_glue_header_icalls();\n");
1819 
1820 #define ADD_INTERNAL_CALL_REGISTRATION(m_icall)                                                              \
1821 	{                                                                                                        \
1822 		output.append("\tmono_add_internal_call(");                                                          \
1823 		output.append("\"" BINDINGS_NAMESPACE ".");                                                          \
1824 		output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
1825 		output.append("::");                                                                                 \
1826 		output.append(m_icall.name);                                                                         \
1827 		output.append("\", (void*)");                                                                        \
1828 		output.append(m_icall.name);                                                                         \
1829 		output.append(");\n");                                                                               \
1830 	}
1831 
1832 	bool tools_sequence = false;
1833 	for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) {
1834 
1835 		if (tools_sequence) {
1836 			if (!E->get().editor_only) {
1837 				tools_sequence = false;
1838 				output.append("#endif\n");
1839 			}
1840 		} else {
1841 			if (E->get().editor_only) {
1842 				output.append("#ifdef TOOLS_ENABLED\n");
1843 				tools_sequence = true;
1844 			}
1845 		}
1846 
1847 		ADD_INTERNAL_CALL_REGISTRATION(E->get());
1848 	}
1849 
1850 	if (tools_sequence) {
1851 		tools_sequence = false;
1852 		output.append("#endif\n");
1853 	}
1854 
1855 	output.append("#ifdef TOOLS_ENABLED\n");
1856 	for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
1857 		ADD_INTERNAL_CALL_REGISTRATION(E->get());
1858 	output.append("#endif // TOOLS_ENABLED\n");
1859 
1860 	for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
1861 		if (tools_sequence) {
1862 			if (!E->get().editor_only) {
1863 				tools_sequence = false;
1864 				output.append("#endif\n");
1865 			}
1866 		} else {
1867 			if (E->get().editor_only) {
1868 				output.append("#ifdef TOOLS_ENABLED\n");
1869 				tools_sequence = true;
1870 			}
1871 		}
1872 
1873 		ADD_INTERNAL_CALL_REGISTRATION(E->get());
1874 	}
1875 
1876 	if (tools_sequence) {
1877 		tools_sequence = false;
1878 		output.append("#endif\n");
1879 	}
1880 
1881 #undef ADD_INTERNAL_CALL_REGISTRATION
1882 
1883 	output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n");
1884 
1885 	output.append("\n#endif // MONO_GLUE_ENABLED\n");
1886 
1887 	Error save_err = _save_file(path::join(p_output_dir, "mono_glue.gen.cpp"), output);
1888 	if (save_err != OK)
1889 		return save_err;
1890 
1891 	OS::get_singleton()->print("Mono glue generated successfully\n");
1892 
1893 	return OK;
1894 }
1895 
get_version()1896 uint32_t BindingsGenerator::get_version() {
1897 	return BINDINGS_GENERATOR_VERSION;
1898 }
1899 
_save_file(const String & p_path,const StringBuilder & p_content)1900 Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
1901 
1902 	FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
1903 
1904 	ERR_FAIL_COND_V_MSG(!file, ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
1905 
1906 	file->store_string(p_content.as_string());
1907 	file->close();
1908 
1909 	return OK;
1910 }
1911 
_generate_glue_method(const BindingsGenerator::TypeInterface & p_itype,const BindingsGenerator::MethodInterface & p_imethod,StringBuilder & p_output)1912 Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) {
1913 
1914 	if (p_imethod.is_virtual)
1915 		return OK; // Ignore
1916 
1917 	bool ret_void = p_imethod.return_type.cname == name_cache.type_void;
1918 
1919 	const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
1920 
1921 	String argc_str = itos(p_imethod.arguments.size());
1922 
1923 	String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE;
1924 	String c_in_statements;
1925 	String c_args_var_content;
1926 
1927 	// Get arguments information
1928 	int i = 0;
1929 	for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
1930 		const ArgumentInterface &iarg = F->get();
1931 		const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
1932 
1933 		String c_param_name = "arg" + itos(i + 1);
1934 
1935 		if (p_imethod.is_vararg) {
1936 			if (i < p_imethod.arguments.size() - 1) {
1937 				c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
1938 				c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(";
1939 				c_in_statements += itos(i);
1940 				c_in_statements += sformat(", &%s_in);\n", c_param_name);
1941 			}
1942 		} else {
1943 			if (i > 0)
1944 				c_args_var_content += ", ";
1945 			if (arg_type->c_in.size())
1946 				c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
1947 			c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
1948 		}
1949 
1950 		c_func_sig += ", ";
1951 		c_func_sig += arg_type->c_type_in;
1952 		c_func_sig += " ";
1953 		c_func_sig += c_param_name;
1954 
1955 		i++;
1956 	}
1957 
1958 	if (return_type->ret_as_byref_arg) {
1959 		c_func_sig += ", ";
1960 		c_func_sig += return_type->c_type_in;
1961 		c_func_sig += " ";
1962 		c_func_sig += "arg_ret";
1963 
1964 		i++;
1965 	}
1966 
1967 	const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
1968 	ERR_FAIL_NULL_V(match, ERR_BUG);
1969 
1970 	const InternalCall *im_icall = match->value();
1971 	String icall_method = im_icall->name;
1972 
1973 	if (!generated_icall_funcs.find(im_icall)) {
1974 		generated_icall_funcs.push_back(im_icall);
1975 
1976 		if (im_icall->editor_only)
1977 			p_output.append("#ifdef TOOLS_ENABLED\n");
1978 
1979 		// Generate icall function
1980 
1981 		p_output.append((ret_void || return_type->ret_as_byref_arg) ? "void " : return_type->c_type_out + " ");
1982 		p_output.append(icall_method);
1983 		p_output.append("(");
1984 		p_output.append(c_func_sig);
1985 		p_output.append(") " OPEN_BLOCK);
1986 
1987 		if (!ret_void) {
1988 			String ptrcall_return_type;
1989 			String initialization;
1990 
1991 			if (p_imethod.is_vararg && return_type->cname != name_cache.type_Variant) {
1992 				// VarArg methods always return Variant, but there are some cases in which MethodInfo provides
1993 				// a specific return type. We trust this information is valid. We need a temporary local to keep
1994 				// the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
1995 				// it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
1996 				// Alternatively, we could just return Variant, but that would result in a worse API.
1997 				p_output.append("\tVariant " C_LOCAL_VARARG_RET ";\n");
1998 			}
1999 
2000 			if (return_type->is_object_type) {
2001 				ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type;
2002 				initialization = return_type->is_reference ? "" : " = NULL";
2003 			} else {
2004 				ptrcall_return_type = return_type->c_type;
2005 			}
2006 
2007 			p_output.append("\t" + ptrcall_return_type);
2008 			p_output.append(" " C_LOCAL_RET);
2009 			p_output.append(initialization + ";\n");
2010 
2011 			String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "NULL" : return_type->c_type_out + "()";
2012 
2013 			if (return_type->ret_as_byref_arg) {
2014 				p_output.append("\tif (" CS_PARAM_INSTANCE " == NULL) { *arg_ret = ");
2015 				p_output.append(fail_ret);
2016 				p_output.append("; ERR_FAIL_MSG(\"Parameter ' arg_ret ' is null.\"); }\n");
2017 			} else {
2018 				p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", ");
2019 				p_output.append(fail_ret);
2020 				p_output.append(");\n");
2021 			}
2022 		} else {
2023 			p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
2024 		}
2025 
2026 		if (p_imethod.arguments.size()) {
2027 			if (p_imethod.is_vararg) {
2028 				String vararg_arg = "arg" + argc_str;
2029 				String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg
2030 
2031 				p_output.append("\tint vararg_length = mono_array_length(");
2032 				p_output.append(vararg_arg);
2033 				p_output.append(");\n\tint total_length = ");
2034 				p_output.append(real_argc_str);
2035 				p_output.append(" + vararg_length;\n"
2036 								"\tArgumentsVector<Variant> varargs(vararg_length);\n"
2037 								"\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n");
2038 				p_output.append(c_in_statements);
2039 				p_output.append("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
2040 								"\t\tMonoObject* elem = mono_array_get(");
2041 				p_output.append(vararg_arg);
2042 				p_output.append(", MonoObject*, i);\n"
2043 								"\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
2044 								"\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
2045 				p_output.append(real_argc_str);
2046 				p_output.append(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK);
2047 			} else {
2048 				p_output.append(c_in_statements);
2049 				p_output.append("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
2050 				p_output.append(argc_str + "] = { ");
2051 				p_output.append(c_args_var_content + " };\n");
2052 			}
2053 		}
2054 
2055 		if (p_imethod.is_vararg) {
2056 			p_output.append("\tVariant::CallError vcall_error;\n\t");
2057 
2058 			if (!ret_void) {
2059 				// See the comment on the C_LOCAL_VARARG_RET declaration
2060 				if (return_type->cname != name_cache.type_Variant) {
2061 					p_output.append(C_LOCAL_VARARG_RET " = ");
2062 				} else {
2063 					p_output.append(C_LOCAL_RET " = ");
2064 				}
2065 			}
2066 
2067 			p_output.append(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", ");
2068 			p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL");
2069 			p_output.append(", total_length, vcall_error);\n");
2070 
2071 			if (!ret_void) {
2072 				// See the comment on the C_LOCAL_VARARG_RET declaration
2073 				if (return_type->cname != name_cache.type_Variant) {
2074 					p_output.append("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n");
2075 				}
2076 			}
2077 		} else {
2078 			p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", ");
2079 			p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, ");
2080 			p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n");
2081 		}
2082 
2083 		if (!ret_void) {
2084 			if (return_type->c_out.empty()) {
2085 				p_output.append("\treturn " C_LOCAL_RET ";\n");
2086 			} else if (return_type->ret_as_byref_arg) {
2087 				p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret"));
2088 			} else {
2089 				p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name));
2090 			}
2091 		}
2092 
2093 		p_output.append(CLOSE_BLOCK "\n");
2094 
2095 		if (im_icall->editor_only)
2096 			p_output.append("#endif // TOOLS_ENABLED\n");
2097 	}
2098 
2099 	return OK;
2100 }
2101 
_get_type_or_null(const TypeReference & p_typeref)2102 const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) {
2103 
2104 	const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname);
2105 
2106 	if (builtin_type_match)
2107 		return &builtin_type_match->get();
2108 
2109 	const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname);
2110 
2111 	if (obj_type_match)
2112 		return &obj_type_match.get();
2113 
2114 	if (p_typeref.is_enum) {
2115 		const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname);
2116 
2117 		if (enum_match)
2118 			return &enum_match->get();
2119 
2120 		// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
2121 		const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int);
2122 		ERR_FAIL_NULL_V(int_match, NULL);
2123 		return &int_match->get();
2124 	}
2125 
2126 	return NULL;
2127 }
2128 
_get_type_or_placeholder(const TypeReference & p_typeref)2129 const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
2130 
2131 	const TypeInterface *found = _get_type_or_null(p_typeref);
2132 
2133 	if (found)
2134 		return found;
2135 
2136 	ERR_PRINTS(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'.");
2137 
2138 	const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname);
2139 
2140 	if (match)
2141 		return &match->get();
2142 
2143 	TypeInterface placeholder;
2144 	TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
2145 
2146 	return &placeholder_types.insert(placeholder.cname, placeholder)->get();
2147 }
2148 
_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta)2149 StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
2150 
2151 	switch (p_meta) {
2152 		case GodotTypeInfo::METADATA_INT_IS_INT8:
2153 			return "sbyte";
2154 			break;
2155 		case GodotTypeInfo::METADATA_INT_IS_INT16:
2156 			return "short";
2157 			break;
2158 		case GodotTypeInfo::METADATA_INT_IS_INT32:
2159 			return "int";
2160 			break;
2161 		case GodotTypeInfo::METADATA_INT_IS_INT64:
2162 			return "long";
2163 			break;
2164 		case GodotTypeInfo::METADATA_INT_IS_UINT8:
2165 			return "byte";
2166 			break;
2167 		case GodotTypeInfo::METADATA_INT_IS_UINT16:
2168 			return "ushort";
2169 			break;
2170 		case GodotTypeInfo::METADATA_INT_IS_UINT32:
2171 			return "uint";
2172 			break;
2173 		case GodotTypeInfo::METADATA_INT_IS_UINT64:
2174 			return "ulong";
2175 			break;
2176 		default:
2177 			// Assume INT32
2178 			return "int";
2179 	}
2180 }
2181 
_get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta)2182 StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
2183 
2184 	switch (p_meta) {
2185 		case GodotTypeInfo::METADATA_REAL_IS_FLOAT:
2186 			return "float";
2187 			break;
2188 		case GodotTypeInfo::METADATA_REAL_IS_DOUBLE:
2189 			return "double";
2190 			break;
2191 		default:
2192 			// Assume real_t (float or double depending of REAL_T_IS_DOUBLE)
2193 #ifdef REAL_T_IS_DOUBLE
2194 			return "double";
2195 #else
2196 			return "float";
2197 #endif
2198 	}
2199 }
2200 
_populate_object_type_interfaces()2201 bool BindingsGenerator::_populate_object_type_interfaces() {
2202 
2203 	obj_types.clear();
2204 
2205 	List<StringName> class_list;
2206 	ClassDB::get_class_list(&class_list);
2207 	class_list.sort_custom<StringName::AlphCompare>();
2208 
2209 	while (class_list.size()) {
2210 		StringName type_cname = class_list.front()->get();
2211 
2212 		ClassDB::APIType api_type = ClassDB::get_api_type(type_cname);
2213 
2214 		if (api_type == ClassDB::API_NONE) {
2215 			class_list.pop_front();
2216 			continue;
2217 		}
2218 
2219 		if (!ClassDB::is_class_exposed(type_cname)) {
2220 			_log("Ignoring type '%s' because it's not exposed\n", String(type_cname).utf8().get_data());
2221 			class_list.pop_front();
2222 			continue;
2223 		}
2224 
2225 		if (!ClassDB::is_class_enabled(type_cname)) {
2226 			_log("Ignoring type '%s' because it's not enabled\n", String(type_cname).utf8().get_data());
2227 			class_list.pop_front();
2228 			continue;
2229 		}
2230 
2231 		ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(type_cname);
2232 
2233 		TypeInterface itype = TypeInterface::create_object_type(type_cname, api_type);
2234 
2235 		itype.base_name = ClassDB::get_parent_class(type_cname);
2236 		itype.is_singleton = Engine::get_singleton()->has_singleton(itype.proxy_name);
2237 		itype.is_instantiable = class_info->creation_func && !itype.is_singleton;
2238 		itype.is_reference = ClassDB::is_parent_class(type_cname, name_cache.type_Reference);
2239 		itype.memory_own = itype.is_reference;
2240 
2241 		itype.c_out = "\treturn ";
2242 		itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
2243 		itype.c_out += itype.is_reference ? "(%1.ptr());\n" : "(%1);\n";
2244 
2245 		itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)";
2246 
2247 		itype.c_type = "Object*";
2248 		itype.c_type_in = itype.c_type;
2249 		itype.c_type_out = "MonoObject*";
2250 		itype.cs_type = itype.proxy_name;
2251 		itype.im_type_in = "IntPtr";
2252 		itype.im_type_out = itype.proxy_name;
2253 
2254 		// Populate properties
2255 
2256 		List<PropertyInfo> property_list;
2257 		ClassDB::get_property_list(type_cname, &property_list, true);
2258 
2259 		Map<StringName, StringName> accessor_methods;
2260 
2261 		for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
2262 			const PropertyInfo &property = E->get();
2263 
2264 			if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_CATEGORY)
2265 				continue;
2266 
2267 			PropertyInterface iprop;
2268 			iprop.cname = property.name;
2269 			iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname);
2270 			iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname);
2271 
2272 			if (iprop.setter != StringName())
2273 				accessor_methods[iprop.setter] = iprop.cname;
2274 			if (iprop.getter != StringName())
2275 				accessor_methods[iprop.getter] = iprop.cname;
2276 
2277 			bool valid = false;
2278 			iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid);
2279 			ERR_FAIL_COND_V(!valid, false);
2280 
2281 			iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname));
2282 
2283 			// Prevent the property and its enclosing type from sharing the same name
2284 			if (iprop.proxy_name == itype.proxy_name) {
2285 				_log("Name of property '%s' is ambiguous with the name of its enclosing class '%s'. Renaming property to '%s_'\n",
2286 						iprop.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), iprop.proxy_name.utf8().get_data());
2287 
2288 				iprop.proxy_name += "_";
2289 			}
2290 
2291 			iprop.proxy_name = iprop.proxy_name.replace("/", "__"); // Some members have a slash...
2292 
2293 			iprop.prop_doc = NULL;
2294 
2295 			for (int i = 0; i < itype.class_doc->properties.size(); i++) {
2296 				const DocData::PropertyDoc &prop_doc = itype.class_doc->properties[i];
2297 
2298 				if (prop_doc.name == iprop.cname) {
2299 					iprop.prop_doc = &prop_doc;
2300 					break;
2301 				}
2302 			}
2303 
2304 			itype.properties.push_back(iprop);
2305 		}
2306 
2307 		// Populate methods
2308 
2309 		List<MethodInfo> virtual_method_list;
2310 		ClassDB::get_virtual_methods(type_cname, &virtual_method_list, true);
2311 
2312 		List<MethodInfo> method_list;
2313 		ClassDB::get_method_list(type_cname, &method_list, true);
2314 		method_list.sort();
2315 
2316 		for (List<MethodInfo>::Element *E = method_list.front(); E; E = E->next()) {
2317 			const MethodInfo &method_info = E->get();
2318 
2319 			int argc = method_info.arguments.size();
2320 
2321 			if (method_info.name.empty())
2322 				continue;
2323 
2324 			String cname = method_info.name;
2325 
2326 			if (blacklisted_methods.find(itype.cname) && blacklisted_methods[itype.cname].find(cname))
2327 				continue;
2328 
2329 			MethodInterface imethod;
2330 			imethod.name = method_info.name;
2331 			imethod.cname = cname;
2332 
2333 			if (method_info.flags & METHOD_FLAG_VIRTUAL)
2334 				imethod.is_virtual = true;
2335 
2336 			PropertyInfo return_info = method_info.return_val;
2337 
2338 			MethodBind *m = imethod.is_virtual ? NULL : ClassDB::get_method(type_cname, method_info.name);
2339 
2340 			imethod.is_vararg = m && m->is_vararg();
2341 
2342 			if (!m && !imethod.is_virtual) {
2343 				ERR_FAIL_COND_V_MSG(!virtual_method_list.find(method_info), false,
2344 						"Missing MethodBind for non-virtual method: '" + itype.name + "." + imethod.name + "'.");
2345 
2346 				// A virtual method without the virtual flag. This is a special case.
2347 
2348 				// There is no method bind, so let's fallback to Godot's object.Call(string, params)
2349 				imethod.requires_object_call = true;
2350 
2351 				// The method Object.free is registered as a virtual method, but without the virtual flag.
2352 				// This is because this method is not supposed to be overridden, but called.
2353 				// We assume the return type is void.
2354 				imethod.return_type.cname = name_cache.type_void;
2355 
2356 				// Actually, more methods like this may be added in the future,
2357 				// which could actually will return something different.
2358 				// Let's put this to notify us if that ever happens.
2359 				if (itype.cname != name_cache.type_Object || imethod.name != "free") {
2360 					WARN_PRINTS("Notification: New unexpected virtual non-overridable method found."
2361 								" We only expected Object.free, but found '" +
2362 								itype.name + "." + imethod.name + "'.");
2363 				}
2364 			} else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
2365 				imethod.return_type.cname = return_info.class_name;
2366 				imethod.return_type.is_enum = true;
2367 			} else if (return_info.class_name != StringName()) {
2368 				imethod.return_type.cname = return_info.class_name;
2369 				if (!imethod.is_virtual && ClassDB::is_parent_class(return_info.class_name, name_cache.type_Reference) && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE) {
2370 					/* clang-format off */
2371 					ERR_PRINTS("Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'."
2372 							" Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'.");
2373 					/* clang-format on */
2374 					ERR_FAIL_V(false);
2375 				}
2376 			} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
2377 				imethod.return_type.cname = return_info.hint_string;
2378 			} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
2379 				imethod.return_type.cname = name_cache.type_Variant;
2380 			} else if (return_info.type == Variant::NIL) {
2381 				imethod.return_type.cname = name_cache.type_void;
2382 			} else {
2383 				if (return_info.type == Variant::INT) {
2384 					imethod.return_type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE);
2385 				} else if (return_info.type == Variant::REAL) {
2386 					imethod.return_type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE);
2387 				} else {
2388 					imethod.return_type.cname = Variant::get_type_name(return_info.type);
2389 				}
2390 			}
2391 
2392 			for (int i = 0; i < argc; i++) {
2393 				PropertyInfo arginfo = method_info.arguments[i];
2394 
2395 				String orig_arg_name = arginfo.name;
2396 
2397 				ArgumentInterface iarg;
2398 				iarg.name = orig_arg_name;
2399 
2400 				if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
2401 					iarg.type.cname = arginfo.class_name;
2402 					iarg.type.is_enum = true;
2403 				} else if (arginfo.class_name != StringName()) {
2404 					iarg.type.cname = arginfo.class_name;
2405 				} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
2406 					iarg.type.cname = arginfo.hint_string;
2407 				} else if (arginfo.type == Variant::NIL) {
2408 					iarg.type.cname = name_cache.type_Variant;
2409 				} else {
2410 					if (arginfo.type == Variant::INT) {
2411 						iarg.type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE);
2412 					} else if (arginfo.type == Variant::REAL) {
2413 						iarg.type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE);
2414 					} else {
2415 						iarg.type.cname = Variant::get_type_name(arginfo.type);
2416 					}
2417 				}
2418 
2419 				iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
2420 
2421 				if (m && m->has_default_argument(i)) {
2422 					bool defval_ok = _arg_default_value_from_variant(m->get_default_argument(i), iarg);
2423 					ERR_FAIL_COND_V_MSG(!defval_ok, false,
2424 							"Cannot determine default value for argument '" + orig_arg_name + "' of method '" + itype.name + "." + imethod.name + "'.");
2425 				}
2426 
2427 				imethod.add_argument(iarg);
2428 			}
2429 
2430 			if (imethod.is_vararg) {
2431 				ArgumentInterface ivararg;
2432 				ivararg.type.cname = name_cache.type_VarArg;
2433 				ivararg.name = "@args";
2434 				imethod.add_argument(ivararg);
2435 			}
2436 
2437 			imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name));
2438 
2439 			// Prevent the method and its enclosing type from sharing the same name
2440 			if (imethod.proxy_name == itype.proxy_name) {
2441 				_log("Name of method '%s' is ambiguous with the name of its enclosing class '%s'. Renaming method to '%s_'\n",
2442 						imethod.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), imethod.proxy_name.utf8().get_data());
2443 
2444 				imethod.proxy_name += "_";
2445 			}
2446 
2447 			Map<StringName, StringName>::Element *accessor = accessor_methods.find(imethod.cname);
2448 			if (accessor) {
2449 				const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value());
2450 
2451 				// We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also
2452 				// we don't know if an accessor method in a different class could have other purposes, so better leave those untouched.
2453 				imethod.is_deprecated = true;
2454 				imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead.";
2455 			}
2456 
2457 			if (itype.class_doc) {
2458 				for (int i = 0; i < itype.class_doc->methods.size(); i++) {
2459 					if (itype.class_doc->methods[i].name == imethod.name) {
2460 						imethod.method_doc = &itype.class_doc->methods[i];
2461 						break;
2462 					}
2463 				}
2464 			}
2465 
2466 			if (!imethod.is_virtual && imethod.name[0] == '_') {
2467 				for (const List<PropertyInterface>::Element *F = itype.properties.front(); F; F = F->next()) {
2468 					const PropertyInterface &iprop = F->get();
2469 
2470 					if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
2471 						imethod.is_internal = true;
2472 						itype.methods.push_back(imethod);
2473 						break;
2474 					}
2475 				}
2476 			} else {
2477 				itype.methods.push_back(imethod);
2478 			}
2479 		}
2480 
2481 		// Populate enums and constants
2482 
2483 		List<String> constants;
2484 		ClassDB::get_integer_constant_list(type_cname, &constants, true);
2485 
2486 		const HashMap<StringName, List<StringName> > &enum_map = class_info->enum_map;
2487 		const StringName *k = NULL;
2488 
2489 		while ((k = enum_map.next(k))) {
2490 			StringName enum_proxy_cname = *k;
2491 			String enum_proxy_name = enum_proxy_cname.operator String();
2492 			if (itype.find_property_by_proxy_name(enum_proxy_cname)) {
2493 				// We have several conflicts between enums and PascalCase properties,
2494 				// so we append 'Enum' to the enum name in those cases.
2495 				enum_proxy_name += "Enum";
2496 				enum_proxy_cname = StringName(enum_proxy_name);
2497 			}
2498 			EnumInterface ienum(enum_proxy_cname);
2499 			const List<StringName> &enum_constants = enum_map.get(*k);
2500 			for (const List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) {
2501 				const StringName &constant_cname = E->get();
2502 				String constant_name = constant_cname.operator String();
2503 				int *value = class_info->constant_map.getptr(constant_cname);
2504 				ERR_FAIL_NULL_V(value, false);
2505 				constants.erase(constant_name);
2506 
2507 				ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
2508 
2509 				iconstant.const_doc = NULL;
2510 				for (int i = 0; i < itype.class_doc->constants.size(); i++) {
2511 					const DocData::ConstantDoc &const_doc = itype.class_doc->constants[i];
2512 
2513 					if (const_doc.name == iconstant.name) {
2514 						iconstant.const_doc = &const_doc;
2515 						break;
2516 					}
2517 				}
2518 
2519 				ienum.constants.push_back(iconstant);
2520 			}
2521 
2522 			int prefix_length = _determine_enum_prefix(ienum);
2523 
2524 			_apply_prefix_to_enum_constants(ienum, prefix_length);
2525 
2526 			itype.enums.push_back(ienum);
2527 
2528 			TypeInterface enum_itype;
2529 			enum_itype.is_enum = true;
2530 			enum_itype.name = itype.name + "." + String(*k);
2531 			enum_itype.cname = StringName(enum_itype.name);
2532 			enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
2533 			TypeInterface::postsetup_enum_type(enum_itype);
2534 			enum_types.insert(enum_itype.cname, enum_itype);
2535 		}
2536 
2537 		for (const List<String>::Element *E = constants.front(); E; E = E->next()) {
2538 			const String &constant_name = E->get();
2539 			int *value = class_info->constant_map.getptr(StringName(E->get()));
2540 			ERR_FAIL_NULL_V(value, false);
2541 
2542 			ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
2543 
2544 			iconstant.const_doc = NULL;
2545 			for (int i = 0; i < itype.class_doc->constants.size(); i++) {
2546 				const DocData::ConstantDoc &const_doc = itype.class_doc->constants[i];
2547 
2548 				if (const_doc.name == iconstant.name) {
2549 					iconstant.const_doc = &const_doc;
2550 					break;
2551 				}
2552 			}
2553 
2554 			itype.constants.push_back(iconstant);
2555 		}
2556 
2557 		obj_types.insert(itype.cname, itype);
2558 
2559 		class_list.pop_front();
2560 	}
2561 
2562 	return true;
2563 }
2564 
_arg_default_value_from_variant(const Variant & p_val,ArgumentInterface & r_iarg)2565 bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, ArgumentInterface &r_iarg) {
2566 
2567 	r_iarg.default_argument = p_val;
2568 
2569 	switch (p_val.get_type()) {
2570 		case Variant::NIL:
2571 			// Either Object type or Variant
2572 			r_iarg.default_argument = "null";
2573 			break;
2574 		// Atomic types
2575 		case Variant::BOOL:
2576 			r_iarg.default_argument = bool(p_val) ? "true" : "false";
2577 			break;
2578 		case Variant::INT:
2579 			if (r_iarg.type.cname != name_cache.type_int) {
2580 				r_iarg.default_argument = "(%s)" + r_iarg.default_argument;
2581 			}
2582 			break;
2583 		case Variant::REAL:
2584 #ifndef REAL_T_IS_DOUBLE
2585 			r_iarg.default_argument += "f";
2586 #endif
2587 			break;
2588 		case Variant::STRING:
2589 		case Variant::NODE_PATH:
2590 			r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
2591 			break;
2592 		case Variant::TRANSFORM:
2593 			if (p_val.operator Transform() == Transform())
2594 				r_iarg.default_argument.clear();
2595 			r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")";
2596 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
2597 			break;
2598 		case Variant::PLANE:
2599 		case Variant::AABB:
2600 		case Variant::COLOR:
2601 			r_iarg.default_argument = "new Color(1, 1, 1, 1)";
2602 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
2603 			break;
2604 		case Variant::VECTOR2:
2605 		case Variant::RECT2:
2606 		case Variant::VECTOR3:
2607 			r_iarg.default_argument = "new %s" + r_iarg.default_argument;
2608 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
2609 			break;
2610 		case Variant::OBJECT:
2611 			ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
2612 					"Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
2613 
2614 			r_iarg.default_argument = "null";
2615 			break;
2616 		case Variant::DICTIONARY:
2617 			r_iarg.default_argument = "new %s()";
2618 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
2619 			break;
2620 		case Variant::_RID:
2621 			ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_RID, false,
2622 					"Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_RID) + "'.");
2623 
2624 			ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
2625 					"Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
2626 
2627 			r_iarg.default_argument = "null";
2628 			break;
2629 		case Variant::ARRAY:
2630 		case Variant::POOL_BYTE_ARRAY:
2631 		case Variant::POOL_INT_ARRAY:
2632 		case Variant::POOL_REAL_ARRAY:
2633 		case Variant::POOL_STRING_ARRAY:
2634 		case Variant::POOL_VECTOR2_ARRAY:
2635 		case Variant::POOL_VECTOR3_ARRAY:
2636 		case Variant::POOL_COLOR_ARRAY:
2637 			r_iarg.default_argument = "new %s {}";
2638 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
2639 			break;
2640 		case Variant::TRANSFORM2D:
2641 		case Variant::BASIS:
2642 		case Variant::QUAT:
2643 			r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity";
2644 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
2645 			break;
2646 		default: {
2647 		}
2648 	}
2649 
2650 	if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
2651 		r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
2652 
2653 	return true;
2654 }
2655 
_populate_builtin_type_interfaces()2656 void BindingsGenerator::_populate_builtin_type_interfaces() {
2657 
2658 	builtin_types.clear();
2659 
2660 	TypeInterface itype;
2661 
2662 #define INSERT_STRUCT_TYPE(m_type)                                     \
2663 	{                                                                  \
2664 		itype = TypeInterface::create_value_type(String(#m_type));     \
2665 		itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \
2666 		itype.c_out = "\t*%3 = MARSHALLED_OUT(" #m_type ", %1);\n";    \
2667 		itype.c_arg_in = "&%s_in";                                     \
2668 		itype.c_type_in = "GDMonoMarshal::M_" #m_type "*";             \
2669 		itype.c_type_out = "GDMonoMarshal::M_" #m_type;                \
2670 		itype.cs_in = "ref %s";                                        \
2671 		/* in cs_out, im_type_out (%3) includes the 'out ' part */     \
2672 		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";        \
2673 		itype.im_type_out = "out " + itype.cs_type;                    \
2674 		itype.ret_as_byref_arg = true;                                 \
2675 		builtin_types.insert(itype.cname, itype);                      \
2676 	}
2677 
2678 	INSERT_STRUCT_TYPE(Vector2)
2679 	INSERT_STRUCT_TYPE(Rect2)
2680 	INSERT_STRUCT_TYPE(Transform2D)
2681 	INSERT_STRUCT_TYPE(Vector3)
2682 	INSERT_STRUCT_TYPE(Basis)
2683 	INSERT_STRUCT_TYPE(Quat)
2684 	INSERT_STRUCT_TYPE(Transform)
2685 	INSERT_STRUCT_TYPE(AABB)
2686 	INSERT_STRUCT_TYPE(Color)
2687 	INSERT_STRUCT_TYPE(Plane)
2688 
2689 #undef INSERT_STRUCT_TYPE
2690 
2691 	// bool
2692 	itype = TypeInterface::create_value_type(String("bool"));
2693 	{
2694 		// MonoBoolean <---> bool
2695 		itype.c_in = "\t%0 %1_in = (%0)%1;\n";
2696 		itype.c_out = "\treturn (%0)%1;\n";
2697 		itype.c_type = "bool";
2698 		itype.c_type_in = "MonoBoolean";
2699 		itype.c_type_out = itype.c_type_in;
2700 		itype.c_arg_in = "&%s_in";
2701 	}
2702 	itype.im_type_in = itype.name;
2703 	itype.im_type_out = itype.name;
2704 	builtin_types.insert(itype.cname, itype);
2705 
2706 	// Integer types
2707 	{
2708 		// C interface for 'uint32_t' is the same as that of enums. Remember to apply
2709 		// any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
2710 #define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type)        \
2711 	{                                                             \
2712 		itype = TypeInterface::create_value_type(String(m_name)); \
2713 		{                                                         \
2714 			itype.c_in = "\t%0 %1_in = (%0)%1;\n";                \
2715 			itype.c_out = "\treturn (%0)%1;\n";                   \
2716 			itype.c_type = #m_c_type;                             \
2717 			itype.c_arg_in = "&%s_in";                            \
2718 		}                                                         \
2719 		itype.c_type_in = #m_c_type_in_out;                       \
2720 		itype.c_type_out = itype.c_type_in;                       \
2721 		itype.im_type_in = itype.name;                            \
2722 		itype.im_type_out = itype.name;                           \
2723 		builtin_types.insert(itype.cname, itype);                 \
2724 	}
2725 
2726 		// The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
2727 
2728 		INSERT_INT_TYPE("sbyte", int8_t, int64_t);
2729 		INSERT_INT_TYPE("short", int16_t, int64_t);
2730 		INSERT_INT_TYPE("int", int32_t, int64_t);
2731 		INSERT_INT_TYPE("byte", uint8_t, int64_t);
2732 		INSERT_INT_TYPE("ushort", uint16_t, int64_t);
2733 		INSERT_INT_TYPE("uint", uint32_t, int64_t);
2734 
2735 		itype = TypeInterface::create_value_type(String("long"));
2736 		{
2737 			itype.c_out = "\treturn (%0)%1;\n";
2738 			itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
2739 			itype.c_out = "\t*%3 = (%0)%1;\n";
2740 			itype.c_type = "int64_t";
2741 			itype.c_arg_in = "&%s_in";
2742 		}
2743 		itype.c_type_in = "int64_t*";
2744 		itype.c_type_out = "int64_t";
2745 		itype.im_type_in = "ref " + itype.name;
2746 		itype.im_type_out = "out " + itype.name;
2747 		itype.cs_in = "ref %0";
2748 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
2749 		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
2750 		itype.ret_as_byref_arg = true;
2751 		builtin_types.insert(itype.cname, itype);
2752 
2753 		itype = TypeInterface::create_value_type(String("ulong"));
2754 		{
2755 			itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
2756 			itype.c_out = "\t*%3 = (%0)%1;\n";
2757 			itype.c_type = "int64_t";
2758 			itype.c_arg_in = "&%s_in";
2759 		}
2760 		itype.c_type_in = "uint64_t*";
2761 		itype.c_type_out = "uint64_t";
2762 		itype.im_type_in = "ref " + itype.name;
2763 		itype.im_type_out = "out " + itype.name;
2764 		itype.cs_in = "ref %0";
2765 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
2766 		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
2767 		itype.ret_as_byref_arg = true;
2768 		builtin_types.insert(itype.cname, itype);
2769 	}
2770 
2771 	// Floating point types
2772 	{
2773 		// float
2774 		itype = TypeInterface();
2775 		itype.name = "float";
2776 		itype.cname = itype.name;
2777 		itype.proxy_name = "float";
2778 		{
2779 			// The expected type for 'float' in ptrcall is 'double'
2780 			itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
2781 			itype.c_out = "\t*%3 = (%0)%1;\n";
2782 			itype.c_type = "double";
2783 			itype.c_type_in = "float*";
2784 			itype.c_type_out = "float";
2785 			itype.c_arg_in = "&%s_in";
2786 		}
2787 		itype.cs_type = itype.proxy_name;
2788 		itype.im_type_in = "ref " + itype.proxy_name;
2789 		itype.im_type_out = "out " + itype.proxy_name;
2790 		itype.cs_in = "ref %0";
2791 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
2792 		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
2793 		itype.ret_as_byref_arg = true;
2794 		builtin_types.insert(itype.cname, itype);
2795 
2796 		// double
2797 		itype = TypeInterface();
2798 		itype.name = "double";
2799 		itype.cname = itype.name;
2800 		itype.proxy_name = "double";
2801 		{
2802 			itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
2803 			itype.c_out = "\t*%3 = (%0)%1;\n";
2804 			itype.c_type = "double";
2805 			itype.c_type_in = "double*";
2806 			itype.c_type_out = "double";
2807 			itype.c_arg_in = "&%s_in";
2808 		}
2809 		itype.cs_type = itype.proxy_name;
2810 		itype.im_type_in = "ref " + itype.proxy_name;
2811 		itype.im_type_out = "out " + itype.proxy_name;
2812 		itype.cs_in = "ref %0";
2813 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
2814 		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
2815 		itype.ret_as_byref_arg = true;
2816 		builtin_types.insert(itype.cname, itype);
2817 	}
2818 
2819 	// String
2820 	itype = TypeInterface();
2821 	itype.name = "String";
2822 	itype.cname = itype.name;
2823 	itype.proxy_name = "string";
2824 	itype.c_in = "\t%0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
2825 	itype.c_out = "\treturn " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n";
2826 	itype.c_arg_in = "&%s_in";
2827 	itype.c_type = itype.name;
2828 	itype.c_type_in = "MonoString*";
2829 	itype.c_type_out = "MonoString*";
2830 	itype.cs_type = itype.proxy_name;
2831 	itype.im_type_in = itype.proxy_name;
2832 	itype.im_type_out = itype.proxy_name;
2833 	builtin_types.insert(itype.cname, itype);
2834 
2835 	// NodePath
2836 	itype = TypeInterface();
2837 	itype.name = "NodePath";
2838 	itype.cname = itype.name;
2839 	itype.proxy_name = "NodePath";
2840 	itype.c_out = "\treturn memnew(NodePath(%1));\n";
2841 	itype.c_type = itype.name;
2842 	itype.c_type_in = itype.c_type + "*";
2843 	itype.c_type_out = itype.c_type + "*";
2844 	itype.cs_type = itype.proxy_name;
2845 	itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)";
2846 	itype.cs_out = "return new %2(%0(%1));";
2847 	itype.im_type_in = "IntPtr";
2848 	itype.im_type_out = "IntPtr";
2849 	builtin_types.insert(itype.cname, itype);
2850 
2851 	// RID
2852 	itype = TypeInterface();
2853 	itype.name = "RID";
2854 	itype.cname = itype.name;
2855 	itype.proxy_name = "RID";
2856 	itype.c_out = "\treturn memnew(RID(%1));\n";
2857 	itype.c_type = itype.name;
2858 	itype.c_type_in = itype.c_type + "*";
2859 	itype.c_type_out = itype.c_type + "*";
2860 	itype.cs_type = itype.proxy_name;
2861 	itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)";
2862 	itype.cs_out = "return new %2(%0(%1));";
2863 	itype.im_type_in = "IntPtr";
2864 	itype.im_type_out = "IntPtr";
2865 	builtin_types.insert(itype.cname, itype);
2866 
2867 	// Variant
2868 	itype = TypeInterface();
2869 	itype.name = "Variant";
2870 	itype.cname = itype.name;
2871 	itype.proxy_name = "object";
2872 	itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n";
2873 	itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_VARIANT "(%1);\n";
2874 	itype.c_arg_in = "&%s_in";
2875 	itype.c_type = itype.name;
2876 	itype.c_type_in = "MonoObject*";
2877 	itype.c_type_out = "MonoObject*";
2878 	itype.cs_type = itype.proxy_name;
2879 	itype.im_type_in = "object";
2880 	itype.im_type_out = itype.proxy_name;
2881 	builtin_types.insert(itype.cname, itype);
2882 
2883 	// VarArg (fictitious type to represent variable arguments)
2884 	itype = TypeInterface();
2885 	itype.name = "VarArg";
2886 	itype.cname = itype.name;
2887 	itype.proxy_name = "object[]";
2888 	itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(Array) "(%1);\n";
2889 	itype.c_arg_in = "&%s_in";
2890 	itype.c_type = "Array";
2891 	itype.c_type_in = "MonoArray*";
2892 	itype.cs_type = "params object[]";
2893 	itype.im_type_in = "object[]";
2894 	builtin_types.insert(itype.cname, itype);
2895 
2896 #define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t)                          \
2897 	{                                                                         \
2898 		itype = TypeInterface();                                              \
2899 		itype.name = #m_name;                                                 \
2900 		itype.cname = itype.name;                                             \
2901 		itype.proxy_name = #m_proxy_t "[]";                                   \
2902 		itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \
2903 		itype.c_out = "\treturn " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n";  \
2904 		itype.c_arg_in = "&%s_in";                                            \
2905 		itype.c_type = #m_type;                                               \
2906 		itype.c_type_in = "MonoArray*";                                       \
2907 		itype.c_type_out = "MonoArray*";                                      \
2908 		itype.cs_type = itype.proxy_name;                                     \
2909 		itype.im_type_in = itype.proxy_name;                                  \
2910 		itype.im_type_out = itype.proxy_name;                                 \
2911 		builtin_types.insert(itype.name, itype);                              \
2912 	}
2913 
2914 #define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
2915 
2916 	INSERT_ARRAY(PoolIntArray, int);
2917 	INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte);
2918 
2919 #ifdef REAL_T_IS_DOUBLE
2920 	INSERT_ARRAY(PoolRealArray, double);
2921 #else
2922 	INSERT_ARRAY(PoolRealArray, float);
2923 #endif
2924 
2925 	INSERT_ARRAY(PoolStringArray, string);
2926 
2927 	INSERT_ARRAY(PoolColorArray, Color);
2928 	INSERT_ARRAY(PoolVector2Array, Vector2);
2929 	INSERT_ARRAY(PoolVector3Array, Vector3);
2930 
2931 #undef INSERT_ARRAY
2932 
2933 	// Array
2934 	itype = TypeInterface();
2935 	itype.name = "Array";
2936 	itype.cname = itype.name;
2937 	itype.proxy_name = itype.name;
2938 	itype.c_out = "\treturn memnew(Array(%1));\n";
2939 	itype.c_type = itype.name;
2940 	itype.c_type_in = itype.c_type + "*";
2941 	itype.c_type_out = itype.c_type + "*";
2942 	itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
2943 	itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
2944 	itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
2945 	itype.im_type_in = "IntPtr";
2946 	itype.im_type_out = "IntPtr";
2947 	builtin_types.insert(itype.cname, itype);
2948 
2949 	// Dictionary
2950 	itype = TypeInterface();
2951 	itype.name = "Dictionary";
2952 	itype.cname = itype.name;
2953 	itype.proxy_name = itype.name;
2954 	itype.c_out = "\treturn memnew(Dictionary(%1));\n";
2955 	itype.c_type = itype.name;
2956 	itype.c_type_in = itype.c_type + "*";
2957 	itype.c_type_out = itype.c_type + "*";
2958 	itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
2959 	itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
2960 	itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
2961 	itype.im_type_in = "IntPtr";
2962 	itype.im_type_out = "IntPtr";
2963 	builtin_types.insert(itype.cname, itype);
2964 
2965 	// void (fictitious type to represent the return type of methods that do not return anything)
2966 	itype = TypeInterface();
2967 	itype.name = "void";
2968 	itype.cname = itype.name;
2969 	itype.proxy_name = itype.name;
2970 	itype.c_type = itype.name;
2971 	itype.c_type_in = itype.c_type;
2972 	itype.c_type_out = itype.c_type;
2973 	itype.cs_type = itype.proxy_name;
2974 	itype.im_type_in = itype.proxy_name;
2975 	itype.im_type_out = itype.proxy_name;
2976 	builtin_types.insert(itype.cname, itype);
2977 }
2978 
_populate_global_constants()2979 void BindingsGenerator::_populate_global_constants() {
2980 
2981 	int global_constants_count = GlobalConstants::get_global_constant_count();
2982 
2983 	if (global_constants_count > 0) {
2984 		Map<String, DocData::ClassDoc>::Element *match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
2985 
2986 		CRASH_COND_MSG(!match, "Could not find '@GlobalScope' in DocData.");
2987 
2988 		const DocData::ClassDoc &global_scope_doc = match->value();
2989 
2990 		for (int i = 0; i < global_constants_count; i++) {
2991 
2992 			String constant_name = GlobalConstants::get_global_constant_name(i);
2993 
2994 			const DocData::ConstantDoc *const_doc = NULL;
2995 			for (int j = 0; j < global_scope_doc.constants.size(); j++) {
2996 				const DocData::ConstantDoc &curr_const_doc = global_scope_doc.constants[j];
2997 
2998 				if (curr_const_doc.name == constant_name) {
2999 					const_doc = &curr_const_doc;
3000 					break;
3001 				}
3002 			}
3003 
3004 			int constant_value = GlobalConstants::get_global_constant_value(i);
3005 			StringName enum_name = GlobalConstants::get_global_constant_enum(i);
3006 
3007 			ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), constant_value);
3008 			iconstant.const_doc = const_doc;
3009 
3010 			if (enum_name != StringName()) {
3011 				EnumInterface ienum(enum_name);
3012 				List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
3013 				if (enum_match) {
3014 					enum_match->get().constants.push_back(iconstant);
3015 				} else {
3016 					ienum.constants.push_back(iconstant);
3017 					global_enums.push_back(ienum);
3018 				}
3019 			} else {
3020 				global_constants.push_back(iconstant);
3021 			}
3022 		}
3023 
3024 		for (List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
3025 			EnumInterface &ienum = E->get();
3026 
3027 			TypeInterface enum_itype;
3028 			enum_itype.is_enum = true;
3029 			enum_itype.name = ienum.cname.operator String();
3030 			enum_itype.cname = ienum.cname;
3031 			enum_itype.proxy_name = enum_itype.name;
3032 			TypeInterface::postsetup_enum_type(enum_itype);
3033 			enum_types.insert(enum_itype.cname, enum_itype);
3034 
3035 			int prefix_length = _determine_enum_prefix(ienum);
3036 
3037 			// HARDCODED: The Error enum have the prefix 'ERR_' for everything except 'OK' and 'FAILED'.
3038 			if (ienum.cname == name_cache.enum_Error) {
3039 				if (prefix_length > 0) { // Just in case it ever changes
3040 					ERR_PRINTS("Prefix for enum '" _STR(Error) "' is not empty.");
3041 				}
3042 
3043 				prefix_length = 1; // 'ERR_'
3044 			}
3045 
3046 			_apply_prefix_to_enum_constants(ienum, prefix_length);
3047 		}
3048 	}
3049 
3050 	// HARDCODED
3051 	List<StringName> hardcoded_enums;
3052 	hardcoded_enums.push_back("Vector3.Axis");
3053 	for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) {
3054 		// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
3055 		// Here, we assume core types do not begin with underscore
3056 		TypeInterface enum_itype;
3057 		enum_itype.is_enum = true;
3058 		enum_itype.name = E->get().operator String();
3059 		enum_itype.cname = E->get();
3060 		enum_itype.proxy_name = enum_itype.name;
3061 		TypeInterface::postsetup_enum_type(enum_itype);
3062 		enum_types.insert(enum_itype.cname, enum_itype);
3063 	}
3064 }
3065 
_initialize_blacklisted_methods()3066 void BindingsGenerator::_initialize_blacklisted_methods() {
3067 
3068 	blacklisted_methods["Object"].push_back("to_string"); // there is already ToString
3069 	blacklisted_methods["Object"].push_back("_to_string"); // override ToString instead
3070 	blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it)
3071 }
3072 
_log(const char * p_format,...)3073 void BindingsGenerator::_log(const char *p_format, ...) {
3074 
3075 	if (log_print_enabled) {
3076 		va_list list;
3077 
3078 		va_start(list, p_format);
3079 		OS::get_singleton()->print("%s", str_format(p_format, list).utf8().get_data());
3080 		va_end(list);
3081 	}
3082 }
3083 
_initialize()3084 void BindingsGenerator::_initialize() {
3085 
3086 	initialized = false;
3087 
3088 	EditorHelp::generate_doc();
3089 
3090 	enum_types.clear();
3091 
3092 	_initialize_blacklisted_methods();
3093 
3094 	bool obj_type_ok = _populate_object_type_interfaces();
3095 	ERR_FAIL_COND_MSG(!obj_type_ok, "Failed to generate object type interfaces");
3096 
3097 	_populate_builtin_type_interfaces();
3098 
3099 	_populate_global_constants();
3100 
3101 	// Generate internal calls (after populating type interfaces and global constants)
3102 
3103 	core_custom_icalls.clear();
3104 	editor_custom_icalls.clear();
3105 
3106 	for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next())
3107 		_generate_method_icalls(E.get());
3108 
3109 	initialized = true;
3110 }
3111 
handle_cmdline_args(const List<String> & p_cmdline_args)3112 void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
3113 
3114 	const int NUM_OPTIONS = 2;
3115 	String generate_all_glue_option = "--generate-mono-glue";
3116 	String generate_cs_glue_option = "--generate-mono-cs-glue";
3117 	String generate_cpp_glue_option = "--generate-mono-cpp-glue";
3118 
3119 	String glue_dir_path;
3120 	String cs_dir_path;
3121 	String cpp_dir_path;
3122 
3123 	int options_left = NUM_OPTIONS;
3124 
3125 	const List<String>::Element *elem = p_cmdline_args.front();
3126 
3127 	while (elem && options_left) {
3128 		if (elem->get() == generate_all_glue_option) {
3129 			const List<String>::Element *path_elem = elem->next();
3130 
3131 			if (path_elem) {
3132 				glue_dir_path = path_elem->get();
3133 				elem = elem->next();
3134 			} else {
3135 				ERR_PRINTS(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue').");
3136 			}
3137 
3138 			--options_left;
3139 		} else if (elem->get() == generate_cs_glue_option) {
3140 			const List<String>::Element *path_elem = elem->next();
3141 
3142 			if (path_elem) {
3143 				cs_dir_path = path_elem->get();
3144 				elem = elem->next();
3145 			} else {
3146 				ERR_PRINTS(generate_cs_glue_option + ": No output directory specified.");
3147 			}
3148 
3149 			--options_left;
3150 		} else if (elem->get() == generate_cpp_glue_option) {
3151 			const List<String>::Element *path_elem = elem->next();
3152 
3153 			if (path_elem) {
3154 				cpp_dir_path = path_elem->get();
3155 				elem = elem->next();
3156 			} else {
3157 				ERR_PRINTS(generate_cpp_glue_option + ": No output directory specified.");
3158 			}
3159 
3160 			--options_left;
3161 		}
3162 
3163 		elem = elem->next();
3164 	}
3165 
3166 	if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) {
3167 		BindingsGenerator bindings_generator;
3168 		bindings_generator.set_log_print_enabled(true);
3169 
3170 		if (!bindings_generator.initialized) {
3171 			ERR_PRINTS("Failed to initialize the bindings generator");
3172 			::exit(0);
3173 		}
3174 
3175 		if (glue_dir_path.length()) {
3176 			if (bindings_generator.generate_glue(glue_dir_path) != OK)
3177 				ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C++ glue.");
3178 
3179 			if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK)
3180 				ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C# API.");
3181 		}
3182 
3183 		if (cs_dir_path.length()) {
3184 			if (bindings_generator.generate_cs_api(cs_dir_path) != OK)
3185 				ERR_PRINTS(generate_cs_glue_option + ": Failed to generate the C# API.");
3186 		}
3187 
3188 		if (cpp_dir_path.length()) {
3189 			if (bindings_generator.generate_glue(cpp_dir_path) != OK)
3190 				ERR_PRINTS(generate_cpp_glue_option + ": Failed to generate the C++ glue.");
3191 		}
3192 
3193 		// Exit once done
3194 		::exit(0);
3195 	}
3196 }
3197 
3198 #endif
3199