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