bindings_generator.cpp 154 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246
  1. /**************************************************************************/
  2. /* bindings_generator.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  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. #include "bindings_generator.h"
  31. #if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
  32. #include "../godotsharp_defs.h"
  33. #include "../utils/naming_utils.h"
  34. #include "../utils/path_utils.h"
  35. #include "../utils/string_utils.h"
  36. #include "core/config/engine.h"
  37. #include "core/core_constants.h"
  38. #include "core/io/compression.h"
  39. #include "core/io/dir_access.h"
  40. #include "core/io/file_access.h"
  41. #include "core/os/os.h"
  42. #include "main/main.h"
  43. StringBuilder &operator<<(StringBuilder &r_sb, const String &p_string) {
  44. r_sb.append(p_string);
  45. return r_sb;
  46. }
  47. StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
  48. r_sb.append(p_cstring);
  49. return r_sb;
  50. }
  51. #define CS_INDENT " " // 4 whitespaces
  52. #define INDENT1 CS_INDENT
  53. #define INDENT2 INDENT1 INDENT1
  54. #define INDENT3 INDENT2 INDENT1
  55. #define INDENT4 INDENT3 INDENT1
  56. #define MEMBER_BEGIN "\n" INDENT1
  57. #define OPEN_BLOCK "{\n"
  58. #define CLOSE_BLOCK "}\n"
  59. #define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK
  60. #define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK
  61. #define CLOSE_BLOCK_L1 INDENT1 CLOSE_BLOCK
  62. #define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
  63. #define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
  64. #define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
  65. #define BINDINGS_NATIVE_NAME_FIELD "NativeName"
  66. #define CS_PARAM_MEMORYOWN "memoryOwn"
  67. #define CS_PARAM_METHODBIND "method"
  68. #define CS_PARAM_INSTANCE "ptr"
  69. #define CS_STATIC_METHOD_GETINSTANCE "GetPtr"
  70. #define CS_METHOD_CALL "Call"
  71. #define CS_PROPERTY_SINGLETON "Singleton"
  72. #define CS_SINGLETON_INSTANCE_SUFFIX "Instance"
  73. #define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
  74. #define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod"
  75. #define CS_METHOD_HAS_GODOT_CLASS_SIGNAL "HasGodotClassSignal"
  76. #define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
  77. #define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
  78. #define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_"
  79. #define CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX "SignalProxyName_"
  80. #define ICALL_PREFIX "godot_icall_"
  81. #define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method"
  82. #define ICALL_CLASSDB_GET_METHOD_WITH_COMPATIBILITY "ClassDB_get_method_with_compatibility"
  83. #define ICALL_CLASSDB_GET_CONSTRUCTOR "ClassDB_get_constructor"
  84. #define C_LOCAL_RET "ret"
  85. #define C_LOCAL_VARARG_RET "vararg_ret"
  86. #define C_LOCAL_PTRCALL_ARGS "call_args"
  87. #define C_CLASS_NATIVE_FUNCS "NativeFuncs"
  88. #define C_NS_MONOUTILS "InteropUtils"
  89. #define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged"
  90. #define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton"
  91. #define C_NS_MONOMARSHAL "Marshaling"
  92. #define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL ".ConvertStringToNative"
  93. #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL ".ConvertStringToManaged"
  94. #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL ".ConvertSystemArrayToNative" #m_type
  95. #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL ".ConvertNative" #m_type "ToSystemArray"
  96. #define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToNative"
  97. #define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToManaged"
  98. #define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToNative"
  99. #define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged"
  100. // Types that will be ignored by the generator and won't be available in C#.
  101. // This must be kept in sync with `ignored_types` in csharp_script.cpp
  102. const Vector<String> ignored_types = {};
  103. void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) {
  104. // C interface for enums is the same as that of 'uint32_t'. Remember to apply
  105. // any of the changes done here to the 'uint32_t' type interface as well.
  106. r_enum_itype.cs_type = r_enum_itype.proxy_name;
  107. r_enum_itype.cs_in_expr = "(int)%0";
  108. r_enum_itype.cs_out = "%5return (%2)%0(%1);";
  109. {
  110. // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
  111. r_enum_itype.c_in = "%5%0 %1_in = %1;\n";
  112. r_enum_itype.c_out = "%5return (%0)(%1);\n";
  113. r_enum_itype.c_type = "long";
  114. r_enum_itype.c_arg_in = "&%s_in";
  115. }
  116. r_enum_itype.c_type_in = "int";
  117. r_enum_itype.c_type_out = r_enum_itype.c_type_in;
  118. r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
  119. }
  120. static String fix_doc_description(const String &p_bbcode) {
  121. // This seems to be the correct way to do this. It's the same EditorHelp does.
  122. return p_bbcode.dedent()
  123. .replace("\t", "")
  124. .replace("\r", "")
  125. .strip_edges();
  126. }
  127. String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype, bool p_is_signal) {
  128. // Based on the version in EditorHelp
  129. if (p_bbcode.is_empty()) {
  130. return String();
  131. }
  132. DocTools *doc = EditorHelp::get_doc_data();
  133. String bbcode = p_bbcode;
  134. StringBuilder xml_output;
  135. xml_output.append("<para>");
  136. List<String> tag_stack;
  137. bool code_tag = false;
  138. bool line_del = false;
  139. int pos = 0;
  140. while (pos < bbcode.length()) {
  141. int brk_pos = bbcode.find("[", pos);
  142. if (brk_pos < 0) {
  143. brk_pos = bbcode.length();
  144. }
  145. if (brk_pos > pos) {
  146. if (!line_del) {
  147. String text = bbcode.substr(pos, brk_pos - pos);
  148. if (code_tag || tag_stack.size() > 0) {
  149. xml_output.append(text.xml_escape());
  150. } else {
  151. Vector<String> lines = text.split("\n");
  152. for (int i = 0; i < lines.size(); i++) {
  153. if (i != 0) {
  154. xml_output.append("<para>");
  155. }
  156. xml_output.append(lines[i].xml_escape());
  157. if (i != lines.size() - 1) {
  158. xml_output.append("</para>\n");
  159. }
  160. }
  161. }
  162. }
  163. }
  164. if (brk_pos == bbcode.length()) {
  165. break; // nothing else to add
  166. }
  167. int brk_end = bbcode.find("]", brk_pos + 1);
  168. if (brk_end == -1) {
  169. if (!line_del) {
  170. String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos);
  171. if (code_tag || tag_stack.size() > 0) {
  172. xml_output.append(text.xml_escape());
  173. } else {
  174. Vector<String> lines = text.split("\n");
  175. for (int i = 0; i < lines.size(); i++) {
  176. if (i != 0) {
  177. xml_output.append("<para>");
  178. }
  179. xml_output.append(lines[i].xml_escape());
  180. if (i != lines.size() - 1) {
  181. xml_output.append("</para>\n");
  182. }
  183. }
  184. }
  185. }
  186. break;
  187. }
  188. String tag = bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
  189. if (tag.begins_with("/")) {
  190. bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
  191. if (!tag_ok) {
  192. if (!line_del) {
  193. xml_output.append("[");
  194. }
  195. pos = brk_pos + 1;
  196. continue;
  197. }
  198. tag_stack.pop_front();
  199. pos = brk_end + 1;
  200. code_tag = false;
  201. if (tag == "/url") {
  202. xml_output.append("</a>");
  203. } else if (tag == "/code") {
  204. xml_output.append("</c>");
  205. } else if (tag == "/codeblock") {
  206. xml_output.append("</code>");
  207. } else if (tag == "/b") {
  208. xml_output.append("</b>");
  209. } else if (tag == "/i") {
  210. xml_output.append("</i>");
  211. } else if (tag == "/csharp") {
  212. xml_output.append("</code>");
  213. line_del = true;
  214. } else if (tag == "/codeblocks") {
  215. line_del = false;
  216. }
  217. } else if (code_tag) {
  218. xml_output.append("[");
  219. pos = brk_pos + 1;
  220. } else if (tag.begins_with("method ") || tag.begins_with("constructor ") || tag.begins_with("operator ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ") || tag.begins_with("param ")) {
  221. const int tag_end = tag.find(" ");
  222. const String link_tag = tag.substr(0, tag_end);
  223. const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
  224. const Vector<String> link_target_parts = link_target.split(".");
  225. if (link_target_parts.size() <= 0 || link_target_parts.size() > 2) {
  226. ERR_PRINT("Invalid reference format: '" + tag + "'.");
  227. xml_output.append("<c>");
  228. xml_output.append(tag);
  229. xml_output.append("</c>");
  230. pos = brk_end + 1;
  231. continue;
  232. }
  233. const TypeInterface *target_itype;
  234. StringName target_cname;
  235. if (link_target_parts.size() == 2) {
  236. target_itype = _get_type_or_null(TypeReference(link_target_parts[0]));
  237. if (!target_itype) {
  238. target_itype = _get_type_or_null(TypeReference("_" + link_target_parts[0]));
  239. }
  240. target_cname = link_target_parts[1];
  241. } else {
  242. target_itype = p_itype;
  243. target_cname = link_target_parts[0];
  244. }
  245. if (link_tag == "method") {
  246. _append_xml_method(xml_output, target_itype, target_cname, link_target, link_target_parts);
  247. } else if (link_tag == "constructor") {
  248. // TODO: Support constructors?
  249. _append_xml_undeclared(xml_output, link_target);
  250. } else if (link_tag == "operator") {
  251. // TODO: Support operators?
  252. _append_xml_undeclared(xml_output, link_target);
  253. } else if (link_tag == "member") {
  254. _append_xml_member(xml_output, target_itype, target_cname, link_target, link_target_parts);
  255. } else if (link_tag == "signal") {
  256. _append_xml_signal(xml_output, target_itype, target_cname, link_target, link_target_parts);
  257. } else if (link_tag == "enum") {
  258. _append_xml_enum(xml_output, target_itype, target_cname, link_target, link_target_parts);
  259. } else if (link_tag == "constant") {
  260. _append_xml_constant(xml_output, target_itype, target_cname, link_target, link_target_parts);
  261. } else if (link_tag == "param") {
  262. _append_xml_param(xml_output, link_target, p_is_signal);
  263. } else if (link_tag == "theme_item") {
  264. // We do not declare theme_items in any way in C#, so there is nothing to reference
  265. _append_xml_undeclared(xml_output, link_target);
  266. }
  267. pos = brk_end + 1;
  268. } else if (doc->class_list.has(tag)) {
  269. if (tag == "Array" || tag == "Dictionary") {
  270. xml_output.append("<see cref=\"" BINDINGS_NAMESPACE_COLLECTIONS ".");
  271. xml_output.append(tag);
  272. xml_output.append("\"/>");
  273. } else if (tag == "bool" || tag == "int") {
  274. xml_output.append("<see cref=\"");
  275. xml_output.append(tag);
  276. xml_output.append("\"/>");
  277. } else if (tag == "float") {
  278. xml_output.append("<see cref=\""
  279. #ifdef REAL_T_IS_DOUBLE
  280. "double"
  281. #else
  282. "float"
  283. #endif
  284. "\"/>");
  285. } else if (tag == "Variant") {
  286. xml_output.append("<see cref=\"Godot.Variant\"/>");
  287. } else if (tag == "String") {
  288. xml_output.append("<see cref=\"string\"/>");
  289. } else if (tag == "Nil") {
  290. xml_output.append("<see langword=\"null\"/>");
  291. } else if (tag.begins_with("@")) {
  292. // @GlobalScope, @GDScript, etc
  293. xml_output.append("<c>");
  294. xml_output.append(tag);
  295. xml_output.append("</c>");
  296. } else if (tag == "PackedByteArray") {
  297. xml_output.append("<see cref=\"byte\"/>[]");
  298. } else if (tag == "PackedInt32Array") {
  299. xml_output.append("<see cref=\"int\"/>[]");
  300. } else if (tag == "PackedInt64Array") {
  301. xml_output.append("<see cref=\"long\"/>[]");
  302. } else if (tag == "PackedFloat32Array") {
  303. xml_output.append("<see cref=\"float\"/>[]");
  304. } else if (tag == "PackedFloat64Array") {
  305. xml_output.append("<see cref=\"double\"/>[]");
  306. } else if (tag == "PackedStringArray") {
  307. xml_output.append("<see cref=\"string\"/>[]");
  308. } else if (tag == "PackedVector2Array") {
  309. xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>[]");
  310. } else if (tag == "PackedVector3Array") {
  311. xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>[]");
  312. } else if (tag == "PackedColorArray") {
  313. xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>[]");
  314. } else {
  315. const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
  316. if (!target_itype) {
  317. target_itype = _get_type_or_null(TypeReference("_" + tag));
  318. }
  319. if (target_itype) {
  320. if ((!p_itype || p_itype->api_type == ClassDB::API_CORE) && target_itype->api_type == ClassDB::API_EDITOR) {
  321. // Editor references in core documentation cannot be resolved,
  322. // handle as standard codeblock.
  323. _log("Cannot reference editor type '%s' in documentation for core type '%s'\n",
  324. target_itype->proxy_name.utf8().get_data(), p_itype ? p_itype->proxy_name.utf8().get_data() : "@GlobalScope");
  325. xml_output.append("<c>");
  326. xml_output.append(target_itype->proxy_name);
  327. xml_output.append("</c>");
  328. } else {
  329. xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  330. xml_output.append(target_itype->proxy_name);
  331. xml_output.append("\"/>");
  332. }
  333. } else {
  334. ERR_PRINT("Cannot resolve type reference in documentation: '" + tag + "'.");
  335. xml_output.append("<c>");
  336. xml_output.append(tag);
  337. xml_output.append("</c>");
  338. }
  339. }
  340. pos = brk_end + 1;
  341. } else if (tag == "b") {
  342. xml_output.append("<b>");
  343. pos = brk_end + 1;
  344. tag_stack.push_front(tag);
  345. } else if (tag == "i") {
  346. xml_output.append("<i>");
  347. pos = brk_end + 1;
  348. tag_stack.push_front(tag);
  349. } else if (tag == "code" || tag.begins_with("code ")) {
  350. xml_output.append("<c>");
  351. code_tag = true;
  352. pos = brk_end + 1;
  353. tag_stack.push_front("code");
  354. } else if (tag == "codeblock" || tag.begins_with("codeblock ")) {
  355. xml_output.append("<code>");
  356. code_tag = true;
  357. pos = brk_end + 1;
  358. tag_stack.push_front("codeblock");
  359. } else if (tag == "codeblocks") {
  360. line_del = true;
  361. pos = brk_end + 1;
  362. tag_stack.push_front(tag);
  363. } else if (tag == "csharp" || tag.begins_with("csharp ")) {
  364. xml_output.append("<code>");
  365. line_del = false;
  366. code_tag = true;
  367. pos = brk_end + 1;
  368. tag_stack.push_front("csharp");
  369. } else if (tag == "kbd") {
  370. // keyboard combinations are not supported in xml comments
  371. pos = brk_end + 1;
  372. tag_stack.push_front(tag);
  373. } else if (tag == "center") {
  374. // center alignment is not supported in xml comments
  375. pos = brk_end + 1;
  376. tag_stack.push_front(tag);
  377. } else if (tag == "br") {
  378. xml_output.append("\n"); // FIXME: Should use <para> instead. Luckily this tag isn't used for now.
  379. pos = brk_end + 1;
  380. } else if (tag == "u") {
  381. // underline is not supported in Rider xml comments
  382. pos = brk_end + 1;
  383. tag_stack.push_front(tag);
  384. } else if (tag == "s") {
  385. // strikethrough is not supported in xml comments
  386. pos = brk_end + 1;
  387. tag_stack.push_front(tag);
  388. } else if (tag == "url") {
  389. int end = bbcode.find("[", brk_end);
  390. if (end == -1) {
  391. end = bbcode.length();
  392. }
  393. String url = bbcode.substr(brk_end + 1, end - brk_end - 1);
  394. xml_output.append("<a href=\"");
  395. xml_output.append(url);
  396. xml_output.append("\">");
  397. xml_output.append(url);
  398. pos = brk_end + 1;
  399. tag_stack.push_front(tag);
  400. } else if (tag.begins_with("url=")) {
  401. String url = tag.substr(4, tag.length());
  402. xml_output.append("<a href=\"");
  403. xml_output.append(url);
  404. xml_output.append("\">");
  405. pos = brk_end + 1;
  406. tag_stack.push_front("url");
  407. } else if (tag == "img") {
  408. int end = bbcode.find("[", brk_end);
  409. if (end == -1) {
  410. end = bbcode.length();
  411. }
  412. String image = bbcode.substr(brk_end + 1, end - brk_end - 1);
  413. // Not supported. Just append the bbcode.
  414. xml_output.append("[img]");
  415. xml_output.append(image);
  416. xml_output.append("[/img]");
  417. pos = end;
  418. tag_stack.push_front(tag);
  419. } else if (tag.begins_with("color=")) {
  420. // Not supported.
  421. pos = brk_end + 1;
  422. tag_stack.push_front("color");
  423. } else if (tag.begins_with("font=")) {
  424. // Not supported.
  425. pos = brk_end + 1;
  426. tag_stack.push_front("font");
  427. } else {
  428. if (!line_del) {
  429. xml_output.append("["); // ignore
  430. }
  431. pos = brk_pos + 1;
  432. }
  433. }
  434. xml_output.append("</para>");
  435. return xml_output.as_string();
  436. }
  437. void BindingsGenerator::_append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
  438. if (p_link_target_parts[0] == name_cache.type_at_GlobalScope) {
  439. if (OS::get_singleton()->is_stdout_verbose()) {
  440. OS::get_singleton()->print("Cannot resolve @GlobalScope method reference in documentation: %s\n", p_link_target.utf8().get_data());
  441. }
  442. // TODO Map what we can
  443. _append_xml_undeclared(p_xml_output, p_link_target);
  444. } else if (!p_target_itype || !p_target_itype->is_object_type) {
  445. if (OS::get_singleton()->is_stdout_verbose()) {
  446. if (p_target_itype) {
  447. OS::get_singleton()->print("Cannot resolve method reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data());
  448. } else {
  449. OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", p_link_target.utf8().get_data());
  450. }
  451. }
  452. // TODO Map what we can
  453. _append_xml_undeclared(p_xml_output, p_link_target);
  454. } else {
  455. if (p_target_cname == "_init") {
  456. // The _init method is not declared in C#, reference the constructor instead
  457. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  458. p_xml_output.append(p_target_itype->proxy_name);
  459. p_xml_output.append(".");
  460. p_xml_output.append(p_target_itype->proxy_name);
  461. p_xml_output.append("()\"/>");
  462. } else {
  463. const MethodInterface *target_imethod = p_target_itype->find_method_by_name(p_target_cname);
  464. if (target_imethod) {
  465. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  466. p_xml_output.append(p_target_itype->proxy_name);
  467. p_xml_output.append(".");
  468. p_xml_output.append(target_imethod->proxy_name);
  469. p_xml_output.append("(");
  470. bool first_key = true;
  471. for (const ArgumentInterface &iarg : target_imethod->arguments) {
  472. const TypeInterface *arg_type = _get_type_or_null(iarg.type);
  473. if (first_key) {
  474. first_key = false;
  475. } else {
  476. p_xml_output.append(", ");
  477. }
  478. if (!arg_type) {
  479. ERR_PRINT("Cannot resolve argument type in documentation: '" + p_link_target + "'.");
  480. p_xml_output.append(iarg.type.cname);
  481. continue;
  482. }
  483. if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
  484. p_xml_output.append("Nullable{");
  485. }
  486. String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
  487. p_xml_output.append(arg_cs_type.replacen("<", "{").replacen(">", "}").replacen("params ", ""));
  488. if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
  489. p_xml_output.append("}");
  490. }
  491. }
  492. p_xml_output.append(")\"/>");
  493. } else {
  494. if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
  495. ERR_PRINT("Cannot resolve method reference in documentation: '" + p_link_target + "'.");
  496. }
  497. _append_xml_undeclared(p_xml_output, p_link_target);
  498. }
  499. }
  500. }
  501. }
  502. void BindingsGenerator::_append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
  503. if (p_link_target.find("/") >= 0) {
  504. // Properties with '/' (slash) in the name are not declared in C#, so there is nothing to reference.
  505. _append_xml_undeclared(p_xml_output, p_link_target);
  506. } else if (!p_target_itype || !p_target_itype->is_object_type) {
  507. if (OS::get_singleton()->is_stdout_verbose()) {
  508. if (p_target_itype) {
  509. OS::get_singleton()->print("Cannot resolve member reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data());
  510. } else {
  511. OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", p_link_target.utf8().get_data());
  512. }
  513. }
  514. // TODO Map what we can
  515. _append_xml_undeclared(p_xml_output, p_link_target);
  516. } else {
  517. const TypeInterface *current_itype = p_target_itype;
  518. const PropertyInterface *target_iprop = nullptr;
  519. while (target_iprop == nullptr && current_itype != nullptr) {
  520. target_iprop = current_itype->find_property_by_name(p_target_cname);
  521. if (target_iprop == nullptr) {
  522. current_itype = _get_type_or_null(TypeReference(current_itype->base_name));
  523. }
  524. }
  525. if (target_iprop) {
  526. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  527. p_xml_output.append(current_itype->proxy_name);
  528. p_xml_output.append(".");
  529. p_xml_output.append(target_iprop->proxy_name);
  530. p_xml_output.append("\"/>");
  531. } else {
  532. if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
  533. ERR_PRINT("Cannot resolve member reference in documentation: '" + p_link_target + "'.");
  534. }
  535. _append_xml_undeclared(p_xml_output, p_link_target);
  536. }
  537. }
  538. }
  539. void BindingsGenerator::_append_xml_signal(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
  540. if (!p_target_itype || !p_target_itype->is_object_type) {
  541. if (OS::get_singleton()->is_stdout_verbose()) {
  542. if (p_target_itype) {
  543. OS::get_singleton()->print("Cannot resolve signal reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data());
  544. } else {
  545. OS::get_singleton()->print("Cannot resolve type from signal reference in documentation: %s\n", p_link_target.utf8().get_data());
  546. }
  547. }
  548. // TODO Map what we can
  549. _append_xml_undeclared(p_xml_output, p_link_target);
  550. } else {
  551. const SignalInterface *target_isignal = p_target_itype->find_signal_by_name(p_target_cname);
  552. if (target_isignal) {
  553. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  554. p_xml_output.append(p_target_itype->proxy_name);
  555. p_xml_output.append(".");
  556. p_xml_output.append(target_isignal->proxy_name);
  557. p_xml_output.append("\"/>");
  558. } else {
  559. if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
  560. ERR_PRINT("Cannot resolve signal reference in documentation: '" + p_link_target + "'.");
  561. }
  562. _append_xml_undeclared(p_xml_output, p_link_target);
  563. }
  564. }
  565. }
  566. void BindingsGenerator::_append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
  567. const StringName search_cname = !p_target_itype ? p_target_cname : StringName(p_target_itype->name + "." + (String)p_target_cname);
  568. HashMap<StringName, TypeInterface>::ConstIterator enum_match = enum_types.find(search_cname);
  569. if (!enum_match && search_cname != p_target_cname) {
  570. enum_match = enum_types.find(p_target_cname);
  571. }
  572. if (enum_match) {
  573. const TypeInterface &target_enum_itype = enum_match->value;
  574. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  575. p_xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
  576. p_xml_output.append("\"/>");
  577. } else {
  578. if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
  579. ERR_PRINT("Cannot resolve enum reference in documentation: '" + p_link_target + "'.");
  580. }
  581. _append_xml_undeclared(p_xml_output, p_link_target);
  582. }
  583. }
  584. void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
  585. if (p_link_target_parts[0] == name_cache.type_at_GlobalScope) {
  586. _append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target);
  587. } else if (!p_target_itype || !p_target_itype->is_object_type) {
  588. // Search in @GlobalScope as a last resort if no class was specified
  589. if (p_link_target_parts.size() == 1) {
  590. _append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target);
  591. return;
  592. }
  593. if (OS::get_singleton()->is_stdout_verbose()) {
  594. if (p_target_itype) {
  595. OS::get_singleton()->print("Cannot resolve constant reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data());
  596. } else {
  597. OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", p_link_target.utf8().get_data());
  598. }
  599. }
  600. // TODO Map what we can
  601. _append_xml_undeclared(p_xml_output, p_link_target);
  602. } else {
  603. // Try to find the constant in the current class
  604. if (p_target_itype->is_singleton_instance) {
  605. // Constants and enums are declared in the static singleton class.
  606. p_target_itype = &obj_types[p_target_itype->cname];
  607. }
  608. const ConstantInterface *target_iconst = find_constant_by_name(p_target_cname, p_target_itype->constants);
  609. if (target_iconst) {
  610. // Found constant in current class
  611. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  612. p_xml_output.append(p_target_itype->proxy_name);
  613. p_xml_output.append(".");
  614. p_xml_output.append(target_iconst->proxy_name);
  615. p_xml_output.append("\"/>");
  616. } else {
  617. // Try to find as enum constant in the current class
  618. const EnumInterface *target_ienum = nullptr;
  619. for (const EnumInterface &ienum : p_target_itype->enums) {
  620. target_ienum = &ienum;
  621. target_iconst = find_constant_by_name(p_target_cname, target_ienum->constants);
  622. if (target_iconst) {
  623. break;
  624. }
  625. }
  626. if (target_iconst) {
  627. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  628. p_xml_output.append(p_target_itype->proxy_name);
  629. p_xml_output.append(".");
  630. p_xml_output.append(target_ienum->proxy_name);
  631. p_xml_output.append(".");
  632. p_xml_output.append(target_iconst->proxy_name);
  633. p_xml_output.append("\"/>");
  634. } else if (p_link_target_parts.size() == 1) {
  635. // Also search in @GlobalScope as a last resort if no class was specified
  636. _append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target);
  637. } else {
  638. if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
  639. ERR_PRINT("Cannot resolve constant reference in documentation: '" + p_link_target + "'.");
  640. }
  641. _append_xml_undeclared(p_xml_output, p_link_target);
  642. }
  643. }
  644. }
  645. }
  646. void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xml_output, const String &p_target_cname, const String &p_link_target) {
  647. // Try to find as a global constant
  648. const ConstantInterface *target_iconst = find_constant_by_name(p_target_cname, global_constants);
  649. if (target_iconst) {
  650. // Found global constant
  651. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "." BINDINGS_GLOBAL_SCOPE_CLASS ".");
  652. p_xml_output.append(target_iconst->proxy_name);
  653. p_xml_output.append("\"/>");
  654. } else {
  655. // Try to find as global enum constant
  656. const EnumInterface *target_ienum = nullptr;
  657. for (const EnumInterface &ienum : global_enums) {
  658. target_ienum = &ienum;
  659. target_iconst = find_constant_by_name(p_target_cname, target_ienum->constants);
  660. if (target_iconst) {
  661. break;
  662. }
  663. }
  664. if (target_iconst) {
  665. p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
  666. p_xml_output.append(target_ienum->proxy_name);
  667. p_xml_output.append(".");
  668. p_xml_output.append(target_iconst->proxy_name);
  669. p_xml_output.append("\"/>");
  670. } else {
  671. ERR_PRINT("Cannot resolve global constant reference in documentation: '" + p_link_target + "'.");
  672. _append_xml_undeclared(p_xml_output, p_link_target);
  673. }
  674. }
  675. }
  676. void BindingsGenerator::_append_xml_param(StringBuilder &p_xml_output, const String &p_link_target, bool p_is_signal) {
  677. const String link_target = snake_to_camel_case(p_link_target);
  678. if (!p_is_signal) {
  679. p_xml_output.append("<paramref name=\"");
  680. p_xml_output.append(link_target);
  681. p_xml_output.append("\"/>");
  682. } else {
  683. // Documentation in C# is added to an event, not the delegate itself;
  684. // as such, we treat these parameters as codeblocks instead.
  685. // See: https://github.com/godotengine/godot/pull/65529
  686. _append_xml_undeclared(p_xml_output, link_target);
  687. }
  688. }
  689. void BindingsGenerator::_append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target) {
  690. p_xml_output.append("<c>");
  691. p_xml_output.append(p_link_target);
  692. p_xml_output.append("</c>");
  693. }
  694. int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
  695. CRASH_COND(p_ienum.constants.is_empty());
  696. const ConstantInterface &front_iconstant = p_ienum.constants.front()->get();
  697. Vector<String> front_parts = front_iconstant.name.split("_", /* p_allow_empty: */ true);
  698. int candidate_len = front_parts.size() - 1;
  699. if (candidate_len == 0) {
  700. return 0;
  701. }
  702. for (const ConstantInterface &iconstant : p_ienum.constants) {
  703. Vector<String> parts = iconstant.name.split("_", /* p_allow_empty: */ true);
  704. int i;
  705. for (i = 0; i < candidate_len && i < parts.size(); i++) {
  706. if (front_parts[i] != parts[i]) {
  707. // HARDCODED: Some Flag enums have the prefix 'FLAG_' for everything except 'FLAGS_DEFAULT' (same for 'METHOD_FLAG_' and'METHOD_FLAGS_DEFAULT').
  708. bool hardcoded_exc = (i == candidate_len - 1 && ((front_parts[i] == "FLAGS" && parts[i] == "FLAG") || (front_parts[i] == "FLAG" && parts[i] == "FLAGS")));
  709. if (!hardcoded_exc) {
  710. break;
  711. }
  712. }
  713. }
  714. candidate_len = i;
  715. if (candidate_len == 0) {
  716. return 0;
  717. }
  718. }
  719. return candidate_len;
  720. }
  721. void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumInterface &p_ienum, int p_prefix_length) {
  722. if (p_prefix_length > 0) {
  723. for (ConstantInterface &iconstant : p_ienum.constants) {
  724. int curr_prefix_length = p_prefix_length;
  725. String constant_name = iconstant.name;
  726. Vector<String> parts = constant_name.split("_", /* p_allow_empty: */ true);
  727. if (parts.size() <= curr_prefix_length) {
  728. continue;
  729. }
  730. if (is_digit(parts[curr_prefix_length][0])) {
  731. // The name of enum constants may begin with a numeric digit when strip from the enum prefix,
  732. // so we make the prefix for this constant one word shorter in those cases.
  733. for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) {
  734. if (!is_digit(parts[curr_prefix_length][0])) {
  735. break;
  736. }
  737. }
  738. }
  739. constant_name = "";
  740. for (int i = curr_prefix_length; i < parts.size(); i++) {
  741. if (i > curr_prefix_length) {
  742. constant_name += "_";
  743. }
  744. constant_name += parts[i];
  745. }
  746. iconstant.proxy_name = snake_to_pascal_case(constant_name, true);
  747. }
  748. }
  749. }
  750. Error BindingsGenerator::_populate_method_icalls_table(const TypeInterface &p_itype) {
  751. for (const MethodInterface &imethod : p_itype.methods) {
  752. if (imethod.is_virtual) {
  753. continue;
  754. }
  755. const TypeInterface *return_type = _get_type_or_null(imethod.return_type);
  756. ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
  757. String im_unique_sig = get_ret_unique_sig(return_type) + ",CallMethodBind";
  758. if (!imethod.is_static) {
  759. im_unique_sig += ",CallInstance";
  760. }
  761. // Get arguments information
  762. for (const ArgumentInterface &iarg : imethod.arguments) {
  763. const TypeInterface *arg_type = _get_type_or_null(iarg.type);
  764. ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
  765. im_unique_sig += ",";
  766. im_unique_sig += get_arg_unique_sig(*arg_type);
  767. }
  768. // godot_icall_{argc}_{icallcount}
  769. String icall_method = ICALL_PREFIX;
  770. icall_method += itos(imethod.arguments.size());
  771. icall_method += "_";
  772. icall_method += itos(method_icalls.size());
  773. InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_unique_sig);
  774. im_icall.is_vararg = imethod.is_vararg;
  775. im_icall.is_static = imethod.is_static;
  776. im_icall.return_type = imethod.return_type;
  777. for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
  778. im_icall.argument_types.push_back(F->get().type);
  779. }
  780. List<InternalCall>::Element *match = method_icalls.find(im_icall);
  781. if (match) {
  782. if (p_itype.api_type != ClassDB::API_EDITOR) {
  783. match->get().editor_only = false;
  784. }
  785. method_icalls_map.insert(&imethod, &match->get());
  786. } else {
  787. List<InternalCall>::Element *added = method_icalls.push_back(im_icall);
  788. method_icalls_map.insert(&imethod, &added->get());
  789. }
  790. }
  791. return OK;
  792. }
  793. void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
  794. p_output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
  795. p_output.append("using System;\n\n");
  796. // The class where we put the extensions doesn't matter, so just use "GD".
  797. p_output.append("public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n{");
  798. #define ARRAY_IS_EMPTY(m_type) \
  799. p_output.append("\n" INDENT1 "/// <summary>\n"); \
  800. p_output.append(INDENT1 "/// Returns true if this " #m_type " array is empty or doesn't exist.\n"); \
  801. p_output.append(INDENT1 "/// </summary>\n"); \
  802. p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \
  803. p_output.append(INDENT1 "/// <returns>Whether or not the array is empty.</returns>\n"); \
  804. p_output.append(INDENT1 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \
  805. p_output.append(OPEN_BLOCK_L1); \
  806. p_output.append(INDENT2 "return instance == null || instance.Length == 0;\n"); \
  807. p_output.append(INDENT1 CLOSE_BLOCK);
  808. #define ARRAY_JOIN(m_type) \
  809. p_output.append("\n" INDENT1 "/// <summary>\n"); \
  810. p_output.append(INDENT1 "/// Converts this " #m_type " array to a string delimited by the given string.\n"); \
  811. p_output.append(INDENT1 "/// </summary>\n"); \
  812. p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
  813. p_output.append(INDENT1 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \
  814. p_output.append(INDENT1 "/// <returns>A single string with all items.</returns>\n"); \
  815. p_output.append(INDENT1 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \
  816. p_output.append(OPEN_BLOCK_L1); \
  817. p_output.append(INDENT2 "return String.Join(delimiter, instance);\n"); \
  818. p_output.append(INDENT1 CLOSE_BLOCK);
  819. #define ARRAY_STRINGIFY(m_type) \
  820. p_output.append("\n" INDENT1 "/// <summary>\n"); \
  821. p_output.append(INDENT1 "/// Converts this " #m_type " array to a string with brackets.\n"); \
  822. p_output.append(INDENT1 "/// </summary>\n"); \
  823. p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
  824. p_output.append(INDENT1 "/// <returns>A single string with all items.</returns>\n"); \
  825. p_output.append(INDENT1 "public static string Stringify(this " #m_type "[] instance)\n"); \
  826. p_output.append(OPEN_BLOCK_L1); \
  827. p_output.append(INDENT2 "return \"[\" + instance.Join() + \"]\";\n"); \
  828. p_output.append(INDENT1 CLOSE_BLOCK);
  829. #define ARRAY_ALL(m_type) \
  830. ARRAY_IS_EMPTY(m_type) \
  831. ARRAY_JOIN(m_type) \
  832. ARRAY_STRINGIFY(m_type)
  833. ARRAY_ALL(byte);
  834. ARRAY_ALL(int);
  835. ARRAY_ALL(long);
  836. ARRAY_ALL(float);
  837. ARRAY_ALL(double);
  838. ARRAY_ALL(string);
  839. ARRAY_ALL(Color);
  840. ARRAY_ALL(Vector2);
  841. ARRAY_ALL(Vector2I);
  842. ARRAY_ALL(Vector3);
  843. ARRAY_ALL(Vector3I);
  844. ARRAY_ALL(Vector4);
  845. ARRAY_ALL(Vector4I);
  846. #undef ARRAY_ALL
  847. #undef ARRAY_IS_EMPTY
  848. #undef ARRAY_JOIN
  849. #undef ARRAY_STRINGIFY
  850. p_output.append(CLOSE_BLOCK); // End of GD class.
  851. }
  852. void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
  853. // Constants (in partial GD class)
  854. p_output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
  855. p_output.append("public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n{");
  856. for (const ConstantInterface &iconstant : global_constants) {
  857. if (iconstant.const_doc && iconstant.const_doc->description.size()) {
  858. String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), nullptr);
  859. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  860. if (summary_lines.size()) {
  861. p_output.append(MEMBER_BEGIN "/// <summary>\n");
  862. for (int i = 0; i < summary_lines.size(); i++) {
  863. p_output.append(INDENT1 "/// ");
  864. p_output.append(summary_lines[i]);
  865. p_output.append("\n");
  866. }
  867. p_output.append(INDENT1 "/// </summary>");
  868. }
  869. }
  870. p_output.append(MEMBER_BEGIN "public const long ");
  871. p_output.append(iconstant.proxy_name);
  872. p_output.append(" = ");
  873. p_output.append(itos(iconstant.value));
  874. p_output.append(";");
  875. }
  876. if (!global_constants.is_empty()) {
  877. p_output.append("\n");
  878. }
  879. p_output.append(CLOSE_BLOCK); // end of GD class
  880. // Enums
  881. for (const EnumInterface &ienum : global_enums) {
  882. CRASH_COND(ienum.constants.is_empty());
  883. String enum_proxy_name = ienum.proxy_name;
  884. bool enum_in_static_class = false;
  885. if (enum_proxy_name.find(".") > 0) {
  886. enum_in_static_class = true;
  887. String enum_class_name = enum_proxy_name.get_slicec('.', 0);
  888. enum_proxy_name = enum_proxy_name.get_slicec('.', 1);
  889. CRASH_COND(enum_class_name != "Variant"); // Hard-coded...
  890. _log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
  891. p_output.append("\npublic partial struct ");
  892. p_output.append(enum_class_name);
  893. p_output.append("\n" OPEN_BLOCK);
  894. }
  895. if (ienum.is_flags) {
  896. p_output.append("\n[System.Flags]");
  897. }
  898. p_output.append("\npublic enum ");
  899. p_output.append(enum_proxy_name);
  900. p_output.append(" : long");
  901. p_output.append("\n" OPEN_BLOCK);
  902. const ConstantInterface &last = ienum.constants.back()->get();
  903. for (const ConstantInterface &iconstant : ienum.constants) {
  904. if (iconstant.const_doc && iconstant.const_doc->description.size()) {
  905. String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), nullptr);
  906. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  907. if (summary_lines.size()) {
  908. p_output.append(INDENT1 "/// <summary>\n");
  909. for (int i = 0; i < summary_lines.size(); i++) {
  910. p_output.append(INDENT1 "/// ");
  911. p_output.append(summary_lines[i]);
  912. p_output.append("\n");
  913. }
  914. p_output.append(INDENT1 "/// </summary>\n");
  915. }
  916. }
  917. p_output.append(INDENT1);
  918. p_output.append(iconstant.proxy_name);
  919. p_output.append(" = ");
  920. p_output.append(itos(iconstant.value));
  921. p_output.append(&iconstant != &last ? ",\n" : "\n");
  922. }
  923. p_output.append(CLOSE_BLOCK);
  924. if (enum_in_static_class) {
  925. p_output.append(CLOSE_BLOCK);
  926. }
  927. }
  928. }
  929. Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
  930. ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
  931. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  932. ERR_FAIL_COND_V(da.is_null(), ERR_CANT_CREATE);
  933. if (!DirAccess::exists(p_proj_dir)) {
  934. Error err = da->make_dir_recursive(p_proj_dir);
  935. ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create directory '" + p_proj_dir + "'.");
  936. }
  937. da->change_dir(p_proj_dir);
  938. da->make_dir("Generated");
  939. da->make_dir("Generated/GodotObjects");
  940. String base_gen_dir = path::join(p_proj_dir, "Generated");
  941. String godot_objects_gen_dir = path::join(base_gen_dir, "GodotObjects");
  942. Vector<String> compile_items;
  943. // Generate source file for global scope constants and enums
  944. {
  945. StringBuilder constants_source;
  946. _generate_global_constants(constants_source);
  947. String output_file = path::join(base_gen_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_constants.cs");
  948. Error save_err = _save_file(output_file, constants_source);
  949. if (save_err != OK) {
  950. return save_err;
  951. }
  952. compile_items.push_back(output_file);
  953. }
  954. // Generate source file for array extensions
  955. {
  956. StringBuilder extensions_source;
  957. _generate_array_extensions(extensions_source);
  958. String output_file = path::join(base_gen_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_extensions.cs");
  959. Error save_err = _save_file(output_file, extensions_source);
  960. if (save_err != OK) {
  961. return save_err;
  962. }
  963. compile_items.push_back(output_file);
  964. }
  965. for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
  966. const TypeInterface &itype = E.value;
  967. if (itype.api_type == ClassDB::API_EDITOR) {
  968. continue;
  969. }
  970. String output_file = path::join(godot_objects_gen_dir, itype.proxy_name + ".cs");
  971. Error err = _generate_cs_type(itype, output_file);
  972. if (err == ERR_SKIP) {
  973. continue;
  974. }
  975. if (err != OK) {
  976. return err;
  977. }
  978. compile_items.push_back(output_file);
  979. }
  980. // Generate native calls
  981. StringBuilder cs_icalls_content;
  982. cs_icalls_content.append("namespace " BINDINGS_NAMESPACE ";\n\n");
  983. cs_icalls_content.append("using System;\n"
  984. "using System.Diagnostics.CodeAnalysis;\n"
  985. "using System.Runtime.InteropServices;\n"
  986. "using Godot.NativeInterop;\n"
  987. "\n");
  988. cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
  989. cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
  990. cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
  991. cs_icalls_content.append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
  992. cs_icalls_content.append("internal static class " BINDINGS_CLASS_NATIVECALLS "\n{");
  993. cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
  994. cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_CORE)) + ";\n");
  995. cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
  996. for (const InternalCall &icall : method_icalls) {
  997. if (icall.editor_only) {
  998. continue;
  999. }
  1000. Error err = _generate_cs_native_calls(icall, cs_icalls_content);
  1001. if (err != OK) {
  1002. return err;
  1003. }
  1004. }
  1005. cs_icalls_content.append(CLOSE_BLOCK);
  1006. String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
  1007. Error err = _save_file(internal_methods_file, cs_icalls_content);
  1008. if (err != OK) {
  1009. return err;
  1010. }
  1011. compile_items.push_back(internal_methods_file);
  1012. // Generate GeneratedIncludes.props
  1013. StringBuilder includes_props_content;
  1014. includes_props_content.append("<Project>\n"
  1015. " <ItemGroup>\n");
  1016. for (int i = 0; i < compile_items.size(); i++) {
  1017. String include = path::relative_to(compile_items[i], p_proj_dir).replace("/", "\\");
  1018. includes_props_content.append(" <Compile Include=\"" + include + "\" />\n");
  1019. }
  1020. includes_props_content.append(" </ItemGroup>\n"
  1021. "</Project>\n");
  1022. String includes_props_file = path::join(base_gen_dir, "GeneratedIncludes.props");
  1023. err = _save_file(includes_props_file, includes_props_content);
  1024. if (err != OK) {
  1025. return err;
  1026. }
  1027. return OK;
  1028. }
  1029. Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
  1030. ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
  1031. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1032. ERR_FAIL_COND_V(da.is_null(), ERR_CANT_CREATE);
  1033. if (!DirAccess::exists(p_proj_dir)) {
  1034. Error err = da->make_dir_recursive(p_proj_dir);
  1035. ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
  1036. }
  1037. da->change_dir(p_proj_dir);
  1038. da->make_dir("Generated");
  1039. da->make_dir("Generated/GodotObjects");
  1040. String base_gen_dir = path::join(p_proj_dir, "Generated");
  1041. String godot_objects_gen_dir = path::join(base_gen_dir, "GodotObjects");
  1042. Vector<String> compile_items;
  1043. for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
  1044. const TypeInterface &itype = E.value;
  1045. if (itype.api_type != ClassDB::API_EDITOR) {
  1046. continue;
  1047. }
  1048. String output_file = path::join(godot_objects_gen_dir, itype.proxy_name + ".cs");
  1049. Error err = _generate_cs_type(itype, output_file);
  1050. if (err == ERR_SKIP) {
  1051. continue;
  1052. }
  1053. if (err != OK) {
  1054. return err;
  1055. }
  1056. compile_items.push_back(output_file);
  1057. }
  1058. // Generate native calls
  1059. StringBuilder cs_icalls_content;
  1060. cs_icalls_content.append("namespace " BINDINGS_NAMESPACE ";\n\n");
  1061. cs_icalls_content.append("using System;\n"
  1062. "using System.Diagnostics.CodeAnalysis;\n"
  1063. "using System.Runtime.InteropServices;\n"
  1064. "using Godot.NativeInterop;\n"
  1065. "\n");
  1066. cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
  1067. cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
  1068. cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
  1069. cs_icalls_content.append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
  1070. cs_icalls_content.append("internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" OPEN_BLOCK);
  1071. cs_icalls_content.append(INDENT1 "internal static ulong godot_api_hash = ");
  1072. cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_EDITOR)) + ";\n");
  1073. cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
  1074. cs_icalls_content.append("\n");
  1075. for (const InternalCall &icall : method_icalls) {
  1076. if (!icall.editor_only) {
  1077. continue;
  1078. }
  1079. Error err = _generate_cs_native_calls(icall, cs_icalls_content);
  1080. if (err != OK) {
  1081. return err;
  1082. }
  1083. }
  1084. cs_icalls_content.append(CLOSE_BLOCK);
  1085. String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
  1086. Error err = _save_file(internal_methods_file, cs_icalls_content);
  1087. if (err != OK) {
  1088. return err;
  1089. }
  1090. compile_items.push_back(internal_methods_file);
  1091. // Generate GeneratedIncludes.props
  1092. StringBuilder includes_props_content;
  1093. includes_props_content.append("<Project>\n"
  1094. " <ItemGroup>\n");
  1095. for (int i = 0; i < compile_items.size(); i++) {
  1096. String include = path::relative_to(compile_items[i], p_proj_dir).replace("/", "\\");
  1097. includes_props_content.append(" <Compile Include=\"" + include + "\" />\n");
  1098. }
  1099. includes_props_content.append(" </ItemGroup>\n"
  1100. "</Project>\n");
  1101. String includes_props_file = path::join(base_gen_dir, "GeneratedIncludes.props");
  1102. err = _save_file(includes_props_file, includes_props_content);
  1103. if (err != OK) {
  1104. return err;
  1105. }
  1106. return OK;
  1107. }
  1108. Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
  1109. ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
  1110. String output_dir = path::abspath(path::realpath(p_output_dir));
  1111. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1112. ERR_FAIL_COND_V(da.is_null(), ERR_CANT_CREATE);
  1113. if (!DirAccess::exists(output_dir)) {
  1114. Error err = da->make_dir_recursive(output_dir);
  1115. ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
  1116. }
  1117. Error proj_err;
  1118. // Generate GodotSharp source files
  1119. String core_proj_dir = output_dir.path_join(CORE_API_ASSEMBLY_NAME);
  1120. proj_err = generate_cs_core_project(core_proj_dir);
  1121. if (proj_err != OK) {
  1122. ERR_PRINT("Generation of the Core API C# project failed.");
  1123. return proj_err;
  1124. }
  1125. // Generate GodotSharpEditor source files
  1126. String editor_proj_dir = output_dir.path_join(EDITOR_API_ASSEMBLY_NAME);
  1127. proj_err = generate_cs_editor_project(editor_proj_dir);
  1128. if (proj_err != OK) {
  1129. ERR_PRINT("Generation of the Editor API C# project failed.");
  1130. return proj_err;
  1131. }
  1132. _log("The Godot API sources were successfully generated\n");
  1133. return OK;
  1134. }
  1135. // FIXME: There are some members that hide other inherited members.
  1136. // - In the case of both members being the same kind, the new one must be declared
  1137. // explicitly as 'new' to avoid the warning (and we must print a message about it).
  1138. // - In the case of both members being of a different kind, then the new one must
  1139. // be renamed to avoid the name collision (and we must print a warning about it).
  1140. // - Csc warning e.g.:
  1141. // ObjectType/LineEdit.cs(140,38): warning CS0108: 'LineEdit.FocusMode' hides inherited member 'Control.FocusMode'. Use the new keyword if hiding was intended.
  1142. Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const String &p_output_file) {
  1143. CRASH_COND(!itype.is_object_type);
  1144. bool is_derived_type = itype.base_name != StringName();
  1145. if (!is_derived_type) {
  1146. // Some GodotObject assertions
  1147. CRASH_COND(itype.cname != name_cache.type_Object);
  1148. CRASH_COND(!itype.is_instantiable);
  1149. CRASH_COND(itype.api_type != ClassDB::API_CORE);
  1150. CRASH_COND(itype.is_ref_counted);
  1151. CRASH_COND(itype.is_singleton);
  1152. }
  1153. _log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
  1154. StringBuilder output;
  1155. output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
  1156. output.append("using System;\n"); // IntPtr
  1157. output.append("using System.ComponentModel;\n"); // EditorBrowsable
  1158. output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
  1159. output.append("using Godot.NativeInterop;\n");
  1160. output.append("\n#nullable disable\n");
  1161. const DocData::ClassDoc *class_doc = itype.class_doc;
  1162. if (class_doc && class_doc->description.size()) {
  1163. String xml_summary = bbcode_to_xml(fix_doc_description(class_doc->description), &itype);
  1164. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  1165. if (summary_lines.size()) {
  1166. output.append("/// <summary>\n");
  1167. for (int i = 0; i < summary_lines.size(); i++) {
  1168. output.append("/// ");
  1169. output.append(summary_lines[i]);
  1170. output.append("\n");
  1171. }
  1172. output.append("/// </summary>\n");
  1173. }
  1174. if (class_doc->is_deprecated) {
  1175. output.append("[Obsolete(\"This class is deprecated.\")]\n");
  1176. }
  1177. }
  1178. // We generate a `GodotClassName` attribute if the engine class name is not the same as the
  1179. // generated C# class name. This allows introspection code to find the name associated with
  1180. // the class. If the attribute is not present, the C# class name can be used instead.
  1181. if (itype.name != itype.proxy_name) {
  1182. output << "[GodotClassName(\"" << itype.name << "\")]\n";
  1183. }
  1184. output.append("public ");
  1185. if (itype.is_singleton) {
  1186. output.append("static partial class ");
  1187. } else {
  1188. // Even if the class is not instantiable, we can't declare it abstract because
  1189. // the engine can still instantiate them and return them via the scripting API.
  1190. // Example: `SceneTreeTimer` returned from `SceneTree.create_timer`.
  1191. // See the reverted commit: ef5672d3f94a7321ed779c922088bb72adbb1521
  1192. output.append("partial class ");
  1193. }
  1194. output.append(itype.proxy_name);
  1195. if (is_derived_type && !itype.is_singleton) {
  1196. if (obj_types.has(itype.base_name)) {
  1197. TypeInterface base_type = obj_types[itype.base_name];
  1198. output.append(" : ");
  1199. output.append(base_type.proxy_name);
  1200. if (base_type.is_singleton) {
  1201. // If the type is a singleton, use the instance type.
  1202. output.append(CS_SINGLETON_INSTANCE_SUFFIX);
  1203. }
  1204. } else {
  1205. ERR_PRINT("Base type '" + itype.base_name.operator String() + "' does not exist, for class '" + itype.name + "'.");
  1206. return ERR_INVALID_DATA;
  1207. }
  1208. }
  1209. output.append("\n{");
  1210. // Add constants
  1211. for (const ConstantInterface &iconstant : itype.constants) {
  1212. if (iconstant.const_doc && iconstant.const_doc->description.size()) {
  1213. String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
  1214. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  1215. if (summary_lines.size()) {
  1216. output.append(MEMBER_BEGIN "/// <summary>\n");
  1217. for (int i = 0; i < summary_lines.size(); i++) {
  1218. output.append(INDENT1 "/// ");
  1219. output.append(summary_lines[i]);
  1220. output.append("\n");
  1221. }
  1222. output.append(INDENT1 "/// </summary>");
  1223. }
  1224. if (iconstant.const_doc->is_deprecated) {
  1225. output.append(MEMBER_BEGIN "[Obsolete(\"This constant is deprecated.\")]");
  1226. }
  1227. }
  1228. output.append(MEMBER_BEGIN "public const long ");
  1229. output.append(iconstant.proxy_name);
  1230. output.append(" = ");
  1231. output.append(itos(iconstant.value));
  1232. output.append(";");
  1233. }
  1234. if (itype.constants.size()) {
  1235. output.append("\n");
  1236. }
  1237. // Add enums
  1238. for (const EnumInterface &ienum : itype.enums) {
  1239. ERR_FAIL_COND_V(ienum.constants.is_empty(), ERR_BUG);
  1240. if (ienum.is_flags) {
  1241. output.append(MEMBER_BEGIN "[System.Flags]");
  1242. }
  1243. output.append(MEMBER_BEGIN "public enum ");
  1244. output.append(ienum.proxy_name);
  1245. output.append(" : long");
  1246. output.append(MEMBER_BEGIN OPEN_BLOCK);
  1247. const ConstantInterface &last = ienum.constants.back()->get();
  1248. for (const ConstantInterface &iconstant : ienum.constants) {
  1249. if (iconstant.const_doc && iconstant.const_doc->description.size()) {
  1250. String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
  1251. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  1252. if (summary_lines.size()) {
  1253. output.append(INDENT2 "/// <summary>\n");
  1254. for (int i = 0; i < summary_lines.size(); i++) {
  1255. output.append(INDENT2 "/// ");
  1256. output.append(summary_lines[i]);
  1257. output.append("\n");
  1258. }
  1259. output.append(INDENT2 "/// </summary>\n");
  1260. }
  1261. if (iconstant.const_doc->is_deprecated) {
  1262. output.append(INDENT2 "[Obsolete(\"This enum member is deprecated.\")]\n");
  1263. }
  1264. }
  1265. output.append(INDENT2);
  1266. output.append(iconstant.proxy_name);
  1267. output.append(" = ");
  1268. output.append(itos(iconstant.value));
  1269. output.append(&iconstant != &last ? ",\n" : "\n");
  1270. }
  1271. output.append(INDENT1 CLOSE_BLOCK);
  1272. }
  1273. // Add properties
  1274. for (const PropertyInterface &iprop : itype.properties) {
  1275. Error prop_err = _generate_cs_property(itype, iprop, output);
  1276. ERR_FAIL_COND_V_MSG(prop_err != OK, prop_err,
  1277. "Failed to generate property '" + iprop.cname.operator String() +
  1278. "' for class '" + itype.name + "'.");
  1279. }
  1280. // Add native name static field and cached type.
  1281. if (is_derived_type && !itype.is_singleton) {
  1282. output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n";
  1283. }
  1284. output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
  1285. output.append(itype.name);
  1286. output.append("\";\n");
  1287. if (itype.is_singleton || itype.is_compat_singleton) {
  1288. // Add the Singleton static property.
  1289. String instance_type_name;
  1290. if (itype.is_singleton) {
  1291. StringName instance_name = itype.name + CS_SINGLETON_INSTANCE_SUFFIX;
  1292. instance_type_name = obj_types.has(instance_name)
  1293. ? obj_types[instance_name].proxy_name
  1294. : "GodotObject";
  1295. } else {
  1296. instance_type_name = itype.proxy_name;
  1297. }
  1298. output.append(MEMBER_BEGIN "private static " + instance_type_name + " singleton;\n");
  1299. output << MEMBER_BEGIN "public static " + instance_type_name + " " CS_PROPERTY_SINGLETON " =>\n"
  1300. << INDENT2 "singleton \?\?= (" + instance_type_name + ")"
  1301. << C_METHOD_ENGINE_GET_SINGLETON "(\"" << itype.name << "\");\n";
  1302. }
  1303. if (!itype.is_singleton) {
  1304. // IMPORTANT: We also generate the static fields for GodotObject instead of declaring
  1305. // them manually in the `GodotObject.base.cs` partial class declaration, because they're
  1306. // required by other static fields in this generated partial class declaration.
  1307. // Static fields are initialized in order of declaration, but when they're in different
  1308. // partial class declarations then it becomes harder to tell (Rider warns about this).
  1309. if (itype.is_instantiable) {
  1310. // Add native constructor static field
  1311. output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
  1312. << INDENT1 "private static readonly unsafe delegate* unmanaged<IntPtr> "
  1313. << CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR
  1314. << "(" BINDINGS_NATIVE_NAME_FIELD ");\n";
  1315. }
  1316. if (is_derived_type) {
  1317. // Add default constructor
  1318. if (itype.is_instantiable) {
  1319. output << MEMBER_BEGIN "public " << itype.proxy_name << "() : this("
  1320. << (itype.memory_own ? "true" : "false") << ")\n" OPEN_BLOCK_L1
  1321. << INDENT2 "unsafe\n" INDENT2 OPEN_BLOCK
  1322. << INDENT3 "_ConstructAndInitialize(" CS_STATIC_FIELD_NATIVE_CTOR ", "
  1323. << BINDINGS_NATIVE_NAME_FIELD ", CachedType, refCounted: "
  1324. << (itype.is_ref_counted ? "true" : "false") << ");\n"
  1325. << CLOSE_BLOCK_L2 CLOSE_BLOCK_L1;
  1326. } else {
  1327. // Hide the constructor
  1328. output.append(MEMBER_BEGIN "internal ");
  1329. output.append(itype.proxy_name);
  1330. output.append("() {}\n");
  1331. }
  1332. // Add.. em.. trick constructor. Sort of.
  1333. output.append(MEMBER_BEGIN "internal ");
  1334. output.append(itype.proxy_name);
  1335. output.append("(bool " CS_PARAM_MEMORYOWN ") : base(" CS_PARAM_MEMORYOWN ") {}\n");
  1336. }
  1337. }
  1338. // Methods
  1339. int method_bind_count = 0;
  1340. for (const MethodInterface &imethod : itype.methods) {
  1341. Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
  1342. ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
  1343. "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
  1344. }
  1345. // Signals
  1346. for (const SignalInterface &isignal : itype.signals_) {
  1347. Error method_err = _generate_cs_signal(itype, isignal, output);
  1348. ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
  1349. "Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
  1350. }
  1351. // Script members look-up
  1352. if (!itype.is_singleton && (is_derived_type || itype.has_virtual_methods)) {
  1353. // Generate method names cache fields
  1354. for (const MethodInterface &imethod : itype.methods) {
  1355. if (!imethod.is_virtual) {
  1356. continue;
  1357. }
  1358. output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
  1359. << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
  1360. << INDENT1 "private static readonly StringName "
  1361. << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
  1362. << " = \"" << imethod.proxy_name << "\";\n";
  1363. }
  1364. // Generate signal names cache fields
  1365. for (const SignalInterface &isignal : itype.signals_) {
  1366. output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
  1367. << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
  1368. << INDENT1 "private static readonly StringName "
  1369. << CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name
  1370. << " = \"" << isignal.proxy_name << "\";\n";
  1371. }
  1372. // TODO: Only generate HasGodotClassMethod and InvokeGodotClassMethod if there's any method
  1373. // Generate InvokeGodotClassMethod
  1374. output << MEMBER_BEGIN "/// <summary>\n"
  1375. << INDENT1 "/// Invokes the method with the given name, using the given arguments.\n"
  1376. << INDENT1 "/// This method is used by Godot to invoke methods from the engine side.\n"
  1377. << INDENT1 "/// Do not call or override this method.\n"
  1378. << INDENT1 "/// </summary>\n"
  1379. << INDENT1 "/// <param name=\"method\">Name of the method to invoke.</param>\n"
  1380. << INDENT1 "/// <param name=\"args\">Arguments to use with the invoked method.</param>\n"
  1381. << INDENT1 "/// <param name=\"ret\">Value returned by the invoked method.</param>\n";
  1382. output << INDENT1 "protected internal " << (is_derived_type ? "override" : "virtual")
  1383. << " bool " CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(in godot_string_name method, "
  1384. << "NativeVariantPtrArgs args, out godot_variant ret)\n"
  1385. << INDENT1 "{\n";
  1386. for (const MethodInterface &imethod : itype.methods) {
  1387. if (!imethod.is_virtual) {
  1388. continue;
  1389. }
  1390. // We also call HasGodotClassMethod to ensure the method is overridden and avoid calling
  1391. // the stub implementation. This solution adds some extra overhead to calls, but it's
  1392. // much simpler than other solutions. This won't be a problem once we move to function
  1393. // pointers of generated wrappers for each method, as lookup will only happen once.
  1394. // We check both native names (snake_case) and proxy names (PascalCase)
  1395. output << INDENT2 "if ((method == " << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
  1396. << " || method == MethodName." << imethod.proxy_name
  1397. << ") && args.Count == " << itos(imethod.arguments.size())
  1398. << " && " << CS_METHOD_HAS_GODOT_CLASS_METHOD << "((godot_string_name)"
  1399. << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name << ".NativeValue))\n"
  1400. << INDENT2 "{\n";
  1401. if (imethod.return_type.cname != name_cache.type_void) {
  1402. output << INDENT3 "var callRet = ";
  1403. } else {
  1404. output << INDENT3;
  1405. }
  1406. output << imethod.proxy_name << "(";
  1407. for (int i = 0; i < imethod.arguments.size(); i++) {
  1408. const ArgumentInterface &iarg = imethod.arguments[i];
  1409. const TypeInterface *arg_type = _get_type_or_null(iarg.type);
  1410. ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
  1411. if (i != 0) {
  1412. output << ", ";
  1413. }
  1414. if (arg_type->cname == name_cache.type_Array_generic || arg_type->cname == name_cache.type_Dictionary_generic) {
  1415. String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
  1416. output << "new " << arg_cs_type << "(" << sformat(arg_type->cs_variant_to_managed, "args[" + itos(i) + "]", arg_type->cs_type, arg_type->name) << ")";
  1417. } else {
  1418. output << sformat(arg_type->cs_variant_to_managed,
  1419. "args[" + itos(i) + "]", arg_type->cs_type, arg_type->name);
  1420. }
  1421. }
  1422. output << ");\n";
  1423. if (imethod.return_type.cname != name_cache.type_void) {
  1424. const TypeInterface *return_type = _get_type_or_null(imethod.return_type);
  1425. ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
  1426. output << INDENT3 "ret = "
  1427. << sformat(return_type->cs_managed_to_variant, "callRet", return_type->cs_type, return_type->name)
  1428. << ";\n"
  1429. << INDENT3 "return true;\n";
  1430. } else {
  1431. output << INDENT3 "ret = default;\n"
  1432. << INDENT3 "return true;\n";
  1433. }
  1434. output << INDENT2 "}\n";
  1435. }
  1436. if (is_derived_type) {
  1437. output << INDENT2 "return base." CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(method, args, out ret);\n";
  1438. } else {
  1439. output << INDENT2 "ret = default;\n"
  1440. << INDENT2 "return false;\n";
  1441. }
  1442. output << INDENT1 "}\n";
  1443. // Generate HasGodotClassMethod
  1444. output << MEMBER_BEGIN "/// <summary>\n"
  1445. << INDENT1 "/// Check if the type contains a method with the given name.\n"
  1446. << INDENT1 "/// This method is used by Godot to check if a method exists before invoking it.\n"
  1447. << INDENT1 "/// Do not call or override this method.\n"
  1448. << INDENT1 "/// </summary>\n"
  1449. << INDENT1 "/// <param name=\"method\">Name of the method to check for.</param>\n";
  1450. output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
  1451. << " bool " CS_METHOD_HAS_GODOT_CLASS_METHOD "(in godot_string_name method)\n"
  1452. << INDENT1 "{\n";
  1453. for (const MethodInterface &imethod : itype.methods) {
  1454. if (!imethod.is_virtual) {
  1455. continue;
  1456. }
  1457. // We check for native names (snake_case). If we detect one, we call HasGodotClassMethod
  1458. // again, but this time with the respective proxy name (PascalCase). It's the job of
  1459. // user derived classes to override the method and check for those. Our C# source
  1460. // generators take care of generating those override methods.
  1461. output << INDENT2 "if (method == MethodName." << imethod.proxy_name
  1462. << ")\n" INDENT2 "{\n"
  1463. << INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_METHOD "("
  1464. << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
  1465. << ".NativeValue.DangerousSelfRef))\n" INDENT3 "{\n"
  1466. << INDENT4 "return true;\n"
  1467. << INDENT3 "}\n" INDENT2 "}\n";
  1468. }
  1469. if (is_derived_type) {
  1470. output << INDENT2 "return base." CS_METHOD_HAS_GODOT_CLASS_METHOD "(method);\n";
  1471. } else {
  1472. output << INDENT2 "return false;\n";
  1473. }
  1474. output << INDENT1 "}\n";
  1475. // Generate HasGodotClassSignal
  1476. output << MEMBER_BEGIN "/// <summary>\n"
  1477. << INDENT1 "/// Check if the type contains a signal with the given name.\n"
  1478. << INDENT1 "/// This method is used by Godot to check if a signal exists before raising it.\n"
  1479. << INDENT1 "/// Do not call or override this method.\n"
  1480. << INDENT1 "/// </summary>\n"
  1481. << INDENT1 "/// <param name=\"signal\">Name of the signal to check for.</param>\n";
  1482. output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
  1483. << " bool " CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(in godot_string_name signal)\n"
  1484. << INDENT1 "{\n";
  1485. for (const SignalInterface &isignal : itype.signals_) {
  1486. // We check for native names (snake_case). If we detect one, we call HasGodotClassSignal
  1487. // again, but this time with the respective proxy name (PascalCase). It's the job of
  1488. // user derived classes to override the method and check for those. Our C# source
  1489. // generators take care of generating those override methods.
  1490. output << INDENT2 "if (signal == SignalName." << isignal.proxy_name
  1491. << ")\n" INDENT2 "{\n"
  1492. << INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_SIGNAL "("
  1493. << CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name
  1494. << ".NativeValue.DangerousSelfRef))\n" INDENT3 "{\n"
  1495. << INDENT4 "return true;\n"
  1496. << INDENT3 "}\n" INDENT2 "}\n";
  1497. }
  1498. if (is_derived_type) {
  1499. output << INDENT2 "return base." CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(signal);\n";
  1500. } else {
  1501. output << INDENT2 "return false;\n";
  1502. }
  1503. output << INDENT1 "}\n";
  1504. }
  1505. //Generate StringName for all class members
  1506. bool is_inherit = !itype.is_singleton && obj_types.has(itype.base_name);
  1507. //PropertyName
  1508. output << MEMBER_BEGIN "/// <summary>\n"
  1509. << INDENT1 "/// Cached StringNames for the properties and fields contained in this class, for fast lookup.\n"
  1510. << INDENT1 "/// </summary>\n";
  1511. if (is_inherit) {
  1512. output << INDENT1 "public new class PropertyName : " << obj_types[itype.base_name].proxy_name << ".PropertyName";
  1513. } else {
  1514. output << INDENT1 "public class PropertyName";
  1515. }
  1516. output << "\n"
  1517. << INDENT1 "{\n";
  1518. for (const PropertyInterface &iprop : itype.properties) {
  1519. output << INDENT2 "/// <summary>\n"
  1520. << INDENT2 "/// Cached name for the '" << iprop.cname << "' property.\n"
  1521. << INDENT2 "/// </summary>\n"
  1522. << INDENT2 "public static readonly StringName " << iprop.proxy_name << " = \"" << iprop.cname << "\";\n";
  1523. }
  1524. output << INDENT1 "}\n";
  1525. //MethodName
  1526. output << MEMBER_BEGIN "/// <summary>\n"
  1527. << INDENT1 "/// Cached StringNames for the methods contained in this class, for fast lookup.\n"
  1528. << INDENT1 "/// </summary>\n";
  1529. if (is_inherit) {
  1530. output << INDENT1 "public new class MethodName : " << obj_types[itype.base_name].proxy_name << ".MethodName";
  1531. } else {
  1532. output << INDENT1 "public class MethodName";
  1533. }
  1534. output << "\n"
  1535. << INDENT1 "{\n";
  1536. HashMap<String, StringName> method_names;
  1537. for (const MethodInterface &imethod : itype.methods) {
  1538. if (method_names.has(imethod.proxy_name)) {
  1539. ERR_FAIL_COND_V_MSG(method_names[imethod.proxy_name] != imethod.cname, ERR_BUG, "Method name '" + imethod.proxy_name + "' already exists with a different value.");
  1540. continue;
  1541. }
  1542. method_names[imethod.proxy_name] = imethod.cname;
  1543. output << INDENT2 "/// <summary>\n"
  1544. << INDENT2 "/// Cached name for the '" << imethod.cname << "' method.\n"
  1545. << INDENT2 "/// </summary>\n"
  1546. << INDENT2 "public static readonly StringName " << imethod.proxy_name << " = \"" << imethod.cname << "\";\n";
  1547. }
  1548. output << INDENT1 "}\n";
  1549. //SignalName
  1550. output << MEMBER_BEGIN "/// <summary>\n"
  1551. << INDENT1 "/// Cached StringNames for the signals contained in this class, for fast lookup.\n"
  1552. << INDENT1 "/// </summary>\n";
  1553. if (is_inherit) {
  1554. output << INDENT1 "public new class SignalName : " << obj_types[itype.base_name].proxy_name << ".SignalName";
  1555. } else {
  1556. output << INDENT1 "public class SignalName";
  1557. }
  1558. output << "\n"
  1559. << INDENT1 "{\n";
  1560. for (const SignalInterface &isignal : itype.signals_) {
  1561. output << INDENT2 "/// <summary>\n"
  1562. << INDENT2 "/// Cached name for the '" << isignal.cname << "' signal.\n"
  1563. << INDENT2 "/// </summary>\n"
  1564. << INDENT2 "public static readonly StringName " << isignal.proxy_name << " = \"" << isignal.cname << "\";\n";
  1565. }
  1566. output << INDENT1 "}\n";
  1567. output.append(CLOSE_BLOCK /* class */);
  1568. return _save_file(p_output_file, output);
  1569. }
  1570. Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output) {
  1571. const MethodInterface *setter = p_itype.find_method_by_name(p_iprop.setter);
  1572. // Search it in base types too
  1573. const TypeInterface *current_type = &p_itype;
  1574. while (!setter && current_type->base_name != StringName()) {
  1575. HashMap<StringName, TypeInterface>::Iterator base_match = obj_types.find(current_type->base_name);
  1576. ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
  1577. current_type = &base_match->value;
  1578. setter = current_type->find_method_by_name(p_iprop.setter);
  1579. }
  1580. const MethodInterface *getter = p_itype.find_method_by_name(p_iprop.getter);
  1581. // Search it in base types too
  1582. current_type = &p_itype;
  1583. while (!getter && current_type->base_name != StringName()) {
  1584. HashMap<StringName, TypeInterface>::Iterator base_match = obj_types.find(current_type->base_name);
  1585. ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
  1586. current_type = &base_match->value;
  1587. getter = current_type->find_method_by_name(p_iprop.getter);
  1588. }
  1589. ERR_FAIL_COND_V(!setter && !getter, ERR_BUG);
  1590. if (setter) {
  1591. int setter_argc = p_iprop.index != -1 ? 2 : 1;
  1592. ERR_FAIL_COND_V(setter->arguments.size() != setter_argc, ERR_BUG);
  1593. }
  1594. if (getter) {
  1595. int getter_argc = p_iprop.index != -1 ? 1 : 0;
  1596. ERR_FAIL_COND_V(getter->arguments.size() != getter_argc, ERR_BUG);
  1597. }
  1598. if (getter && setter) {
  1599. const ArgumentInterface &setter_first_arg = setter->arguments.back()->get();
  1600. if (getter->return_type.cname != setter_first_arg.type.cname) {
  1601. // Special case for Node::set_name
  1602. bool whitelisted = getter->return_type.cname == name_cache.type_StringName &&
  1603. setter_first_arg.type.cname == name_cache.type_String;
  1604. ERR_FAIL_COND_V_MSG(!whitelisted, ERR_BUG,
  1605. "Return type from getter doesn't match first argument of setter for property: '" +
  1606. p_itype.name + "." + String(p_iprop.cname) + "'.");
  1607. }
  1608. }
  1609. const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
  1610. const TypeInterface *prop_itype = _get_type_or_singleton_or_null(proptype_name);
  1611. ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found
  1612. ERR_FAIL_COND_V_MSG(prop_itype->is_singleton, ERR_BUG,
  1613. "Property type is a singleton: '" + p_itype.name + "." + String(p_iprop.cname) + "'.");
  1614. if (p_itype.api_type == ClassDB::API_CORE) {
  1615. ERR_FAIL_COND_V_MSG(prop_itype->api_type == ClassDB::API_EDITOR, ERR_BUG,
  1616. "Property '" + p_itype.name + "." + String(p_iprop.cname) + "' has type '" + prop_itype->name +
  1617. "' from the editor API. Core API cannot have dependencies on the editor API.");
  1618. }
  1619. if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) {
  1620. String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype);
  1621. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  1622. if (summary_lines.size()) {
  1623. p_output.append(MEMBER_BEGIN "/// <summary>\n");
  1624. for (int i = 0; i < summary_lines.size(); i++) {
  1625. p_output.append(INDENT1 "/// ");
  1626. p_output.append(summary_lines[i]);
  1627. p_output.append("\n");
  1628. }
  1629. p_output.append(INDENT1 "/// </summary>");
  1630. }
  1631. if (p_iprop.prop_doc->is_deprecated) {
  1632. p_output.append(MEMBER_BEGIN "[Obsolete(\"This property is deprecated.\")]");
  1633. }
  1634. }
  1635. p_output.append(MEMBER_BEGIN "public ");
  1636. if (p_itype.is_singleton) {
  1637. p_output.append("static ");
  1638. }
  1639. String prop_cs_type = prop_itype->cs_type + _get_generic_type_parameters(*prop_itype, proptype_name.generic_type_parameters);
  1640. p_output.append(prop_cs_type);
  1641. p_output.append(" ");
  1642. p_output.append(p_iprop.proxy_name);
  1643. p_output.append("\n" OPEN_BLOCK_L1);
  1644. if (getter) {
  1645. p_output.append(INDENT2 "get\n" OPEN_BLOCK_L2 INDENT3);
  1646. p_output.append("return ");
  1647. p_output.append(getter->proxy_name + "(");
  1648. if (p_iprop.index != -1) {
  1649. const ArgumentInterface &idx_arg = getter->arguments.front()->get();
  1650. if (idx_arg.type.cname != name_cache.type_int) {
  1651. // Assume the index parameter is an enum
  1652. const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
  1653. CRASH_COND(idx_arg_type == nullptr);
  1654. p_output.append("(" + idx_arg_type->proxy_name + ")(" + itos(p_iprop.index) + ")");
  1655. } else {
  1656. p_output.append(itos(p_iprop.index));
  1657. }
  1658. }
  1659. p_output.append(");\n" CLOSE_BLOCK_L2);
  1660. }
  1661. if (setter) {
  1662. p_output.append(INDENT2 "set\n" OPEN_BLOCK_L2 INDENT3);
  1663. p_output.append(setter->proxy_name + "(");
  1664. if (p_iprop.index != -1) {
  1665. const ArgumentInterface &idx_arg = setter->arguments.front()->get();
  1666. if (idx_arg.type.cname != name_cache.type_int) {
  1667. // Assume the index parameter is an enum
  1668. const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
  1669. CRASH_COND(idx_arg_type == nullptr);
  1670. p_output.append("(" + idx_arg_type->proxy_name + ")(" + itos(p_iprop.index) + "), ");
  1671. } else {
  1672. p_output.append(itos(p_iprop.index) + ", ");
  1673. }
  1674. }
  1675. p_output.append("value);\n" CLOSE_BLOCK_L2);
  1676. }
  1677. p_output.append(CLOSE_BLOCK_L1);
  1678. return OK;
  1679. }
  1680. Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
  1681. const TypeInterface *return_type = _get_type_or_singleton_or_null(p_imethod.return_type);
  1682. ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
  1683. ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG,
  1684. "Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'.");
  1685. if (p_itype.api_type == ClassDB::API_CORE) {
  1686. ERR_FAIL_COND_V_MSG(return_type->api_type == ClassDB::API_EDITOR, ERR_BUG,
  1687. "Method '" + p_itype.name + "." + p_imethod.name + "' has return type '" + return_type->name +
  1688. "' from the editor API. Core API cannot have dependencies on the editor API.");
  1689. }
  1690. String method_bind_field = CS_STATIC_FIELD_METHOD_BIND_PREFIX + itos(p_method_bind_count);
  1691. String arguments_sig;
  1692. StringBuilder cs_in_statements;
  1693. bool cs_in_expr_is_unsafe = false;
  1694. String icall_params = method_bind_field;
  1695. if (!p_imethod.is_static) {
  1696. String self_reference = "this";
  1697. if (p_itype.is_singleton) {
  1698. self_reference = CS_PROPERTY_SINGLETON;
  1699. }
  1700. if (p_itype.cs_in.size()) {
  1701. cs_in_statements << sformat(p_itype.cs_in, p_itype.c_type, self_reference,
  1702. String(), String(), String(), INDENT2);
  1703. }
  1704. icall_params += ", " + sformat(p_itype.cs_in_expr, self_reference);
  1705. }
  1706. StringBuilder default_args_doc;
  1707. // Retrieve information from the arguments
  1708. const ArgumentInterface &first = p_imethod.arguments.front()->get();
  1709. for (const ArgumentInterface &iarg : p_imethod.arguments) {
  1710. const TypeInterface *arg_type = _get_type_or_singleton_or_null(iarg.type);
  1711. ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
  1712. ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
  1713. "Argument type is a singleton: '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
  1714. if (p_itype.api_type == ClassDB::API_CORE) {
  1715. ERR_FAIL_COND_V_MSG(arg_type->api_type == ClassDB::API_EDITOR, ERR_BUG,
  1716. "Argument '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "' has type '" +
  1717. arg_type->name + "' from the editor API. Core API cannot have dependencies on the editor API.");
  1718. }
  1719. if (iarg.default_argument.size()) {
  1720. CRASH_COND_MSG(!_arg_default_value_is_assignable_to_type(iarg.def_param_value, *arg_type),
  1721. "Invalid default value for parameter '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
  1722. }
  1723. String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
  1724. // Add the current arguments to the signature
  1725. // If the argument has a default value which is not a constant, we will make it Nullable
  1726. {
  1727. if (&iarg != &first) {
  1728. arguments_sig += ", ";
  1729. }
  1730. if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
  1731. arguments_sig += "Nullable<";
  1732. }
  1733. arguments_sig += arg_cs_type;
  1734. if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
  1735. arguments_sig += "> ";
  1736. } else {
  1737. arguments_sig += " ";
  1738. }
  1739. arguments_sig += iarg.name;
  1740. if (!p_imethod.is_compat && iarg.default_argument.size()) {
  1741. if (iarg.def_param_mode != ArgumentInterface::CONSTANT) {
  1742. arguments_sig += " = null";
  1743. } else {
  1744. arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type);
  1745. }
  1746. }
  1747. }
  1748. icall_params += ", ";
  1749. if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
  1750. // The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
  1751. // Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
  1752. String arg_or_defval_local = iarg.name;
  1753. arg_or_defval_local += "OrDefVal";
  1754. cs_in_statements << INDENT2 << arg_cs_type << " " << arg_or_defval_local << " = " << iarg.name;
  1755. if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
  1756. cs_in_statements << ".HasValue ? ";
  1757. } else {
  1758. cs_in_statements << " != null ? ";
  1759. }
  1760. cs_in_statements << iarg.name;
  1761. if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
  1762. cs_in_statements << ".Value : ";
  1763. } else {
  1764. cs_in_statements << " : ";
  1765. }
  1766. String cs_type = arg_cs_type;
  1767. if (cs_type.ends_with("[]")) {
  1768. cs_type = cs_type.substr(0, cs_type.length() - 2);
  1769. }
  1770. String def_arg = sformat(iarg.default_argument, cs_type);
  1771. cs_in_statements << def_arg << ";\n";
  1772. if (arg_type->cs_in.size()) {
  1773. cs_in_statements << sformat(arg_type->cs_in, arg_type->c_type, arg_or_defval_local,
  1774. String(), String(), String(), INDENT2);
  1775. }
  1776. if (arg_type->cs_in_expr.is_empty()) {
  1777. icall_params += arg_or_defval_local;
  1778. } else {
  1779. icall_params += sformat(arg_type->cs_in_expr, arg_or_defval_local, arg_type->c_type);
  1780. }
  1781. // Apparently the name attribute must not include the @
  1782. String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name;
  1783. // Escape < and > in the attribute default value
  1784. String param_def_arg = def_arg.replacen("<", "&lt;").replacen(">", "&gt;");
  1785. default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is <c>" + param_def_arg + "</c>.</param>");
  1786. } else {
  1787. if (arg_type->cs_in.size()) {
  1788. cs_in_statements << sformat(arg_type->cs_in, arg_type->c_type, iarg.name,
  1789. String(), String(), String(), INDENT2);
  1790. }
  1791. icall_params += arg_type->cs_in_expr.is_empty() ? iarg.name : sformat(arg_type->cs_in_expr, iarg.name, arg_type->c_type);
  1792. }
  1793. cs_in_expr_is_unsafe |= arg_type->cs_in_expr_is_unsafe;
  1794. }
  1795. // Collect caller name for MethodBind
  1796. if (p_imethod.is_vararg) {
  1797. icall_params += ", (godot_string_name)MethodName." + p_imethod.proxy_name + ".NativeValue";
  1798. }
  1799. // Generate method
  1800. {
  1801. if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
  1802. p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
  1803. << INDENT1 "private static readonly IntPtr " << method_bind_field << " = ";
  1804. if (p_itype.is_singleton) {
  1805. // Singletons are static classes. They don't derive GodotObject,
  1806. // so we need to specify the type to call the static method.
  1807. p_output << "GodotObject.";
  1808. }
  1809. p_output << ICALL_CLASSDB_GET_METHOD_WITH_COMPATIBILITY "(" BINDINGS_NATIVE_NAME_FIELD ", MethodName."
  1810. << p_imethod.proxy_name << ", " << itos(p_imethod.hash) << "ul"
  1811. << ");\n";
  1812. }
  1813. if (p_imethod.method_doc && p_imethod.method_doc->description.size()) {
  1814. String xml_summary = bbcode_to_xml(fix_doc_description(p_imethod.method_doc->description), &p_itype);
  1815. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  1816. if (summary_lines.size()) {
  1817. p_output.append(MEMBER_BEGIN "/// <summary>\n");
  1818. for (int i = 0; i < summary_lines.size(); i++) {
  1819. p_output.append(INDENT1 "/// ");
  1820. p_output.append(summary_lines[i]);
  1821. p_output.append("\n");
  1822. }
  1823. p_output.append(INDENT1 "/// </summary>");
  1824. }
  1825. if (p_imethod.method_doc->is_deprecated) {
  1826. p_output.append(MEMBER_BEGIN "[Obsolete(\"This method is deprecated.\")]");
  1827. }
  1828. }
  1829. if (default_args_doc.get_string_length()) {
  1830. p_output.append(default_args_doc.as_string());
  1831. }
  1832. if (p_imethod.is_deprecated) {
  1833. if (p_imethod.deprecation_message.is_empty()) {
  1834. WARN_PRINT("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'.");
  1835. }
  1836. p_output.append(MEMBER_BEGIN "[Obsolete(\"");
  1837. p_output.append(p_imethod.deprecation_message);
  1838. p_output.append("\")]");
  1839. }
  1840. if (p_imethod.is_compat) {
  1841. p_output.append(MEMBER_BEGIN "[EditorBrowsable(EditorBrowsableState.Never)]");
  1842. }
  1843. p_output.append(MEMBER_BEGIN);
  1844. p_output.append(p_imethod.is_internal ? "internal " : "public ");
  1845. if (p_itype.is_singleton || p_imethod.is_static) {
  1846. p_output.append("static ");
  1847. } else if (p_imethod.is_virtual) {
  1848. p_output.append("virtual ");
  1849. }
  1850. if (cs_in_expr_is_unsafe) {
  1851. p_output.append("unsafe ");
  1852. }
  1853. String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters);
  1854. p_output.append(return_cs_type + " ");
  1855. p_output.append(p_imethod.proxy_name + "(");
  1856. p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L1);
  1857. if (p_imethod.is_virtual) {
  1858. // Godot virtual method must be overridden, therefore we return a default value by default.
  1859. if (return_type->cname == name_cache.type_void) {
  1860. p_output.append(CLOSE_BLOCK_L1);
  1861. } else {
  1862. p_output.append(INDENT2 "return default;\n" CLOSE_BLOCK_L1);
  1863. }
  1864. return OK; // Won't increment method bind count
  1865. }
  1866. if (p_imethod.requires_object_call) {
  1867. // Fallback to Godot's object.Call(string, params)
  1868. p_output.append(INDENT2 CS_METHOD_CALL "(\"");
  1869. p_output.append(p_imethod.name);
  1870. p_output.append("\"");
  1871. for (const ArgumentInterface &iarg : p_imethod.arguments) {
  1872. p_output.append(", ");
  1873. p_output.append(iarg.name);
  1874. }
  1875. p_output.append(");\n" CLOSE_BLOCK_L1);
  1876. return OK; // Won't increment method bind count
  1877. }
  1878. HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
  1879. ERR_FAIL_NULL_V(match, ERR_BUG);
  1880. const InternalCall *im_icall = match->value;
  1881. String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
  1882. im_call += ".";
  1883. im_call += im_icall->name;
  1884. if (p_imethod.arguments.size() && cs_in_statements.get_string_length() > 0) {
  1885. p_output.append(cs_in_statements.as_string());
  1886. }
  1887. if (return_type->cname == name_cache.type_void) {
  1888. p_output << INDENT2 << im_call << "(" << icall_params << ");\n";
  1889. } else if (return_type->cs_out.is_empty()) {
  1890. p_output << INDENT2 "return " << im_call << "(" << icall_params << ");\n";
  1891. } else {
  1892. p_output.append(sformat(return_type->cs_out, im_call, icall_params,
  1893. return_cs_type, return_type->c_type_out, String(), INDENT2));
  1894. p_output.append("\n");
  1895. }
  1896. p_output.append(CLOSE_BLOCK_L1);
  1897. }
  1898. p_method_bind_count++;
  1899. return OK;
  1900. }
  1901. Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) {
  1902. String arguments_sig;
  1903. String delegate_type_params;
  1904. if (!p_isignal.arguments.is_empty()) {
  1905. delegate_type_params += "<";
  1906. }
  1907. // Retrieve information from the arguments
  1908. const ArgumentInterface &first = p_isignal.arguments.front()->get();
  1909. for (const ArgumentInterface &iarg : p_isignal.arguments) {
  1910. const TypeInterface *arg_type = _get_type_or_singleton_or_null(iarg.type);
  1911. ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
  1912. ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
  1913. "Argument type is a singleton: '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "'.");
  1914. if (p_itype.api_type == ClassDB::API_CORE) {
  1915. ERR_FAIL_COND_V_MSG(arg_type->api_type == ClassDB::API_EDITOR, ERR_BUG,
  1916. "Argument '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "' has type '" +
  1917. arg_type->name + "' from the editor API. Core API cannot have dependencies on the editor API.");
  1918. }
  1919. // Add the current arguments to the signature
  1920. if (&iarg != &first) {
  1921. arguments_sig += ", ";
  1922. delegate_type_params += ", ";
  1923. }
  1924. arguments_sig += arg_type->cs_type;
  1925. arguments_sig += " ";
  1926. arguments_sig += iarg.name;
  1927. delegate_type_params += arg_type->cs_type;
  1928. }
  1929. if (!p_isignal.arguments.is_empty()) {
  1930. delegate_type_params += ">";
  1931. }
  1932. // Generate signal
  1933. {
  1934. bool is_parameterless = p_isignal.arguments.size() == 0;
  1935. // Delegate name is [SignalName]EventHandler
  1936. String delegate_name = is_parameterless ? "Action" : p_isignal.proxy_name + "EventHandler";
  1937. if (!is_parameterless) {
  1938. p_output.append(MEMBER_BEGIN "/// <summary>\n");
  1939. p_output.append(INDENT1 "/// ");
  1940. p_output.append("Represents the method that handles the ");
  1941. p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>");
  1942. p_output.append(" event of a ");
  1943. p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>");
  1944. p_output.append(" class.\n");
  1945. p_output.append(INDENT1 "/// </summary>");
  1946. if (p_isignal.is_deprecated) {
  1947. if (p_isignal.deprecation_message.is_empty()) {
  1948. WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
  1949. }
  1950. p_output.append(MEMBER_BEGIN "[Obsolete(\"");
  1951. p_output.append(p_isignal.deprecation_message);
  1952. p_output.append("\")]");
  1953. }
  1954. // Generate delegate
  1955. p_output.append(MEMBER_BEGIN "public delegate void ");
  1956. p_output.append(delegate_name);
  1957. p_output.append("(");
  1958. p_output.append(arguments_sig);
  1959. p_output.append(");\n");
  1960. // Generate Callable trampoline for the delegate
  1961. p_output << MEMBER_BEGIN "private static void " << p_isignal.proxy_name << "Trampoline"
  1962. << "(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)\n"
  1963. << INDENT1 "{\n"
  1964. << INDENT2 "Callable.ThrowIfArgCountMismatch(args, " << itos(p_isignal.arguments.size()) << ");\n"
  1965. << INDENT2 "((" << delegate_name << ")delegateObj)(";
  1966. int idx = 0;
  1967. for (const ArgumentInterface &iarg : p_isignal.arguments) {
  1968. const TypeInterface *arg_type = _get_type_or_null(iarg.type);
  1969. ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
  1970. if (idx != 0) {
  1971. p_output << ",";
  1972. }
  1973. p_output << sformat(arg_type->cs_variant_to_managed,
  1974. "args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name);
  1975. idx++;
  1976. }
  1977. p_output << ");\n"
  1978. << INDENT2 "ret = default;\n"
  1979. << INDENT1 "}\n";
  1980. }
  1981. if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
  1982. String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype, true);
  1983. Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
  1984. if (summary_lines.size()) {
  1985. p_output.append(MEMBER_BEGIN "/// <summary>\n");
  1986. for (int i = 0; i < summary_lines.size(); i++) {
  1987. p_output.append(INDENT1 "/// ");
  1988. p_output.append(summary_lines[i]);
  1989. p_output.append("\n");
  1990. }
  1991. p_output.append(INDENT1 "/// </summary>");
  1992. }
  1993. if (p_isignal.method_doc->is_deprecated) {
  1994. p_output.append(MEMBER_BEGIN "[Obsolete(\"This signal is deprecated.\")]");
  1995. }
  1996. }
  1997. if (p_isignal.is_deprecated) {
  1998. p_output.append(MEMBER_BEGIN "[Obsolete(\"");
  1999. p_output.append(p_isignal.deprecation_message);
  2000. p_output.append("\")]");
  2001. }
  2002. // TODO:
  2003. // Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
  2004. // If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
  2005. // Generate event
  2006. p_output.append(MEMBER_BEGIN "public ");
  2007. if (p_itype.is_singleton) {
  2008. p_output.append("static ");
  2009. }
  2010. if (!is_parameterless) {
  2011. // `unsafe` is needed for taking the trampoline's function pointer
  2012. p_output << "unsafe ";
  2013. }
  2014. p_output.append("event ");
  2015. p_output.append(delegate_name);
  2016. p_output.append(" ");
  2017. p_output.append(p_isignal.proxy_name);
  2018. p_output.append("\n" OPEN_BLOCK_L1 INDENT2);
  2019. if (p_itype.is_singleton) {
  2020. p_output.append("add => " CS_PROPERTY_SINGLETON ".Connect(SignalName.");
  2021. } else {
  2022. p_output.append("add => Connect(SignalName.");
  2023. }
  2024. if (is_parameterless) {
  2025. // Delegate type is Action. No need for custom trampoline.
  2026. p_output << p_isignal.proxy_name << ", Callable.From(value));\n";
  2027. } else {
  2028. p_output << p_isignal.proxy_name
  2029. << ", Callable.CreateWithUnsafeTrampoline(value, &" << p_isignal.proxy_name << "Trampoline));\n";
  2030. }
  2031. if (p_itype.is_singleton) {
  2032. p_output.append(INDENT2 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(SignalName.");
  2033. } else {
  2034. p_output.append(INDENT2 "remove => Disconnect(SignalName.");
  2035. }
  2036. if (is_parameterless) {
  2037. // Delegate type is Action. No need for custom trampoline.
  2038. p_output << p_isignal.proxy_name << ", Callable.From(value));\n";
  2039. } else {
  2040. p_output << p_isignal.proxy_name
  2041. << ", Callable.CreateWithUnsafeTrampoline(value, &" << p_isignal.proxy_name << "Trampoline));\n";
  2042. }
  2043. p_output.append(CLOSE_BLOCK_L1);
  2044. }
  2045. return OK;
  2046. }
  2047. Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output) {
  2048. bool ret_void = p_icall.return_type.cname == name_cache.type_void;
  2049. const TypeInterface *return_type = _get_type_or_null(p_icall.return_type);
  2050. ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
  2051. StringBuilder c_func_sig;
  2052. StringBuilder c_in_statements;
  2053. StringBuilder c_args_var_content;
  2054. c_func_sig << "IntPtr " CS_PARAM_METHODBIND;
  2055. if (!p_icall.is_static) {
  2056. c_func_sig += ", IntPtr " CS_PARAM_INSTANCE;
  2057. }
  2058. // Get arguments information
  2059. int i = 0;
  2060. for (const TypeReference &arg_type_ref : p_icall.argument_types) {
  2061. const TypeInterface *arg_type = _get_type_or_null(arg_type_ref);
  2062. ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Return type not found
  2063. String c_param_name = "arg" + itos(i + 1);
  2064. if (p_icall.is_vararg) {
  2065. if (i < p_icall.get_arguments_count() - 1) {
  2066. String c_in_vararg = arg_type->c_in_vararg;
  2067. if (arg_type->is_object_type) {
  2068. c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObjectPtr(%1);\n";
  2069. }
  2070. ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG,
  2071. "VarArg support not implemented for parameter type: " + arg_type->name);
  2072. c_in_statements
  2073. << sformat(c_in_vararg, return_type->c_type, c_param_name,
  2074. String(), String(), String(), INDENT3)
  2075. << INDENT3 C_LOCAL_PTRCALL_ARGS "[" << itos(i)
  2076. << "] = new IntPtr(&" << c_param_name << "_in);\n";
  2077. }
  2078. } else {
  2079. if (i > 0) {
  2080. c_args_var_content << ", ";
  2081. }
  2082. if (arg_type->c_in.size()) {
  2083. c_in_statements << sformat(arg_type->c_in, arg_type->c_type, c_param_name,
  2084. String(), String(), String(), INDENT2);
  2085. }
  2086. c_args_var_content << sformat(arg_type->c_arg_in, c_param_name);
  2087. }
  2088. c_func_sig << ", " << arg_type->c_type_in << " " << c_param_name;
  2089. i++;
  2090. }
  2091. // Collect caller name for MethodBind
  2092. if (p_icall.is_vararg) {
  2093. c_func_sig << ", godot_string_name caller";
  2094. }
  2095. String icall_method = p_icall.name;
  2096. // Generate icall function
  2097. r_output << MEMBER_BEGIN "internal static unsafe " << (ret_void ? "void" : return_type->c_type_out) << " "
  2098. << icall_method << "(" << c_func_sig.as_string() << ") " OPEN_BLOCK;
  2099. if (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) {
  2100. String ptrcall_return_type;
  2101. String initialization;
  2102. if (return_type->is_object_type) {
  2103. ptrcall_return_type = return_type->is_ref_counted ? "godot_ref" : return_type->c_type;
  2104. initialization = " = default";
  2105. } else {
  2106. ptrcall_return_type = return_type->c_type;
  2107. }
  2108. r_output << INDENT2;
  2109. if (return_type->is_ref_counted || return_type->c_type_is_disposable_struct) {
  2110. r_output << "using ";
  2111. if (initialization.is_empty()) {
  2112. initialization = " = default";
  2113. }
  2114. } else if (return_type->c_ret_needs_default_initialization) {
  2115. initialization = " = default";
  2116. }
  2117. r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n";
  2118. }
  2119. if (!p_icall.is_static) {
  2120. r_output << INDENT2 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n"
  2121. << INDENT3 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n";
  2122. }
  2123. String argc_str = itos(p_icall.get_arguments_count());
  2124. auto generate_call_and_return_stmts = [&](const char *base_indent) {
  2125. if (p_icall.is_vararg) {
  2126. // MethodBind Call
  2127. r_output << base_indent;
  2128. // VarArg methods always return Variant, but there are some cases in which MethodInfo provides
  2129. // a specific return type. We trust this information is valid. We need a temporary local to keep
  2130. // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
  2131. // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
  2132. // Alternatively, we could just return Variant, but that would result in a worse API.
  2133. if (!ret_void) {
  2134. if (return_type->cname != name_cache.type_Variant) {
  2135. // Usually the return value takes ownership, but in this case the variant is only used
  2136. // for conversion to another return type. As such, the local variable takes ownership.
  2137. r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = ";
  2138. } else {
  2139. // Variant's [c_out] takes ownership of the variant value
  2140. r_output << "godot_variant " << C_LOCAL_RET " = ";
  2141. }
  2142. }
  2143. r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call("
  2144. << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
  2145. << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
  2146. << ", total_length, out godot_variant_call_error vcall_error);\n";
  2147. r_output << base_indent << "ExceptionUtils.DebugCheckCallError(caller"
  2148. << ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
  2149. << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
  2150. << ", total_length, vcall_error);\n";
  2151. if (!ret_void) {
  2152. if (return_type->cname != name_cache.type_Variant) {
  2153. if (return_type->cname == name_cache.enum_Error) {
  2154. r_output << base_indent << C_LOCAL_RET " = VariantUtils.ConvertToInt64(" C_LOCAL_VARARG_RET ");\n";
  2155. } else {
  2156. // TODO: Use something similar to c_in_vararg (see usage above, with error if not implemented)
  2157. CRASH_NOW_MSG("Custom VarArg return type not implemented: " + return_type->name);
  2158. r_output << base_indent << C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n";
  2159. }
  2160. }
  2161. }
  2162. } else {
  2163. // MethodBind PtrCall
  2164. r_output << base_indent << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_ptrcall("
  2165. << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
  2166. << ", " << (p_icall.get_arguments_count() ? C_LOCAL_PTRCALL_ARGS : "null")
  2167. << ", " << (!ret_void ? "&" C_LOCAL_RET ");\n" : "null);\n");
  2168. }
  2169. // Return statement
  2170. if (!ret_void) {
  2171. if (return_type->c_out.is_empty()) {
  2172. r_output << base_indent << "return " C_LOCAL_RET ";\n";
  2173. } else {
  2174. r_output << sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET,
  2175. return_type->name, String(), String(), base_indent);
  2176. }
  2177. }
  2178. };
  2179. if (p_icall.get_arguments_count()) {
  2180. if (p_icall.is_vararg) {
  2181. String vararg_arg = "arg" + argc_str;
  2182. String real_argc_str = itos(p_icall.get_arguments_count() - 1); // Arguments count without vararg
  2183. p_icall.get_arguments_count();
  2184. r_output << INDENT2 "int vararg_length = " << vararg_arg << ".Length;\n"
  2185. << INDENT2 "int total_length = " << real_argc_str << " + vararg_length;\n";
  2186. r_output << INDENT2 "Span<godot_variant.movable> varargs_span = vararg_length <= VarArgsSpanThreshold ?\n"
  2187. << INDENT3 "stackalloc godot_variant.movable[VarArgsSpanThreshold] :\n"
  2188. << INDENT3 "new godot_variant.movable[vararg_length];\n";
  2189. r_output << INDENT2 "Span<IntPtr> " C_LOCAL_PTRCALL_ARGS "_span = total_length <= VarArgsSpanThreshold ?\n"
  2190. << INDENT3 "stackalloc IntPtr[VarArgsSpanThreshold] :\n"
  2191. << INDENT3 "new IntPtr[total_length];\n";
  2192. r_output << INDENT2 "fixed (godot_variant.movable* varargs = &MemoryMarshal.GetReference(varargs_span))\n"
  2193. << INDENT2 "fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = "
  2194. "&MemoryMarshal.GetReference(" C_LOCAL_PTRCALL_ARGS "_span))\n"
  2195. << OPEN_BLOCK_L2;
  2196. r_output << c_in_statements.as_string();
  2197. r_output << INDENT3 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
  2198. << INDENT4 "varargs[i] = " << vararg_arg << "[i].NativeVar;\n"
  2199. << INDENT4 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n"
  2200. << CLOSE_BLOCK_L3;
  2201. generate_call_and_return_stmts(INDENT3);
  2202. r_output << CLOSE_BLOCK_L2;
  2203. } else {
  2204. r_output << c_in_statements.as_string();
  2205. r_output << INDENT2 "void** " C_LOCAL_PTRCALL_ARGS " = stackalloc void*["
  2206. << argc_str << "] { " << c_args_var_content.as_string() << " };\n";
  2207. generate_call_and_return_stmts(INDENT2);
  2208. }
  2209. } else {
  2210. generate_call_and_return_stmts(INDENT2);
  2211. }
  2212. r_output << CLOSE_BLOCK_L1;
  2213. return OK;
  2214. }
  2215. Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
  2216. Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
  2217. ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
  2218. file->store_string(p_content.as_string());
  2219. return OK;
  2220. }
  2221. const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) {
  2222. HashMap<StringName, TypeInterface>::ConstIterator builtin_type_match = builtin_types.find(p_typeref.cname);
  2223. if (builtin_type_match) {
  2224. return &builtin_type_match->value;
  2225. }
  2226. HashMap<StringName, TypeInterface>::ConstIterator obj_type_match = obj_types.find(p_typeref.cname);
  2227. if (obj_type_match) {
  2228. return &obj_type_match->value;
  2229. }
  2230. if (p_typeref.is_enum) {
  2231. HashMap<StringName, TypeInterface>::ConstIterator enum_match = enum_types.find(p_typeref.cname);
  2232. if (enum_match) {
  2233. return &enum_match->value;
  2234. }
  2235. // Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
  2236. HashMap<StringName, TypeInterface>::ConstIterator int_match = builtin_types.find(name_cache.type_int);
  2237. ERR_FAIL_NULL_V(int_match, nullptr);
  2238. return &int_match->value;
  2239. }
  2240. return nullptr;
  2241. }
  2242. const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_singleton_or_null(const TypeReference &p_typeref) {
  2243. const TypeInterface *itype = _get_type_or_null(p_typeref);
  2244. if (itype == nullptr) {
  2245. return nullptr;
  2246. }
  2247. if (itype->is_singleton) {
  2248. StringName instance_type_name = itype->name + CS_SINGLETON_INSTANCE_SUFFIX;
  2249. itype = &obj_types.find(instance_type_name)->value;
  2250. }
  2251. return itype;
  2252. }
  2253. const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) {
  2254. if (p_generic_type_parameters.is_empty()) {
  2255. return "";
  2256. }
  2257. ERR_FAIL_COND_V_MSG(p_itype.type_parameter_count != p_generic_type_parameters.size(), "",
  2258. "Generic type parameter count mismatch for type '" + p_itype.name + "'." +
  2259. " Found " + itos(p_generic_type_parameters.size()) + ", but requires " +
  2260. itos(p_itype.type_parameter_count) + ".");
  2261. int i = 0;
  2262. String params = "<";
  2263. for (const TypeReference &param_type : p_generic_type_parameters) {
  2264. const TypeInterface *param_itype = _get_type_or_singleton_or_null(param_type);
  2265. ERR_FAIL_NULL_V(param_itype, ""); // Parameter type not found
  2266. ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
  2267. "Generic type parameter is a singleton: '" + param_itype->name + "'.");
  2268. if (p_itype.api_type == ClassDB::API_CORE) {
  2269. ERR_FAIL_COND_V_MSG(param_itype->api_type == ClassDB::API_EDITOR, "",
  2270. "Generic type parameter '" + param_itype->name + "' has type from the editor API." +
  2271. " Core API cannot have dependencies on the editor API.");
  2272. }
  2273. params += param_itype->cs_type;
  2274. if (i < p_generic_type_parameters.size() - 1) {
  2275. params += ", ";
  2276. }
  2277. i++;
  2278. }
  2279. params += ">";
  2280. return params;
  2281. }
  2282. StringName BindingsGenerator::_get_type_name_from_meta(Variant::Type p_type, GodotTypeInfo::Metadata p_meta) {
  2283. if (p_type == Variant::INT) {
  2284. return _get_int_type_name_from_meta(p_meta);
  2285. } else if (p_type == Variant::FLOAT) {
  2286. return _get_float_type_name_from_meta(p_meta);
  2287. } else {
  2288. return Variant::get_type_name(p_type);
  2289. }
  2290. }
  2291. StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
  2292. switch (p_meta) {
  2293. case GodotTypeInfo::METADATA_INT_IS_INT8:
  2294. return "sbyte";
  2295. break;
  2296. case GodotTypeInfo::METADATA_INT_IS_INT16:
  2297. return "short";
  2298. break;
  2299. case GodotTypeInfo::METADATA_INT_IS_INT32:
  2300. return "int";
  2301. break;
  2302. case GodotTypeInfo::METADATA_INT_IS_INT64:
  2303. return "long";
  2304. break;
  2305. case GodotTypeInfo::METADATA_INT_IS_UINT8:
  2306. return "byte";
  2307. break;
  2308. case GodotTypeInfo::METADATA_INT_IS_UINT16:
  2309. return "ushort";
  2310. break;
  2311. case GodotTypeInfo::METADATA_INT_IS_UINT32:
  2312. return "uint";
  2313. break;
  2314. case GodotTypeInfo::METADATA_INT_IS_UINT64:
  2315. return "ulong";
  2316. break;
  2317. default:
  2318. // Assume INT64
  2319. return "long";
  2320. }
  2321. }
  2322. StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
  2323. switch (p_meta) {
  2324. case GodotTypeInfo::METADATA_REAL_IS_FLOAT:
  2325. return "float";
  2326. break;
  2327. case GodotTypeInfo::METADATA_REAL_IS_DOUBLE:
  2328. return "double";
  2329. break;
  2330. default:
  2331. // Assume FLOAT64
  2332. return "double";
  2333. }
  2334. }
  2335. bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &p_val, const TypeInterface &p_arg_type) {
  2336. if (p_arg_type.name == name_cache.type_Variant) {
  2337. // Variant can take anything
  2338. return true;
  2339. }
  2340. switch (p_val.get_type()) {
  2341. case Variant::NIL:
  2342. return p_arg_type.is_object_type ||
  2343. name_cache.is_nullable_type(p_arg_type.name);
  2344. case Variant::BOOL:
  2345. return p_arg_type.name == name_cache.type_bool;
  2346. case Variant::INT:
  2347. return p_arg_type.name == name_cache.type_sbyte ||
  2348. p_arg_type.name == name_cache.type_short ||
  2349. p_arg_type.name == name_cache.type_int ||
  2350. p_arg_type.name == name_cache.type_byte ||
  2351. p_arg_type.name == name_cache.type_ushort ||
  2352. p_arg_type.name == name_cache.type_uint ||
  2353. p_arg_type.name == name_cache.type_long ||
  2354. p_arg_type.name == name_cache.type_ulong ||
  2355. p_arg_type.name == name_cache.type_float ||
  2356. p_arg_type.name == name_cache.type_double ||
  2357. p_arg_type.is_enum;
  2358. case Variant::FLOAT:
  2359. return p_arg_type.name == name_cache.type_float ||
  2360. p_arg_type.name == name_cache.type_double;
  2361. case Variant::STRING:
  2362. case Variant::STRING_NAME:
  2363. return p_arg_type.name == name_cache.type_String ||
  2364. p_arg_type.name == name_cache.type_StringName ||
  2365. p_arg_type.name == name_cache.type_NodePath;
  2366. case Variant::NODE_PATH:
  2367. return p_arg_type.name == name_cache.type_NodePath;
  2368. case Variant::TRANSFORM2D:
  2369. case Variant::TRANSFORM3D:
  2370. case Variant::BASIS:
  2371. case Variant::QUATERNION:
  2372. case Variant::PLANE:
  2373. case Variant::AABB:
  2374. case Variant::COLOR:
  2375. case Variant::VECTOR2:
  2376. case Variant::RECT2:
  2377. case Variant::VECTOR3:
  2378. case Variant::RID:
  2379. case Variant::PACKED_BYTE_ARRAY:
  2380. case Variant::PACKED_INT32_ARRAY:
  2381. case Variant::PACKED_INT64_ARRAY:
  2382. case Variant::PACKED_FLOAT32_ARRAY:
  2383. case Variant::PACKED_FLOAT64_ARRAY:
  2384. case Variant::PACKED_STRING_ARRAY:
  2385. case Variant::PACKED_VECTOR2_ARRAY:
  2386. case Variant::PACKED_VECTOR3_ARRAY:
  2387. case Variant::PACKED_COLOR_ARRAY:
  2388. case Variant::CALLABLE:
  2389. case Variant::SIGNAL:
  2390. return p_arg_type.name == Variant::get_type_name(p_val.get_type());
  2391. case Variant::ARRAY:
  2392. return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Array_generic;
  2393. case Variant::DICTIONARY:
  2394. return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Dictionary_generic;
  2395. case Variant::OBJECT:
  2396. return p_arg_type.is_object_type;
  2397. case Variant::VECTOR2I:
  2398. return p_arg_type.name == name_cache.type_Vector2 ||
  2399. p_arg_type.name == Variant::get_type_name(p_val.get_type());
  2400. case Variant::RECT2I:
  2401. return p_arg_type.name == name_cache.type_Rect2 ||
  2402. p_arg_type.name == Variant::get_type_name(p_val.get_type());
  2403. case Variant::VECTOR3I:
  2404. return p_arg_type.name == name_cache.type_Vector3 ||
  2405. p_arg_type.name == Variant::get_type_name(p_val.get_type());
  2406. default:
  2407. CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
  2408. break;
  2409. }
  2410. return false;
  2411. }
  2412. bool method_has_ptr_parameter(MethodInfo p_method_info) {
  2413. if (p_method_info.return_val.type == Variant::INT && p_method_info.return_val.hint == PROPERTY_HINT_INT_IS_POINTER) {
  2414. return true;
  2415. }
  2416. for (PropertyInfo arg : p_method_info.arguments) {
  2417. if (arg.type == Variant::INT && arg.hint == PROPERTY_HINT_INT_IS_POINTER) {
  2418. return true;
  2419. }
  2420. }
  2421. return false;
  2422. }
  2423. struct SortMethodWithHashes {
  2424. _FORCE_INLINE_ bool operator()(const Pair<MethodInfo, uint32_t> &p_a, const Pair<MethodInfo, uint32_t> &p_b) const {
  2425. return p_a.first < p_b.first;
  2426. }
  2427. };
  2428. bool BindingsGenerator::_populate_object_type_interfaces() {
  2429. obj_types.clear();
  2430. List<StringName> class_list;
  2431. ClassDB::get_class_list(&class_list);
  2432. class_list.sort_custom<StringName::AlphCompare>();
  2433. while (class_list.size()) {
  2434. StringName type_cname = class_list.front()->get();
  2435. ClassDB::APIType api_type = ClassDB::get_api_type(type_cname);
  2436. if (api_type == ClassDB::API_NONE) {
  2437. class_list.pop_front();
  2438. continue;
  2439. }
  2440. if (ignored_types.has(type_cname)) {
  2441. _log("Ignoring type '%s' because it's in the list of ignored types\n", String(type_cname).utf8().get_data());
  2442. class_list.pop_front();
  2443. continue;
  2444. }
  2445. if (!ClassDB::is_class_exposed(type_cname)) {
  2446. _log("Ignoring type '%s' because it's not exposed\n", String(type_cname).utf8().get_data());
  2447. class_list.pop_front();
  2448. continue;
  2449. }
  2450. if (!ClassDB::is_class_enabled(type_cname)) {
  2451. _log("Ignoring type '%s' because it's not enabled\n", String(type_cname).utf8().get_data());
  2452. class_list.pop_front();
  2453. continue;
  2454. }
  2455. ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(type_cname);
  2456. TypeInterface itype = TypeInterface::create_object_type(type_cname, pascal_to_pascal_case(type_cname), api_type);
  2457. itype.base_name = ClassDB::get_parent_class(type_cname);
  2458. itype.is_singleton = Engine::get_singleton()->has_singleton(type_cname);
  2459. itype.is_instantiable = class_info->creation_func && !itype.is_singleton;
  2460. itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted);
  2461. itype.memory_own = itype.is_ref_counted;
  2462. if (itype.is_singleton && compat_singletons.has(itype.cname)) {
  2463. itype.is_singleton = false;
  2464. itype.is_compat_singleton = true;
  2465. }
  2466. itype.c_out = "%5return ";
  2467. itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
  2468. itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n";
  2469. itype.cs_type = itype.proxy_name;
  2470. itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(%0)";
  2471. itype.cs_out = "%5return (%2)%0(%1);";
  2472. itype.c_arg_in = "&%s";
  2473. itype.c_type = "IntPtr";
  2474. itype.c_type_in = itype.c_type;
  2475. itype.c_type_out = "GodotObject";
  2476. // Populate properties
  2477. List<PropertyInfo> property_list;
  2478. ClassDB::get_property_list(type_cname, &property_list, true);
  2479. HashMap<StringName, StringName> accessor_methods;
  2480. for (const PropertyInfo &property : property_list) {
  2481. if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY || (property.type == Variant::NIL && property.usage & PROPERTY_USAGE_ARRAY)) {
  2482. continue;
  2483. }
  2484. if (property.name.find("/") >= 0) {
  2485. // Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
  2486. continue;
  2487. }
  2488. PropertyInterface iprop;
  2489. iprop.cname = property.name;
  2490. iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname);
  2491. iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname);
  2492. if (iprop.setter != StringName()) {
  2493. accessor_methods[iprop.setter] = iprop.cname;
  2494. }
  2495. if (iprop.getter != StringName()) {
  2496. accessor_methods[iprop.getter] = iprop.cname;
  2497. }
  2498. bool valid = false;
  2499. iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid);
  2500. ERR_FAIL_COND_V_MSG(!valid, false, "Invalid property: '" + itype.name + "." + String(iprop.cname) + "'.");
  2501. iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname));
  2502. // Prevent the property and its enclosing type from sharing the same name
  2503. if (iprop.proxy_name == itype.proxy_name) {
  2504. _log("Name of property '%s' is ambiguous with the name of its enclosing class '%s'. Renaming property to '%s_'\n",
  2505. iprop.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), iprop.proxy_name.utf8().get_data());
  2506. iprop.proxy_name += "_";
  2507. }
  2508. iprop.prop_doc = nullptr;
  2509. for (int i = 0; i < itype.class_doc->properties.size(); i++) {
  2510. const DocData::PropertyDoc &prop_doc = itype.class_doc->properties[i];
  2511. if (prop_doc.name == iprop.cname) {
  2512. iprop.prop_doc = &prop_doc;
  2513. break;
  2514. }
  2515. }
  2516. itype.properties.push_back(iprop);
  2517. }
  2518. // Populate methods
  2519. List<MethodInfo> virtual_method_list;
  2520. ClassDB::get_virtual_methods(type_cname, &virtual_method_list, true);
  2521. List<Pair<MethodInfo, uint32_t>> method_list_with_hashes;
  2522. ClassDB::get_method_list_with_compatibility(type_cname, &method_list_with_hashes, true);
  2523. method_list_with_hashes.sort_custom_inplace<SortMethodWithHashes>();
  2524. List<MethodInterface> compat_methods;
  2525. for (const Pair<MethodInfo, uint32_t> &E : method_list_with_hashes) {
  2526. const MethodInfo &method_info = E.first;
  2527. const uint32_t hash = E.second;
  2528. int argc = method_info.arguments.size();
  2529. if (method_info.name.is_empty()) {
  2530. continue;
  2531. }
  2532. String cname = method_info.name;
  2533. if (blacklisted_methods.find(itype.cname) && blacklisted_methods[itype.cname].find(cname)) {
  2534. continue;
  2535. }
  2536. if (method_has_ptr_parameter(method_info)) {
  2537. // Pointers are not supported.
  2538. itype.ignored_members.insert(method_info.name);
  2539. continue;
  2540. }
  2541. MethodInterface imethod;
  2542. imethod.name = method_info.name;
  2543. imethod.cname = cname;
  2544. imethod.hash = hash;
  2545. if (method_info.flags & METHOD_FLAG_STATIC) {
  2546. imethod.is_static = true;
  2547. }
  2548. if (method_info.flags & METHOD_FLAG_VIRTUAL) {
  2549. imethod.is_virtual = true;
  2550. itype.has_virtual_methods = true;
  2551. }
  2552. PropertyInfo return_info = method_info.return_val;
  2553. MethodBind *m = nullptr;
  2554. if (!imethod.is_virtual) {
  2555. bool method_exists = false;
  2556. m = ClassDB::get_method_with_compatibility(type_cname, method_info.name, hash, &method_exists, &imethod.is_compat);
  2557. if (unlikely(!method_exists)) {
  2558. ERR_FAIL_COND_V_MSG(!virtual_method_list.find(method_info), false,
  2559. "Missing MethodBind for non-virtual method: '" + itype.name + "." + imethod.name + "'.");
  2560. }
  2561. }
  2562. imethod.is_vararg = m && m->is_vararg();
  2563. if (!m && !imethod.is_virtual) {
  2564. ERR_FAIL_COND_V_MSG(!virtual_method_list.find(method_info), false,
  2565. "Missing MethodBind for non-virtual method: '" + itype.name + "." + imethod.name + "'.");
  2566. // A virtual method without the virtual flag. This is a special case.
  2567. // There is no method bind, so let's fallback to Godot's object.Call(string, params)
  2568. imethod.requires_object_call = true;
  2569. // The method Object.free is registered as a virtual method, but without the virtual flag.
  2570. // This is because this method is not supposed to be overridden, but called.
  2571. // We assume the return type is void.
  2572. imethod.return_type.cname = name_cache.type_void;
  2573. // Actually, more methods like this may be added in the future, which could return
  2574. // something different. Let's put this check to notify us if that ever happens.
  2575. if (itype.cname != name_cache.type_Object || imethod.name != "free") {
  2576. WARN_PRINT("Notification: New unexpected virtual non-overridable method found."
  2577. " We only expected Object.free, but found '" +
  2578. itype.name + "." + imethod.name + "'.");
  2579. }
  2580. } else if (return_info.type == Variant::INT && return_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
  2581. imethod.return_type.cname = return_info.class_name;
  2582. imethod.return_type.is_enum = true;
  2583. } else if (return_info.class_name != StringName()) {
  2584. imethod.return_type.cname = return_info.class_name;
  2585. bool bad_reference_hint = !imethod.is_virtual && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE &&
  2586. ClassDB::is_parent_class(return_info.class_name, name_cache.type_RefCounted);
  2587. ERR_FAIL_COND_V_MSG(bad_reference_hint, false,
  2588. String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." +
  2589. " Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'.");
  2590. } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) {
  2591. imethod.return_type.cname = Variant::get_type_name(return_info.type) + "_@generic";
  2592. imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string));
  2593. } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
  2594. imethod.return_type.cname = return_info.hint_string;
  2595. } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
  2596. imethod.return_type.cname = name_cache.type_Variant;
  2597. } else if (return_info.type == Variant::NIL) {
  2598. imethod.return_type.cname = name_cache.type_void;
  2599. } else {
  2600. imethod.return_type.cname = _get_type_name_from_meta(return_info.type, m ? m->get_argument_meta(-1) : (GodotTypeInfo::Metadata)method_info.return_val_metadata);
  2601. }
  2602. for (int i = 0; i < argc; i++) {
  2603. PropertyInfo arginfo = method_info.arguments[i];
  2604. String orig_arg_name = arginfo.name;
  2605. ArgumentInterface iarg;
  2606. iarg.name = orig_arg_name;
  2607. if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
  2608. iarg.type.cname = arginfo.class_name;
  2609. iarg.type.is_enum = true;
  2610. } else if (arginfo.class_name != StringName()) {
  2611. iarg.type.cname = arginfo.class_name;
  2612. } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
  2613. iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic";
  2614. iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
  2615. } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
  2616. iarg.type.cname = arginfo.hint_string;
  2617. } else if (arginfo.type == Variant::NIL) {
  2618. iarg.type.cname = name_cache.type_Variant;
  2619. } else {
  2620. iarg.type.cname = _get_type_name_from_meta(arginfo.type, m ? m->get_argument_meta(i) : (GodotTypeInfo::Metadata)method_info.get_argument_meta(i));
  2621. }
  2622. iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
  2623. if (m && m->has_default_argument(i)) {
  2624. bool defval_ok = _arg_default_value_from_variant(m->get_default_argument(i), iarg);
  2625. ERR_FAIL_COND_V_MSG(!defval_ok, false,
  2626. "Cannot determine default value for argument '" + orig_arg_name + "' of method '" + itype.name + "." + imethod.name + "'.");
  2627. }
  2628. imethod.add_argument(iarg);
  2629. }
  2630. if (imethod.is_vararg) {
  2631. ArgumentInterface ivararg;
  2632. ivararg.type.cname = name_cache.type_VarArg;
  2633. ivararg.name = "@args";
  2634. imethod.add_argument(ivararg);
  2635. }
  2636. imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name));
  2637. // Prevent the method and its enclosing type from sharing the same name
  2638. if (imethod.proxy_name == itype.proxy_name) {
  2639. _log("Name of method '%s' is ambiguous with the name of its enclosing class '%s'. Renaming method to '%s_'\n",
  2640. imethod.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), imethod.proxy_name.utf8().get_data());
  2641. imethod.proxy_name += "_";
  2642. }
  2643. HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname);
  2644. if (accessor) {
  2645. // We only make internal an accessor method if it's in the same class as the property.
  2646. // It's easier this way, but also we don't know if an accessor method in a different class
  2647. // could have other purposes, so better leave those untouched.
  2648. imethod.is_internal = true;
  2649. }
  2650. if (itype.class_doc) {
  2651. for (int i = 0; i < itype.class_doc->methods.size(); i++) {
  2652. if (itype.class_doc->methods[i].name == imethod.name) {
  2653. imethod.method_doc = &itype.class_doc->methods[i];
  2654. break;
  2655. }
  2656. }
  2657. }
  2658. ERR_FAIL_COND_V_MSG(itype.find_property_by_name(imethod.cname), false,
  2659. "Method name conflicts with property: '" + itype.name + "." + imethod.name + "'.");
  2660. // Compat methods aren't added to the type yet, they need to be checked for conflicts
  2661. // after all the non-compat methods have been added. The compat methods are added in
  2662. // reverse so the most recently added ones take precedence over older compat methods.
  2663. if (imethod.is_compat) {
  2664. compat_methods.push_front(imethod);
  2665. continue;
  2666. }
  2667. // Methods starting with an underscore are ignored unless they're used as a property setter or getter
  2668. if (!imethod.is_virtual && imethod.name[0] == '_') {
  2669. for (const PropertyInterface &iprop : itype.properties) {
  2670. if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
  2671. imethod.is_internal = true;
  2672. itype.methods.push_back(imethod);
  2673. break;
  2674. }
  2675. }
  2676. } else {
  2677. itype.methods.push_back(imethod);
  2678. }
  2679. }
  2680. // Add compat methods that don't conflict with other methods in the type.
  2681. for (const MethodInterface &imethod : compat_methods) {
  2682. if (_method_has_conflicting_signature(imethod, itype)) {
  2683. WARN_PRINT("Method '" + imethod.name + "' conflicts with an already existing method in type '" + itype.name + "' and has been ignored.");
  2684. continue;
  2685. }
  2686. itype.methods.push_back(imethod);
  2687. }
  2688. // Populate signals
  2689. const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map;
  2690. for (const KeyValue<StringName, MethodInfo> &E : signal_map) {
  2691. SignalInterface isignal;
  2692. const MethodInfo &method_info = E.value;
  2693. isignal.name = method_info.name;
  2694. isignal.cname = method_info.name;
  2695. int argc = method_info.arguments.size();
  2696. for (int i = 0; i < argc; i++) {
  2697. PropertyInfo arginfo = method_info.arguments[i];
  2698. String orig_arg_name = arginfo.name;
  2699. ArgumentInterface iarg;
  2700. iarg.name = orig_arg_name;
  2701. if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
  2702. iarg.type.cname = arginfo.class_name;
  2703. iarg.type.is_enum = true;
  2704. } else if (arginfo.class_name != StringName()) {
  2705. iarg.type.cname = arginfo.class_name;
  2706. } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
  2707. iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic";
  2708. iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
  2709. } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
  2710. iarg.type.cname = arginfo.hint_string;
  2711. } else if (arginfo.type == Variant::NIL) {
  2712. iarg.type.cname = name_cache.type_Variant;
  2713. } else {
  2714. iarg.type.cname = _get_type_name_from_meta(arginfo.type, (GodotTypeInfo::Metadata)method_info.get_argument_meta(i));
  2715. }
  2716. iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
  2717. isignal.add_argument(iarg);
  2718. }
  2719. isignal.proxy_name = escape_csharp_keyword(snake_to_pascal_case(isignal.name));
  2720. // Prevent the signal and its enclosing type from sharing the same name
  2721. if (isignal.proxy_name == itype.proxy_name) {
  2722. _log("Name of signal '%s' is ambiguous with the name of its enclosing class '%s'. Renaming signal to '%s_'\n",
  2723. isignal.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), isignal.proxy_name.utf8().get_data());
  2724. isignal.proxy_name += "_";
  2725. }
  2726. if (itype.find_property_by_proxy_name(isignal.proxy_name) || itype.find_method_by_proxy_name(isignal.proxy_name)) {
  2727. // ClassDB allows signal names that conflict with method or property names.
  2728. // While registering a signal with a conflicting name is considered wrong,
  2729. // it may still happen and it may take some time until someone fixes the name.
  2730. // We can't allow the bindings to be in a broken state while we wait for a fix;
  2731. // that's why we must handle this possibility by renaming the signal.
  2732. isignal.proxy_name += "Signal";
  2733. }
  2734. if (itype.class_doc) {
  2735. for (int i = 0; i < itype.class_doc->signals.size(); i++) {
  2736. const DocData::MethodDoc &signal_doc = itype.class_doc->signals[i];
  2737. if (signal_doc.name == isignal.name) {
  2738. isignal.method_doc = &signal_doc;
  2739. break;
  2740. }
  2741. }
  2742. }
  2743. itype.signals_.push_back(isignal);
  2744. }
  2745. // Populate enums and constants
  2746. List<String> constants;
  2747. ClassDB::get_integer_constant_list(type_cname, &constants, true);
  2748. const HashMap<StringName, ClassDB::ClassInfo::EnumInfo> &enum_map = class_info->enum_map;
  2749. for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &E : enum_map) {
  2750. StringName enum_proxy_cname = E.key;
  2751. String enum_proxy_name = pascal_to_pascal_case(enum_proxy_cname.operator String());
  2752. if (itype.find_property_by_proxy_name(enum_proxy_name) || itype.find_method_by_proxy_name(enum_proxy_name) || itype.find_signal_by_proxy_name(enum_proxy_name)) {
  2753. // In case the enum name conflicts with other PascalCase members,
  2754. // we append 'Enum' to the enum name in those cases.
  2755. // We have several conflicts between enums and PascalCase properties.
  2756. enum_proxy_name += "Enum";
  2757. enum_proxy_cname = StringName(enum_proxy_name);
  2758. }
  2759. EnumInterface ienum(enum_proxy_cname, enum_proxy_name, E.value.is_bitfield);
  2760. const List<StringName> &enum_constants = E.value.constants;
  2761. for (const StringName &constant_cname : enum_constants) {
  2762. String constant_name = constant_cname.operator String();
  2763. int64_t *value = class_info->constant_map.getptr(constant_cname);
  2764. ERR_FAIL_NULL_V(value, false);
  2765. constants.erase(constant_name);
  2766. ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
  2767. iconstant.const_doc = nullptr;
  2768. for (int i = 0; i < itype.class_doc->constants.size(); i++) {
  2769. const DocData::ConstantDoc &const_doc = itype.class_doc->constants[i];
  2770. if (const_doc.name == iconstant.name) {
  2771. iconstant.const_doc = &const_doc;
  2772. break;
  2773. }
  2774. }
  2775. ienum.constants.push_back(iconstant);
  2776. }
  2777. int prefix_length = _determine_enum_prefix(ienum);
  2778. _apply_prefix_to_enum_constants(ienum, prefix_length);
  2779. itype.enums.push_back(ienum);
  2780. TypeInterface enum_itype;
  2781. enum_itype.is_enum = true;
  2782. enum_itype.name = itype.name + "." + String(E.key);
  2783. enum_itype.cname = StringName(enum_itype.name);
  2784. enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
  2785. TypeInterface::postsetup_enum_type(enum_itype);
  2786. enum_types.insert(enum_itype.cname, enum_itype);
  2787. }
  2788. for (const String &constant_name : constants) {
  2789. int64_t *value = class_info->constant_map.getptr(StringName(constant_name));
  2790. ERR_FAIL_NULL_V(value, false);
  2791. String constant_proxy_name = snake_to_pascal_case(constant_name, true);
  2792. if (itype.find_property_by_proxy_name(constant_proxy_name) || itype.find_method_by_proxy_name(constant_proxy_name) || itype.find_signal_by_proxy_name(constant_proxy_name)) {
  2793. // In case the constant name conflicts with other PascalCase members,
  2794. // we append 'Constant' to the constant name in those cases.
  2795. constant_proxy_name += "Constant";
  2796. }
  2797. ConstantInterface iconstant(constant_name, constant_proxy_name, *value);
  2798. iconstant.const_doc = nullptr;
  2799. for (int i = 0; i < itype.class_doc->constants.size(); i++) {
  2800. const DocData::ConstantDoc &const_doc = itype.class_doc->constants[i];
  2801. if (const_doc.name == iconstant.name) {
  2802. iconstant.const_doc = &const_doc;
  2803. break;
  2804. }
  2805. }
  2806. itype.constants.push_back(iconstant);
  2807. }
  2808. obj_types.insert(itype.cname, itype);
  2809. if (itype.is_singleton) {
  2810. // Add singleton instance type.
  2811. itype.proxy_name += CS_SINGLETON_INSTANCE_SUFFIX;
  2812. itype.is_singleton = false;
  2813. itype.is_singleton_instance = true;
  2814. // Remove constants and enums, those will remain in the static class.
  2815. itype.constants.clear();
  2816. itype.enums.clear();
  2817. obj_types.insert(itype.name + CS_SINGLETON_INSTANCE_SUFFIX, itype);
  2818. }
  2819. class_list.pop_front();
  2820. }
  2821. return true;
  2822. }
  2823. bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, ArgumentInterface &r_iarg) {
  2824. r_iarg.def_param_value = p_val;
  2825. r_iarg.default_argument = p_val.operator String();
  2826. switch (p_val.get_type()) {
  2827. case Variant::NIL:
  2828. // Either Object type or Variant
  2829. r_iarg.default_argument = "default";
  2830. break;
  2831. // Atomic types
  2832. case Variant::BOOL:
  2833. r_iarg.default_argument = bool(p_val) ? "true" : "false";
  2834. break;
  2835. case Variant::INT:
  2836. if (r_iarg.type.cname != name_cache.type_int) {
  2837. r_iarg.default_argument = "(%s)(" + r_iarg.default_argument + ")";
  2838. }
  2839. break;
  2840. case Variant::FLOAT:
  2841. if (r_iarg.type.cname == name_cache.type_float) {
  2842. r_iarg.default_argument += "f";
  2843. }
  2844. break;
  2845. case Variant::STRING:
  2846. case Variant::STRING_NAME:
  2847. case Variant::NODE_PATH:
  2848. if (r_iarg.type.cname == name_cache.type_StringName || r_iarg.type.cname == name_cache.type_NodePath) {
  2849. if (r_iarg.default_argument.length() > 0) {
  2850. r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
  2851. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
  2852. } else {
  2853. // No need for a special `in` statement to change `null` to `""`. Marshaling takes care of this already.
  2854. r_iarg.default_argument = "null";
  2855. }
  2856. } else {
  2857. CRASH_COND(r_iarg.type.cname != name_cache.type_String);
  2858. r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
  2859. }
  2860. break;
  2861. case Variant::PLANE: {
  2862. Plane plane = p_val.operator Plane();
  2863. r_iarg.default_argument = "new Plane(new Vector3" + plane.normal.operator String() + ", " + rtos(plane.d) + ")";
  2864. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2865. } break;
  2866. case Variant::AABB: {
  2867. AABB aabb = p_val.operator ::AABB();
  2868. r_iarg.default_argument = "new Aabb(new Vector3" + aabb.position.operator String() + ", new Vector3" + aabb.size.operator String() + ")";
  2869. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2870. } break;
  2871. case Variant::RECT2: {
  2872. Rect2 rect = p_val.operator Rect2();
  2873. r_iarg.default_argument = "new Rect2(new Vector2" + rect.position.operator String() + ", new Vector2" + rect.size.operator String() + ")";
  2874. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2875. } break;
  2876. case Variant::RECT2I: {
  2877. Rect2i rect = p_val.operator Rect2i();
  2878. r_iarg.default_argument = "new Rect2I(new Vector2I" + rect.position.operator String() + ", new Vector2I" + rect.size.operator String() + ")";
  2879. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2880. } break;
  2881. case Variant::COLOR:
  2882. case Variant::VECTOR2:
  2883. case Variant::VECTOR2I:
  2884. case Variant::VECTOR3:
  2885. case Variant::VECTOR3I:
  2886. r_iarg.default_argument = "new %s" + r_iarg.default_argument;
  2887. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2888. break;
  2889. case Variant::VECTOR4:
  2890. case Variant::VECTOR4I:
  2891. r_iarg.default_argument = "new %s" + r_iarg.default_argument;
  2892. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2893. break;
  2894. case Variant::OBJECT:
  2895. ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
  2896. "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
  2897. r_iarg.default_argument = "null";
  2898. break;
  2899. case Variant::DICTIONARY:
  2900. ERR_FAIL_COND_V_MSG(!p_val.operator Dictionary().is_empty(), false,
  2901. "Default value of type 'Dictionary' must be an empty dictionary.");
  2902. // The [cs_in] expression already interprets null values as empty dictionaries.
  2903. r_iarg.default_argument = "null";
  2904. r_iarg.def_param_mode = ArgumentInterface::CONSTANT;
  2905. break;
  2906. case Variant::RID:
  2907. ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_RID, false,
  2908. "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_RID) + "'.");
  2909. ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
  2910. "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
  2911. r_iarg.default_argument = "default";
  2912. break;
  2913. case Variant::ARRAY:
  2914. ERR_FAIL_COND_V_MSG(!p_val.operator Array().is_empty(), false,
  2915. "Default value of type 'Array' must be an empty array.");
  2916. // The [cs_in] expression already interprets null values as empty arrays.
  2917. r_iarg.default_argument = "null";
  2918. r_iarg.def_param_mode = ArgumentInterface::CONSTANT;
  2919. break;
  2920. case Variant::PACKED_BYTE_ARRAY:
  2921. case Variant::PACKED_INT32_ARRAY:
  2922. case Variant::PACKED_INT64_ARRAY:
  2923. case Variant::PACKED_FLOAT32_ARRAY:
  2924. case Variant::PACKED_FLOAT64_ARRAY:
  2925. case Variant::PACKED_STRING_ARRAY:
  2926. case Variant::PACKED_VECTOR2_ARRAY:
  2927. case Variant::PACKED_VECTOR3_ARRAY:
  2928. case Variant::PACKED_COLOR_ARRAY:
  2929. r_iarg.default_argument = "Array.Empty<%s>()";
  2930. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
  2931. break;
  2932. case Variant::TRANSFORM2D: {
  2933. Transform2D transform = p_val.operator Transform2D();
  2934. if (transform == Transform2D()) {
  2935. r_iarg.default_argument = "Transform2D.Identity";
  2936. } else {
  2937. r_iarg.default_argument = "new Transform2D(new Vector2" + transform.columns[0].operator String() + ", new Vector2" + transform.columns[1].operator String() + ", new Vector2" + transform.columns[2].operator String() + ")";
  2938. }
  2939. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2940. } break;
  2941. case Variant::TRANSFORM3D: {
  2942. Transform3D transform = p_val.operator Transform3D();
  2943. if (transform == Transform3D()) {
  2944. r_iarg.default_argument = "Transform3D.Identity";
  2945. } else {
  2946. Basis basis = transform.basis;
  2947. r_iarg.default_argument = "new Transform3D(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ", new Vector3" + transform.origin.operator String() + ")";
  2948. }
  2949. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2950. } break;
  2951. case Variant::PROJECTION: {
  2952. Projection projection = p_val.operator Projection();
  2953. if (projection == Projection()) {
  2954. r_iarg.default_argument = "Projection.Identity";
  2955. } else {
  2956. r_iarg.default_argument = "new Projection(new Vector4" + projection.columns[0].operator String() + ", new Vector4" + projection.columns[1].operator String() + ", new Vector4" + projection.columns[2].operator String() + ", new Vector4" + projection.columns[3].operator String() + ")";
  2957. }
  2958. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2959. } break;
  2960. case Variant::BASIS: {
  2961. Basis basis = p_val.operator Basis();
  2962. if (basis == Basis()) {
  2963. r_iarg.default_argument = "Basis.Identity";
  2964. } else {
  2965. r_iarg.default_argument = "new Basis(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ")";
  2966. }
  2967. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2968. } break;
  2969. case Variant::QUATERNION: {
  2970. Quaternion quaternion = p_val.operator Quaternion();
  2971. if (quaternion == Quaternion()) {
  2972. r_iarg.default_argument = "Quaternion.Identity";
  2973. } else {
  2974. r_iarg.default_argument = "new Quaternion" + quaternion.operator String();
  2975. }
  2976. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2977. } break;
  2978. case Variant::CALLABLE:
  2979. ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Callable, false,
  2980. "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Callable) + "'.");
  2981. ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
  2982. "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
  2983. r_iarg.default_argument = "default";
  2984. break;
  2985. case Variant::SIGNAL:
  2986. ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Signal, false,
  2987. "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Signal) + "'.");
  2988. ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
  2989. "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
  2990. r_iarg.default_argument = "default";
  2991. break;
  2992. default:
  2993. ERR_FAIL_V_MSG(false, "Unexpected Variant type: " + itos(p_val.get_type()));
  2994. break;
  2995. }
  2996. if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "default") {
  2997. r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
  2998. }
  2999. return true;
  3000. }
  3001. void BindingsGenerator::_populate_builtin_type_interfaces() {
  3002. builtin_types.clear();
  3003. TypeInterface itype;
  3004. #define INSERT_STRUCT_TYPE(m_type, m_proxy_name) \
  3005. { \
  3006. itype = TypeInterface::create_value_type(String(#m_type), String(#m_proxy_name)); \
  3007. itype.cs_in_expr = "&%0"; \
  3008. itype.cs_in_expr_is_unsafe = true; \
  3009. builtin_types.insert(itype.cname, itype); \
  3010. }
  3011. INSERT_STRUCT_TYPE(Vector2, Vector2)
  3012. INSERT_STRUCT_TYPE(Vector2i, Vector2I)
  3013. INSERT_STRUCT_TYPE(Rect2, Rect2)
  3014. INSERT_STRUCT_TYPE(Rect2i, Rect2I)
  3015. INSERT_STRUCT_TYPE(Transform2D, Transform2D)
  3016. INSERT_STRUCT_TYPE(Vector3, Vector3)
  3017. INSERT_STRUCT_TYPE(Vector3i, Vector3I)
  3018. INSERT_STRUCT_TYPE(Basis, Basis)
  3019. INSERT_STRUCT_TYPE(Quaternion, Quaternion)
  3020. INSERT_STRUCT_TYPE(Transform3D, Transform3D)
  3021. INSERT_STRUCT_TYPE(AABB, Aabb)
  3022. INSERT_STRUCT_TYPE(Color, Color)
  3023. INSERT_STRUCT_TYPE(Plane, Plane)
  3024. INSERT_STRUCT_TYPE(Vector4, Vector4)
  3025. INSERT_STRUCT_TYPE(Vector4i, Vector4I)
  3026. INSERT_STRUCT_TYPE(Projection, Projection)
  3027. #undef INSERT_STRUCT_TYPE
  3028. // bool
  3029. itype = TypeInterface::create_value_type(String("bool"));
  3030. itype.cs_in_expr = "%0.ToGodotBool()";
  3031. itype.cs_out = "%5return %0(%1).ToBool();";
  3032. itype.c_type = "godot_bool";
  3033. itype.c_type_in = itype.c_type;
  3034. itype.c_type_out = itype.c_type;
  3035. itype.c_arg_in = "&%s";
  3036. itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n";
  3037. builtin_types.insert(itype.cname, itype);
  3038. // Integer types
  3039. {
  3040. // C interface for 'uint32_t' is the same as that of enums. Remember to apply
  3041. // any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
  3042. #define INSERT_INT_TYPE(m_name, m_int_struct_name) \
  3043. { \
  3044. itype = TypeInterface::create_value_type(String(m_name)); \
  3045. if (itype.name != "long" && itype.name != "ulong") { \
  3046. itype.c_in = "%5%0 %1_in = %1;\n"; \
  3047. itype.c_out = "%5return (%0)(%1);\n"; \
  3048. itype.c_type = "long"; \
  3049. itype.c_arg_in = "&%s_in"; \
  3050. } else { \
  3051. itype.c_arg_in = "&%s"; \
  3052. } \
  3053. itype.c_type_in = itype.name; \
  3054. itype.c_type_out = itype.name; \
  3055. itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \
  3056. builtin_types.insert(itype.cname, itype); \
  3057. }
  3058. // The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
  3059. INSERT_INT_TYPE("sbyte", "Int8");
  3060. INSERT_INT_TYPE("short", "Int16");
  3061. INSERT_INT_TYPE("int", "Int32");
  3062. INSERT_INT_TYPE("long", "Int64");
  3063. INSERT_INT_TYPE("byte", "UInt8");
  3064. INSERT_INT_TYPE("ushort", "UInt16");
  3065. INSERT_INT_TYPE("uint", "UInt32");
  3066. INSERT_INT_TYPE("ulong", "UInt64");
  3067. #undef INSERT_INT_TYPE
  3068. }
  3069. // Floating point types
  3070. {
  3071. // float
  3072. itype = TypeInterface();
  3073. itype.name = "float";
  3074. itype.cname = itype.name;
  3075. itype.proxy_name = "float";
  3076. itype.cs_type = itype.proxy_name;
  3077. {
  3078. // The expected type for 'float' in ptrcall is 'double'
  3079. itype.c_in = "%5%0 %1_in = %1;\n";
  3080. itype.c_out = "%5return (%0)%1;\n";
  3081. itype.c_type = "double";
  3082. itype.c_arg_in = "&%s_in";
  3083. }
  3084. itype.c_type_in = itype.proxy_name;
  3085. itype.c_type_out = itype.proxy_name;
  3086. itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
  3087. builtin_types.insert(itype.cname, itype);
  3088. // double
  3089. itype = TypeInterface();
  3090. itype.name = "double";
  3091. itype.cname = itype.name;
  3092. itype.proxy_name = "double";
  3093. itype.cs_type = itype.proxy_name;
  3094. itype.c_type = "double";
  3095. itype.c_arg_in = "&%s";
  3096. itype.c_type_in = itype.proxy_name;
  3097. itype.c_type_out = itype.proxy_name;
  3098. itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
  3099. builtin_types.insert(itype.cname, itype);
  3100. }
  3101. // String
  3102. itype = TypeInterface();
  3103. itype.name = "String";
  3104. itype.cname = itype.name;
  3105. itype.proxy_name = "string";
  3106. itype.cs_type = itype.proxy_name;
  3107. itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
  3108. itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n";
  3109. itype.c_arg_in = "&%s_in";
  3110. itype.c_type = "godot_string";
  3111. itype.c_type_in = itype.cs_type;
  3112. itype.c_type_out = itype.cs_type;
  3113. itype.c_type_is_disposable_struct = true;
  3114. itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromString(%1);\n";
  3115. builtin_types.insert(itype.cname, itype);
  3116. // StringName
  3117. itype = TypeInterface();
  3118. itype.name = "StringName";
  3119. itype.cname = itype.name;
  3120. itype.proxy_name = "StringName";
  3121. itype.cs_type = itype.proxy_name;
  3122. itype.cs_in_expr = "(%1)(%0?.NativeValue ?? default)";
  3123. // Cannot pass null StringName to ptrcall
  3124. itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
  3125. itype.c_arg_in = "&%s";
  3126. itype.c_type = "godot_string_name";
  3127. itype.c_type_in = itype.c_type;
  3128. itype.c_type_out = itype.cs_type;
  3129. itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n";
  3130. itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
  3131. itype.c_ret_needs_default_initialization = true;
  3132. builtin_types.insert(itype.cname, itype);
  3133. // NodePath
  3134. itype = TypeInterface();
  3135. itype.name = "NodePath";
  3136. itype.cname = itype.name;
  3137. itype.proxy_name = "NodePath";
  3138. itype.cs_type = itype.proxy_name;
  3139. itype.cs_in_expr = "(%1)(%0?.NativeValue ?? default)";
  3140. // Cannot pass null NodePath to ptrcall
  3141. itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
  3142. itype.c_arg_in = "&%s";
  3143. itype.c_type = "godot_node_path";
  3144. itype.c_type_in = itype.c_type;
  3145. itype.c_type_out = itype.cs_type;
  3146. itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
  3147. itype.c_ret_needs_default_initialization = true;
  3148. builtin_types.insert(itype.cname, itype);
  3149. // RID
  3150. itype = TypeInterface();
  3151. itype.name = "RID";
  3152. itype.cname = itype.name;
  3153. itype.proxy_name = "Rid";
  3154. itype.cs_type = itype.proxy_name;
  3155. itype.c_arg_in = "&%s";
  3156. itype.c_type = itype.cs_type;
  3157. itype.c_type_in = itype.c_type;
  3158. itype.c_type_out = itype.c_type;
  3159. builtin_types.insert(itype.cname, itype);
  3160. // Variant
  3161. itype = TypeInterface();
  3162. itype.name = "Variant";
  3163. itype.cname = itype.name;
  3164. itype.proxy_name = "Variant";
  3165. itype.cs_type = itype.proxy_name;
  3166. itype.c_in = "%5%0 %1_in = (%0)%1.NativeVar;\n";
  3167. itype.c_out = "%5return Variant.CreateTakingOwnershipOfDisposableValue(%1);\n";
  3168. itype.c_arg_in = "&%s_in";
  3169. itype.c_type = "godot_variant";
  3170. itype.c_type_in = itype.cs_type;
  3171. itype.c_type_out = itype.cs_type;
  3172. itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
  3173. itype.c_ret_needs_default_initialization = true;
  3174. builtin_types.insert(itype.cname, itype);
  3175. // Callable
  3176. itype = TypeInterface::create_value_type(String("Callable"));
  3177. itype.cs_in_expr = "%0";
  3178. itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(in %1);\n";
  3179. itype.c_out = "%5return " C_METHOD_MANAGED_FROM_CALLABLE "(in %1);\n";
  3180. itype.c_arg_in = "&%s_in";
  3181. itype.c_type = "godot_callable";
  3182. itype.c_type_in = "in " + itype.cs_type;
  3183. itype.c_type_out = itype.cs_type;
  3184. itype.c_type_is_disposable_struct = true;
  3185. builtin_types.insert(itype.cname, itype);
  3186. // Signal
  3187. itype = TypeInterface();
  3188. itype.name = "Signal";
  3189. itype.cname = itype.name;
  3190. itype.proxy_name = "Signal";
  3191. itype.cs_type = itype.proxy_name;
  3192. itype.cs_in_expr = "%0";
  3193. itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(in %1);\n";
  3194. itype.c_out = "%5return " C_METHOD_MANAGED_FROM_SIGNAL "(in %1);\n";
  3195. itype.c_arg_in = "&%s_in";
  3196. itype.c_type = "godot_signal";
  3197. itype.c_type_in = "in " + itype.cs_type;
  3198. itype.c_type_out = itype.cs_type;
  3199. itype.c_type_is_disposable_struct = true;
  3200. builtin_types.insert(itype.cname, itype);
  3201. // VarArg (fictitious type to represent variable arguments)
  3202. itype = TypeInterface();
  3203. itype.name = "VarArg";
  3204. itype.cname = itype.name;
  3205. itype.proxy_name = "Variant[]";
  3206. itype.cs_type = "params Variant[]";
  3207. itype.cs_in_expr = "%0 ?? Array.Empty<Variant>()";
  3208. // c_type, c_in and c_arg_in are hard-coded in the generator.
  3209. // c_out and c_type_out are not applicable to VarArg.
  3210. itype.c_arg_in = "&%s_in";
  3211. itype.c_type_in = "Variant[]";
  3212. builtin_types.insert(itype.cname, itype);
  3213. #define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \
  3214. { \
  3215. itype = TypeInterface(); \
  3216. itype.name = #m_name; \
  3217. itype.cname = itype.name; \
  3218. itype.proxy_name = #m_proxy_t "[]"; \
  3219. itype.cs_type = itype.proxy_name; \
  3220. itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \
  3221. itype.c_out = "%5return " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \
  3222. itype.c_arg_in = "&%s_in"; \
  3223. itype.c_type = #m_managed_type; \
  3224. itype.c_type_in = itype.proxy_name; \
  3225. itype.c_type_out = itype.proxy_name; \
  3226. itype.c_type_is_disposable_struct = true; \
  3227. builtin_types.insert(itype.name, itype); \
  3228. }
  3229. #define INSERT_ARRAY(m_type, m_managed_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_managed_type, m_proxy_t)
  3230. INSERT_ARRAY(PackedInt32Array, godot_packed_int32_array, int);
  3231. INSERT_ARRAY(PackedInt64Array, godot_packed_int64_array, long);
  3232. INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, godot_packed_byte_array, byte);
  3233. INSERT_ARRAY(PackedFloat32Array, godot_packed_float32_array, float);
  3234. INSERT_ARRAY(PackedFloat64Array, godot_packed_float64_array, double);
  3235. INSERT_ARRAY(PackedStringArray, godot_packed_string_array, string);
  3236. INSERT_ARRAY(PackedColorArray, godot_packed_color_array, Color);
  3237. INSERT_ARRAY(PackedVector2Array, godot_packed_vector2_array, Vector2);
  3238. INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3);
  3239. #undef INSERT_ARRAY
  3240. // Array
  3241. itype = TypeInterface();
  3242. itype.name = "Array";
  3243. itype.cname = itype.name;
  3244. itype.proxy_name = itype.name;
  3245. itype.type_parameter_count = 1;
  3246. itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
  3247. itype.cs_in_expr = "(%1)(%0 ?? new()).NativeValue";
  3248. itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
  3249. itype.c_arg_in = "&%s";
  3250. itype.c_type = "godot_array";
  3251. itype.c_type_in = itype.c_type;
  3252. itype.c_type_out = itype.cs_type;
  3253. itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
  3254. itype.c_ret_needs_default_initialization = true;
  3255. builtin_types.insert(itype.cname, itype);
  3256. // Array_@generic
  3257. // Reuse Array's itype
  3258. itype.name = "Array_@generic";
  3259. itype.cname = itype.name;
  3260. itype.cs_out = "%5return new %2(%0(%1));";
  3261. // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
  3262. itype.cs_variant_to_managed = "VariantUtils.ConvertToArray(%0)";
  3263. itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
  3264. builtin_types.insert(itype.cname, itype);
  3265. // Dictionary
  3266. itype = TypeInterface();
  3267. itype.name = "Dictionary";
  3268. itype.cname = itype.name;
  3269. itype.proxy_name = itype.name;
  3270. itype.type_parameter_count = 2;
  3271. itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
  3272. itype.cs_in_expr = "(%1)(%0 ?? new()).NativeValue";
  3273. itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
  3274. itype.c_arg_in = "&%s";
  3275. itype.c_type = "godot_dictionary";
  3276. itype.c_type_in = itype.c_type;
  3277. itype.c_type_out = itype.cs_type;
  3278. itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
  3279. itype.c_ret_needs_default_initialization = true;
  3280. builtin_types.insert(itype.cname, itype);
  3281. // Dictionary_@generic
  3282. // Reuse Dictionary's itype
  3283. itype.name = "Dictionary_@generic";
  3284. itype.cname = itype.name;
  3285. itype.cs_out = "%5return new %2(%0(%1));";
  3286. // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
  3287. itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionary(%0)";
  3288. itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
  3289. builtin_types.insert(itype.cname, itype);
  3290. // void (fictitious type to represent the return type of methods that do not return anything)
  3291. itype = TypeInterface();
  3292. itype.name = "void";
  3293. itype.cname = itype.name;
  3294. itype.proxy_name = itype.name;
  3295. itype.cs_type = itype.proxy_name;
  3296. itype.c_type = itype.proxy_name;
  3297. itype.c_type_in = itype.c_type;
  3298. itype.c_type_out = itype.c_type;
  3299. builtin_types.insert(itype.cname, itype);
  3300. }
  3301. void BindingsGenerator::_populate_global_constants() {
  3302. int global_constants_count = CoreConstants::get_global_constant_count();
  3303. if (global_constants_count > 0) {
  3304. HashMap<String, DocData::ClassDoc>::Iterator match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
  3305. CRASH_COND_MSG(!match, "Could not find '@GlobalScope' in DocData.");
  3306. const DocData::ClassDoc &global_scope_doc = match->value;
  3307. for (int i = 0; i < global_constants_count; i++) {
  3308. String constant_name = CoreConstants::get_global_constant_name(i);
  3309. const DocData::ConstantDoc *const_doc = nullptr;
  3310. for (int j = 0; j < global_scope_doc.constants.size(); j++) {
  3311. const DocData::ConstantDoc &curr_const_doc = global_scope_doc.constants[j];
  3312. if (curr_const_doc.name == constant_name) {
  3313. const_doc = &curr_const_doc;
  3314. break;
  3315. }
  3316. }
  3317. int64_t constant_value = CoreConstants::get_global_constant_value(i);
  3318. StringName enum_name = CoreConstants::get_global_constant_enum(i);
  3319. ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), constant_value);
  3320. iconstant.const_doc = const_doc;
  3321. if (enum_name != StringName()) {
  3322. EnumInterface ienum(enum_name, pascal_to_pascal_case(enum_name.operator String()), CoreConstants::is_global_constant_bitfield(i));
  3323. List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
  3324. if (enum_match) {
  3325. enum_match->get().constants.push_back(iconstant);
  3326. } else {
  3327. ienum.constants.push_back(iconstant);
  3328. global_enums.push_back(ienum);
  3329. }
  3330. } else {
  3331. global_constants.push_back(iconstant);
  3332. }
  3333. }
  3334. for (EnumInterface &ienum : global_enums) {
  3335. TypeInterface enum_itype;
  3336. enum_itype.is_enum = true;
  3337. enum_itype.name = ienum.cname.operator String();
  3338. enum_itype.cname = ienum.cname;
  3339. enum_itype.proxy_name = ienum.proxy_name;
  3340. TypeInterface::postsetup_enum_type(enum_itype);
  3341. enum_types.insert(enum_itype.cname, enum_itype);
  3342. int prefix_length = _determine_enum_prefix(ienum);
  3343. // HARDCODED: The Error enum have the prefix 'ERR_' for everything except 'OK' and 'FAILED'.
  3344. if (ienum.cname == name_cache.enum_Error) {
  3345. if (prefix_length > 0) { // Just in case it ever changes
  3346. ERR_PRINT("Prefix for enum '" _STR(Error) "' is not empty.");
  3347. }
  3348. prefix_length = 1; // 'ERR_'
  3349. }
  3350. _apply_prefix_to_enum_constants(ienum, prefix_length);
  3351. }
  3352. }
  3353. // HARDCODED
  3354. List<StringName> hardcoded_enums;
  3355. hardcoded_enums.push_back("Vector2.Axis");
  3356. hardcoded_enums.push_back("Vector2I.Axis");
  3357. hardcoded_enums.push_back("Vector3.Axis");
  3358. hardcoded_enums.push_back("Vector3I.Axis");
  3359. for (const StringName &enum_cname : hardcoded_enums) {
  3360. // These enums are not generated and must be written manually (e.g.: Vector3.Axis)
  3361. // Here, we assume core types do not begin with underscore
  3362. TypeInterface enum_itype;
  3363. enum_itype.is_enum = true;
  3364. enum_itype.name = enum_cname.operator String();
  3365. enum_itype.cname = enum_cname;
  3366. enum_itype.proxy_name = pascal_to_pascal_case(enum_itype.name);
  3367. TypeInterface::postsetup_enum_type(enum_itype);
  3368. enum_types.insert(enum_itype.cname, enum_itype);
  3369. }
  3370. }
  3371. bool BindingsGenerator::_method_has_conflicting_signature(const MethodInterface &p_imethod, const TypeInterface &p_itype) {
  3372. // Compare p_imethod with all the methods already registered in p_itype.
  3373. for (const MethodInterface &method : p_itype.methods) {
  3374. if (method.proxy_name == p_imethod.proxy_name) {
  3375. if (_method_has_conflicting_signature(p_imethod, method)) {
  3376. return true;
  3377. }
  3378. }
  3379. }
  3380. return false;
  3381. }
  3382. bool BindingsGenerator::_method_has_conflicting_signature(const MethodInterface &p_imethod_left, const MethodInterface &p_imethod_right) {
  3383. // Check if a method already exists in p_itype with a method signature that would conflict with p_imethod.
  3384. // The return type is ignored because only changing the return type is not enough to avoid conflicts.
  3385. // The const keyword is also ignored since it doesn't generate different C# code.
  3386. if (p_imethod_left.arguments.size() != p_imethod_right.arguments.size()) {
  3387. // Different argument count, so no conflict.
  3388. return false;
  3389. }
  3390. for (int i = 0; i < p_imethod_left.arguments.size(); i++) {
  3391. const ArgumentInterface &iarg_left = p_imethod_left.arguments[i];
  3392. const ArgumentInterface &iarg_right = p_imethod_right.arguments[i];
  3393. if (iarg_left.type.cname != iarg_right.type.cname) {
  3394. // Different types for arguments in the same position, so no conflict.
  3395. return false;
  3396. }
  3397. if (iarg_left.def_param_mode != iarg_right.def_param_mode) {
  3398. // If the argument is a value type and nullable, it will be 'Nullable<T>' instead of 'T'
  3399. // and will not create a conflict.
  3400. if (iarg_left.def_param_mode == ArgumentInterface::NULLABLE_VAL || iarg_right.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
  3401. return false;
  3402. }
  3403. }
  3404. }
  3405. return true;
  3406. }
  3407. void BindingsGenerator::_initialize_blacklisted_methods() {
  3408. blacklisted_methods["Object"].push_back("to_string"); // there is already ToString
  3409. blacklisted_methods["Object"].push_back("_to_string"); // override ToString instead
  3410. blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it)
  3411. }
  3412. void BindingsGenerator::_initialize_compat_singletons() {
  3413. compat_singletons.insert("EditorInterface");
  3414. }
  3415. void BindingsGenerator::_log(const char *p_format, ...) {
  3416. if (log_print_enabled) {
  3417. va_list list;
  3418. va_start(list, p_format);
  3419. OS::get_singleton()->print("%s", str_format(p_format, list).utf8().get_data());
  3420. va_end(list);
  3421. }
  3422. }
  3423. void BindingsGenerator::_initialize() {
  3424. initialized = false;
  3425. EditorHelp::generate_doc(false);
  3426. enum_types.clear();
  3427. _initialize_blacklisted_methods();
  3428. _initialize_compat_singletons();
  3429. bool obj_type_ok = _populate_object_type_interfaces();
  3430. ERR_FAIL_COND_MSG(!obj_type_ok, "Failed to generate object type interfaces");
  3431. _populate_builtin_type_interfaces();
  3432. _populate_global_constants();
  3433. // Generate internal calls (after populating type interfaces and global constants)
  3434. for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
  3435. const TypeInterface &itype = E.value;
  3436. Error err = _populate_method_icalls_table(itype);
  3437. ERR_FAIL_COND_MSG(err != OK, "Failed to generate icalls table for type: " + itype.name);
  3438. }
  3439. initialized = true;
  3440. }
  3441. static String generate_all_glue_option = "--generate-mono-glue";
  3442. static void handle_cmdline_options(String glue_dir_path) {
  3443. BindingsGenerator bindings_generator;
  3444. bindings_generator.set_log_print_enabled(true);
  3445. if (!bindings_generator.is_initialized()) {
  3446. ERR_PRINT("Failed to initialize the bindings generator");
  3447. return;
  3448. }
  3449. CRASH_COND(glue_dir_path.is_empty());
  3450. if (bindings_generator.generate_cs_api(glue_dir_path.path_join(API_SOLUTION_NAME)) != OK) {
  3451. ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
  3452. }
  3453. }
  3454. static void cleanup_and_exit_godot() {
  3455. // Exit once done
  3456. Main::cleanup(true);
  3457. ::exit(0);
  3458. }
  3459. void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
  3460. String glue_dir_path;
  3461. const List<String>::Element *elem = p_cmdline_args.front();
  3462. while (elem) {
  3463. if (elem->get() == generate_all_glue_option) {
  3464. const List<String>::Element *path_elem = elem->next();
  3465. if (path_elem) {
  3466. glue_dir_path = path_elem->get();
  3467. elem = elem->next();
  3468. } else {
  3469. ERR_PRINT(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue').");
  3470. // Exit once done with invalid command line arguments
  3471. cleanup_and_exit_godot();
  3472. }
  3473. break;
  3474. }
  3475. elem = elem->next();
  3476. }
  3477. if (glue_dir_path.length()) {
  3478. handle_cmdline_options(glue_dir_path);
  3479. // Exit once done
  3480. cleanup_and_exit_godot();
  3481. }
  3482. }
  3483. #endif