2017-10-02 23:24:00 +02:00
/*************************************************************************/
/* bindings_generator.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-10-02 23:24:00 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-10-02 23:24:00 +02:00
# include "bindings_generator.h"
2019-09-28 01:07:57 +02:00
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
2017-10-02 23:24:00 +02:00
2020-11-07 19:33:38 -03:00
# include "core/config/engine.h"
# include "core/core_constants.h"
2018-09-11 18:13:45 +02:00
# include "core/io/compression.h"
2021-06-11 14:51:48 +02:00
# include "core/io/dir_access.h"
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/os/os.h"
2020-11-07 19:33:38 -03:00
# include "core/string/ucaps.h"
2021-02-22 11:06:33 +01:00
# include "main/main.h"
2017-10-02 23:24:00 +02:00
# include "../godotsharp_defs.h"
# include "../utils/path_utils.h"
# include "../utils/string_utils.h"
2021-05-03 15:21:06 +02:00
StringBuilder & operator < < ( StringBuilder & r_sb , const String & p_string ) {
r_sb . append ( p_string ) ;
return r_sb ;
}
StringBuilder & operator < < ( StringBuilder & r_sb , const char * p_cstring ) {
r_sb . append ( p_cstring ) ;
return r_sb ;
}
2018-09-04 05:40:41 +02:00
# define CS_INDENT " " // 4 whitespaces
2017-10-02 23:24:00 +02:00
# define INDENT1 CS_INDENT
# define INDENT2 INDENT1 INDENT1
# define INDENT3 INDENT2 INDENT1
# define INDENT4 INDENT3 INDENT1
2022-02-27 21:57:50 +01:00
# define MEMBER_BEGIN "\n" INDENT1
2017-10-02 23:24:00 +02:00
# define OPEN_BLOCK "{\n"
# define CLOSE_BLOCK "}\n"
2021-05-03 15:21:06 +02:00
# define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK
# define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK
2022-02-27 21:57:50 +01:00
# define CLOSE_BLOCK_L1 INDENT1 CLOSE_BLOCK
2017-10-02 23:24:00 +02:00
# define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
# define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
2021-12-28 23:25:16 +01:00
# define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
# define BINDINGS_NATIVE_NAME_FIELD "NativeName"
2021-05-03 15:21:06 +02:00
# define CS_PARAM_MEMORYOWN "memoryOwn"
2017-10-02 23:24:00 +02:00
# define CS_PARAM_METHODBIND "method"
# define CS_PARAM_INSTANCE "ptr"
2021-05-03 15:21:06 +02:00
# define CS_STATIC_METHOD_GETINSTANCE "GetPtr"
2017-10-02 23:24:00 +02:00
# define CS_METHOD_CALL "Call"
2021-05-03 15:21:06 +02:00
# define CS_PROPERTY_SINGLETON "Singleton"
2021-12-28 23:25:16 +01:00
# define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
2021-12-28 23:25:16 +01:00
# define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod"
2021-05-03 15:21:06 +02:00
# define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
# define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
2021-12-28 23:25:16 +01:00
# define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_"
2017-10-02 23:24:00 +02:00
# define ICALL_PREFIX "godot_icall_"
2021-05-03 15:21:06 +02:00
# define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method"
# define ICALL_CLASSDB_GET_CONSTRUCTOR "ClassDB_get_constructor"
2018-09-04 05:40:41 +02:00
# define C_LOCAL_RET "ret"
2019-01-18 00:41:41 +01:00
# define C_LOCAL_VARARG_RET "vararg_ret"
2017-10-02 23:24:00 +02:00
# define C_LOCAL_PTRCALL_ARGS "call_args"
2021-05-03 15:21:06 +02:00
# define C_CLASS_NATIVE_FUNCS "NativeFuncs"
# define C_NS_MONOUTILS "InteropUtils"
# define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged"
2021-09-12 20:21:15 +02:00
# define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton"
2021-05-03 15:21:06 +02:00
# define C_NS_MONOMARSHAL "Marshaling"
2021-12-28 23:25:16 +01:00
# define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL ".ConvertStringToNative"
# define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL ".ConvertStringToManaged"
# define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL ".ConvertSystemArrayToNative" #m_type
# define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL ".ConvertNative" #m_type "ToSystemArray"
2021-05-03 15:21:06 +02:00
# define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToNative"
# define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToManaged"
# define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToNative"
# define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged"
2018-02-22 13:13:51 +01:00
2022-03-18 17:28:48 +01:00
// Types that will be ignored by the generator and won't be available in C#.
const Vector < String > ignored_types = { " PhysicsServer3DExtension " } ;
2021-05-03 15:21:06 +02:00
void BindingsGenerator : : TypeInterface : : postsetup_enum_type ( BindingsGenerator : : TypeInterface & r_enum_itype ) {
// C interface for enums is the same as that of 'uint32_t'. Remember to apply
// any of the changes done here to the 'uint32_t' type interface as well.
r_enum_itype . cs_type = r_enum_itype . proxy_name ;
2021-12-28 23:25:16 +01:00
r_enum_itype . cs_in_expr = " (int)%0 " ;
2021-05-03 15:21:06 +02:00
r_enum_itype . cs_out = " %5return (%2)%0(%1); " ;
{
// The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
r_enum_itype . c_in = " %5%0 %1_in = %1; \n " ;
r_enum_itype . c_out = " %5return (%0)%1; \n " ;
r_enum_itype . c_type = " long " ;
r_enum_itype . c_arg_in = " &%s_in " ;
}
r_enum_itype . c_type_in = " int " ;
r_enum_itype . c_type_out = r_enum_itype . c_type_in ;
r_enum_itype . class_doc = & EditorHelp : : get_doc_data ( ) - > class_list [ r_enum_itype . proxy_name ] ;
}
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
static String fix_doc_description ( const String & p_bbcode ) {
// This seems to be the correct way to do this. It's the same EditorHelp does.
return p_bbcode . dedent ( )
. replace ( " \t " , " " )
. replace ( " \r " , " " )
. strip_edges ( ) ;
}
2017-12-24 03:17:48 +01:00
static String snake_to_pascal_case ( const String & p_identifier , bool p_input_is_upper = false ) {
2017-10-02 23:24:00 +02:00
String ret ;
Vector < String > parts = p_identifier . split ( " _ " , true ) ;
for ( int i = 0 ; i < parts . size ( ) ; i + + ) {
String part = parts [ i ] ;
if ( part . length ( ) ) {
part [ 0 ] = _find_upper ( part [ 0 ] ) ;
2017-12-24 03:17:48 +01:00
if ( p_input_is_upper ) {
2020-05-16 04:03:05 +02:00
for ( int j = 1 ; j < part . length ( ) ; j + + ) {
2017-12-24 03:17:48 +01:00
part [ j ] = _find_lower ( part [ j ] ) ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
}
2017-10-02 23:24:00 +02:00
ret + = part ;
} else {
if ( i = = 0 | | i = = ( parts . size ( ) - 1 ) ) {
// Preserve underscores at the beginning and end
ret + = " _ " ;
} else {
// Preserve contiguous underscores
if ( parts [ i - 1 ] . length ( ) ) {
ret + = " __ " ;
} else {
ret + = " _ " ;
}
}
}
}
return ret ;
}
2017-12-24 03:17:48 +01:00
static String snake_to_camel_case ( const String & p_identifier , bool p_input_is_upper = false ) {
2017-10-02 23:24:00 +02:00
String ret ;
Vector < String > parts = p_identifier . split ( " _ " , true ) ;
for ( int i = 0 ; i < parts . size ( ) ; i + + ) {
String part = parts [ i ] ;
if ( part . length ( ) ) {
2017-12-24 03:17:48 +01:00
if ( i ! = 0 ) {
2017-10-02 23:24:00 +02:00
part [ 0 ] = _find_upper ( part [ 0 ] ) ;
2017-12-24 03:17:48 +01:00
}
if ( p_input_is_upper ) {
2020-05-16 04:03:05 +02:00
for ( int j = i ! = 0 ? 1 : 0 ; j < part . length ( ) ; j + + ) {
2017-12-24 03:17:48 +01:00
part [ j ] = _find_lower ( part [ j ] ) ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
}
2017-10-02 23:24:00 +02:00
ret + = part ;
} else {
if ( i = = 0 | | i = = ( parts . size ( ) - 1 ) ) {
// Preserve underscores at the beginning and end
ret + = " _ " ;
} else {
// Preserve contiguous underscores
if ( parts [ i - 1 ] . length ( ) ) {
ret + = " __ " ;
} else {
ret + = " _ " ;
}
}
}
}
return ret ;
}
2019-03-18 21:59:35 +01:00
String BindingsGenerator : : bbcode_to_xml ( const String & p_bbcode , const TypeInterface * p_itype ) {
// Based on the version in EditorHelp
2020-12-15 12:04:21 +00:00
if ( p_bbcode . is_empty ( ) ) {
2019-03-18 21:59:35 +01:00
return String ( ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
2020-11-29 09:12:06 +05:30
DocTools * doc = EditorHelp : : get_doc_data ( ) ;
2019-03-18 21:59:35 +01:00
String bbcode = p_bbcode ;
StringBuilder xml_output ;
xml_output . append ( " <para> " ) ;
List < String > tag_stack ;
bool code_tag = false ;
int pos = 0 ;
while ( pos < bbcode . length ( ) ) {
int brk_pos = bbcode . find ( " [ " , pos ) ;
2020-05-16 04:03:05 +02:00
if ( brk_pos < 0 ) {
2019-03-18 21:59:35 +01:00
brk_pos = bbcode . length ( ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
if ( brk_pos > pos ) {
String text = bbcode . substr ( pos , brk_pos - pos ) ;
if ( code_tag | | tag_stack . size ( ) > 0 ) {
xml_output . append ( text . xml_escape ( ) ) ;
} else {
Vector < String > lines = text . split ( " \n " ) ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
2020-05-16 04:03:05 +02:00
if ( i ! = 0 ) {
2019-03-18 21:59:35 +01:00
xml_output . append ( " <para> " ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
xml_output . append ( lines [ i ] . xml_escape ( ) ) ;
2020-05-16 04:03:05 +02:00
if ( i ! = lines . size ( ) - 1 ) {
2019-03-18 21:59:35 +01:00
xml_output . append ( " </para> \n " ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
}
}
}
2020-05-16 04:03:05 +02:00
if ( brk_pos = = bbcode . length ( ) ) {
2019-03-18 21:59:35 +01:00
break ; // nothing else to add
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
int brk_end = bbcode . find ( " ] " , brk_pos + 1 ) ;
if ( brk_end = = - 1 ) {
String text = bbcode . substr ( brk_pos , bbcode . length ( ) - brk_pos ) ;
if ( code_tag | | tag_stack . size ( ) > 0 ) {
xml_output . append ( text . xml_escape ( ) ) ;
} else {
Vector < String > lines = text . split ( " \n " ) ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
2020-05-16 04:03:05 +02:00
if ( i ! = 0 ) {
2019-03-18 21:59:35 +01:00
xml_output . append ( " <para> " ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
xml_output . append ( lines [ i ] . xml_escape ( ) ) ;
2020-05-16 04:03:05 +02:00
if ( i ! = lines . size ( ) - 1 ) {
2019-03-18 21:59:35 +01:00
xml_output . append ( " </para> \n " ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
}
}
break ;
}
String tag = bbcode . substr ( brk_pos + 1 , brk_end - brk_pos - 1 ) ;
if ( tag . begins_with ( " / " ) ) {
bool tag_ok = tag_stack . size ( ) & & tag_stack . front ( ) - > get ( ) = = tag . substr ( 1 , tag . length ( ) ) ;
if ( ! tag_ok ) {
xml_output . append ( " [ " ) ;
pos = brk_pos + 1 ;
continue ;
}
tag_stack . pop_front ( ) ;
pos = brk_end + 1 ;
code_tag = false ;
if ( tag = = " /url " ) {
xml_output . append ( " </a> " ) ;
} else if ( tag = = " /code " ) {
xml_output . append ( " </c> " ) ;
} else if ( tag = = " /codeblock " ) {
xml_output . append ( " </code> " ) ;
}
} else if ( code_tag ) {
xml_output . append ( " [ " ) ;
pos = brk_pos + 1 ;
2022-02-14 16:24:35 +01:00
} else if ( tag . begins_with ( " method " ) | | tag . begins_with ( " member " ) | | tag . begins_with ( " signal " ) | | tag . begins_with ( " enum " ) | | tag . begins_with ( " constant " ) | | tag . begins_with ( " theme_item " ) ) {
2022-02-09 19:01:18 +01:00
const int tag_end = tag . find ( " " ) ;
const String link_tag = tag . substr ( 0 , tag_end ) ;
const String link_target = tag . substr ( tag_end + 1 , tag . length ( ) ) . lstrip ( " " ) ;
2019-03-18 21:59:35 +01:00
2022-03-03 23:35:10 +01:00
const Vector < String > link_target_parts = link_target . split ( " . " ) ;
2019-03-18 21:59:35 +01:00
if ( link_target_parts . size ( ) < = 0 | | link_target_parts . size ( ) > 2 ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Invalid reference format: ' " + tag + " '. " ) ;
2019-03-18 21:59:35 +01:00
xml_output . append ( " <c> " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " </c> " ) ;
pos = brk_end + 1 ;
continue ;
}
const TypeInterface * target_itype ;
StringName target_cname ;
if ( link_target_parts . size ( ) = = 2 ) {
target_itype = _get_type_or_null ( TypeReference ( link_target_parts [ 0 ] ) ) ;
if ( ! target_itype ) {
target_itype = _get_type_or_null ( TypeReference ( " _ " + link_target_parts [ 0 ] ) ) ;
}
target_cname = link_target_parts [ 1 ] ;
} else {
target_itype = p_itype ;
target_cname = link_target_parts [ 0 ] ;
}
if ( link_tag = = " method " ) {
2022-03-03 23:35:10 +01:00
_append_xml_method ( xml_output , target_itype , target_cname , link_target , link_target_parts ) ;
2019-03-18 21:59:35 +01:00
} else if ( link_tag = = " member " ) {
2022-03-03 23:35:10 +01:00
_append_xml_member ( xml_output , target_itype , target_cname , link_target , link_target_parts ) ;
2019-03-18 21:59:35 +01:00
} else if ( link_tag = = " signal " ) {
2022-03-03 23:35:10 +01:00
_append_xml_signal ( xml_output , target_itype , target_cname , link_target , link_target_parts ) ;
2019-03-18 21:59:35 +01:00
} else if ( link_tag = = " enum " ) {
2022-03-03 23:35:10 +01:00
_append_xml_enum ( xml_output , target_itype , target_cname , link_target , link_target_parts ) ;
2021-08-04 21:17:16 +02:00
} else if ( link_tag = = " constant " ) {
2022-03-03 23:35:10 +01:00
_append_xml_constant ( xml_output , target_itype , target_cname , link_target , link_target_parts ) ;
2022-02-14 16:24:35 +01:00
} else if ( link_tag = = " theme_item " ) {
// We do not declare theme_items in any way in C#, so there is nothing to reference
2022-03-03 23:35:10 +01:00
_append_xml_undeclared ( xml_output , link_target ) ;
2019-03-18 21:59:35 +01:00
}
pos = brk_end + 1 ;
} else if ( doc - > class_list . has ( tag ) ) {
if ( tag = = " Array " | | tag = = " Dictionary " ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE_COLLECTIONS " . " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " \" /> " ) ;
} else if ( tag = = " bool " | | tag = = " int " ) {
xml_output . append ( " <see cref= \" " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " \" /> " ) ;
} else if ( tag = = " float " ) {
xml_output . append ( " <see cref= \" "
# ifdef REAL_T_IS_DOUBLE
" double "
# else
" float "
# endif
" \" /> " ) ;
} else if ( tag = = " Variant " ) {
// We use System.Object for Variant, so there is no Variant type in C#
xml_output . append ( " <c>Variant</c> " ) ;
} else if ( tag = = " String " ) {
xml_output . append ( " <see cref= \" string \" /> " ) ;
} else if ( tag = = " Nil " ) {
xml_output . append ( " <see langword= \" null \" /> " ) ;
} else if ( tag . begins_with ( " @ " ) ) {
2019-03-27 20:01:16 +01:00
// @GlobalScope, @GDScript, etc
2019-03-18 21:59:35 +01:00
xml_output . append ( " <c> " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " </c> " ) ;
2020-02-17 18:06:54 -03:00
} else if ( tag = = " PackedByteArray " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" byte \" />[] " ) ;
2020-02-24 15:20:53 -03:00
} else if ( tag = = " PackedInt32Array " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" int \" />[] " ) ;
2020-03-14 19:20:17 +01:00
} else if ( tag = = " PackedInt64Array " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" long \" />[] " ) ;
2020-02-24 15:20:53 -03:00
} else if ( tag = = " PackedFloat32Array " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" float \" />[] " ) ;
2020-03-14 19:20:17 +01:00
} else if ( tag = = " PackedFloat64Array " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" double \" />[] " ) ;
2020-02-17 18:06:54 -03:00
} else if ( tag = = " PackedStringArray " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" string \" />[] " ) ;
2020-02-17 18:06:54 -03:00
} else if ( tag = = " PackedVector2Array " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " .Vector2 \" />[] " ) ;
2020-02-17 18:06:54 -03:00
} else if ( tag = = " PackedVector3Array " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " .Vector3 \" />[] " ) ;
2020-02-17 18:06:54 -03:00
} else if ( tag = = " PackedColorArray " ) {
2021-12-28 23:25:16 +01:00
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " .Color \" />[] " ) ;
2019-03-18 21:59:35 +01:00
} else {
const TypeInterface * target_itype = _get_type_or_null ( TypeReference ( tag ) ) ;
if ( ! target_itype ) {
target_itype = _get_type_or_null ( TypeReference ( " _ " + tag ) ) ;
}
if ( target_itype ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
} else {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Cannot resolve type reference in documentation: ' " + tag + " '. " ) ;
2019-03-18 21:59:35 +01:00
xml_output . append ( " <c> " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " </c> " ) ;
}
}
pos = brk_end + 1 ;
} else if ( tag = = " b " ) {
// bold is not supported in xml comments
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " i " ) {
// italics is not supported in xml comments
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " code " ) {
xml_output . append ( " <c> " ) ;
code_tag = true ;
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " codeblock " ) {
xml_output . append ( " <code> " ) ;
code_tag = true ;
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
2020-03-10 13:41:36 +03:00
} else if ( tag = = " kbd " ) {
// keyboard combinations are not supported in xml comments
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
2019-03-18 21:59:35 +01:00
} else if ( tag = = " center " ) {
2020-03-10 13:41:36 +03:00
// center alignment is not supported in xml comments
2019-03-18 21:59:35 +01:00
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " br " ) {
xml_output . append ( " \n " ) ; // FIXME: Should use <para> instead. Luckily this tag isn't used for now.
pos = brk_end + 1 ;
} else if ( tag = = " u " ) {
// underline is not supported in xml comments
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " s " ) {
// strikethrough is not supported in xml comments
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " url " ) {
int end = bbcode . find ( " [ " , brk_end ) ;
2020-05-16 04:03:05 +02:00
if ( end = = - 1 ) {
2019-03-18 21:59:35 +01:00
end = bbcode . length ( ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
String url = bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
xml_output . append ( " <a href= \" " ) ;
xml_output . append ( url ) ;
xml_output . append ( " \" > " ) ;
xml_output . append ( url ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag . begins_with ( " url= " ) ) {
String url = tag . substr ( 4 , tag . length ( ) ) ;
xml_output . append ( " <a href= \" " ) ;
xml_output . append ( url ) ;
xml_output . append ( " \" > " ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " url " ) ;
} else if ( tag = = " img " ) {
int end = bbcode . find ( " [ " , brk_end ) ;
2020-05-16 04:03:05 +02:00
if ( end = = - 1 ) {
2019-03-18 21:59:35 +01:00
end = bbcode . length ( ) ;
2020-05-16 04:03:05 +02:00
}
2019-03-18 21:59:35 +01:00
String image = bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
// Not supported. Just append the bbcode.
xml_output . append ( " [img] " ) ;
xml_output . append ( image ) ;
xml_output . append ( " [/img] " ) ;
pos = end ;
tag_stack . push_front ( tag ) ;
} else if ( tag . begins_with ( " color= " ) ) {
// Not supported.
pos = brk_end + 1 ;
tag_stack . push_front ( " color " ) ;
} else if ( tag . begins_with ( " font= " ) ) {
// Not supported.
pos = brk_end + 1 ;
tag_stack . push_front ( " font " ) ;
} else {
xml_output . append ( " [ " ) ; // ignore
pos = brk_pos + 1 ;
}
}
xml_output . append ( " </para> " ) ;
return xml_output . as_string ( ) ;
}
2022-03-03 23:35:10 +01:00
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 ) {
if ( p_link_target_parts [ 0 ] = = name_cache . type_at_GlobalScope ) {
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve @GlobalScope method reference in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
}
// TODO Map what we can
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
} else if ( ! p_target_itype | | ! p_target_itype - > is_object_type ) {
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( p_target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve method reference for non-Godot.Object type in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from method reference in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
} else {
if ( p_target_cname = = " _init " ) {
// The _init method is not declared in C#, reference the constructor instead
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( p_target_itype - > proxy_name ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( p_target_itype - > proxy_name ) ;
p_xml_output . append ( " () \" /> " ) ;
} else {
const MethodInterface * target_imethod = p_target_itype - > find_method_by_name ( p_target_cname ) ;
if ( target_imethod ) {
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( p_target_itype - > proxy_name ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( target_imethod - > proxy_name ) ;
p_xml_output . append ( " \" /> " ) ;
} else {
ERR_PRINT ( " Cannot resolve method reference in documentation: ' " + p_link_target + " '. " ) ;
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
}
}
}
}
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 ) {
if ( p_link_target . find ( " / " ) > = 0 ) {
// Properties with '/' (slash) in the name are not declared in C#, so there is nothing to reference.
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
} else if ( ! p_target_itype | | ! p_target_itype - > is_object_type ) {
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( p_target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve member reference for non-Godot.Object type in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from member reference in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
} else {
const TypeInterface * current_itype = p_target_itype ;
const PropertyInterface * target_iprop = nullptr ;
while ( target_iprop = = nullptr & & current_itype ! = nullptr ) {
target_iprop = current_itype - > find_property_by_name ( p_target_cname ) ;
if ( target_iprop = = nullptr ) {
current_itype = _get_type_or_null ( TypeReference ( current_itype - > base_name ) ) ;
}
}
if ( target_iprop ) {
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( current_itype - > proxy_name ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( target_iprop - > proxy_name ) ;
p_xml_output . append ( " \" /> " ) ;
} else {
ERR_PRINT ( " Cannot resolve member reference in documentation: ' " + p_link_target + " '. " ) ;
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
}
}
}
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 ) {
if ( ! p_target_itype | | ! p_target_itype - > is_object_type ) {
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( p_target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve signal reference for non-Godot.Object type in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from signal reference in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
} else {
const SignalInterface * target_isignal = p_target_itype - > find_signal_by_name ( p_target_cname ) ;
if ( target_isignal ) {
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( p_target_itype - > proxy_name ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( target_isignal - > proxy_name ) ;
p_xml_output . append ( " \" /> " ) ;
} else {
ERR_PRINT ( " Cannot resolve signal reference in documentation: ' " + p_link_target + " '. " ) ;
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
}
}
}
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 ) {
const StringName search_cname = ! p_target_itype ? p_target_cname : StringName ( p_target_itype - > name + " . " + ( String ) p_target_cname ) ;
2022-05-13 15:04:37 +02:00
HashMap < StringName , TypeInterface > : : ConstIterator enum_match = enum_types . find ( search_cname ) ;
2022-03-03 23:35:10 +01:00
if ( ! enum_match & & search_cname ! = p_target_cname ) {
enum_match = enum_types . find ( p_target_cname ) ;
}
if ( enum_match ) {
2022-05-13 15:04:37 +02:00
const TypeInterface & target_enum_itype = enum_match - > value ;
2022-03-03 23:35:10 +01:00
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( target_enum_itype . proxy_name ) ; // Includes nesting class if any
p_xml_output . append ( " \" /> " ) ;
} else {
ERR_PRINT ( " Cannot resolve enum reference in documentation: ' " + p_link_target + " '. " ) ;
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
}
}
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 ) {
if ( p_link_target_parts [ 0 ] = = name_cache . type_at_GlobalScope ) {
_append_xml_constant_in_global_scope ( p_xml_output , p_target_cname , p_link_target ) ;
} else if ( ! p_target_itype | | ! p_target_itype - > is_object_type ) {
// Search in @GlobalScope as a last resort if no class was specified
if ( p_link_target_parts . size ( ) = = 1 ) {
_append_xml_constant_in_global_scope ( p_xml_output , p_target_cname , p_link_target ) ;
return ;
}
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( p_target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve constant reference for non-Godot.Object type in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from constant reference in documentation: %s \n " , p_link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
} else {
// Try to find the constant in the current class
const ConstantInterface * target_iconst = find_constant_by_name ( p_target_cname , p_target_itype - > constants ) ;
if ( target_iconst ) {
// Found constant in current class
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( p_target_itype - > proxy_name ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( target_iconst - > proxy_name ) ;
p_xml_output . append ( " \" /> " ) ;
} else {
// Try to find as enum constant in the current class
const EnumInterface * target_ienum = nullptr ;
for ( const EnumInterface & ienum : p_target_itype - > enums ) {
target_ienum = & ienum ;
target_iconst = find_constant_by_name ( p_target_cname , target_ienum - > constants ) ;
if ( target_iconst ) {
break ;
}
}
if ( target_iconst ) {
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( p_target_itype - > proxy_name ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( target_ienum - > cname ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( target_iconst - > proxy_name ) ;
p_xml_output . append ( " \" /> " ) ;
} else if ( p_link_target_parts . size ( ) = = 1 ) {
// Also search in @GlobalScope as a last resort if no class was specified
_append_xml_constant_in_global_scope ( p_xml_output , p_target_cname , p_link_target ) ;
} else {
ERR_PRINT ( " Cannot resolve constant reference in documentation: ' " + p_link_target + " '. " ) ;
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
}
}
}
}
void BindingsGenerator : : _append_xml_constant_in_global_scope ( StringBuilder & p_xml_output , const String & p_target_cname , const String & p_link_target ) {
// Try to find as a global constant
const ConstantInterface * target_iconst = find_constant_by_name ( p_target_cname , global_constants ) ;
if ( target_iconst ) {
// Found global constant
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " BINDINGS_GLOBAL_SCOPE_CLASS " . " ) ;
p_xml_output . append ( target_iconst - > proxy_name ) ;
p_xml_output . append ( " \" /> " ) ;
} else {
// Try to find as global enum constant
const EnumInterface * target_ienum = nullptr ;
for ( const EnumInterface & ienum : global_enums ) {
target_ienum = & ienum ;
target_iconst = find_constant_by_name ( p_target_cname , target_ienum - > constants ) ;
if ( target_iconst ) {
break ;
}
}
if ( target_iconst ) {
p_xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
p_xml_output . append ( target_ienum - > cname ) ;
p_xml_output . append ( " . " ) ;
p_xml_output . append ( target_iconst - > proxy_name ) ;
p_xml_output . append ( " \" /> " ) ;
} else {
ERR_PRINT ( " Cannot resolve global constant reference in documentation: ' " + p_link_target + " '. " ) ;
_append_xml_undeclared ( p_xml_output , p_link_target ) ;
}
}
}
void BindingsGenerator : : _append_xml_undeclared ( StringBuilder & p_xml_output , const String & p_link_target ) {
p_xml_output . append ( " <c> " ) ;
p_xml_output . append ( p_link_target ) ;
p_xml_output . append ( " </c> " ) ;
}
2018-10-18 19:41:10 +02:00
int BindingsGenerator : : _determine_enum_prefix ( const EnumInterface & p_ienum ) {
2020-12-15 12:04:21 +00:00
CRASH_COND ( p_ienum . constants . is_empty ( ) ) ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
const ConstantInterface & front_iconstant = p_ienum . constants . front ( ) - > get ( ) ;
Vector < String > front_parts = front_iconstant . name . split ( " _ " , /* p_allow_empty: */ true ) ;
int candidate_len = front_parts . size ( ) - 1 ;
2017-12-24 03:17:48 +01:00
2020-05-16 04:03:05 +02:00
if ( candidate_len = = 0 ) {
2018-10-18 19:41:10 +02:00
return 0 ;
2020-05-16 04:03:05 +02:00
}
2018-10-18 19:41:10 +02:00
2021-07-26 21:31:17 +02:00
for ( const ConstantInterface & iconstant : p_ienum . constants ) {
2018-10-18 19:41:10 +02:00
Vector < String > parts = iconstant . name . split ( " _ " , /* p_allow_empty: */ true ) ;
int i ;
for ( i = 0 ; i < candidate_len & & i < parts . size ( ) ; i + + ) {
if ( front_parts [ i ] ! = parts [ i ] ) {
// HARDCODED: Some Flag enums have the prefix 'FLAG_' for everything except 'FLAGS_DEFAULT' (same for 'METHOD_FLAG_' and'METHOD_FLAGS_DEFAULT').
bool hardcoded_exc = ( i = = candidate_len - 1 & & ( ( front_parts [ i ] = = " FLAGS " & & parts [ i ] = = " FLAG " ) | | ( front_parts [ i ] = = " FLAG " & & parts [ i ] = = " FLAGS " ) ) ) ;
2020-05-16 04:03:05 +02:00
if ( ! hardcoded_exc ) {
2018-10-18 19:41:10 +02:00
break ;
2020-05-16 04:03:05 +02:00
}
2018-10-18 19:41:10 +02:00
}
2017-12-24 03:17:48 +01:00
}
2018-10-18 19:41:10 +02:00
candidate_len = i ;
2020-05-16 04:03:05 +02:00
if ( candidate_len = = 0 ) {
2018-10-18 19:41:10 +02:00
return 0 ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
}
2018-10-18 19:41:10 +02:00
return candidate_len ;
}
void BindingsGenerator : : _apply_prefix_to_enum_constants ( BindingsGenerator : : EnumInterface & p_ienum , int p_prefix_length ) {
if ( p_prefix_length > 0 ) {
2021-07-25 18:45:00 +02:00
for ( ConstantInterface & iconstant : p_ienum . constants ) {
2018-10-18 19:41:10 +02:00
int curr_prefix_length = p_prefix_length ;
2021-07-25 18:45:00 +02:00
String constant_name = iconstant . name ;
2018-10-18 19:41:10 +02:00
Vector < String > parts = constant_name . split ( " _ " , /* p_allow_empty: */ true ) ;
2020-05-16 04:03:05 +02:00
if ( parts . size ( ) < = curr_prefix_length ) {
2018-10-18 19:41:10 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2018-10-18 19:41:10 +02:00
2022-02-04 10:32:20 +02:00
if ( is_digit ( parts [ curr_prefix_length ] [ 0 ] ) ) {
2018-10-18 19:41:10 +02:00
// The name of enum constants may begin with a numeric digit when strip from the enum prefix,
// so we make the prefix for this constant one word shorter in those cases.
for ( curr_prefix_length = curr_prefix_length - 1 ; curr_prefix_length > 0 ; curr_prefix_length - - ) {
2022-02-04 10:32:20 +02:00
if ( ! is_digit ( parts [ curr_prefix_length ] [ 0 ] ) ) {
2018-10-18 19:41:10 +02:00
break ;
2020-05-16 04:03:05 +02:00
}
2018-10-18 19:41:10 +02:00
}
}
constant_name = " " ;
for ( int i = curr_prefix_length ; i < parts . size ( ) ; i + + ) {
2020-05-16 04:03:05 +02:00
if ( i > curr_prefix_length ) {
2018-10-18 19:41:10 +02:00
constant_name + = " _ " ;
2020-05-16 04:03:05 +02:00
}
2018-10-18 19:41:10 +02:00
constant_name + = parts [ i ] ;
}
2021-07-25 18:45:00 +02:00
iconstant . proxy_name = snake_to_pascal_case ( constant_name , true ) ;
2018-10-18 19:41:10 +02:00
}
}
2017-12-24 03:17:48 +01:00
}
2021-05-03 15:21:06 +02:00
Error BindingsGenerator : : _populate_method_icalls_table ( const TypeInterface & p_itype ) {
2021-07-15 23:45:57 -04:00
for ( const MethodInterface & imethod : p_itype . methods ) {
2020-05-16 04:03:05 +02:00
if ( imethod . is_virtual ) {
2017-10-02 23:24:00 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
const TypeInterface * return_type = _get_type_or_null ( imethod . return_type ) ;
ERR_FAIL_NULL_V ( return_type , ERR_BUG ) ; // Return type not found
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
String im_unique_sig = get_ret_unique_sig ( return_type ) + " ,CallMethodBind " ;
2018-01-25 23:44:37 +01:00
2022-03-21 01:25:50 +01:00
if ( ! imethod . is_static ) {
2021-05-03 15:21:06 +02:00
im_unique_sig + = " ,CallInstance " ;
2022-03-21 01:25:50 +01:00
}
2017-10-02 23:24:00 +02:00
// Get arguments information
2021-07-25 18:45:00 +02:00
for ( const ArgumentInterface & iarg : imethod . arguments ) {
2021-05-03 15:21:06 +02:00
const TypeInterface * arg_type = _get_type_or_null ( iarg . type ) ;
ERR_FAIL_NULL_V ( arg_type , ERR_BUG ) ; // Argument type not found
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
im_unique_sig + = " , " ;
2021-05-03 15:21:06 +02:00
im_unique_sig + = get_arg_unique_sig ( * arg_type ) ;
2019-11-14 21:48:49 +01:00
}
2018-09-04 05:40:41 +02:00
// godot_icall_{argc}_{icallcount}
2018-01-25 23:44:37 +01:00
String icall_method = ICALL_PREFIX ;
2018-09-04 05:40:41 +02:00
icall_method + = itos ( imethod . arguments . size ( ) ) ;
icall_method + = " _ " ;
icall_method + = itos ( method_icalls . size ( ) ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
InternalCall im_icall = InternalCall ( p_itype . api_type , icall_method , im_unique_sig ) ;
im_icall . is_vararg = imethod . is_vararg ;
im_icall . is_static = imethod . is_static ;
im_icall . return_type = imethod . return_type ;
for ( const List < ArgumentInterface > : : Element * F = imethod . arguments . front ( ) ; F ; F = F - > next ( ) ) {
im_icall . argument_types . push_back ( F - > get ( ) . type ) ;
}
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
List < InternalCall > : : Element * match = method_icalls . find ( im_icall ) ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
if ( match ) {
2020-05-16 04:03:05 +02:00
if ( p_itype . api_type ! = ClassDB : : API_EDITOR ) {
2018-09-04 05:40:41 +02:00
match - > get ( ) . editor_only = false ;
2020-05-16 04:03:05 +02:00
}
2021-07-25 18:45:00 +02:00
method_icalls_map . insert ( & imethod , & match - > get ( ) ) ;
2017-10-02 23:24:00 +02:00
} else {
2018-09-04 05:40:41 +02:00
List < InternalCall > : : Element * added = method_icalls . push_back ( im_icall ) ;
2021-07-25 18:45:00 +02:00
method_icalls_map . insert ( & imethod , & added - > get ( ) ) ;
2017-10-02 23:24:00 +02:00
}
}
2021-05-03 15:21:06 +02:00
return OK ;
2017-10-02 23:24:00 +02:00
}
2021-01-13 05:17:39 -05:00
void BindingsGenerator : : _generate_array_extensions ( StringBuilder & p_output ) {
2022-02-27 21:57:50 +01:00
p_output . append ( " namespace " BINDINGS_NAMESPACE " ; \n \n " ) ;
2021-01-13 05:17:39 -05:00
p_output . append ( " using System; \n \n " ) ;
// The class where we put the extensions doesn't matter, so just use "GD".
2022-02-27 21:57:50 +01:00
p_output . append ( " public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS " \n { " ) ;
2021-01-13 05:17:39 -05:00
# define ARRAY_IS_EMPTY(m_type) \
2022-02-27 21:57:50 +01:00
p_output.append("\n" INDENT1 " /// <summary>\n"); \
p_output . append ( INDENT1 " /// Returns true if this " # m_type " array is empty or doesn't exist. \n " ) ; \
p_output . append ( INDENT1 " /// </summary> \n " ) ; \
p_output . append ( INDENT1 " /// <param name= \" instance \" >The " # m_type " array check.</param> \n " ) ; \
p_output . append ( INDENT1 " /// <returns>Whether or not the array is empty.</returns> \n " ) ; \
p_output . append ( INDENT1 " public static bool IsEmpty(this " # m_type " [] instance) \n " ) ; \
p_output . append ( OPEN_BLOCK_L1 ) ; \
p_output . append ( INDENT2 " return instance == null || instance.Length == 0; \n " ) ; \
p_output . append ( INDENT1 CLOSE_BLOCK ) ;
2021-01-13 05:17:39 -05:00
# define ARRAY_JOIN(m_type) \
2022-02-27 21:57:50 +01:00
p_output.append("\n" INDENT1 " /// <summary>\n"); \
p_output . append ( INDENT1 " /// Converts this " # m_type " array to a string delimited by the given string. \n " ) ; \
p_output . append ( INDENT1 " /// </summary> \n " ) ; \
p_output . append ( INDENT1 " /// <param name= \" instance \" >The " # m_type " array to convert.</param> \n " ) ; \
p_output . append ( INDENT1 " /// <param name= \" delimiter \" >The delimiter to use between items.</param> \n " ) ; \
p_output . append ( INDENT1 " /// <returns>A single string with all items.</returns> \n " ) ; \
p_output . append ( INDENT1 " public static string Join(this " # m_type " [] instance, string delimiter = \" , \" ) \n " ) ; \
p_output . append ( OPEN_BLOCK_L1 ) ; \
p_output . append ( INDENT2 " return String.Join(delimiter, instance); \n " ) ; \
p_output . append ( INDENT1 CLOSE_BLOCK ) ;
2021-01-13 05:17:39 -05:00
# define ARRAY_STRINGIFY(m_type) \
2022-02-27 21:57:50 +01:00
p_output.append("\n" INDENT1 " /// <summary>\n"); \
p_output . append ( INDENT1 " /// Converts this " # m_type " array to a string with brackets. \n " ) ; \
p_output . append ( INDENT1 " /// </summary> \n " ) ; \
p_output . append ( INDENT1 " /// <param name= \" instance \" >The " # m_type " array to convert.</param> \n " ) ; \
p_output . append ( INDENT1 " /// <returns>A single string with all items.</returns> \n " ) ; \
p_output . append ( INDENT1 " public static string Stringify(this " # m_type " [] instance) \n " ) ; \
p_output . append ( OPEN_BLOCK_L1 ) ; \
p_output . append ( INDENT2 " return \" [ \" + instance.Join() + \" ] \" ; \n " ) ; \
p_output . append ( INDENT1 CLOSE_BLOCK ) ;
2021-01-13 05:17:39 -05:00
# define ARRAY_ALL(m_type) \
ARRAY_IS_EMPTY(m_type) \
ARRAY_JOIN(m_type) \
ARRAY_STRINGIFY(m_type)
ARRAY_ALL ( byte ) ;
ARRAY_ALL ( int ) ;
ARRAY_ALL ( long ) ;
ARRAY_ALL ( float ) ;
ARRAY_ALL ( double ) ;
ARRAY_ALL ( string ) ;
ARRAY_ALL ( Color ) ;
ARRAY_ALL ( Vector2 ) ;
ARRAY_ALL ( Vector2i ) ;
ARRAY_ALL ( Vector3 ) ;
ARRAY_ALL ( Vector3i ) ;
2022-07-20 01:11:13 +02:00
ARRAY_ALL ( Vector4 ) ;
ARRAY_ALL ( Vector4i ) ;
2021-01-13 05:17:39 -05:00
# undef ARRAY_ALL
# undef ARRAY_IS_EMPTY
# undef ARRAY_JOIN
# undef ARRAY_STRINGIFY
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK ) ; // End of GD class.
2021-01-13 05:17:39 -05:00
}
2019-04-19 01:10:08 +02:00
void BindingsGenerator : : _generate_global_constants ( StringBuilder & p_output ) {
2017-12-24 03:17:48 +01:00
// Constants (in partial GD class)
2022-02-27 21:57:50 +01:00
p_output . append ( " namespace " BINDINGS_NAMESPACE " ; \n \n " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( " \n #pragma warning disable CS1591 // Disable warning: "
" 'Missing XML comment for publicly visible type or member' \n " ) ;
2019-03-18 21:59:35 +01:00
2022-02-27 21:57:50 +01:00
p_output . append ( " public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS " \n { " ) ;
2017-12-24 03:17:48 +01:00
2021-07-15 23:45:57 -04:00
for ( const ConstantInterface & iconstant : global_constants ) {
2017-12-24 03:17:48 +01:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2020-04-02 01:20:12 +02:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , nullptr ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// </summary> " ) ;
2019-03-18 21:59:35 +01:00
}
2017-12-24 03:17:48 +01:00
}
2022-05-09 12:47:10 +03:00
p_output . append ( MEMBER_BEGIN " public const long " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( iconstant . proxy_name ) ;
p_output . append ( " = " ) ;
p_output . append ( itos ( iconstant . value ) ) ;
p_output . append ( " ; " ) ;
2017-12-24 03:17:48 +01:00
}
2020-12-15 12:04:21 +00:00
if ( ! global_constants . is_empty ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " \n " ) ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK ) ; // end of GD class
2017-12-24 03:17:48 +01:00
// Enums
2021-07-24 15:46:25 +02:00
for ( const EnumInterface & ienum : global_enums ) {
2020-12-15 12:04:21 +00:00
CRASH_COND ( ienum . constants . is_empty ( ) ) ;
2017-12-24 03:17:48 +01:00
String enum_proxy_name = ienum . cname . operator String ( ) ;
bool enum_in_static_class = false ;
if ( enum_proxy_name . find ( " . " ) > 0 ) {
enum_in_static_class = true ;
String enum_class_name = enum_proxy_name . get_slicec ( ' . ' , 0 ) ;
enum_proxy_name = enum_proxy_name . get_slicec ( ' . ' , 1 ) ;
CRASH_COND ( enum_class_name ! = " Variant " ) ; // Hard-coded...
2022-07-28 17:41:49 +02:00
_log ( " Declaring global enum '%s' inside struct '%s' \n " , enum_proxy_name . utf8 ( ) . get_data ( ) , enum_class_name . utf8 ( ) . get_data ( ) ) ;
2017-12-24 03:17:48 +01:00
2022-07-28 17:41:49 +02:00
p_output . append ( " \n public partial struct " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( enum_class_name ) ;
2022-02-27 21:57:50 +01:00
p_output . append ( " \n " OPEN_BLOCK ) ;
2017-12-24 03:17:48 +01:00
}
2022-06-24 20:25:30 +02:00
if ( ienum . is_flags ) {
2022-02-27 21:57:50 +01:00
p_output . append ( " \n [System.Flags] " ) ;
2022-06-24 20:25:30 +02:00
}
2022-02-27 21:57:50 +01:00
p_output . append ( " \n public enum " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( enum_proxy_name ) ;
2022-05-09 12:47:10 +03:00
p_output . append ( " : long " ) ;
2022-02-27 21:57:50 +01:00
p_output . append ( " \n " OPEN_BLOCK ) ;
2017-12-24 03:17:48 +01:00
2021-07-25 18:45:00 +02:00
const ConstantInterface & last = ienum . constants . back ( ) - > get ( ) ;
for ( const ConstantInterface & iconstant : ienum . constants ) {
2017-12-24 03:17:48 +01:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2020-04-02 01:20:12 +02:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , nullptr ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// <summary> \n " ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// </summary> \n " ) ;
2019-03-18 21:59:35 +01:00
}
2017-12-24 03:17:48 +01:00
}
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( iconstant . proxy_name ) ;
p_output . append ( " = " ) ;
p_output . append ( itos ( iconstant . value ) ) ;
2021-07-25 18:45:00 +02:00
p_output . append ( & iconstant ! = & last ? " , \n " : " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK ) ;
2017-12-24 03:17:48 +01:00
2020-05-16 04:03:05 +02:00
if ( enum_in_static_class ) {
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK ) ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( " \n #pragma warning restore CS1591 \n " ) ;
2017-12-24 03:17:48 +01:00
}
2019-12-28 19:12:32 +01:00
Error BindingsGenerator : : generate_cs_core_project ( const String & p_proj_dir ) {
2019-09-24 15:09:54 +02:00
ERR_FAIL_COND_V ( ! initialized , ERR_UNCONFIGURED ) ;
2022-03-23 11:08:58 +02:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( da . is_null ( ) , ERR_CANT_CREATE ) ;
2017-10-02 23:24:00 +02:00
2019-07-03 09:44:53 +02:00
if ( ! DirAccess : : exists ( p_proj_dir ) ) {
Error err = da - > make_dir_recursive ( p_proj_dir ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , ERR_CANT_CREATE , " Cannot create directory ' " + p_proj_dir + " '. " ) ;
2017-10-02 23:24:00 +02:00
}
2019-07-03 09:44:53 +02:00
da - > change_dir ( p_proj_dir ) ;
2019-12-28 19:12:32 +01:00
da - > make_dir ( " Generated " ) ;
da - > make_dir ( " Generated/GodotObjects " ) ;
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
String base_gen_dir = path : : join ( p_proj_dir , " Generated " ) ;
String godot_objects_gen_dir = path : : join ( base_gen_dir , " GodotObjects " ) ;
Vector < String > compile_items ;
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
// Generate source file for global scope constants and enums
{
2019-04-19 01:10:08 +02:00
StringBuilder constants_source ;
2017-12-24 03:17:48 +01:00
_generate_global_constants ( constants_source ) ;
2019-12-28 19:12:32 +01:00
String output_file = path : : join ( base_gen_dir , BINDINGS_GLOBAL_SCOPE_CLASS " _constants.cs " ) ;
2017-12-24 03:17:48 +01:00
Error save_err = _save_file ( output_file , constants_source ) ;
2020-05-16 04:03:05 +02:00
if ( save_err ! = OK ) {
2017-12-24 03:17:48 +01:00
return save_err ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
2019-12-28 19:12:32 +01:00
compile_items . push_back ( output_file ) ;
2017-12-24 03:17:48 +01:00
}
2021-01-13 05:17:39 -05:00
// Generate source file for array extensions
{
StringBuilder extensions_source ;
_generate_array_extensions ( extensions_source ) ;
String output_file = path : : join ( base_gen_dir , BINDINGS_GLOBAL_SCOPE_CLASS " _extensions.cs " ) ;
Error save_err = _save_file ( output_file , extensions_source ) ;
if ( save_err ! = OK ) {
return save_err ;
}
2019-12-28 19:12:32 +01:00
compile_items . push_back ( output_file ) ;
2017-12-24 03:17:48 +01:00
}
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , TypeInterface > & E : obj_types ) {
const TypeInterface & itype = E . value ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( itype . api_type = = ClassDB : : API_EDITOR ) {
2017-10-02 23:24:00 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
String output_file = path : : join ( godot_objects_gen_dir , itype . proxy_name + " .cs " ) ;
2018-01-09 19:06:59 +01:00
Error err = _generate_cs_type ( itype , output_file ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( err = = ERR_SKIP ) {
2017-10-02 23:24:00 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( err ! = OK ) {
2017-10-02 23:24:00 +02:00
return err ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
compile_items . push_back ( output_file ) ;
2017-10-02 23:24:00 +02:00
}
2021-05-03 15:21:06 +02:00
// Generate native calls
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
StringBuilder cs_icalls_content ;
2022-02-27 21:57:50 +01:00
cs_icalls_content . append ( " namespace " BINDINGS_NAMESPACE " ; \n \n " ) ;
2019-04-19 01:10:08 +02:00
cs_icalls_content . append ( " using System; \n "
2021-05-03 15:21:06 +02:00
" using System.Diagnostics.CodeAnalysis; \n "
" using System.Runtime.InteropServices; \n "
" using Godot.NativeInterop; \n "
2019-04-19 01:10:08 +02:00
" \n " ) ;
2022-02-27 21:57:50 +01:00
cs_icalls_content . append ( " [SuppressMessage( \" ReSharper \" , \" InconsistentNaming \" )] \n " ) ;
cs_icalls_content . append ( " [SuppressMessage( \" ReSharper \" , \" RedundantUnsafeContext \" )] \n " ) ;
cs_icalls_content . append ( " [SuppressMessage( \" ReSharper \" , \" RedundantNameQualifier \" )] \n " ) ;
cs_icalls_content . append ( " [System.Runtime.CompilerServices.SkipLocalsInit] \n " ) ;
cs_icalls_content . append ( " internal static class " BINDINGS_CLASS_NATIVECALLS " \n { " ) ;
2019-04-19 01:10:08 +02:00
cs_icalls_content . append ( MEMBER_BEGIN " internal static ulong godot_api_hash = " ) ;
2021-05-03 15:21:06 +02:00
cs_icalls_content . append ( String : : num_uint64 ( ClassDB : : get_api_hash ( ClassDB : : API_CORE ) ) + " ; \n " ) ;
2019-04-19 01:10:08 +02:00
2021-05-03 15:21:06 +02:00
cs_icalls_content . append ( MEMBER_BEGIN " private const int VarArgsSpanThreshold = 10; \n " ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
for ( const InternalCall & icall : method_icalls ) {
if ( icall . editor_only ) {
continue ;
}
Error err = _generate_cs_native_calls ( icall , cs_icalls_content ) ;
if ( err ! = OK ) {
return err ;
}
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
cs_icalls_content . append ( CLOSE_BLOCK ) ;
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
String internal_methods_file = path : : join ( base_gen_dir , BINDINGS_CLASS_NATIVECALLS " .cs " ) ;
2017-10-02 23:24:00 +02:00
Error err = _save_file ( internal_methods_file , cs_icalls_content ) ;
2020-05-16 04:03:05 +02:00
if ( err ! = OK ) {
2017-10-02 23:24:00 +02:00
return err ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
compile_items . push_back ( internal_methods_file ) ;
2021-05-03 15:21:06 +02:00
// Generate GeneratedIncludes.props
2019-12-28 19:12:32 +01:00
StringBuilder includes_props_content ;
includes_props_content . append ( " <Project> \n "
" <ItemGroup> \n " ) ;
for ( int i = 0 ; i < compile_items . size ( ) ; i + + ) {
String include = path : : relative_to ( compile_items [ i ] , p_proj_dir ) . replace ( " / " , " \\ " ) ;
includes_props_content . append ( " <Compile Include= \" " + include + " \" /> \n " ) ;
}
includes_props_content . append ( " </ItemGroup> \n "
" </Project> \n " ) ;
String includes_props_file = path : : join ( base_gen_dir , " GeneratedIncludes.props " ) ;
err = _save_file ( includes_props_file , includes_props_content ) ;
2020-05-16 04:03:05 +02:00
if ( err ! = OK ) {
2019-12-28 19:12:32 +01:00
return err ;
2020-05-16 04:03:05 +02:00
}
2017-10-24 16:18:47 +02:00
2017-10-02 23:24:00 +02:00
return OK ;
}
2019-12-28 19:12:32 +01:00
Error BindingsGenerator : : generate_cs_editor_project ( const String & p_proj_dir ) {
2019-09-24 15:09:54 +02:00
ERR_FAIL_COND_V ( ! initialized , ERR_UNCONFIGURED ) ;
2022-03-23 11:08:58 +02:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( da . is_null ( ) , ERR_CANT_CREATE ) ;
2017-10-02 23:24:00 +02:00
2019-07-03 09:44:53 +02:00
if ( ! DirAccess : : exists ( p_proj_dir ) ) {
Error err = da - > make_dir_recursive ( p_proj_dir ) ;
2017-10-02 23:24:00 +02:00
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
2019-07-03 09:44:53 +02:00
da - > change_dir ( p_proj_dir ) ;
2019-12-28 19:12:32 +01:00
da - > make_dir ( " Generated " ) ;
da - > make_dir ( " Generated/GodotObjects " ) ;
String base_gen_dir = path : : join ( p_proj_dir , " Generated " ) ;
String godot_objects_gen_dir = path : : join ( base_gen_dir , " GodotObjects " ) ;
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
Vector < String > compile_items ;
2017-10-02 23:24:00 +02:00
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , TypeInterface > & E : obj_types ) {
const TypeInterface & itype = E . value ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( itype . api_type ! = ClassDB : : API_EDITOR ) {
2017-10-02 23:24:00 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
String output_file = path : : join ( godot_objects_gen_dir , itype . proxy_name + " .cs " ) ;
2018-01-09 19:06:59 +01:00
Error err = _generate_cs_type ( itype , output_file ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( err = = ERR_SKIP ) {
2017-10-02 23:24:00 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( err ! = OK ) {
2017-10-02 23:24:00 +02:00
return err ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
compile_items . push_back ( output_file ) ;
2017-10-02 23:24:00 +02:00
}
2021-05-03 15:21:06 +02:00
// Generate native calls
2019-04-19 01:10:08 +02:00
StringBuilder cs_icalls_content ;
2022-02-27 21:57:50 +01:00
cs_icalls_content . append ( " namespace " BINDINGS_NAMESPACE " ; \n \n " ) ;
2019-04-19 01:10:08 +02:00
cs_icalls_content . append ( " using System; \n "
2021-05-03 15:21:06 +02:00
" using System.Diagnostics.CodeAnalysis; \n "
" using System.Runtime.InteropServices; \n "
" using Godot.NativeInterop; \n "
2019-04-19 01:10:08 +02:00
" \n " ) ;
2022-02-27 21:57:50 +01:00
cs_icalls_content . append ( " [SuppressMessage( \" ReSharper \" , \" InconsistentNaming \" )] \n " ) ;
cs_icalls_content . append ( " [SuppressMessage( \" ReSharper \" , \" RedundantUnsafeContext \" )] \n " ) ;
cs_icalls_content . append ( " [SuppressMessage( \" ReSharper \" , \" RedundantNameQualifier \" )] \n " ) ;
cs_icalls_content . append ( " [System.Runtime.CompilerServices.SkipLocalsInit] \n " ) ;
cs_icalls_content . append ( " internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR " \n " OPEN_BLOCK ) ;
cs_icalls_content . append ( INDENT1 " internal static ulong godot_api_hash = " ) ;
2021-05-03 15:21:06 +02:00
cs_icalls_content . append ( String : : num_uint64 ( ClassDB : : get_api_hash ( ClassDB : : API_EDITOR ) ) + " ; \n " ) ;
2019-04-19 01:10:08 +02:00
2021-05-03 15:21:06 +02:00
cs_icalls_content . append ( MEMBER_BEGIN " private const int VarArgsSpanThreshold = 10; \n " ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
cs_icalls_content . append ( " \n " ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
for ( const InternalCall & icall : method_icalls ) {
if ( ! icall . editor_only ) {
continue ;
}
Error err = _generate_cs_native_calls ( icall , cs_icalls_content ) ;
if ( err ! = OK ) {
return err ;
}
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
cs_icalls_content . append ( CLOSE_BLOCK ) ;
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
String internal_methods_file = path : : join ( base_gen_dir , BINDINGS_CLASS_NATIVECALLS_EDITOR " .cs " ) ;
2017-10-02 23:24:00 +02:00
Error err = _save_file ( internal_methods_file , cs_icalls_content ) ;
2020-05-16 04:03:05 +02:00
if ( err ! = OK ) {
2017-10-02 23:24:00 +02:00
return err ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
compile_items . push_back ( internal_methods_file ) ;
2021-05-03 15:21:06 +02:00
// Generate GeneratedIncludes.props
2019-12-28 19:12:32 +01:00
StringBuilder includes_props_content ;
includes_props_content . append ( " <Project> \n "
" <ItemGroup> \n " ) ;
for ( int i = 0 ; i < compile_items . size ( ) ; i + + ) {
String include = path : : relative_to ( compile_items [ i ] , p_proj_dir ) . replace ( " / " , " \\ " ) ;
includes_props_content . append ( " <Compile Include= \" " + include + " \" /> \n " ) ;
}
includes_props_content . append ( " </ItemGroup> \n "
" </Project> \n " ) ;
String includes_props_file = path : : join ( base_gen_dir , " GeneratedIncludes.props " ) ;
err = _save_file ( includes_props_file , includes_props_content ) ;
2020-05-16 04:03:05 +02:00
if ( err ! = OK ) {
2019-12-28 19:12:32 +01:00
return err ;
2020-05-16 04:03:05 +02:00
}
2018-11-08 01:05:19 +01:00
return OK ;
}
2017-10-02 23:24:00 +02:00
2019-04-25 15:45:12 +02:00
Error BindingsGenerator : : generate_cs_api ( const String & p_output_dir ) {
2019-09-24 15:09:54 +02:00
ERR_FAIL_COND_V ( ! initialized , ERR_UNCONFIGURED ) ;
2019-07-08 15:16:35 +02:00
String output_dir = path : : abspath ( path : : realpath ( p_output_dir ) ) ;
2019-07-03 09:44:53 +02:00
2022-03-23 11:08:58 +02:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( da . is_null ( ) , ERR_CANT_CREATE ) ;
2018-11-08 01:05:19 +01:00
2019-07-03 09:44:53 +02:00
if ( ! DirAccess : : exists ( output_dir ) ) {
Error err = da - > make_dir_recursive ( output_dir ) ;
2018-11-08 01:05:19 +01:00
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
2019-07-03 09:44:53 +02:00
Error proj_err ;
2018-11-08 01:05:19 +01:00
2019-07-03 09:44:53 +02:00
// Generate GodotSharp source files
2018-11-08 01:05:19 +01:00
2022-08-29 19:34:01 -05:00
String core_proj_dir = output_dir . path_join ( CORE_API_ASSEMBLY_NAME ) ;
2018-11-08 01:05:19 +01:00
2019-12-28 19:12:32 +01:00
proj_err = generate_cs_core_project ( core_proj_dir ) ;
2018-11-08 01:05:19 +01:00
if ( proj_err ! = OK ) {
2019-08-09 03:39:45 +02:00
ERR_PRINT ( " Generation of the Core API C# project failed. " ) ;
2018-11-08 01:05:19 +01:00
return proj_err ;
}
2019-07-03 09:44:53 +02:00
// Generate GodotSharpEditor source files
2022-08-29 19:34:01 -05:00
String editor_proj_dir = output_dir . path_join ( EDITOR_API_ASSEMBLY_NAME ) ;
2019-07-03 09:44:53 +02:00
2019-12-28 19:12:32 +01:00
proj_err = generate_cs_editor_project ( editor_proj_dir ) ;
2018-11-08 01:05:19 +01:00
if ( proj_err ! = OK ) {
2019-08-09 03:39:45 +02:00
ERR_PRINT ( " Generation of the Editor API C# project failed. " ) ;
2018-11-08 01:05:19 +01:00
return proj_err ;
}
2017-10-02 23:24:00 +02:00
2019-12-28 19:12:32 +01:00
_log ( " The Godot API sources were successfully generated \n " ) ;
2019-07-03 09:44:53 +02:00
2017-10-02 23:24:00 +02:00
return OK ;
}
2018-09-04 05:40:41 +02:00
// FIXME: There are some members that hide other inherited members.
// - In the case of both members being the same kind, the new one must be declared
2019-08-09 03:39:45 +02:00
// explicitly as 'new' to avoid the warning (and we must print a message about it).
2018-09-04 05:40:41 +02:00
// - In the case of both members being of a different kind, then the new one must
// be renamed to avoid the name collision (and we must print a warning about it).
// - Csc warning e.g.:
// ObjectType/LineEdit.cs(140,38): warning CS0108: 'LineEdit.FocusMode' hides inherited member 'Control.FocusMode'. Use the new keyword if hiding was intended.
2017-10-02 23:24:00 +02:00
Error BindingsGenerator : : _generate_cs_type ( const TypeInterface & itype , const String & p_output_file ) {
2018-09-04 05:40:41 +02:00
CRASH_COND ( ! itype . is_object_type ) ;
2017-12-24 03:17:48 +01:00
bool is_derived_type = itype . base_name ! = StringName ( ) ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
if ( ! is_derived_type ) {
// Some Godot.Object assertions
CRASH_COND ( itype . cname ! = name_cache . type_Object ) ;
CRASH_COND ( ! itype . is_instantiable ) ;
CRASH_COND ( itype . api_type ! = ClassDB : : API_CORE ) ;
2021-06-04 18:03:15 +02:00
CRASH_COND ( itype . is_ref_counted ) ;
2018-09-04 05:40:41 +02:00
CRASH_COND ( itype . is_singleton ) ;
}
2019-04-25 15:45:12 +02:00
_log ( " Generating %s.cs... \n " , itype . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
StringBuilder output ;
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
output . append ( " namespace " BINDINGS_NAMESPACE " ; \n \n " ) ;
2019-04-19 01:10:08 +02:00
output . append ( " using System; \n " ) ; // IntPtr
output . append ( " using System.Diagnostics; \n " ) ; // DebuggerBrowsable
2021-09-12 20:21:15 +02:00
output . append ( " using Godot.NativeInterop; \n " ) ;
2018-10-16 17:22:27 +02:00
2019-04-19 01:10:08 +02:00
output . append ( " \n "
" #pragma warning disable CS1591 // Disable warning: "
" 'Missing XML comment for publicly visible type or member' \n "
" #pragma warning disable CS1573 // Disable warning: "
" 'Parameter has no matching param tag in the XML comment' \n " ) ;
2018-10-16 17:22:27 +02:00
2021-12-28 23:25:16 +01:00
output . append ( " \n #nullable disable \n " ) ;
2017-10-02 23:24:00 +02:00
const DocData : : ClassDoc * class_doc = itype . class_doc ;
if ( class_doc & & class_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( class_doc - > description ) , & itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2022-02-27 21:57:50 +01:00
output . append ( " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
output . append ( " /// " ) ;
2019-04-19 01:10:08 +02:00
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2017-10-02 23:24:00 +02:00
}
2022-02-27 21:57:50 +01:00
output . append ( " /// </summary> \n " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-02 23:24:00 +02:00
}
2022-02-27 21:57:30 +01:00
// We generate a `GodotClassName` attribute if the engine class name is not the same as the
// generated C# class name. This allows introspection code to find the name associated with
// the class. If the attribute is not present, the C# class name can be used instead.
if ( itype . name ! = itype . proxy_name ) {
2022-02-27 21:57:50 +01:00
output < < " [GodotClassName( \" " < < itype . name < < " \" )] \n " ;
2022-02-27 21:57:30 +01:00
}
2022-02-27 21:57:50 +01:00
output . append ( " public " ) ;
2018-09-04 05:40:41 +02:00
if ( itype . is_singleton ) {
2019-04-19 01:10:08 +02:00
output . append ( " static partial class " ) ;
2018-08-27 20:39:51 +02:00
} else {
2021-09-12 20:21:15 +02:00
// Even if the class is not instantiable, we can't declare it abstract because
// the engine can still instantiate them and return them via the scripting API.
// Example: `SceneTreeTimer` returned from `SceneTree.create_timer`.
// See the reverted commit: ef5672d3f94a7321ed779c922088bb72adbb1521
output . append ( " partial class " ) ;
2018-08-27 20:39:51 +02:00
}
2019-04-19 01:10:08 +02:00
output . append ( itype . proxy_name ) ;
2017-10-02 23:24:00 +02:00
2021-12-28 23:25:16 +01:00
if ( is_derived_type & & ! itype . is_singleton ) {
2018-09-04 05:40:41 +02:00
if ( obj_types . has ( itype . base_name ) ) {
2019-04-19 01:10:08 +02:00
output . append ( " : " ) ;
output . append ( obj_types [ itype . base_name ] . proxy_name ) ;
2018-09-04 05:40:41 +02:00
} else {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Base type ' " + itype . base_name . operator String ( ) + " ' does not exist, for class ' " + itype . name + " '. " ) ;
2018-09-04 05:40:41 +02:00
return ERR_INVALID_DATA ;
}
2017-10-02 23:24:00 +02:00
}
2022-02-27 21:57:50 +01:00
output . append ( " \n { " ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
// Add constants
2017-10-02 23:24:00 +02:00
2021-07-15 23:45:57 -04:00
for ( const ConstantInterface & iconstant : itype . constants ) {
2020-05-16 04:03:05 +02:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , & itype ) ;
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( summary_lines . size ( ) ) {
output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
output . append ( INDENT1 " /// " ) ;
2020-05-16 04:03:05 +02:00
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
output . append ( INDENT1 " /// </summary> " ) ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
}
2022-05-09 12:47:10 +03:00
output . append ( MEMBER_BEGIN " public const long " ) ;
2020-05-16 04:03:05 +02:00
output . append ( iconstant . proxy_name ) ;
output . append ( " = " ) ;
output . append ( itos ( iconstant . value ) ) ;
output . append ( " ; " ) ;
}
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( itype . constants . size ( ) ) {
output . append ( " \n " ) ;
}
2017-12-24 03:17:48 +01:00
2020-05-16 04:03:05 +02:00
// Add enums
2017-12-24 03:17:48 +01:00
2021-07-15 23:45:57 -04:00
for ( const EnumInterface & ienum : itype . enums ) {
2020-12-15 12:04:21 +00:00
ERR_FAIL_COND_V ( ienum . constants . is_empty ( ) , ERR_BUG ) ;
2017-12-24 03:17:48 +01:00
2022-06-24 20:25:30 +02:00
if ( ienum . is_flags ) {
output . append ( MEMBER_BEGIN " [System.Flags] " ) ;
}
2020-05-16 04:03:05 +02:00
output . append ( MEMBER_BEGIN " public enum " ) ;
output . append ( ienum . cname . operator String ( ) ) ;
2022-05-09 12:47:10 +03:00
output . append ( " : long " ) ;
2020-05-16 04:03:05 +02:00
output . append ( MEMBER_BEGIN OPEN_BLOCK ) ;
2017-12-24 03:17:48 +01:00
2021-07-25 18:45:00 +02:00
const ConstantInterface & last = ienum . constants . back ( ) - > get ( ) ;
for ( const ConstantInterface & iconstant : ienum . constants ) {
2020-05-16 04:03:05 +02:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , & itype ) ;
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 03:17:48 +01:00
2020-05-16 04:03:05 +02:00
if ( summary_lines . size ( ) ) {
2022-02-27 21:57:50 +01:00
output . append ( INDENT2 " /// <summary> \n " ) ;
2017-12-24 03:17:48 +01:00
2020-05-16 04:03:05 +02:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
output . append ( INDENT2 " /// " ) ;
2020-05-16 04:03:05 +02:00
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2019-03-18 21:59:35 +01:00
}
2017-12-24 03:17:48 +01:00
2022-02-27 21:57:50 +01:00
output . append ( INDENT2 " /// </summary> \n " ) ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
}
2022-02-27 21:57:50 +01:00
output . append ( INDENT2 ) ;
2020-05-16 04:03:05 +02:00
output . append ( iconstant . proxy_name ) ;
output . append ( " = " ) ;
output . append ( itos ( iconstant . value ) ) ;
2021-07-25 18:45:00 +02:00
output . append ( & iconstant ! = & last ? " , \n " : " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2022-02-27 21:57:50 +01:00
output . append ( INDENT1 CLOSE_BLOCK ) ;
2017-10-02 23:24:00 +02:00
}
2020-05-16 04:03:05 +02:00
// Add properties
2021-07-15 23:45:57 -04:00
for ( const PropertyInterface & iprop : itype . properties ) {
2020-05-16 04:03:05 +02:00
Error prop_err = _generate_cs_property ( itype , iprop , output ) ;
ERR_FAIL_COND_V_MSG ( prop_err ! = OK , prop_err ,
" Failed to generate property ' " + iprop . cname . operator String ( ) +
" ' for class ' " + itype . name + " '. " ) ;
}
2019-02-04 20:39:02 +01:00
2018-09-04 05:40:41 +02:00
if ( itype . is_singleton ) {
2017-10-02 23:24:00 +02:00
// Add the type name and the singleton pointer as static fields
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " private static Godot.Object singleton; \n " ) ;
2022-02-27 21:57:50 +01:00
output < < MEMBER_BEGIN " public static Godot.Object " CS_PROPERTY_SINGLETON " \n " INDENT1 " { \n "
< < INDENT2 " get \n " INDENT2 " { \n " INDENT3 " if (singleton == null) \n "
< < INDENT4 " singleton = " C_METHOD_ENGINE_GET_SINGLETON " (typeof( "
2021-05-03 15:21:06 +02:00
< < itype . proxy_name
2022-02-27 21:57:50 +01:00
< < " ).Name); \n " INDENT3 " return singleton; \n " INDENT2 " } \n " INDENT1 " } \n " ;
2021-05-03 15:21:06 +02:00
output . append ( MEMBER_BEGIN " private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \" " ) ;
2019-04-19 01:10:08 +02:00
output . append ( itype . name ) ;
output . append ( " \" ; \n " ) ;
2021-05-03 15:21:06 +02:00
} else {
// IMPORTANT: We also generate the static fields for Godot.Object instead of declaring
// them manually in the `Object.base.cs` partial class declaration, because they're
// required by other static fields in this generated partial class declaration.
// Static fields are initialized in order of declaration, but when they're in different
// partial class declarations then it becomes harder to tell (Rider warns about this).
2019-04-19 01:10:08 +02:00
2021-05-03 15:21:06 +02:00
// Add native name static field
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
if ( is_derived_type ) {
2021-12-28 23:25:16 +01:00
output < < MEMBER_BEGIN " private static readonly System.Type CachedType = typeof( " < < itype . proxy_name < < " ); \n " ;
2021-09-12 20:21:15 +02:00
}
2021-05-03 15:21:06 +02:00
output . append ( MEMBER_BEGIN " private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \" " ) ;
2019-04-19 01:10:08 +02:00
output . append ( itype . name ) ;
output . append ( " \" ; \n " ) ;
2017-10-02 23:24:00 +02:00
if ( itype . is_instantiable ) {
2021-05-03 15:21:06 +02:00
// Add native constructor static field
output < < MEMBER_BEGIN < < " [DebuggerBrowsable(DebuggerBrowsableState.Never)] \n "
2022-02-27 21:57:50 +01:00
< < INDENT1 " private static readonly unsafe delegate* unmanaged<IntPtr> "
2021-09-12 20:21:15 +02:00
< < CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR
2021-09-12 20:23:05 +02:00
< < " ( " BINDINGS_NATIVE_NAME_FIELD " ); \n " ;
2021-05-03 15:21:06 +02:00
}
if ( is_derived_type ) {
// Add default constructor
if ( itype . is_instantiable ) {
output < < MEMBER_BEGIN " public " < < itype . proxy_name < < " () : this( "
2022-02-27 21:57:50 +01:00
< < ( itype . memory_own ? " true " : " false " ) < < " ) \n " OPEN_BLOCK_L1
< < INDENT2 " unsafe \n " INDENT2 OPEN_BLOCK
< < INDENT3 " _ConstructAndInitialize( " CS_STATIC_FIELD_NATIVE_CTOR " , "
2021-12-28 23:25:16 +01:00
< < BINDINGS_NATIVE_NAME_FIELD " , CachedType, refCounted: "
< < ( itype . is_ref_counted ? " true " : " false " ) < < " ); \n "
2022-02-27 21:57:50 +01:00
< < CLOSE_BLOCK_L2 CLOSE_BLOCK_L1 ;
2021-05-03 15:21:06 +02:00
} else {
// Hide the constructor
output . append ( MEMBER_BEGIN " internal " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " () {} \n " ) ;
}
// Add.. em.. trick constructor. Sort of.
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " internal " ) ;
output . append ( itype . proxy_name ) ;
2021-05-03 15:21:06 +02:00
output . append ( " (bool " CS_PARAM_MEMORYOWN " ) : base( " CS_PARAM_MEMORYOWN " ) {} \n " ) ;
2017-10-02 23:24:00 +02:00
}
}
2021-09-12 20:21:15 +02:00
// Methods
2017-10-29 02:37:13 +01:00
int method_bind_count = 0 ;
2021-07-15 23:45:57 -04:00
for ( const MethodInterface & imethod : itype . methods ) {
2017-10-29 02:37:13 +01:00
Error method_err = _generate_cs_method ( itype , imethod , method_bind_count , output ) ;
2019-08-09 03:39:45 +02:00
ERR_FAIL_COND_V_MSG ( method_err ! = OK , method_err ,
" Failed to generate method ' " + imethod . name + " ' for class ' " + itype . name + " '. " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
// Signals
2021-07-15 23:45:57 -04:00
for ( const SignalInterface & isignal : itype . signals_ ) {
2020-03-14 19:20:17 +01:00
Error method_err = _generate_cs_signal ( itype , isignal , output ) ;
ERR_FAIL_COND_V_MSG ( method_err ! = OK , method_err ,
" Failed to generate signal ' " + isignal . name + " ' for class ' " + itype . name + " '. " ) ;
}
2021-12-28 23:25:16 +01:00
// Script members look-up
2021-09-12 20:21:15 +02:00
if ( ! itype . is_singleton & & ( is_derived_type | | itype . has_virtual_methods ) ) {
2021-12-28 23:25:16 +01:00
// Generate method names cache fields
2021-12-28 23:25:16 +01:00
for ( const MethodInterface & imethod : itype . methods ) {
if ( ! imethod . is_virtual ) {
continue ;
}
output < < MEMBER_BEGIN " // ReSharper disable once InconsistentNaming \n "
2022-02-27 21:57:50 +01:00
< < INDENT1 " [DebuggerBrowsable(DebuggerBrowsableState.Never)] \n "
< < INDENT1 " private static readonly StringName "
2021-12-28 23:25:16 +01:00
< < CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX < < imethod . name
< < " = \" " < < imethod . proxy_name < < " \" ; \n " ;
}
2021-12-28 23:25:16 +01:00
// TODO: Only generate HasGodotClassMethod and InvokeGodotClassMethod if there's any method
// Generate InvokeGodotClassMethod
2021-12-28 23:25:16 +01:00
output < < MEMBER_BEGIN " protected internal " < < ( is_derived_type ? " override " : " virtual " )
< < " bool " CS_METHOD_INVOKE_GODOT_CLASS_METHOD " (in godot_string_name method, "
< < " NativeVariantPtrArgs args, int argCount, out godot_variant ret) \n "
2022-02-27 21:57:50 +01:00
< < INDENT1 " { \n " ;
2021-09-12 20:21:15 +02:00
for ( const MethodInterface & imethod : itype . methods ) {
if ( ! imethod . is_virtual ) {
continue ;
}
2021-12-28 23:25:16 +01:00
// We also call HasGodotClassMethod to ensure the method is overridden and avoid calling
// the stub implementation. This solution adds some extra overhead to calls, but it's
// much simpler than other solutions. This won't be a problem once we move to function
// pointers of generated wrappers for each method, as lookup will only happen once.
// We check both native names (snake_case) and proxy names (PascalCase)
2022-02-27 21:57:50 +01:00
output < < INDENT2 " if ((method == " < < CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX < < imethod . name
2022-08-25 23:07:20 +08:00
< < " || method == MethodName. " < < imethod . proxy_name
2021-12-28 23:25:16 +01:00
< < " ) && argCount == " < < itos ( imethod . arguments . size ( ) )
< < " && " < < CS_METHOD_HAS_GODOT_CLASS_METHOD < < " ((godot_string_name) "
< < CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX < < imethod . name < < " .NativeValue)) \n "
2022-02-27 21:57:50 +01:00
< < INDENT2 " { \n " ;
2021-09-12 20:21:15 +02:00
if ( imethod . return_type . cname ! = name_cache . type_void ) {
2022-02-27 21:57:50 +01:00
output < < INDENT3 " var callRet = " ;
2021-09-12 20:21:15 +02:00
} else {
2022-02-27 21:57:50 +01:00
output < < INDENT3 ;
2021-09-12 20:21:15 +02:00
}
output < < imethod . proxy_name < < " ( " ;
for ( int i = 0 ; i < imethod . arguments . size ( ) ; i + + ) {
const ArgumentInterface & iarg = imethod . arguments [ i ] ;
const TypeInterface * arg_type = _get_type_or_null ( iarg . type ) ;
ERR_FAIL_NULL_V ( arg_type , ERR_BUG ) ; // Argument type not found
if ( i ! = 0 ) {
output < < " , " ;
}
if ( arg_type - > cname = = name_cache . type_Array_generic | | arg_type - > cname = = name_cache . type_Dictionary_generic ) {
String arg_cs_type = arg_type - > cs_type + _get_generic_type_parameters ( * arg_type , iarg . type . generic_type_parameters ) ;
2022-07-28 17:41:50 +02:00
output < < " new " < < arg_cs_type < < " ( " < < sformat ( arg_type - > cs_variant_to_managed , " args[ " + itos ( i ) + " ] " , arg_type - > cs_type , arg_type - > name ) < < " ) " ;
2021-09-12 20:21:15 +02:00
} else {
2022-02-27 21:57:46 +01:00
output < < sformat ( arg_type - > cs_variant_to_managed ,
" args[ " + itos ( i ) + " ] " , arg_type - > cs_type , arg_type - > name ) ;
2021-09-12 20:21:15 +02:00
}
}
output < < " ); \n " ;
if ( imethod . return_type . cname ! = name_cache . type_void ) {
2022-02-27 21:57:46 +01:00
const TypeInterface * return_type = _get_type_or_null ( imethod . return_type ) ;
ERR_FAIL_NULL_V ( return_type , ERR_BUG ) ; // Return type not found
2022-02-27 21:57:50 +01:00
output < < INDENT3 " ret = "
2022-02-27 21:57:46 +01:00
< < sformat ( return_type - > cs_managed_to_variant , " callRet " , return_type - > cs_type , return_type - > name )
< < " ; \n "
2022-02-27 21:57:50 +01:00
< < INDENT3 " return true; \n " ;
2021-09-12 20:21:15 +02:00
} else {
2022-02-27 21:57:50 +01:00
output < < INDENT3 " ret = default; \n "
< < INDENT3 " return true; \n " ;
2021-09-12 20:21:15 +02:00
}
2022-02-27 21:57:50 +01:00
output < < INDENT2 " } \n " ;
2021-09-12 20:21:15 +02:00
}
if ( is_derived_type ) {
2022-02-27 21:57:50 +01:00
output < < INDENT2 " return base. " CS_METHOD_INVOKE_GODOT_CLASS_METHOD " (method, args, argCount, out ret); \n " ;
2021-09-12 20:21:15 +02:00
} else {
2022-02-27 21:57:50 +01:00
output < < INDENT2 " ret = default; \n "
< < INDENT2 " return false; \n " ;
2021-09-12 20:21:15 +02:00
}
2022-02-27 21:57:50 +01:00
output < < INDENT1 " } \n " ;
2021-12-28 23:25:16 +01:00
// Generate HasGodotClassMethod
output < < MEMBER_BEGIN " protected internal " < < ( is_derived_type ? " override " : " virtual " )
< < " bool " CS_METHOD_HAS_GODOT_CLASS_METHOD " (in godot_string_name method) \n "
2022-02-27 21:57:50 +01:00
< < INDENT1 " { \n " ;
2021-12-28 23:25:16 +01:00
for ( const MethodInterface & imethod : itype . methods ) {
if ( ! imethod . is_virtual ) {
continue ;
}
// We check for native names (snake_case). If we detect one, we call HasGodotClassMethod
// again, but this time with the respective proxy name (PascalCase). It's the job of
// user derived classes to override the method and check for those. Our C# source
// generators take care of generating those override methods.
2022-08-25 23:07:20 +08:00
output < < INDENT2 " if (method == MethodName. " < < imethod . proxy_name
2022-02-27 21:57:50 +01:00
< < " ) \n " INDENT2 " { \n "
< < INDENT3 " if ( " CS_METHOD_HAS_GODOT_CLASS_METHOD " ( "
2021-12-28 23:25:16 +01:00
< < CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX < < imethod . name
2022-02-27 21:57:50 +01:00
< < " .NativeValue.DangerousSelfRef)) \n " INDENT3 " { \n "
< < INDENT4 " return true; \n "
< < INDENT3 " } \n " INDENT2 " } \n " ;
2021-12-28 23:25:16 +01:00
}
if ( is_derived_type ) {
2022-02-27 21:57:50 +01:00
output < < INDENT2 " return base. " CS_METHOD_HAS_GODOT_CLASS_METHOD " (method); \n " ;
2021-12-28 23:25:16 +01:00
} else {
2022-02-27 21:57:50 +01:00
output < < INDENT2 " return false; \n " ;
2021-12-28 23:25:16 +01:00
}
2022-02-27 21:57:50 +01:00
output < < INDENT1 " } \n " ;
2021-09-12 20:21:15 +02:00
}
2022-08-25 23:07:20 +08:00
//Generate StringName for all class members
bool is_inherit = ! itype . is_singleton & & obj_types . has ( itype . base_name ) ;
//PropertyName
if ( is_inherit ) {
output < < MEMBER_BEGIN " public new class PropertyName : " < < obj_types [ itype . base_name ] . proxy_name < < " .PropertyName " ;
} else {
output < < MEMBER_BEGIN " public class PropertyName " ;
}
output < < " \n "
< < INDENT1 " { \n " ;
for ( const PropertyInterface & iprop : itype . properties ) {
output < < INDENT2 " public static readonly StringName " < < iprop . proxy_name < < " = \" " < < iprop . cname < < " \" ; \n " ;
}
output < < INDENT1 " } \n " ;
//MethodName
if ( is_inherit ) {
output < < MEMBER_BEGIN " public new class MethodName : " < < obj_types [ itype . base_name ] . proxy_name < < " .MethodName " ;
} else {
output < < MEMBER_BEGIN " public class MethodName " ;
}
output < < " \n "
< < INDENT1 " { \n " ;
for ( const MethodInterface & imethod : itype . methods ) {
output < < INDENT2 " public static readonly StringName " < < imethod . proxy_name < < " = \" " < < imethod . cname < < " \" ; \n " ;
}
output < < INDENT1 " } \n " ;
//SignalName
if ( is_inherit ) {
output < < MEMBER_BEGIN " public new class SignalName : " < < obj_types [ itype . base_name ] . proxy_name < < " .SignalName " ;
} else {
output < < MEMBER_BEGIN " public class SignalName " ;
}
output < < " \n "
< < INDENT1 " { \n " ;
for ( const SignalInterface & isignal : itype . signals_ ) {
output < < INDENT2 " public static readonly StringName " < < isignal . proxy_name < < " = \" " < < isignal . cname < < " \" ; \n " ;
}
output < < INDENT1 " } \n " ;
2022-02-27 21:57:50 +01:00
output . append ( CLOSE_BLOCK /* class */ ) ;
2018-08-27 20:39:51 +02:00
2019-04-19 01:10:08 +02:00
output . append ( " \n "
" #pragma warning restore CS1591 \n "
" #pragma warning restore CS1573 \n " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return _save_file ( p_output_file , output ) ;
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
Error BindingsGenerator : : _generate_cs_property ( const BindingsGenerator : : TypeInterface & p_itype , const PropertyInterface & p_iprop , StringBuilder & p_output ) {
2017-12-24 03:17:48 +01:00
const MethodInterface * setter = p_itype . find_method_by_name ( p_iprop . setter ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Search it in base types too
const TypeInterface * current_type = & p_itype ;
2017-12-24 03:17:48 +01:00
while ( ! setter & & current_type - > base_name ! = StringName ( ) ) {
2022-05-08 10:09:19 +02:00
HashMap < StringName , TypeInterface > : : Iterator base_match = obj_types . find ( current_type - > base_name ) ;
2020-04-03 01:38:48 +02:00
ERR_FAIL_COND_V_MSG ( ! base_match , ERR_BUG , " Type not found ' " + current_type - > base_name + " '. Inherited by ' " + current_type - > name + " '. " ) ;
2022-05-08 10:09:19 +02:00
current_type = & base_match - > value ;
2017-12-24 03:17:48 +01:00
setter = current_type - > find_method_by_name ( p_iprop . setter ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
const MethodInterface * getter = p_itype . find_method_by_name ( p_iprop . getter ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Search it in base types too
current_type = & p_itype ;
2017-12-24 03:17:48 +01:00
while ( ! getter & & current_type - > base_name ! = StringName ( ) ) {
2022-05-08 10:09:19 +02:00
HashMap < StringName , TypeInterface > : : Iterator base_match = obj_types . find ( current_type - > base_name ) ;
2020-04-03 01:38:48 +02:00
ERR_FAIL_COND_V_MSG ( ! base_match , ERR_BUG , " Type not found ' " + current_type - > base_name + " '. Inherited by ' " + current_type - > name + " '. " ) ;
2022-05-08 10:09:19 +02:00
current_type = & base_match - > value ;
2017-12-24 03:17:48 +01:00
getter = current_type - > find_method_by_name ( p_iprop . getter ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
ERR_FAIL_COND_V ( ! setter & & ! getter , ERR_BUG ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( setter ) {
2017-12-24 03:17:48 +01:00
int setter_argc = p_iprop . index ! = - 1 ? 2 : 1 ;
2017-10-29 02:37:13 +01:00
ERR_FAIL_COND_V ( setter - > arguments . size ( ) ! = setter_argc , ERR_BUG ) ;
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( getter ) {
2017-12-24 03:17:48 +01:00
int getter_argc = p_iprop . index ! = - 1 ? 1 : 0 ;
2017-10-29 02:37:13 +01:00
ERR_FAIL_COND_V ( getter - > arguments . size ( ) ! = getter_argc , ERR_BUG ) ;
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( getter & & setter ) {
2020-03-14 19:20:17 +01:00
const ArgumentInterface & setter_first_arg = setter - > arguments . back ( ) - > get ( ) ;
if ( getter - > return_type . cname ! = setter_first_arg . type . cname ) {
// Special case for Node::set_name
bool whitelisted = getter - > return_type . cname = = name_cache . type_StringName & &
2021-10-28 15:19:35 +02:00
setter_first_arg . type . cname = = name_cache . type_String ;
2020-03-14 19:20:17 +01:00
ERR_FAIL_COND_V_MSG ( ! whitelisted , ERR_BUG ,
" Return type from getter doesn't match first argument of setter for property: ' " +
p_itype . name + " . " + String ( p_iprop . cname ) + " '. " ) ;
}
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
const TypeReference & proptype_name = getter ? getter - > return_type : setter - > arguments . back ( ) - > get ( ) . type ;
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
const TypeInterface * prop_itype = _get_type_or_null ( proptype_name ) ;
2017-12-24 03:17:48 +01:00
ERR_FAIL_NULL_V ( prop_itype , ERR_BUG ) ; // Property type not found
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
ERR_FAIL_COND_V_MSG ( prop_itype - > is_singleton , ERR_BUG ,
" Property type is a singleton: ' " + p_itype . name + " . " + String ( p_iprop . cname ) + " '. " ) ;
2021-01-09 00:14:25 +01:00
if ( p_itype . api_type = = ClassDB : : API_CORE ) {
ERR_FAIL_COND_V_MSG ( prop_itype - > api_type = = ClassDB : : API_EDITOR , ERR_BUG ,
" Property ' " + p_itype . name + " . " + String ( p_iprop . cname ) + " ' has type ' " + prop_itype - > name +
" ' from the editor API. Core API cannot have dependencies on the editor API. " ) ;
}
2017-12-24 03:17:48 +01:00
if ( p_iprop . prop_doc & & p_iprop . prop_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( p_iprop . prop_doc - > description ) , & p_itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// </summary> " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " public " ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( p_itype . is_singleton ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " static " ) ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2022-02-09 00:56:10 +01:00
String prop_cs_type = prop_itype - > cs_type + _get_generic_type_parameters ( * prop_itype , proptype_name . generic_type_parameters ) ;
p_output . append ( prop_cs_type ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( " " ) ;
p_output . append ( p_iprop . proxy_name ) ;
2022-02-27 21:57:50 +01:00
p_output . append ( " \n " OPEN_BLOCK_L1 ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( getter ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT2 " get \n "
2019-04-25 17:11:01 +02:00
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
" #pragma warning disable CS0618 // Disable warning about obsolete method \n "
2022-02-27 21:57:50 +01:00
OPEN_BLOCK_L2 INDENT3 ) ;
2019-04-25 17:11:01 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( " return " ) ;
p_output . append ( getter - > proxy_name + " ( " ) ;
2017-12-24 03:17:48 +01:00
if ( p_iprop . index ! = - 1 ) {
const ArgumentInterface & idx_arg = getter - > arguments . front ( ) - > get ( ) ;
2018-04-28 22:25:25 +02:00
if ( idx_arg . type . cname ! = name_cache . type_int ) {
2017-12-24 03:17:48 +01:00
// Assume the index parameter is an enum
2018-04-28 22:25:25 +02:00
const TypeInterface * idx_arg_type = _get_type_or_null ( idx_arg . type ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( idx_arg_type = = nullptr ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( " ( " + idx_arg_type - > proxy_name + " ) " + itos ( p_iprop . index ) ) ;
2017-12-24 03:17:48 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( itos ( p_iprop . index ) ) ;
2017-12-24 03:17:48 +01:00
}
}
2019-04-25 17:11:01 +02:00
p_output . append ( " ); \n "
2022-02-27 21:57:50 +01:00
CLOSE_BLOCK_L2
2019-04-25 17:11:01 +02:00
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
" #pragma warning restore CS0618 \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( setter ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT2 " set \n "
2019-04-25 17:11:01 +02:00
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
" #pragma warning disable CS0618 // Disable warning about obsolete method \n "
2022-02-27 21:57:50 +01:00
OPEN_BLOCK_L2 INDENT3 ) ;
2019-04-25 17:11:01 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( setter - > proxy_name + " ( " ) ;
2017-12-24 03:17:48 +01:00
if ( p_iprop . index ! = - 1 ) {
const ArgumentInterface & idx_arg = setter - > arguments . front ( ) - > get ( ) ;
2018-04-28 22:25:25 +02:00
if ( idx_arg . type . cname ! = name_cache . type_int ) {
2017-12-24 03:17:48 +01:00
// Assume the index parameter is an enum
2018-04-28 22:25:25 +02:00
const TypeInterface * idx_arg_type = _get_type_or_null ( idx_arg . type ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( idx_arg_type = = nullptr ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( " ( " + idx_arg_type - > proxy_name + " ) " + itos ( p_iprop . index ) + " , " ) ;
2017-12-24 03:17:48 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( itos ( p_iprop . index ) + " , " ) ;
2017-12-24 03:17:48 +01:00
}
}
2019-04-25 17:11:01 +02:00
p_output . append ( " value); \n "
2022-02-27 21:57:50 +01:00
CLOSE_BLOCK_L2
2019-04-25 17:11:01 +02:00
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
" #pragma warning restore CS0618 \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK_L1 ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return OK ;
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
Error BindingsGenerator : : _generate_cs_method ( const BindingsGenerator : : TypeInterface & p_itype , const BindingsGenerator : : MethodInterface & p_imethod , int & p_method_bind_count , StringBuilder & p_output ) {
2021-05-03 15:21:06 +02:00
const TypeInterface * return_type = _get_type_or_null ( p_imethod . return_type ) ;
ERR_FAIL_NULL_V ( return_type , ERR_BUG ) ; // Return type not found
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
ERR_FAIL_COND_V_MSG ( return_type - > is_singleton , ERR_BUG ,
" Method return type is a singleton: ' " + p_itype . name + " . " + p_imethod . name + " '. " ) ;
2021-01-09 00:14:25 +01:00
if ( p_itype . api_type = = ClassDB : : API_CORE ) {
ERR_FAIL_COND_V_MSG ( return_type - > api_type = = ClassDB : : API_EDITOR , ERR_BUG ,
" Method ' " + p_itype . name + " . " + p_imethod . name + " ' has return type ' " + return_type - > name +
" ' from the editor API. Core API cannot have dependencies on the editor API. " ) ;
}
2021-05-03 15:21:06 +02:00
String method_bind_field = CS_STATIC_FIELD_METHOD_BIND_PREFIX + itos ( p_method_bind_count ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
String arguments_sig ;
2021-05-03 15:21:06 +02:00
StringBuilder cs_in_statements ;
2021-12-28 23:25:16 +01:00
bool cs_in_expr_is_unsafe = false ;
2017-10-02 23:24:00 +02:00
2022-03-21 01:25:50 +01:00
String icall_params = method_bind_field ;
2021-12-28 23:25:16 +01:00
2022-03-21 01:25:50 +01:00
if ( ! p_imethod . is_static ) {
2021-12-28 23:25:16 +01:00
if ( p_itype . cs_in . size ( ) ) {
cs_in_statements < < sformat ( p_itype . cs_in , p_itype . c_type , " this " ,
2022-02-27 21:57:50 +01:00
String ( ) , String ( ) , String ( ) , INDENT2 ) ;
2021-12-28 23:25:16 +01:00
}
icall_params + = " , " + sformat ( p_itype . cs_in_expr , " this " ) ;
2022-03-21 01:25:50 +01:00
}
2018-01-25 23:44:37 +01:00
2019-04-19 01:10:08 +02:00
StringBuilder default_args_doc ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Retrieve information from the arguments
2021-07-25 18:45:00 +02:00
const ArgumentInterface & first = p_imethod . arguments . front ( ) - > get ( ) ;
for ( const ArgumentInterface & iarg : p_imethod . arguments ) {
2021-05-03 15:21:06 +02:00
const TypeInterface * arg_type = _get_type_or_null ( iarg . type ) ;
ERR_FAIL_NULL_V ( arg_type , ERR_BUG ) ; // Argument type not found
2017-10-29 02:37:13 +01:00
2020-05-16 04:03:05 +02:00
ERR_FAIL_COND_V_MSG ( arg_type - > is_singleton , ERR_BUG ,
" Argument type is a singleton: ' " + iarg . name + " ' of method ' " + p_itype . name + " . " + p_imethod . name + " '. " ) ;
2021-01-09 00:14:25 +01:00
if ( p_itype . api_type = = ClassDB : : API_CORE ) {
ERR_FAIL_COND_V_MSG ( arg_type - > api_type = = ClassDB : : API_EDITOR , ERR_BUG ,
" Argument ' " + iarg . name + " ' of method ' " + p_itype . name + " . " + p_imethod . name + " ' has type ' " +
arg_type - > name + " ' from the editor API. Core API cannot have dependencies on the editor API. " ) ;
}
2020-05-16 04:03:05 +02:00
if ( iarg . default_argument . size ( ) ) {
CRASH_COND_MSG ( ! _arg_default_value_is_assignable_to_type ( iarg . def_param_value , * arg_type ) ,
" Invalid default value for parameter ' " + iarg . name + " ' of method ' " + p_itype . name + " . " + p_imethod . name + " '. " ) ;
}
2022-02-09 00:56:10 +01:00
String arg_cs_type = arg_type - > cs_type + _get_generic_type_parameters ( * arg_type , iarg . type . generic_type_parameters ) ;
2017-10-29 02:37:13 +01:00
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
2021-07-25 18:45:00 +02:00
if ( & iarg ! = & first ) {
2017-10-29 02:37:13 +01:00
arguments_sig + = " , " ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2017-10-29 02:37:13 +01:00
arguments_sig + = " Nullable< " ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2022-02-09 00:56:10 +01:00
arguments_sig + = arg_cs_type ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2017-10-29 02:37:13 +01:00
arguments_sig + = " > " ;
2020-05-16 04:03:05 +02:00
} else {
2017-10-29 02:37:13 +01:00
arguments_sig + = " " ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
arguments_sig + = iarg . name ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . default_argument . size ( ) ) {
2020-05-16 04:03:05 +02:00
if ( iarg . def_param_mode ! = ArgumentInterface : : CONSTANT ) {
2017-10-29 02:37:13 +01:00
arguments_sig + = " = null " ;
2020-05-16 04:03:05 +02:00
} else {
2017-10-29 02:37:13 +01:00
arguments_sig + = " = " + sformat ( iarg . default_argument , arg_type - > cs_type ) ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
}
}
2017-10-29 02:37:13 +01:00
icall_params + = " , " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . default_argument . size ( ) & & iarg . def_param_mode ! = ArgumentInterface : : CONSTANT ) {
// The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
// Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
2021-12-28 23:25:16 +01:00
String arg_or_defval_local = iarg . name ;
arg_or_defval_local + = " OrDefVal " ;
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
cs_in_statements < < INDENT2 < < arg_cs_type < < " " < < arg_or_defval_local < < " = " < < iarg . name ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2021-05-03 15:21:06 +02:00
cs_in_statements < < " .HasValue ? " ;
2020-05-16 04:03:05 +02:00
} else {
2021-05-03 15:21:06 +02:00
cs_in_statements < < " != null ? " ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
cs_in_statements < < iarg . name ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2021-05-03 15:21:06 +02:00
cs_in_statements < < " .Value : " ;
2020-05-16 04:03:05 +02:00
} else {
2021-05-03 15:21:06 +02:00
cs_in_statements < < " : " ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2022-02-09 00:56:10 +01:00
String cs_type = arg_cs_type ;
2021-07-25 13:46:31 +02:00
if ( cs_type . ends_with ( " [] " ) ) {
cs_type = cs_type . substr ( 0 , cs_type . length ( ) - 2 ) ;
}
String def_arg = sformat ( iarg . default_argument , cs_type ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
cs_in_statements < < def_arg < < " ; \n " ;
2017-10-02 23:24:00 +02:00
2021-12-28 23:25:16 +01:00
if ( arg_type - > cs_in . size ( ) ) {
cs_in_statements < < sformat ( arg_type - > cs_in , arg_type - > c_type , arg_or_defval_local ,
2022-02-27 21:57:50 +01:00
String ( ) , String ( ) , String ( ) , INDENT2 ) ;
2021-12-28 23:25:16 +01:00
}
if ( arg_type - > cs_in_expr . is_empty ( ) ) {
icall_params + = arg_or_defval_local ;
} else {
icall_params + = sformat ( arg_type - > cs_in_expr , arg_or_defval_local , arg_type - > c_type ) ;
}
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
// Apparently the name attribute must not include the @
String param_tag_name = iarg . name . begins_with ( " @ " ) ? iarg . name . substr ( 1 , iarg . name . length ( ) ) : iarg . name ;
2021-07-25 13:46:31 +02:00
// Escape < and > in the attribute default value
String param_def_arg = def_arg . replacen ( " < " , " < " ) . replacen ( " > " , " > " ) ;
2019-03-18 21:59:35 +01:00
2022-02-09 00:56:10 +01:00
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> " ) ;
2017-10-29 02:37:13 +01:00
} else {
2021-12-28 23:25:16 +01:00
if ( arg_type - > cs_in . size ( ) ) {
cs_in_statements < < sformat ( arg_type - > cs_in , arg_type - > c_type , iarg . name ,
2022-02-27 21:57:50 +01:00
String ( ) , String ( ) , String ( ) , INDENT2 ) ;
2021-12-28 23:25:16 +01:00
}
icall_params + = arg_type - > cs_in_expr . is_empty ( ) ? iarg . name : sformat ( arg_type - > cs_in_expr , iarg . name , arg_type - > c_type ) ;
2017-10-29 02:37:13 +01:00
}
2021-05-03 15:21:06 +02:00
2021-12-28 23:25:16 +01:00
cs_in_expr_is_unsafe | = arg_type - > cs_in_expr_is_unsafe ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Generate method
{
2018-09-04 05:40:41 +02:00
if ( ! p_imethod . is_virtual & & ! p_imethod . requires_object_call ) {
2021-12-28 23:25:16 +01:00
p_output < < MEMBER_BEGIN " [DebuggerBrowsable(DebuggerBrowsableState.Never)] \n "
2022-02-27 21:57:50 +01:00
< < INDENT1 " private static readonly IntPtr " < < method_bind_field < < " = " ;
2021-05-03 15:21:06 +02:00
if ( p_itype . is_singleton ) {
// Singletons are static classes. They don't derive Godot.Object,
// so we need to specify the type to call the static method.
p_output < < " Object. " ;
}
2022-08-25 23:07:20 +08:00
p_output < < ICALL_CLASSDB_GET_METHOD " ( " BINDINGS_NATIVE_NAME_FIELD " , MethodName. "
< < p_imethod . proxy_name
< < " ); \n " ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_imethod . method_doc & & p_imethod . method_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( p_imethod . method_doc - > description ) , & p_itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2020-01-02 18:09:59 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// </summary> " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2020-01-02 18:09:59 +01:00
if ( default_args_doc . get_string_length ( ) ) {
p_output . append ( default_args_doc . as_string ( ) ) ;
}
2019-04-25 17:11:01 +02:00
if ( p_imethod . is_deprecated ) {
2020-12-15 12:04:21 +00:00
if ( p_imethod . deprecation_message . is_empty ( ) ) {
2019-11-07 09:44:15 +01:00
WARN_PRINT ( " An empty deprecation message is discouraged. Method: ' " + p_imethod . proxy_name + " '. " ) ;
2020-05-16 04:03:05 +02:00
}
2019-04-25 17:11:01 +02:00
p_output . append ( MEMBER_BEGIN " [Obsolete( \" " ) ;
p_output . append ( p_imethod . deprecation_message ) ;
p_output . append ( " \" )] " ) ;
}
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN ) ;
p_output . append ( p_imethod . is_internal ? " internal " : " public " ) ;
2017-10-02 23:24:00 +02:00
2022-03-21 01:25:50 +01:00
if ( p_itype . is_singleton | | p_imethod . is_static ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " static " ) ;
2017-10-29 02:37:13 +01:00
} else if ( p_imethod . is_virtual ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " virtual " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2021-12-28 23:25:16 +01:00
if ( cs_in_expr_is_unsafe ) {
2021-05-03 15:21:06 +02:00
p_output . append ( " unsafe " ) ;
}
2022-02-09 00:56:10 +01:00
String return_cs_type = return_type - > cs_type + _get_generic_type_parameters ( * return_type , p_imethod . return_type . generic_type_parameters ) ;
p_output . append ( return_cs_type + " " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( p_imethod . proxy_name + " ( " ) ;
2022-02-27 21:57:50 +01:00
p_output . append ( arguments_sig + " ) \n " OPEN_BLOCK_L1 ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_imethod . is_virtual ) {
// Godot virtual method must be overridden, therefore we return a default value by default.
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
if ( return_type - > cname = = name_cache . type_void ) {
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK_L1 ) ;
2017-10-29 02:37:13 +01:00
} else {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT2 " return default; \n " CLOSE_BLOCK_L1 ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return OK ; // Won't increment method bind count
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_imethod . requires_object_call ) {
// Fallback to Godot's object.Call(string, params)
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT2 CS_METHOD_CALL " ( \" " ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( p_imethod . name ) ;
p_output . append ( " \" " ) ;
2017-10-02 23:24:00 +02:00
2021-07-25 18:45:00 +02:00
for ( const ArgumentInterface & iarg : p_imethod . arguments ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " , " ) ;
2021-07-25 18:45:00 +02:00
p_output . append ( iarg . name ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
p_output . append ( " ); \n " CLOSE_BLOCK_L1 ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return OK ; // Won't increment method bind count
}
2017-10-02 23:24:00 +02:00
2022-05-13 15:04:37 +02:00
HashMap < const MethodInterface * , const InternalCall * > : : ConstIterator match = method_icalls_map . find ( & p_imethod ) ;
2017-10-29 02:37:13 +01:00
ERR_FAIL_NULL_V ( match , ERR_BUG ) ;
2017-10-02 23:24:00 +02:00
2022-05-13 15:04:37 +02:00
const InternalCall * im_icall = match - > value ;
2017-10-02 23:24:00 +02:00
2018-02-22 13:13:51 +01:00
String im_call = im_icall - > editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS ;
2019-11-14 21:48:49 +01:00
im_call + = " . " ;
im_call + = im_icall - > name ;
2017-10-02 23:24:00 +02:00
2021-12-28 23:25:16 +01:00
if ( p_imethod . arguments . size ( ) & & cs_in_statements . get_string_length ( ) > 0 ) {
2021-05-03 15:21:06 +02:00
p_output . append ( cs_in_statements . as_string ( ) ) ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
if ( return_type - > cname = = name_cache . type_void ) {
2022-02-27 21:57:50 +01:00
p_output < < INDENT2 < < im_call < < " ( " < < icall_params < < " ); \n " ;
2020-12-15 12:04:21 +00:00
} else if ( return_type - > cs_out . is_empty ( ) ) {
2022-02-27 21:57:50 +01:00
p_output < < INDENT2 " return " < < im_call < < " ( " < < icall_params < < " ); \n " ;
2017-10-29 02:37:13 +01:00
} else {
2021-05-03 15:21:06 +02:00
p_output . append ( sformat ( return_type - > cs_out , im_call , icall_params ,
2022-02-27 21:57:50 +01:00
return_cs_type , return_type - > c_type_out , String ( ) , INDENT2 ) ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( " \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK_L1 ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
p_method_bind_count + + ;
2019-04-19 01:10:08 +02:00
2017-10-29 02:37:13 +01:00
return OK ;
}
2017-10-02 23:24:00 +02:00
2020-03-14 19:20:17 +01:00
Error BindingsGenerator : : _generate_cs_signal ( const BindingsGenerator : : TypeInterface & p_itype , const BindingsGenerator : : SignalInterface & p_isignal , StringBuilder & p_output ) {
String arguments_sig ;
// Retrieve information from the arguments
2021-07-25 18:45:00 +02:00
const ArgumentInterface & first = p_isignal . arguments . front ( ) - > get ( ) ;
for ( const ArgumentInterface & iarg : p_isignal . arguments ) {
2021-05-03 15:21:06 +02:00
const TypeInterface * arg_type = _get_type_or_null ( iarg . type ) ;
ERR_FAIL_NULL_V ( arg_type , ERR_BUG ) ; // Argument type not found
2020-03-14 19:20:17 +01:00
2020-05-16 04:03:05 +02:00
ERR_FAIL_COND_V_MSG ( arg_type - > is_singleton , ERR_BUG ,
2021-01-09 00:14:25 +01:00
" Argument type is a singleton: ' " + iarg . name + " ' of signal ' " + p_itype . name + " . " + p_isignal . name + " '. " ) ;
if ( p_itype . api_type = = ClassDB : : API_CORE ) {
ERR_FAIL_COND_V_MSG ( arg_type - > api_type = = ClassDB : : API_EDITOR , ERR_BUG ,
" Argument ' " + iarg . name + " ' of signal ' " + p_itype . name + " . " + p_isignal . name + " ' has type ' " +
arg_type - > name + " ' from the editor API. Core API cannot have dependencies on the editor API. " ) ;
}
2020-05-16 04:03:05 +02:00
2020-03-14 19:20:17 +01:00
// Add the current arguments to the signature
2021-07-25 18:45:00 +02:00
if ( & iarg ! = & first ) {
2020-03-14 19:20:17 +01:00
arguments_sig + = " , " ;
2020-05-16 04:03:05 +02:00
}
2020-03-14 19:20:17 +01:00
arguments_sig + = arg_type - > cs_type ;
arguments_sig + = " " ;
arguments_sig + = iarg . name ;
}
// Generate signal
{
if ( p_isignal . method_doc & & p_isignal . method_doc - > description . size ( ) ) {
String xml_summary = bbcode_to_xml ( fix_doc_description ( p_isignal . method_doc - > description ) , & p_itype ) ;
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
if ( summary_lines . size ( ) ) {
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// " ) ;
2020-03-14 19:20:17 +01:00
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
}
2022-02-27 21:57:50 +01:00
p_output . append ( INDENT1 " /// </summary> " ) ;
2020-03-14 19:20:17 +01:00
}
}
if ( p_isignal . is_deprecated ) {
2020-12-15 12:04:21 +00:00
if ( p_isignal . deprecation_message . is_empty ( ) ) {
2020-03-14 19:20:17 +01:00
WARN_PRINT ( " An empty deprecation message is discouraged. Signal: ' " + p_isignal . proxy_name + " '. " ) ;
2020-05-16 04:03:05 +02:00
}
2020-03-14 19:20:17 +01:00
p_output . append ( MEMBER_BEGIN " [Obsolete( \" " ) ;
p_output . append ( p_isignal . deprecation_message ) ;
p_output . append ( " \" )] " ) ;
}
String delegate_name = p_isignal . proxy_name ;
2022-08-26 00:49:14 +02:00
delegate_name + = " EventHandler " ; // Delegate name is [SignalName]EventHandler
2020-03-14 19:20:17 +01:00
// Generate delegate
p_output . append ( MEMBER_BEGIN " public delegate void " ) ;
p_output . append ( delegate_name ) ;
p_output . append ( " ( " ) ;
p_output . append ( arguments_sig ) ;
p_output . append ( " ); \n " ) ;
// TODO:
// Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
// Generate event
2022-08-26 00:49:14 +02:00
p_output . append ( MEMBER_BEGIN " public " ) ;
2020-03-14 19:20:17 +01:00
2020-05-16 04:03:05 +02:00
if ( p_itype . is_singleton ) {
2020-03-14 19:20:17 +01:00
p_output . append ( " static " ) ;
2020-05-16 04:03:05 +02:00
}
2020-03-14 19:20:17 +01:00
p_output . append ( " event " ) ;
p_output . append ( delegate_name ) ;
p_output . append ( " " ) ;
p_output . append ( p_isignal . proxy_name ) ;
2022-02-27 21:57:50 +01:00
p_output . append ( " \n " OPEN_BLOCK_L1 INDENT2 ) ;
2020-03-14 19:20:17 +01:00
2020-05-16 04:03:05 +02:00
if ( p_itype . is_singleton ) {
2022-08-25 23:07:20 +08:00
p_output . append ( " add => " CS_PROPERTY_SINGLETON " .Connect(SignalName. " ) ;
2020-05-16 04:03:05 +02:00
} else {
2022-08-25 23:07:20 +08:00
p_output . append ( " add => Connect(SignalName. " ) ;
2020-05-16 04:03:05 +02:00
}
2020-03-14 19:20:17 +01:00
2022-08-25 23:07:20 +08:00
p_output . append ( p_isignal . proxy_name ) ;
2020-03-14 19:20:17 +01:00
p_output . append ( " , new Callable(value)); \n " ) ;
2020-05-16 04:03:05 +02:00
if ( p_itype . is_singleton ) {
2022-08-25 23:07:20 +08:00
p_output . append ( INDENT2 " remove => " CS_PROPERTY_SINGLETON " .Disconnect(SignalName. " ) ;
2020-05-16 04:03:05 +02:00
} else {
2022-08-25 23:07:20 +08:00
p_output . append ( INDENT2 " remove => Disconnect(SignalName. " ) ;
2020-05-16 04:03:05 +02:00
}
2020-03-14 19:20:17 +01:00
2022-08-25 23:07:20 +08:00
p_output . append ( p_isignal . proxy_name ) ;
2020-03-14 19:20:17 +01:00
p_output . append ( " , new Callable(value)); \n " ) ;
2022-02-27 21:57:50 +01:00
p_output . append ( CLOSE_BLOCK_L1 ) ;
2020-03-14 19:20:17 +01:00
}
return OK ;
}
2021-05-03 15:21:06 +02:00
Error BindingsGenerator : : _generate_cs_native_calls ( const InternalCall & p_icall , StringBuilder & r_output ) {
bool ret_void = p_icall . return_type . cname = = name_cache . type_void ;
2018-09-04 05:40:41 +02:00
2021-05-03 15:21:06 +02:00
const TypeInterface * return_type = _get_type_or_null ( p_icall . return_type ) ;
ERR_FAIL_NULL_V ( return_type , ERR_BUG ) ; // Return type not found
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
StringBuilder c_func_sig ;
StringBuilder c_in_statements ;
StringBuilder c_args_var_content ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
c_func_sig < < " IntPtr " CS_PARAM_METHODBIND ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
if ( ! p_icall . is_static ) {
c_func_sig + = " , IntPtr " CS_PARAM_INSTANCE ;
2017-10-02 23:24:00 +02:00
}
2017-10-29 02:37:13 +01:00
// Get arguments information
int i = 0 ;
2021-05-03 15:21:06 +02:00
for ( const TypeReference & arg_type_ref : p_icall . argument_types ) {
const TypeInterface * arg_type = _get_type_or_null ( arg_type_ref ) ;
ERR_FAIL_NULL_V ( arg_type , ERR_BUG ) ; // Return type not found
2017-10-29 02:37:13 +01:00
String c_param_name = " arg " + itos ( i + 1 ) ;
2021-05-03 15:21:06 +02:00
if ( p_icall . is_vararg ) {
if ( i < p_icall . get_arguments_count ( ) - 1 ) {
2021-12-28 23:25:16 +01:00
String c_in_vararg = arg_type - > c_in_vararg ;
2021-05-03 15:21:06 +02:00
if ( arg_type - > is_object_type ) {
2022-02-27 21:57:46 +01:00
c_in_vararg = " %5using godot_variant %1_in = VariantUtils.CreateFromGodotObjectPtr(%1); \n " ;
2021-05-03 15:21:06 +02:00
}
ERR_FAIL_COND_V_MSG ( c_in_vararg . is_empty ( ) , ERR_BUG ,
" VarArg support not implemented for parameter type: " + arg_type - > name ) ;
c_in_statements
< < sformat ( c_in_vararg , return_type - > c_type , c_param_name ,
2022-02-27 21:57:50 +01:00
String ( ) , String ( ) , String ( ) , INDENT3 )
< < INDENT3 C_LOCAL_PTRCALL_ARGS " [ " < < itos ( i )
2021-05-03 15:21:06 +02:00
< < " ] = new IntPtr(& " < < c_param_name < < " _in); \n " ;
2017-10-29 02:37:13 +01:00
}
} else {
2020-05-16 04:03:05 +02:00
if ( i > 0 ) {
2021-05-03 15:21:06 +02:00
c_args_var_content < < " , " ;
2020-05-16 04:03:05 +02:00
}
if ( arg_type - > c_in . size ( ) ) {
2021-05-03 15:21:06 +02:00
c_in_statements < < sformat ( arg_type - > c_in , arg_type - > c_type , c_param_name ,
2022-02-27 21:57:50 +01:00
String ( ) , String ( ) , String ( ) , INDENT2 ) ;
2020-05-16 04:03:05 +02:00
}
2021-05-03 15:21:06 +02:00
c_args_var_content < < sformat ( arg_type - > c_arg_in , c_param_name ) ;
2017-10-29 02:37:13 +01:00
}
2021-05-03 15:21:06 +02:00
c_func_sig < < " , " < < arg_type - > c_type_in < < " " < < c_param_name ;
2017-10-29 02:37:13 +01:00
i + + ;
}
2021-05-03 15:21:06 +02:00
String icall_method = p_icall . name ;
2019-11-14 21:48:49 +01:00
2021-05-03 15:21:06 +02:00
// Generate icall function
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
r_output < < MEMBER_BEGIN " internal static unsafe " < < ( ret_void ? " void " : return_type - > c_type_out ) < < " "
< < icall_method < < " ( " < < c_func_sig . as_string ( ) < < " ) " OPEN_BLOCK ;
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
if ( ! ret_void & & ( ! p_icall . is_vararg | | return_type - > cname ! = name_cache . type_Variant ) ) {
String ptrcall_return_type ;
String initialization ;
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
if ( return_type - > is_object_type ) {
ptrcall_return_type = return_type - > is_ref_counted ? " godot_ref " : return_type - > c_type ;
initialization = " = default " ;
} else {
ptrcall_return_type = return_type - > c_type ;
2020-05-16 04:03:05 +02:00
}
2017-10-29 02:37:13 +01:00
2022-02-27 21:57:50 +01:00
r_output < < INDENT2 ;
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
if ( return_type - > is_ref_counted | | return_type - > c_type_is_disposable_struct ) {
r_output < < " using " ;
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
if ( initialization . is_empty ( ) ) {
initialization = " = default " ;
2019-01-18 00:41:41 +01:00
}
2021-05-03 15:21:06 +02:00
} else if ( return_type - > c_ret_needs_default_initialization ) {
initialization = " = default " ;
}
2019-01-18 00:41:41 +01:00
2021-05-03 15:21:06 +02:00
r_output < < ptrcall_return_type < < " " C_LOCAL_RET < < initialization < < " ; \n " ;
}
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
if ( ! p_icall . is_static ) {
2022-02-27 21:57:50 +01:00
r_output < < INDENT2 " if ( " CS_PARAM_INSTANCE " == IntPtr.Zero) \n "
< < INDENT3 " throw new ArgumentNullException(nameof( " CS_PARAM_INSTANCE " )); \n " ;
2021-05-03 15:21:06 +02:00
}
2019-11-14 21:48:49 +01:00
2021-05-03 15:21:06 +02:00
String argc_str = itos ( p_icall . get_arguments_count ( ) ) ;
2019-11-14 21:48:49 +01:00
2021-05-03 15:21:06 +02:00
auto generate_call_and_return_stmts = [ & ] ( const char * base_indent ) {
if ( p_icall . is_vararg ) {
// MethodBind Call
r_output < < base_indent ;
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
// VarArg methods always return Variant, but there are some cases in which MethodInfo provides
// a specific return type. We trust this information is valid. We need a temporary local to keep
// the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
// it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
// Alternatively, we could just return Variant, but that would result in a worse API.
2017-10-29 02:37:13 +01:00
2019-01-18 00:41:41 +01:00
if ( ! ret_void ) {
if ( return_type - > cname ! = name_cache . type_Variant ) {
2021-05-03 15:21:06 +02:00
r_output < < " using godot_variant " < < C_LOCAL_VARARG_RET " = " ;
2019-01-18 00:41:41 +01:00
} else {
2021-05-03 15:21:06 +02:00
r_output < < " using godot_variant " < < C_LOCAL_RET " = " ;
2019-01-18 00:41:41 +01:00
}
}
2017-10-29 02:37:13 +01:00
2021-05-03 15:21:06 +02:00
r_output < < C_CLASS_NATIVE_FUNCS " .godotsharp_method_bind_call( "
< < CS_PARAM_METHODBIND " , " < < ( p_icall . is_static ? " IntPtr.Zero " : CS_PARAM_INSTANCE )
< < " , " < < ( p_icall . get_arguments_count ( ) ? " (godot_variant**) " C_LOCAL_PTRCALL_ARGS : " null " )
2021-12-28 23:25:16 +01:00
< < " , total_length, out _); \n " ;
2019-01-18 00:41:41 +01:00
2020-01-02 18:02:45 +01:00
if ( ! ret_void ) {
if ( return_type - > cname ! = name_cache . type_Variant ) {
2021-05-03 15:21:06 +02:00
if ( return_type - > cname = = name_cache . enum_Error ) {
2021-12-28 23:25:16 +01:00
r_output < < base_indent < < C_LOCAL_RET " = VariantUtils.ConvertToInt64( " C_LOCAL_VARARG_RET " ); \n " ;
2021-05-03 15:21:06 +02:00
} else {
// TODO: Use something similar to c_in_vararg (see usage above, with error if not implemented)
CRASH_NOW_MSG ( " Custom VarArg return type not implemented: " + return_type - > name ) ;
r_output < < base_indent < < C_LOCAL_RET " = " C_LOCAL_VARARG_RET " ; \n " ;
}
2020-01-02 18:02:45 +01:00
}
2019-01-18 00:41:41 +01:00
}
2017-10-29 02:37:13 +01:00
} else {
2021-05-03 15:21:06 +02:00
// MethodBind PtrCall
r_output < < base_indent < < C_CLASS_NATIVE_FUNCS " .godotsharp_method_bind_ptrcall( "
< < CS_PARAM_METHODBIND " , " < < ( p_icall . is_static ? " IntPtr.Zero " : CS_PARAM_INSTANCE )
< < " , " < < ( p_icall . get_arguments_count ( ) ? C_LOCAL_PTRCALL_ARGS : " null " )
< < " , " < < ( ! ret_void ? " & " C_LOCAL_RET " ); \n " : " null); \n " ) ;
2017-10-29 02:37:13 +01:00
}
2021-05-03 15:21:06 +02:00
// Return statement
2017-10-29 02:37:13 +01:00
if ( ! ret_void ) {
2020-12-15 12:04:21 +00:00
if ( return_type - > c_out . is_empty ( ) ) {
2021-05-03 15:21:06 +02:00
r_output < < base_indent < < " return " C_LOCAL_RET " ; \n " ;
2019-11-14 21:48:49 +01:00
} else {
2021-05-03 15:21:06 +02:00
r_output < < sformat ( return_type - > c_out , return_type - > c_type_out , C_LOCAL_RET ,
return_type - > name , String ( ) , String ( ) , base_indent ) ;
2019-11-14 21:48:49 +01:00
}
2017-10-29 02:37:13 +01:00
}
2021-05-03 15:21:06 +02:00
} ;
if ( p_icall . get_arguments_count ( ) ) {
if ( p_icall . is_vararg ) {
String vararg_arg = " arg " + argc_str ;
String real_argc_str = itos ( p_icall . get_arguments_count ( ) - 1 ) ; // Arguments count without vararg
p_icall . get_arguments_count ( ) ;
2022-02-27 21:57:50 +01:00
r_output < < INDENT2 " int vararg_length = " < < vararg_arg < < " .Length; \n "
< < INDENT2 " int total_length = " < < real_argc_str < < " + vararg_length; \n " ;
2021-05-03 15:21:06 +02:00
2022-02-27 21:57:50 +01:00
r_output < < INDENT2 " Span<godot_variant.movable> varargs_span = vararg_length <= VarArgsSpanThreshold ? \n "
< < INDENT3 " stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() : \n "
< < INDENT3 " new godot_variant.movable[vararg_length]; \n " ;
2021-05-03 15:21:06 +02:00
2022-02-27 21:57:50 +01:00
r_output < < INDENT2 " Span<IntPtr> " C_LOCAL_PTRCALL_ARGS " _span = total_length <= VarArgsSpanThreshold ? \n "
< < INDENT3 " stackalloc IntPtr[VarArgsSpanThreshold] : \n "
< < INDENT3 " new IntPtr[total_length]; \n " ;
2021-05-03 15:21:06 +02:00
2022-02-27 21:57:50 +01:00
r_output < < INDENT2 " using var variantSpanDisposer = new VariantSpanDisposer(varargs_span); \n " ;
2021-05-03 15:21:06 +02:00
2022-07-28 17:41:49 +02:00
r_output < < INDENT2 " fixed (godot_variant.movable* varargs = &MemoryMarshal.GetReference(varargs_span)) \n "
2022-02-27 21:57:50 +01:00
< < INDENT2 " fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = "
2021-05-03 15:21:06 +02:00
" &MemoryMarshal.GetReference( " C_LOCAL_PTRCALL_ARGS " _span)) \n "
2022-02-27 21:57:50 +01:00
< < OPEN_BLOCK_L2 ;
2021-05-03 15:21:06 +02:00
r_output < < c_in_statements . as_string ( ) ;
2017-10-29 02:37:13 +01:00
2022-02-27 21:57:50 +01:00
r_output < < INDENT3 " for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
2022-07-28 17:41:49 +02:00
< < INDENT4 " varargs[i] = " < < vararg_arg < < " [i].NativeVar; \n "
2022-02-27 21:57:50 +01:00
< < INDENT4 C_LOCAL_PTRCALL_ARGS " [ " < < real_argc_str < < " + i] = new IntPtr(&varargs[i]); \n "
< < CLOSE_BLOCK_L3 ;
2017-10-29 02:37:13 +01:00
2022-02-27 21:57:50 +01:00
generate_call_and_return_stmts ( INDENT3 ) ;
2021-05-03 15:21:06 +02:00
2022-02-27 21:57:50 +01:00
r_output < < CLOSE_BLOCK_L2 ;
2021-05-03 15:21:06 +02:00
} else {
r_output < < c_in_statements . as_string ( ) ;
2022-02-27 21:57:50 +01:00
r_output < < INDENT2 " void** " C_LOCAL_PTRCALL_ARGS " = stackalloc void*[ "
2021-05-03 15:21:06 +02:00
< < argc_str < < " ] { " < < c_args_var_content . as_string ( ) < < " }; \n " ;
2022-02-27 21:57:50 +01:00
generate_call_and_return_stmts ( INDENT2 ) ;
2020-05-16 04:03:05 +02:00
}
2021-05-03 15:21:06 +02:00
} else {
2022-02-27 21:57:50 +01:00
generate_call_and_return_stmts ( INDENT2 ) ;
2017-10-29 02:37:13 +01:00
}
2022-02-27 21:57:50 +01:00
r_output < < CLOSE_BLOCK_L1 ;
2021-05-03 15:21:06 +02:00
return OK ;
}
Error BindingsGenerator : : _save_file ( const String & p_path , const StringBuilder & p_content ) {
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V_MSG ( file . is_null ( ) , ERR_FILE_CANT_WRITE , " Cannot open file: ' " + p_path + " '. " ) ;
file - > store_string ( p_content . as_string ( ) ) ;
2017-10-29 02:37:13 +01:00
return OK ;
}
2018-04-28 22:25:25 +02:00
const BindingsGenerator : : TypeInterface * BindingsGenerator : : _get_type_or_null ( const TypeReference & p_typeref ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , TypeInterface > : : ConstIterator builtin_type_match = builtin_types . find ( p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( builtin_type_match ) {
2022-05-13 15:04:37 +02:00
return & builtin_type_match - > value ;
2020-05-16 04:03:05 +02:00
}
2017-12-24 03:17:48 +01:00
2022-05-13 15:04:37 +02:00
HashMap < StringName , TypeInterface > : : ConstIterator obj_type_match = obj_types . find ( p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( obj_type_match ) {
2022-05-08 10:09:19 +02:00
return & obj_type_match - > value ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
if ( p_typeref . is_enum ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , TypeInterface > : : ConstIterator enum_match = enum_types . find ( p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
2020-05-16 04:03:05 +02:00
if ( enum_match ) {
2022-05-13 15:04:37 +02:00
return & enum_match - > value ;
2020-05-16 04:03:05 +02:00
}
2018-04-28 22:25:25 +02:00
// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
2022-05-13 15:04:37 +02:00
HashMap < StringName , TypeInterface > : : ConstIterator int_match = builtin_types . find ( name_cache . type_int ) ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_NULL_V ( int_match , nullptr ) ;
2022-05-13 15:04:37 +02:00
return & int_match - > value ;
2018-04-28 22:25:25 +02:00
}
2017-10-02 23:24:00 +02:00
2020-04-02 01:20:12 +02:00
return nullptr ;
2017-10-02 23:24:00 +02:00
}
2022-02-09 00:56:10 +01:00
const String BindingsGenerator : : _get_generic_type_parameters ( const TypeInterface & p_itype , const List < TypeReference > & p_generic_type_parameters ) {
if ( p_generic_type_parameters . is_empty ( ) ) {
return " " ;
}
ERR_FAIL_COND_V_MSG ( p_itype . type_parameter_count ! = p_generic_type_parameters . size ( ) , " " ,
" Generic type parameter count mismatch for type ' " + p_itype . name + " '. " +
" Found " + itos ( p_generic_type_parameters . size ( ) ) + " , but requires " +
itos ( p_itype . type_parameter_count ) + " . " ) ;
int i = 0 ;
String params = " < " ;
for ( const TypeReference & param_type : p_generic_type_parameters ) {
2021-05-03 15:21:06 +02:00
const TypeInterface * param_itype = _get_type_or_null ( param_type ) ;
ERR_FAIL_NULL_V ( param_itype , " " ) ;
2022-02-09 00:56:10 +01:00
ERR_FAIL_COND_V_MSG ( param_itype - > is_singleton , " " ,
" Generic type parameter is a singleton: ' " + param_itype - > name + " '. " ) ;
if ( p_itype . api_type = = ClassDB : : API_CORE ) {
ERR_FAIL_COND_V_MSG ( param_itype - > api_type = = ClassDB : : API_EDITOR , " " ,
" Generic type parameter ' " + param_itype - > name + " ' has type from the editor API. " +
" Core API cannot have dependencies on the editor API. " ) ;
}
params + = param_itype - > cs_type ;
if ( i < p_generic_type_parameters . size ( ) - 1 ) {
params + = " , " ;
}
i + + ;
}
params + = " > " ;
return params ;
}
2022-09-01 00:40:59 +02:00
StringName BindingsGenerator : : _get_type_name_from_meta ( Variant : : Type p_type , GodotTypeInfo : : Metadata p_meta ) {
if ( p_type = = Variant : : INT ) {
return _get_int_type_name_from_meta ( p_meta ) ;
} else if ( p_type = = Variant : : FLOAT ) {
return _get_float_type_name_from_meta ( p_meta ) ;
} else {
return Variant : : get_type_name ( p_type ) ;
}
}
2019-04-25 20:24:48 +02:00
StringName BindingsGenerator : : _get_int_type_name_from_meta ( GodotTypeInfo : : Metadata p_meta ) {
switch ( p_meta ) {
case GodotTypeInfo : : METADATA_INT_IS_INT8 :
return " sbyte " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_INT16 :
return " short " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_INT32 :
return " int " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_INT64 :
return " long " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT8 :
return " byte " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT16 :
return " ushort " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT32 :
return " uint " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT64 :
return " ulong " ;
break ;
default :
2022-09-01 00:40:59 +02:00
// Assume INT64
return " long " ;
2019-04-25 20:24:48 +02:00
}
}
StringName BindingsGenerator : : _get_float_type_name_from_meta ( GodotTypeInfo : : Metadata p_meta ) {
switch ( p_meta ) {
case GodotTypeInfo : : METADATA_REAL_IS_FLOAT :
return " float " ;
break ;
case GodotTypeInfo : : METADATA_REAL_IS_DOUBLE :
return " double " ;
break ;
default :
2022-09-01 00:40:59 +02:00
// Assume FLOAT64
2019-04-25 20:24:48 +02:00
return " double " ;
}
}
2020-05-16 04:03:05 +02:00
bool BindingsGenerator : : _arg_default_value_is_assignable_to_type ( const Variant & p_val , const TypeInterface & p_arg_type ) {
if ( p_arg_type . name = = name_cache . type_Variant ) {
// Variant can take anything
return true ;
}
switch ( p_val . get_type ( ) ) {
case Variant : : NIL :
return p_arg_type . is_object_type | |
2021-10-28 15:19:35 +02:00
name_cache . is_nullable_type ( p_arg_type . name ) ;
2020-05-16 04:03:05 +02:00
case Variant : : BOOL :
return p_arg_type . name = = name_cache . type_bool ;
case Variant : : INT :
return p_arg_type . name = = name_cache . type_sbyte | |
2021-10-28 15:19:35 +02:00
p_arg_type . name = = name_cache . type_short | |
p_arg_type . name = = name_cache . type_int | |
p_arg_type . name = = name_cache . type_byte | |
p_arg_type . name = = name_cache . type_ushort | |
p_arg_type . name = = name_cache . type_uint | |
p_arg_type . name = = name_cache . type_long | |
p_arg_type . name = = name_cache . type_ulong | |
p_arg_type . name = = name_cache . type_float | |
p_arg_type . name = = name_cache . type_double | |
p_arg_type . is_enum ;
2020-05-16 04:03:05 +02:00
case Variant : : FLOAT :
return p_arg_type . name = = name_cache . type_float | |
2021-10-28 15:19:35 +02:00
p_arg_type . name = = name_cache . type_double ;
2020-05-16 04:03:05 +02:00
case Variant : : STRING :
case Variant : : STRING_NAME :
return p_arg_type . name = = name_cache . type_String | |
2021-10-28 15:19:35 +02:00
p_arg_type . name = = name_cache . type_StringName | |
p_arg_type . name = = name_cache . type_NodePath ;
2020-05-16 04:03:05 +02:00
case Variant : : NODE_PATH :
return p_arg_type . name = = name_cache . type_NodePath ;
case Variant : : TRANSFORM2D :
2021-04-28 03:36:08 -04:00
case Variant : : TRANSFORM3D :
2020-05-16 04:03:05 +02:00
case Variant : : BASIS :
2021-01-20 07:02:02 +00:00
case Variant : : QUATERNION :
2020-05-16 04:03:05 +02:00
case Variant : : PLANE :
case Variant : : AABB :
case Variant : : COLOR :
case Variant : : VECTOR2 :
case Variant : : RECT2 :
case Variant : : VECTOR3 :
2020-11-09 14:53:05 +01:00
case Variant : : RID :
2020-05-16 04:03:05 +02:00
case Variant : : PACKED_BYTE_ARRAY :
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
case Variant : : PACKED_STRING_ARRAY :
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : PACKED_VECTOR3_ARRAY :
case Variant : : PACKED_COLOR_ARRAY :
case Variant : : CALLABLE :
case Variant : : SIGNAL :
return p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) ;
2021-05-03 15:21:06 +02:00
case Variant : : ARRAY :
return p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) | | p_arg_type . cname = = name_cache . type_Array_generic ;
case Variant : : DICTIONARY :
return p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) | | p_arg_type . cname = = name_cache . type_Dictionary_generic ;
2020-05-16 04:03:05 +02:00
case Variant : : OBJECT :
return p_arg_type . is_object_type ;
case Variant : : VECTOR2I :
return p_arg_type . name = = name_cache . type_Vector2 | |
2021-10-28 15:19:35 +02:00
p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) ;
2020-05-16 04:03:05 +02:00
case Variant : : RECT2I :
return p_arg_type . name = = name_cache . type_Rect2 | |
2021-10-28 15:19:35 +02:00
p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) ;
2020-05-16 04:03:05 +02:00
case Variant : : VECTOR3I :
return p_arg_type . name = = name_cache . type_Vector3 | |
2021-10-28 15:19:35 +02:00
p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) ;
2020-05-16 04:03:05 +02:00
default :
CRASH_NOW_MSG ( " Unexpected Variant type: " + itos ( p_val . get_type ( ) ) ) ;
break ;
}
return false ;
}
2019-09-24 15:09:54 +02:00
bool BindingsGenerator : : _populate_object_type_interfaces ( ) {
2017-10-02 23:24:00 +02:00
obj_types . clear ( ) ;
List < StringName > class_list ;
ClassDB : : get_class_list ( & class_list ) ;
class_list . sort_custom < StringName : : AlphCompare > ( ) ;
while ( class_list . size ( ) ) {
StringName type_cname = class_list . front ( ) - > get ( ) ;
ClassDB : : APIType api_type = ClassDB : : get_api_type ( type_cname ) ;
if ( api_type = = ClassDB : : API_NONE ) {
class_list . pop_front ( ) ;
continue ;
}
2022-03-18 17:28:48 +01:00
if ( ignored_types . has ( type_cname ) ) {
_log ( " Ignoring type '%s' because it's in the list of ignored types \n " , String ( type_cname ) . utf8 ( ) . get_data ( ) ) ;
class_list . pop_front ( ) ;
continue ;
}
2017-12-24 03:17:48 +01:00
if ( ! ClassDB : : is_class_exposed ( type_cname ) ) {
2019-08-09 03:39:45 +02:00
_log ( " Ignoring type '%s' because it's not exposed \n " , String ( type_cname ) . utf8 ( ) . get_data ( ) ) ;
2017-12-24 03:17:48 +01:00
class_list . pop_front ( ) ;
continue ;
}
2018-07-18 21:15:41 +02:00
if ( ! ClassDB : : is_class_enabled ( type_cname ) ) {
2019-08-09 03:39:45 +02:00
_log ( " Ignoring type '%s' because it's not enabled \n " , String ( type_cname ) . utf8 ( ) . get_data ( ) ) ;
2018-07-18 21:15:41 +02:00
class_list . pop_front ( ) ;
continue ;
}
2017-12-24 03:17:48 +01:00
ClassDB : : ClassInfo * class_info = ClassDB : : classes . getptr ( type_cname ) ;
2017-10-02 23:24:00 +02:00
TypeInterface itype = TypeInterface : : create_object_type ( type_cname , api_type ) ;
itype . base_name = ClassDB : : get_parent_class ( type_cname ) ;
2017-11-13 21:46:57 +01:00
itype . is_singleton = Engine : : get_singleton ( ) - > has_singleton ( itype . proxy_name ) ;
2019-07-08 15:13:57 +02:00
itype . is_instantiable = class_info - > creation_func & & ! itype . is_singleton ;
2021-06-04 18:03:15 +02:00
itype . is_ref_counted = ClassDB : : is_parent_class ( type_cname , name_cache . type_RefCounted ) ;
itype . memory_own = itype . is_ref_counted ;
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " (%1)VariantUtils.ConvertToGodotObject(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromGodotObject(%0) " ;
2021-05-03 15:21:06 +02:00
itype . c_out = " %5return " ;
2017-10-02 23:24:00 +02:00
itype . c_out + = C_METHOD_UNMANAGED_GET_MANAGED ;
2021-12-28 23:25:16 +01:00
itype . c_out + = itype . is_ref_counted ? " (%1.Reference); \n " : " (%1); \n " ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
itype . cs_type = itype . proxy_name ;
if ( itype . is_singleton ) {
2021-12-28 23:25:16 +01:00
itype . cs_in_expr = " Object. " CS_STATIC_METHOD_GETINSTANCE " ( " CS_PROPERTY_SINGLETON " ) " ;
2021-05-03 15:21:06 +02:00
} else {
2021-12-28 23:25:16 +01:00
itype . cs_in_expr = " Object. " CS_STATIC_METHOD_GETINSTANCE " (%0) " ;
2021-05-03 15:21:06 +02:00
}
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
itype . cs_out = " %5return (%2)%0(%1); " ;
itype . c_arg_in = " (void*)%s " ;
itype . c_type = " IntPtr " ;
2017-10-02 23:24:00 +02:00
itype . c_type_in = itype . c_type ;
2021-05-03 15:21:06 +02:00
itype . c_type_out = " Object " ;
2017-10-02 23:24:00 +02:00
2019-04-25 17:11:01 +02:00
// Populate properties
2017-12-29 02:18:46 +01:00
List < PropertyInfo > property_list ;
ClassDB : : get_property_list ( type_cname , & property_list , true ) ;
2022-05-13 15:04:37 +02:00
HashMap < StringName , StringName > accessor_methods ;
2017-12-29 02:18:46 +01:00
2021-07-15 23:45:57 -04:00
for ( const PropertyInfo & property : property_list ) {
2021-09-08 12:51:45 +02:00
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 ) ) {
2017-12-29 02:18:46 +01:00
continue ;
2020-05-16 04:03:05 +02:00
}
2017-12-29 02:18:46 +01:00
2020-05-09 20:54:07 +02:00
if ( property . name . find ( " / " ) > = 0 ) {
// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
continue ;
}
2017-12-29 02:18:46 +01:00
PropertyInterface iprop ;
iprop . cname = property . name ;
iprop . setter = ClassDB : : get_property_setter ( type_cname , iprop . cname ) ;
iprop . getter = ClassDB : : get_property_getter ( type_cname , iprop . cname ) ;
2020-05-16 04:03:05 +02:00
if ( iprop . setter ! = StringName ( ) ) {
2019-04-25 17:11:01 +02:00
accessor_methods [ iprop . setter ] = iprop . cname ;
2020-05-16 04:03:05 +02:00
}
if ( iprop . getter ! = StringName ( ) ) {
2019-04-25 17:11:01 +02:00
accessor_methods [ iprop . getter ] = iprop . cname ;
2020-05-16 04:03:05 +02:00
}
2019-04-25 17:11:01 +02:00
2017-12-29 02:18:46 +01:00
bool valid = false ;
iprop . index = ClassDB : : get_property_index ( type_cname , iprop . cname , & valid ) ;
2020-05-09 20:54:07 +02:00
ERR_FAIL_COND_V_MSG ( ! valid , false , " Invalid property: ' " + itype . name + " . " + String ( iprop . cname ) + " '. " ) ;
2017-12-29 02:18:46 +01:00
2019-03-18 21:59:35 +01:00
iprop . proxy_name = escape_csharp_keyword ( snake_to_pascal_case ( iprop . cname ) ) ;
2019-04-25 15:45:12 +02:00
// Prevent the property and its enclosing type from sharing the same name
2017-12-29 02:18:46 +01:00
if ( iprop . proxy_name = = itype . proxy_name ) {
2019-08-09 03:39:45 +02:00
_log ( " Name of property '%s' is ambiguous with the name of its enclosing class '%s'. Renaming property to '%s_' \n " ,
2019-04-25 15:45:12 +02:00
iprop . proxy_name . utf8 ( ) . get_data ( ) , itype . proxy_name . utf8 ( ) . get_data ( ) , iprop . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-12-29 02:18:46 +01:00
iprop . proxy_name + = " _ " ;
}
2020-04-02 01:20:12 +02:00
iprop . prop_doc = nullptr ;
2017-12-29 02:18:46 +01:00
for ( int i = 0 ; i < itype . class_doc - > properties . size ( ) ; i + + ) {
const DocData : : PropertyDoc & prop_doc = itype . class_doc - > properties [ i ] ;
if ( prop_doc . name = = iprop . cname ) {
iprop . prop_doc = & prop_doc ;
break ;
}
}
itype . properties . push_back ( iprop ) ;
}
2017-12-24 03:17:48 +01:00
// Populate methods
2017-10-02 23:24:00 +02:00
List < MethodInfo > virtual_method_list ;
ClassDB : : get_virtual_methods ( type_cname , & virtual_method_list , true ) ;
List < MethodInfo > method_list ;
ClassDB : : get_method_list ( type_cname , & method_list , true ) ;
method_list . sort ( ) ;
2021-07-25 18:45:00 +02:00
for ( const MethodInfo & method_info : method_list ) {
2017-10-02 23:24:00 +02:00
int argc = method_info . arguments . size ( ) ;
2020-12-15 12:04:21 +00:00
if ( method_info . name . is_empty ( ) ) {
2017-10-02 23:24:00 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
2019-05-24 00:40:16 +02:00
String cname = method_info . name ;
2020-05-16 04:03:05 +02:00
if ( blacklisted_methods . find ( itype . cname ) & & blacklisted_methods [ itype . cname ] . find ( cname ) ) {
2019-05-24 00:40:16 +02:00
continue ;
2020-05-16 04:03:05 +02:00
}
2019-05-24 00:40:16 +02:00
2017-10-02 23:24:00 +02:00
MethodInterface imethod ;
imethod . name = method_info . name ;
2019-05-24 00:40:16 +02:00
imethod . cname = cname ;
2017-10-02 23:24:00 +02:00
2022-03-21 01:25:50 +01:00
if ( method_info . flags & METHOD_FLAG_STATIC ) {
imethod . is_static = true ;
}
2020-05-16 04:03:05 +02:00
if ( method_info . flags & METHOD_FLAG_VIRTUAL ) {
2017-10-02 23:24:00 +02:00
imethod . is_virtual = true ;
2021-09-12 20:21:15 +02:00
itype . has_virtual_methods = true ;
2020-05-16 04:03:05 +02:00
}
2017-10-02 23:24:00 +02:00
PropertyInfo return_info = method_info . return_val ;
2020-04-02 01:20:12 +02:00
MethodBind * m = imethod . is_virtual ? nullptr : ClassDB : : get_method ( type_cname , method_info . name ) ;
2017-10-02 23:24:00 +02:00
imethod . is_vararg = m & & m - > is_vararg ( ) ;
if ( ! m & & ! imethod . is_virtual ) {
2019-09-24 15:09:54 +02:00
ERR_FAIL_COND_V_MSG ( ! virtual_method_list . find ( method_info ) , false ,
2019-08-09 03:39:45 +02:00
" Missing MethodBind for non-virtual method: ' " + itype . name + " . " + imethod . name + " '. " ) ;
// A virtual method without the virtual flag. This is a special case.
// There is no method bind, so let's fallback to Godot's object.Call(string, params)
imethod . requires_object_call = true ;
// The method Object.free is registered as a virtual method, but without the virtual flag.
// This is because this method is not supposed to be overridden, but called.
// We assume the return type is void.
imethod . return_type . cname = name_cache . type_void ;
2020-05-16 04:03:05 +02:00
// Actually, more methods like this may be added in the future, which could return
// something different. Let's put this check to notify us if that ever happens.
2019-08-09 03:39:45 +02:00
if ( itype . cname ! = name_cache . type_Object | | imethod . name ! = " free " ) {
2019-11-07 09:44:15 +01:00
WARN_PRINT ( " Notification: New unexpected virtual non-overridable method found. "
" We only expected Object.free, but found ' " +
2021-10-28 15:19:35 +02:00
itype . name + " . " + imethod . name + " '. " ) ;
2017-10-02 23:24:00 +02:00
}
2022-06-24 11:16:37 +02:00
} else if ( return_info . type = = Variant : : INT & & return_info . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = return_info . class_name ;
imethod . return_type . is_enum = true ;
2017-10-02 23:24:00 +02:00
} else if ( return_info . class_name ! = StringName ( ) ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = return_info . class_name ;
2020-05-16 04:03:05 +02:00
bool bad_reference_hint = ! imethod . is_virtual & & return_info . hint ! = PROPERTY_HINT_RESOURCE_TYPE & &
2021-10-28 15:19:35 +02:00
ClassDB : : is_parent_class ( return_info . class_name , name_cache . type_RefCounted ) ;
2020-05-16 04:03:05 +02:00
ERR_FAIL_COND_V_MSG ( bad_reference_hint , false ,
String ( ) + " Return type is reference but hint is not ' " _STR ( PROPERTY_HINT_RESOURCE_TYPE ) " '. " +
" Are you returning a reference type by pointer? Method: ' " + itype . name + " . " + imethod . name + " '. " ) ;
2022-02-09 00:56:10 +01:00
} else if ( return_info . type = = Variant : : ARRAY & & return_info . hint = = PROPERTY_HINT_ARRAY_TYPE ) {
2021-05-03 15:21:06 +02:00
imethod . return_type . cname = Variant : : get_type_name ( return_info . type ) + " _@generic " ;
2022-02-09 00:56:10 +01:00
imethod . return_type . generic_type_parameters . push_back ( TypeReference ( return_info . hint_string ) ) ;
2017-10-02 23:24:00 +02:00
} else if ( return_info . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = return_info . hint_string ;
2017-10-02 23:24:00 +02:00
} else if ( return_info . type = = Variant : : NIL & & return_info . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = name_cache . type_Variant ;
2017-10-02 23:24:00 +02:00
} else if ( return_info . type = = Variant : : NIL ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = name_cache . type_void ;
2017-10-02 23:24:00 +02:00
} else {
2022-09-01 00:40:59 +02:00
imethod . return_type . cname = _get_type_name_from_meta ( return_info . type , m ? m - > get_argument_meta ( - 1 ) : GodotTypeInfo : : METADATA_NONE ) ;
2017-10-02 23:24:00 +02:00
}
for ( int i = 0 ; i < argc ; i + + ) {
PropertyInfo arginfo = method_info . arguments [ i ] ;
2019-09-24 15:09:54 +02:00
String orig_arg_name = arginfo . name ;
2017-10-02 23:24:00 +02:00
ArgumentInterface iarg ;
2019-09-24 15:09:54 +02:00
iarg . name = orig_arg_name ;
2017-10-02 23:24:00 +02:00
2022-06-24 11:16:37 +02:00
if ( arginfo . type = = Variant : : INT & & arginfo . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) {
2018-04-28 22:25:25 +02:00
iarg . type . cname = arginfo . class_name ;
iarg . type . is_enum = true ;
2017-10-02 23:24:00 +02:00
} else if ( arginfo . class_name ! = StringName ( ) ) {
2018-04-28 22:25:25 +02:00
iarg . type . cname = arginfo . class_name ;
2022-02-09 00:56:10 +01:00
} else if ( arginfo . type = = Variant : : ARRAY & & arginfo . hint = = PROPERTY_HINT_ARRAY_TYPE ) {
2021-05-03 15:21:06 +02:00
iarg . type . cname = Variant : : get_type_name ( arginfo . type ) + " _@generic " ;
2022-02-09 00:56:10 +01:00
iarg . type . generic_type_parameters . push_back ( TypeReference ( arginfo . hint_string ) ) ;
2017-10-02 23:24:00 +02:00
} else if ( arginfo . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
2018-04-28 22:25:25 +02:00
iarg . type . cname = arginfo . hint_string ;
2017-10-02 23:24:00 +02:00
} else if ( arginfo . type = = Variant : : NIL ) {
2018-04-28 22:25:25 +02:00
iarg . type . cname = name_cache . type_Variant ;
2017-10-02 23:24:00 +02:00
} else {
2022-09-01 00:40:59 +02:00
iarg . type . cname = _get_type_name_from_meta ( arginfo . type , m ? m - > get_argument_meta ( i ) : GodotTypeInfo : : METADATA_NONE ) ;
2017-10-02 23:24:00 +02:00
}
iarg . name = escape_csharp_keyword ( snake_to_camel_case ( iarg . name ) ) ;
if ( m & & m - > has_default_argument ( i ) ) {
2019-09-24 15:09:54 +02:00
bool defval_ok = _arg_default_value_from_variant ( m - > get_default_argument ( i ) , iarg ) ;
ERR_FAIL_COND_V_MSG ( ! defval_ok , false ,
" Cannot determine default value for argument ' " + orig_arg_name + " ' of method ' " + itype . name + " . " + imethod . name + " '. " ) ;
2017-10-02 23:24:00 +02:00
}
imethod . add_argument ( iarg ) ;
}
if ( imethod . is_vararg ) {
ArgumentInterface ivararg ;
2018-04-28 22:25:25 +02:00
ivararg . type . cname = name_cache . type_VarArg ;
2017-10-02 23:24:00 +02:00
ivararg . name = " @args " ;
imethod . add_argument ( ivararg ) ;
}
imethod . proxy_name = escape_csharp_keyword ( snake_to_pascal_case ( imethod . name ) ) ;
2019-04-25 15:45:12 +02:00
// Prevent the method and its enclosing type from sharing the same name
2017-10-02 23:24:00 +02:00
if ( imethod . proxy_name = = itype . proxy_name ) {
2019-08-09 03:39:45 +02:00
_log ( " Name of method '%s' is ambiguous with the name of its enclosing class '%s'. Renaming method to '%s_' \n " ,
2019-04-25 15:45:12 +02:00
imethod . proxy_name . utf8 ( ) . get_data ( ) , itype . proxy_name . utf8 ( ) . get_data ( ) , imethod . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 23:24:00 +02:00
imethod . proxy_name + = " _ " ;
}
2022-05-13 15:04:37 +02:00
HashMap < StringName , StringName > : : Iterator accessor = accessor_methods . find ( imethod . cname ) ;
2019-04-25 17:11:01 +02:00
if ( accessor ) {
2022-05-13 15:04:37 +02:00
const PropertyInterface * accessor_property = itype . find_property_by_name ( accessor - > value ) ;
2019-04-25 17:11:01 +02:00
// We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also
// we don't know if an accessor method in a different class could have other purposes, so better leave those untouched.
imethod . is_deprecated = true ;
imethod . deprecation_message = imethod . proxy_name + " is deprecated. Use the " + accessor_property - > proxy_name + " property instead. " ;
}
2017-10-02 23:24:00 +02:00
if ( itype . class_doc ) {
for ( int i = 0 ; i < itype . class_doc - > methods . size ( ) ; i + + ) {
if ( itype . class_doc - > methods [ i ] . name = = imethod . name ) {
imethod . method_doc = & itype . class_doc - > methods [ i ] ;
break ;
}
}
}
2020-05-16 04:03:05 +02:00
ERR_FAIL_COND_V_MSG ( itype . find_property_by_name ( imethod . cname ) , false ,
" Method name conflicts with property: ' " + itype . name + " . " + imethod . name + " '. " ) ;
2021-09-12 20:21:15 +02:00
// Methods starting with an underscore are ignored unless they're used as a property setter or getter
2017-10-02 23:24:00 +02:00
if ( ! imethod . is_virtual & & imethod . name [ 0 ] = = ' _ ' ) {
2021-07-25 18:45:00 +02:00
for ( const PropertyInterface & iprop : itype . properties ) {
2017-12-29 02:18:46 +01:00
if ( iprop . setter = = imethod . name | | iprop . getter = = imethod . name ) {
2017-10-02 23:24:00 +02:00
imethod . is_internal = true ;
itype . methods . push_back ( imethod ) ;
break ;
}
}
} else {
itype . methods . push_back ( imethod ) ;
}
}
2020-03-14 19:20:17 +01:00
// Populate signals
const HashMap < StringName , MethodInfo > & signal_map = class_info - > signal_map ;
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , MethodInfo > & E : signal_map ) {
2020-03-14 19:20:17 +01:00
SignalInterface isignal ;
2022-05-08 10:09:19 +02:00
const MethodInfo & method_info = E . value ;
2020-03-14 19:20:17 +01:00
isignal . name = method_info . name ;
isignal . cname = method_info . name ;
int argc = method_info . arguments . size ( ) ;
for ( int i = 0 ; i < argc ; i + + ) {
PropertyInfo arginfo = method_info . arguments [ i ] ;
String orig_arg_name = arginfo . name ;
ArgumentInterface iarg ;
iarg . name = orig_arg_name ;
2022-06-24 11:16:37 +02:00
if ( arginfo . type = = Variant : : INT & & arginfo . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) {
2020-03-14 19:20:17 +01:00
iarg . type . cname = arginfo . class_name ;
iarg . type . is_enum = true ;
} else if ( arginfo . class_name ! = StringName ( ) ) {
iarg . type . cname = arginfo . class_name ;
2022-02-09 00:56:10 +01:00
} else if ( arginfo . type = = Variant : : ARRAY & & arginfo . hint = = PROPERTY_HINT_ARRAY_TYPE ) {
2021-05-03 15:21:06 +02:00
iarg . type . cname = Variant : : get_type_name ( arginfo . type ) + " _@generic " ;
2022-02-09 00:56:10 +01:00
iarg . type . generic_type_parameters . push_back ( TypeReference ( arginfo . hint_string ) ) ;
2020-03-14 19:20:17 +01:00
} else if ( arginfo . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
iarg . type . cname = arginfo . hint_string ;
} else if ( arginfo . type = = Variant : : NIL ) {
iarg . type . cname = name_cache . type_Variant ;
} else {
2022-09-01 00:40:59 +02:00
iarg . type . cname = _get_type_name_from_meta ( arginfo . type , GodotTypeInfo : : METADATA_NONE ) ;
2020-03-14 19:20:17 +01:00
}
iarg . name = escape_csharp_keyword ( snake_to_camel_case ( iarg . name ) ) ;
isignal . add_argument ( iarg ) ;
}
isignal . proxy_name = escape_csharp_keyword ( snake_to_pascal_case ( isignal . name ) ) ;
// Prevent the signal and its enclosing type from sharing the same name
if ( isignal . proxy_name = = itype . proxy_name ) {
_log ( " Name of signal '%s' is ambiguous with the name of its enclosing class '%s'. Renaming signal to '%s_' \n " ,
isignal . proxy_name . utf8 ( ) . get_data ( ) , itype . proxy_name . utf8 ( ) . get_data ( ) , isignal . proxy_name . utf8 ( ) . get_data ( ) ) ;
isignal . proxy_name + = " _ " ;
}
if ( itype . find_property_by_proxy_name ( isignal . proxy_name ) | | itype . find_method_by_proxy_name ( isignal . proxy_name ) ) {
// ClassDB allows signal names that conflict with method or property names.
// While registering a signal with a conflicting name is considered wrong,
// it may still happen and it may take some time until someone fixes the name.
// We can't allow the bindings to be in a broken state while we wait for a fix;
// that's why we must handle this possibility by renaming the signal.
isignal . proxy_name + = " Signal " ;
}
if ( itype . class_doc ) {
for ( int i = 0 ; i < itype . class_doc - > signals . size ( ) ; i + + ) {
const DocData : : MethodDoc & signal_doc = itype . class_doc - > signals [ i ] ;
if ( signal_doc . name = = isignal . name ) {
isignal . method_doc = & signal_doc ;
break ;
}
}
}
itype . signals_ . push_back ( isignal ) ;
}
2017-12-24 03:17:48 +01:00
// Populate enums and constants
2019-04-19 01:10:08 +02:00
List < String > constants ;
ClassDB : : get_integer_constant_list ( type_cname , & constants , true ) ;
2017-12-24 03:17:48 +01:00
2022-06-24 11:16:37 +02:00
const HashMap < StringName , ClassDB : : ClassInfo : : EnumInfo > & enum_map = class_info - > enum_map ;
2017-12-24 03:17:48 +01:00
2022-06-24 11:16:37 +02:00
for ( const KeyValue < StringName , ClassDB : : ClassInfo : : EnumInfo > & E : enum_map ) {
2022-05-08 10:09:19 +02:00
StringName enum_proxy_cname = E . key ;
2017-12-24 03:17:48 +01:00
String enum_proxy_name = enum_proxy_cname . operator String ( ) ;
if ( itype . find_property_by_proxy_name ( enum_proxy_cname ) ) {
// We have several conflicts between enums and PascalCase properties,
// so we append 'Enum' to the enum name in those cases.
enum_proxy_name + = " Enum " ;
enum_proxy_cname = StringName ( enum_proxy_name ) ;
}
EnumInterface ienum ( enum_proxy_cname ) ;
2022-06-24 20:25:30 +02:00
ienum . is_flags = E . value . is_bitfield ;
2022-06-24 11:16:37 +02:00
const List < StringName > & enum_constants = E . value . constants ;
2021-07-25 18:45:00 +02:00
for ( const StringName & constant_cname : enum_constants ) {
2018-10-18 19:41:10 +02:00
String constant_name = constant_cname . operator String ( ) ;
2022-05-09 12:47:10 +03:00
int64_t * value = class_info - > constant_map . getptr ( constant_cname ) ;
2019-09-24 15:09:54 +02:00
ERR_FAIL_NULL_V ( value , false ) ;
2019-04-19 01:10:08 +02:00
constants . erase ( constant_name ) ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , * value ) ;
2017-12-24 03:17:48 +01:00
2020-04-02 01:20:12 +02:00
iconstant . const_doc = nullptr ;
2017-12-24 03:17:48 +01:00
for ( int i = 0 ; i < itype . class_doc - > constants . size ( ) ; i + + ) {
const DocData : : ConstantDoc & const_doc = itype . class_doc - > constants [ i ] ;
if ( const_doc . name = = iconstant . name ) {
iconstant . const_doc = & const_doc ;
break ;
}
}
ienum . constants . push_back ( iconstant ) ;
}
2018-10-18 19:41:10 +02:00
int prefix_length = _determine_enum_prefix ( ienum ) ;
_apply_prefix_to_enum_constants ( ienum , prefix_length ) ;
2017-12-24 03:17:48 +01:00
itype . enums . push_back ( ienum ) ;
TypeInterface enum_itype ;
2018-04-28 22:25:25 +02:00
enum_itype . is_enum = true ;
2022-05-08 10:09:19 +02:00
enum_itype . name = itype . name + " . " + String ( E . key ) ;
2017-12-24 03:17:48 +01:00
enum_itype . cname = StringName ( enum_itype . name ) ;
enum_itype . proxy_name = itype . proxy_name + " . " + enum_proxy_name ;
2018-04-28 22:25:25 +02:00
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2022-02-27 21:57:46 +01:00
enum_itype . cs_variant_to_managed = " (%1)VariantUtils.ConvertToInt32(%0) " ;
enum_itype . cs_managed_to_variant = " VariantUtils.CreateFromInt((int)%0) " ;
2017-12-24 03:17:48 +01:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
}
2021-07-25 18:45:00 +02:00
for ( const String & constant_name : constants ) {
2022-05-09 12:47:10 +03:00
int64_t * value = class_info - > constant_map . getptr ( StringName ( constant_name ) ) ;
2019-09-24 15:09:54 +02:00
ERR_FAIL_NULL_V ( value , false ) ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , * value ) ;
2017-12-24 03:17:48 +01:00
2020-04-02 01:20:12 +02:00
iconstant . const_doc = nullptr ;
2017-12-24 03:17:48 +01:00
for ( int i = 0 ; i < itype . class_doc - > constants . size ( ) ; i + + ) {
const DocData : : ConstantDoc & const_doc = itype . class_doc - > constants [ i ] ;
if ( const_doc . name = = iconstant . name ) {
iconstant . const_doc = & const_doc ;
break ;
}
}
itype . constants . push_back ( iconstant ) ;
}
obj_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
class_list . pop_front ( ) ;
}
2019-09-24 15:09:54 +02:00
return true ;
2017-10-02 23:24:00 +02:00
}
2019-09-24 15:09:54 +02:00
bool BindingsGenerator : : _arg_default_value_from_variant ( const Variant & p_val , ArgumentInterface & r_iarg ) {
2020-05-16 04:03:05 +02:00
r_iarg . def_param_value = p_val ;
r_iarg . default_argument = p_val . operator String ( ) ;
2017-10-02 23:24:00 +02:00
switch ( p_val . get_type ( ) ) {
case Variant : : NIL :
2019-06-15 17:34:07 +02:00
// Either Object type or Variant
2022-07-28 17:41:49 +02:00
r_iarg . default_argument = " default " ;
2017-10-02 23:24:00 +02:00
break ;
// Atomic types
case Variant : : BOOL :
r_iarg . default_argument = bool ( p_val ) ? " true " : " false " ;
break ;
case Variant : : INT :
2018-04-28 22:25:25 +02:00
if ( r_iarg . type . cname ! = name_cache . type_int ) {
2017-12-24 03:17:48 +01:00
r_iarg . default_argument = " (%s) " + r_iarg . default_argument ;
}
break ;
2020-02-24 15:20:53 -03:00
case Variant : : FLOAT :
2020-07-24 03:48:21 -04:00
if ( r_iarg . type . cname = = name_cache . type_float ) {
r_iarg . default_argument + = " f " ;
}
2017-10-02 23:24:00 +02:00
break ;
case Variant : : STRING :
2020-03-14 19:20:17 +01:00
case Variant : : STRING_NAME :
2017-10-02 23:24:00 +02:00
case Variant : : NODE_PATH :
2020-03-14 19:20:17 +01:00
if ( r_iarg . type . cname = = name_cache . type_StringName | | r_iarg . type . cname = = name_cache . type_NodePath ) {
2021-12-28 23:25:16 +01:00
if ( r_iarg . default_argument . length ( ) > 0 ) {
r_iarg . default_argument = " (%s) \" " + r_iarg . default_argument + " \" " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
} else {
// No need for a special `in` statement to change `null` to `""`. Marshaling takes care of this already.
r_iarg . default_argument = " null " ;
}
2020-03-14 19:20:17 +01:00
} else {
CRASH_COND ( r_iarg . type . cname ! = name_cache . type_String ) ;
r_iarg . default_argument = " \" " + r_iarg . default_argument + " \" " ;
}
2017-10-02 23:24:00 +02:00
break ;
2020-07-24 03:48:21 -04:00
case Variant : : PLANE : {
Plane plane = p_val . operator Plane ( ) ;
2021-02-25 09:54:50 -05:00
r_iarg . default_argument = " new Plane(new Vector3 " + plane . normal . operator String ( ) + " , " + rtos ( plane . d ) + " ) " ;
2017-10-02 23:24:00 +02:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
2020-07-24 03:48:21 -04:00
} break ;
case Variant : : AABB : {
AABB aabb = p_val . operator : : AABB ( ) ;
2021-06-18 01:13:00 -04:00
r_iarg . default_argument = " new AABB(new Vector3 " + aabb . position . operator String ( ) + " , new Vector3 " + aabb . size . operator String ( ) + " ) " ;
2020-07-24 03:48:21 -04:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
case Variant : : RECT2 : {
Rect2 rect = p_val . operator Rect2 ( ) ;
2021-06-18 01:13:00 -04:00
r_iarg . default_argument = " new Rect2(new Vector2 " + rect . position . operator String ( ) + " , new Vector2 " + rect . size . operator String ( ) + " ) " ;
2020-07-24 03:48:21 -04:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
case Variant : : RECT2I : {
Rect2i rect = p_val . operator Rect2i ( ) ;
2021-06-18 01:13:00 -04:00
r_iarg . default_argument = " new Rect2i(new Vector2i " + rect . position . operator String ( ) + " , new Vector2i " + rect . size . operator String ( ) + " ) " ;
2020-07-24 03:48:21 -04:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
2017-10-02 23:24:00 +02:00
case Variant : : COLOR :
case Variant : : VECTOR2 :
2020-03-03 04:42:20 -05:00
case Variant : : VECTOR2I :
2017-10-02 23:24:00 +02:00
case Variant : : VECTOR3 :
2020-03-03 04:42:20 -05:00
case Variant : : VECTOR3I :
2017-10-02 23:24:00 +02:00
r_iarg . default_argument = " new %s " + r_iarg . default_argument ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
break ;
2022-07-20 01:11:13 +02:00
case Variant : : VECTOR4 :
case Variant : : VECTOR4I :
r_iarg . default_argument = " new %s " + r_iarg . default_argument ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
break ;
2017-10-02 23:24:00 +02:00
case Variant : : OBJECT :
2019-09-24 15:09:54 +02:00
ERR_FAIL_COND_V_MSG ( ! p_val . is_zero ( ) , false ,
" Parameter of type ' " + String ( r_iarg . type . cname ) + " ' can only have null/zero as the default value. " ) ;
r_iarg . default_argument = " null " ;
break ;
2017-10-02 23:24:00 +02:00
case Variant : : DICTIONARY :
2021-12-28 23:25:16 +01:00
ERR_FAIL_COND_V_MSG ( ! p_val . operator Dictionary ( ) . is_empty ( ) , false ,
" Default value of type 'Dictionary' must be an empty dictionary. " ) ;
// The [cs_in] expression already interprets null values as empty dictionaries.
r_iarg . default_argument = " null " ;
r_iarg . def_param_mode = ArgumentInterface : : CONSTANT ;
2017-10-02 23:24:00 +02:00
break ;
2020-11-09 14:53:05 +01:00
case Variant : : RID :
2019-09-24 15:09:54 +02:00
ERR_FAIL_COND_V_MSG ( r_iarg . type . cname ! = name_cache . type_RID , false ,
" Parameter of type ' " + String ( r_iarg . type . cname ) + " ' cannot have a default value of type ' " + String ( name_cache . type_RID ) + " '. " ) ;
ERR_FAIL_COND_V_MSG ( ! p_val . is_zero ( ) , false ,
" Parameter of type ' " + String ( r_iarg . type . cname ) + " ' can only have null/zero as the default value. " ) ;
2021-05-03 15:21:06 +02:00
r_iarg . default_argument = " default " ;
2019-09-24 15:09:54 +02:00
break ;
2017-10-02 23:24:00 +02:00
case Variant : : ARRAY :
2021-12-28 23:25:16 +01:00
ERR_FAIL_COND_V_MSG ( ! p_val . operator Array ( ) . is_empty ( ) , false ,
" Default value of type 'Array' must be an empty array. " ) ;
// The [cs_in] expression already interprets null values as empty arrays.
r_iarg . default_argument = " null " ;
r_iarg . def_param_mode = ArgumentInterface : : CONSTANT ;
2021-07-25 13:46:31 +02:00
break ;
2020-02-17 18:06:54 -03:00
case Variant : : PACKED_BYTE_ARRAY :
2020-02-24 15:20:53 -03:00
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
2020-03-14 19:20:17 +01:00
case Variant : : PACKED_FLOAT32_ARRAY :
2020-02-24 15:20:53 -03:00
case Variant : : PACKED_FLOAT64_ARRAY :
2020-02-17 18:06:54 -03:00
case Variant : : PACKED_STRING_ARRAY :
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : PACKED_VECTOR3_ARRAY :
case Variant : : PACKED_COLOR_ARRAY :
2021-07-25 13:46:31 +02:00
r_iarg . default_argument = " Array.Empty<%s>() " ;
2017-10-02 23:24:00 +02:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
break ;
2020-07-24 03:48:21 -04:00
case Variant : : TRANSFORM2D : {
Transform2D transform = p_val . operator Transform2D ( ) ;
if ( transform = = Transform2D ( ) ) {
r_iarg . default_argument = " Transform2D.Identity " ;
} else {
2022-04-24 16:59:24 -05:00
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 ( ) + " ) " ;
2020-07-24 03:48:21 -04:00
}
2017-10-02 23:24:00 +02:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
2020-07-24 03:48:21 -04:00
} break ;
2021-04-28 03:36:08 -04:00
case Variant : : TRANSFORM3D : {
2020-10-17 01:08:21 -04:00
Transform3D transform = p_val . operator Transform3D ( ) ;
if ( transform = = Transform3D ( ) ) {
2021-02-26 23:26:56 -05:00
r_iarg . default_argument = " Transform3D.Identity " ;
2020-07-24 03:48:21 -04:00
} else {
Basis basis = transform . basis ;
2021-02-26 23:26:56 -05:00
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 ( ) + " ) " ;
2020-07-24 03:48:21 -04:00
}
2022-07-20 01:11:13 +02:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
case Variant : : PROJECTION : {
Projection transform = p_val . operator Projection ( ) ;
if ( transform = = Projection ( ) ) {
r_iarg . default_argument = " Projection.Identity " ;
} else {
r_iarg . default_argument = " new Projection(new Vector4 " + transform . matrix [ 0 ] . operator String ( ) + " , new Vector4 " + transform . matrix [ 1 ] . operator String ( ) + " , new Vector4 " + transform . matrix [ 2 ] . operator String ( ) + " , new Vector4 " + transform . matrix [ 3 ] . operator String ( ) + " ) " ;
}
2020-07-24 03:48:21 -04:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
case Variant : : BASIS : {
Basis basis = p_val . operator Basis ( ) ;
if ( basis = = Basis ( ) ) {
r_iarg . default_argument = " Basis.Identity " ;
} else {
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 ( ) + " ) " ;
}
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
2021-01-20 07:02:02 +00:00
case Variant : : QUATERNION : {
Quaternion quaternion = p_val . operator Quaternion ( ) ;
if ( quaternion = = Quaternion ( ) ) {
r_iarg . default_argument = " Quaternion.Identity " ;
2020-07-24 03:48:21 -04:00
} else {
2021-01-20 07:02:02 +00:00
r_iarg . default_argument = " new Quaternion " + quaternion . operator String ( ) ;
2020-07-24 03:48:21 -04:00
}
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
2020-03-14 19:20:17 +01:00
case Variant : : CALLABLE :
2021-08-20 11:18:04 +02:00
ERR_FAIL_COND_V_MSG ( r_iarg . type . cname ! = name_cache . type_Callable , false ,
" Parameter of type ' " + String ( r_iarg . type . cname ) + " ' cannot have a default value of type ' " + String ( name_cache . type_Callable ) + " '. " ) ;
ERR_FAIL_COND_V_MSG ( ! p_val . is_zero ( ) , false ,
" Parameter of type ' " + String ( r_iarg . type . cname ) + " ' can only have null/zero as the default value. " ) ;
r_iarg . default_argument = " default " ;
break ;
2020-03-14 19:20:17 +01:00
case Variant : : SIGNAL :
2021-08-20 11:18:04 +02:00
ERR_FAIL_COND_V_MSG ( r_iarg . type . cname ! = name_cache . type_Signal , false ,
" Parameter of type ' " + String ( r_iarg . type . cname ) + " ' cannot have a default value of type ' " + String ( name_cache . type_Signal ) + " '. " ) ;
ERR_FAIL_COND_V_MSG ( ! p_val . is_zero ( ) , false ,
" Parameter of type ' " + String ( r_iarg . type . cname ) + " ' can only have null/zero as the default value. " ) ;
r_iarg . default_argument = " default " ;
2020-03-14 19:20:17 +01:00
break ;
default :
2021-05-03 15:21:06 +02:00
ERR_FAIL_V_MSG ( false , " Unexpected Variant type: " + itos ( p_val . get_type ( ) ) ) ;
2020-03-14 19:20:17 +01:00
break ;
2017-10-02 23:24:00 +02:00
}
2022-07-28 17:41:49 +02:00
if ( r_iarg . def_param_mode = = ArgumentInterface : : CONSTANT & & r_iarg . type . cname = = name_cache . type_Variant & & r_iarg . default_argument ! = " default " ) {
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
2020-05-16 04:03:05 +02:00
}
2019-09-24 15:09:54 +02:00
return true ;
2017-10-02 23:24:00 +02:00
}
void BindingsGenerator : : _populate_builtin_type_interfaces ( ) {
builtin_types . clear ( ) ;
TypeInterface itype ;
2022-02-27 21:57:46 +01:00
# define INSERT_STRUCT_TYPE(m_type) \
{ \
itype = TypeInterface::create_value_type(String(#m_type)); \
itype.c_type_in = #m_type "*"; \
itype.c_type_out = itype.cs_type; \
itype.cs_in_expr = "&%0"; \
itype.cs_in_expr_is_unsafe = true; \
itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)"; \
itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
builtin_types.insert(itype.cname, itype); \
2017-10-02 23:24:00 +02:00
}
2019-11-14 21:48:49 +01:00
INSERT_STRUCT_TYPE ( Vector2 )
2020-03-03 04:42:20 -05:00
INSERT_STRUCT_TYPE ( Vector2i )
2019-11-14 21:48:49 +01:00
INSERT_STRUCT_TYPE ( Rect2 )
2020-03-03 04:42:20 -05:00
INSERT_STRUCT_TYPE ( Rect2i )
2019-11-14 21:48:49 +01:00
INSERT_STRUCT_TYPE ( Transform2D )
INSERT_STRUCT_TYPE ( Vector3 )
2020-03-03 04:42:20 -05:00
INSERT_STRUCT_TYPE ( Vector3i )
2019-11-14 21:48:49 +01:00
INSERT_STRUCT_TYPE ( Basis )
2021-01-20 07:02:02 +00:00
INSERT_STRUCT_TYPE ( Quaternion )
2020-10-17 01:08:21 -04:00
INSERT_STRUCT_TYPE ( Transform3D )
2019-11-14 21:48:49 +01:00
INSERT_STRUCT_TYPE ( AABB )
INSERT_STRUCT_TYPE ( Color )
INSERT_STRUCT_TYPE ( Plane )
2021-05-03 15:21:06 +02:00
INSERT_STRUCT_TYPE ( Vector4 )
INSERT_STRUCT_TYPE ( Vector4i )
INSERT_STRUCT_TYPE ( Projection )
2017-10-02 23:24:00 +02:00
# undef INSERT_STRUCT_TYPE
2018-01-25 23:44:37 +01:00
// bool
itype = TypeInterface : : create_value_type ( String ( " bool " ) ) ;
2021-12-28 23:25:16 +01:00
itype . cs_in_expr = " %0.ToGodotBool() " ;
2021-09-12 20:23:05 +02:00
itype . cs_out = " %5return %0(%1).ToBool(); " ;
2021-05-03 15:21:06 +02:00
itype . c_type = " godot_bool " ;
itype . c_type_in = itype . c_type ;
itype . c_type_out = itype . c_type ;
itype . c_arg_in = " &%s " ;
itype . c_in_vararg = " %5using godot_variant %1_in = VariantUtils.CreateFromBool(%1); \n " ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToBool(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromBool(%0) " ;
2018-01-25 23:44:37 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
2019-04-25 20:24:48 +02:00
// Integer types
2018-10-17 21:37:57 +02:00
{
2019-04-25 20:24:48 +02:00
// C interface for 'uint32_t' is the same as that of enums. Remember to apply
// any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
2022-02-27 21:57:46 +01:00
# define INSERT_INT_TYPE(m_name, m_int_struct_name) \
2021-05-03 15:21:06 +02:00
{ \
itype = TypeInterface::create_value_type(String(m_name)); \
if (itype.name != "long" && itype.name != "ulong") { \
itype.c_in = "%5%0 %1_in = %1;\n"; \
itype.c_out = "%5return (%0)%1;\n"; \
itype.c_type = "long"; \
itype.c_arg_in = "&%s_in"; \
} else { \
itype.c_arg_in = "&%s"; \
} \
itype.c_type_in = itype.name; \
itype.c_type_out = itype.name; \
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \
2022-02-27 21:57:46 +01:00
itype.cs_variant_to_managed = "VariantUtils.ConvertTo" m_int_struct_name "(%0)"; \
itype.cs_managed_to_variant = "VariantUtils.CreateFromInt(%0)"; \
2021-05-03 15:21:06 +02:00
builtin_types.insert(itype.cname, itype); \
2018-10-17 21:37:57 +02:00
}
2017-10-02 23:24:00 +02:00
2019-04-25 20:24:48 +02:00
// The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
2022-02-27 21:57:46 +01:00
INSERT_INT_TYPE ( " sbyte " , " Int8 " ) ;
INSERT_INT_TYPE ( " short " , " Int16 " ) ;
INSERT_INT_TYPE ( " int " , " Int32 " ) ;
INSERT_INT_TYPE ( " long " , " Int64 " ) ;
INSERT_INT_TYPE ( " byte " , " UInt8 " ) ;
INSERT_INT_TYPE ( " ushort " , " UInt16 " ) ;
INSERT_INT_TYPE ( " uint " , " UInt32 " ) ;
INSERT_INT_TYPE ( " ulong " , " UInt64 " ) ;
2019-04-25 20:24:48 +02:00
2021-05-03 15:21:06 +02:00
# undef INSERT_INT_TYPE
2022-02-27 21:57:46 +01:00
}
2021-05-03 15:21:06 +02:00
2019-04-25 20:24:48 +02:00
// Floating point types
2018-10-17 21:37:57 +02:00
{
2019-04-25 20:24:48 +02:00
// float
itype = TypeInterface ( ) ;
itype . name = " float " ;
itype . cname = itype . name ;
itype . proxy_name = " float " ;
2021-05-03 15:21:06 +02:00
itype . cs_type = itype . proxy_name ;
2019-04-25 20:24:48 +02:00
{
// The expected type for 'float' in ptrcall is 'double'
2021-05-03 15:21:06 +02:00
itype . c_in = " %5%0 %1_in = %1; \n " ;
itype . c_out = " %5return (%0)%1; \n " ;
2019-04-25 20:24:48 +02:00
itype . c_type = " double " ;
itype . c_arg_in = " &%s_in " ;
}
2021-05-03 15:21:06 +02:00
itype . c_type_in = itype . proxy_name ;
itype . c_type_out = itype . proxy_name ;
itype . c_in_vararg = " %5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1); \n " ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToFloat32(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromFloat(%0) " ;
2019-04-25 20:24:48 +02:00
builtin_types . insert ( itype . cname , itype ) ;
// double
itype = TypeInterface ( ) ;
itype . name = " double " ;
itype . cname = itype . name ;
itype . proxy_name = " double " ;
itype . cs_type = itype . proxy_name ;
2021-05-03 15:21:06 +02:00
itype . c_type = " double " ;
itype . c_arg_in = " &%s " ;
itype . c_type_in = itype . proxy_name ;
itype . c_type_out = itype . proxy_name ;
itype . c_in_vararg = " %5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1); \n " ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToFloat64(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromFloat(%0) " ;
2019-04-25 20:24:48 +02:00
builtin_types . insert ( itype . cname , itype ) ;
2018-10-17 21:37:57 +02:00
}
2017-10-02 23:24:00 +02:00
// String
itype = TypeInterface ( ) ;
itype . name = " String " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = " string " ;
itype . cs_type = itype . proxy_name ;
2021-05-03 15:21:06 +02:00
itype . c_in = " %5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT " (%1); \n " ;
2021-09-12 20:21:15 +02:00
itype . c_out = " %5return " C_METHOD_MONOSTR_FROM_GODOT " (%1); \n " ;
2021-05-03 15:21:06 +02:00
itype . c_arg_in = " &%s_in " ;
itype . c_type = " godot_string " ;
itype . c_type_in = itype . cs_type ;
itype . c_type_out = itype . cs_type ;
itype . c_type_is_disposable_struct = true ;
2022-02-27 21:57:46 +01:00
itype . c_in_vararg = " %5using godot_variant %1_in = VariantUtils.CreateFromString(%1); \n " ;
itype . cs_variant_to_managed = " VariantUtils.ConvertToStringObject(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromString(%0) " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
2020-03-14 19:20:17 +01:00
// StringName
itype = TypeInterface ( ) ;
itype . name = " StringName " ;
itype . cname = itype . name ;
itype . proxy_name = " StringName " ;
itype . cs_type = itype . proxy_name ;
2021-12-28 23:25:16 +01:00
itype . cs_in_expr = " (%1)(%0?.NativeValue ?? default) " ;
2021-05-03 15:21:06 +02:00
// Cannot pass null StringName to ptrcall
itype . c_out = " %5return %0.CreateTakingOwnershipOfDisposableValue(%1); \n " ;
2021-12-28 23:25:16 +01:00
itype . c_arg_in = " &%s " ;
2021-05-03 15:21:06 +02:00
itype . c_type = " godot_string_name " ;
2021-12-28 23:25:16 +01:00
itype . c_type_in = itype . c_type ;
2021-05-03 15:21:06 +02:00
itype . c_type_out = itype . cs_type ;
2021-12-28 23:25:16 +01:00
itype . c_in_vararg = " %5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1); \n " ;
2021-05-03 15:21:06 +02:00
itype . c_type_is_disposable_struct = false ; // [c_out] takes ownership
itype . c_ret_needs_default_initialization = true ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToStringNameObject(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromStringName(%0) " ;
2020-03-14 19:20:17 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// NodePath
itype = TypeInterface ( ) ;
itype . name = " NodePath " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = " NodePath " ;
itype . cs_type = itype . proxy_name ;
2021-12-28 23:25:16 +01:00
itype . cs_in_expr = " (%1)(%0?.NativeValue ?? default) " ;
2021-05-03 15:21:06 +02:00
// Cannot pass null NodePath to ptrcall
itype . c_out = " %5return %0.CreateTakingOwnershipOfDisposableValue(%1); \n " ;
2021-12-28 23:25:16 +01:00
itype . c_arg_in = " &%s " ;
2021-05-03 15:21:06 +02:00
itype . c_type = " godot_node_path " ;
2021-12-28 23:25:16 +01:00
itype . c_type_in = itype . c_type ;
2021-05-03 15:21:06 +02:00
itype . c_type_out = itype . cs_type ;
itype . c_type_is_disposable_struct = false ; // [c_out] takes ownership
itype . c_ret_needs_default_initialization = true ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToNodePathObject(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromNodePath(%0) " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// RID
itype = TypeInterface ( ) ;
itype . name = " RID " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = " RID " ;
itype . cs_type = itype . proxy_name ;
2021-05-03 15:21:06 +02:00
itype . c_arg_in = " &%s " ;
itype . c_type = itype . cs_type ;
itype . c_type_in = itype . c_type ;
itype . c_type_out = itype . c_type ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToRID(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromRID(%0) " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// Variant
itype = TypeInterface ( ) ;
itype . name = " Variant " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2022-07-28 17:41:49 +02:00
itype . proxy_name = " Variant " ;
2017-10-02 23:24:00 +02:00
itype . cs_type = itype . proxy_name ;
2022-07-28 17:41:49 +02:00
itype . c_in = " %5%0 %1_in = (%0)%1.NativeVar; \n " ;
itype . c_out = " %5return Variant.CreateTakingOwnershipOfDisposableValue(%1); \n " ;
2021-05-03 15:21:06 +02:00
itype . c_arg_in = " &%s_in " ;
itype . c_type = " godot_variant " ;
itype . c_type_in = itype . cs_type ;
itype . c_type_out = itype . cs_type ;
2022-07-28 17:41:49 +02:00
itype . c_type_is_disposable_struct = false ; // [c_out] takes ownership
itype . c_ret_needs_default_initialization = true ;
itype . cs_variant_to_managed = " Variant.CreateCopyingBorrowed(%0) " ;
itype . cs_managed_to_variant = " %0.CopyNativeVariant() " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
2020-03-14 19:20:17 +01:00
// Callable
itype = TypeInterface : : create_value_type ( String ( " Callable " ) ) ;
2022-02-27 21:57:46 +01:00
itype . cs_in_expr = " %0 " ;
itype . c_in = " %5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE " (in %1); \n " ;
2021-12-28 23:25:16 +01:00
itype . c_out = " %5return " C_METHOD_MANAGED_FROM_CALLABLE " (in %1); \n " ;
2021-05-03 15:21:06 +02:00
itype . c_arg_in = " &%s_in " ;
itype . c_type = " godot_callable " ;
2022-02-27 21:57:46 +01:00
itype . c_type_in = " in " + itype . cs_type ;
2021-05-03 15:21:06 +02:00
itype . c_type_out = itype . cs_type ;
itype . c_type_is_disposable_struct = true ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToCallableManaged(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromCallable(%0) " ;
2020-03-14 19:20:17 +01:00
builtin_types . insert ( itype . cname , itype ) ;
// Signal
itype = TypeInterface ( ) ;
itype . name = " Signal " ;
itype . cname = itype . name ;
itype . proxy_name = " SignalInfo " ;
itype . cs_type = itype . proxy_name ;
2022-02-27 21:57:46 +01:00
itype . cs_in_expr = " %0 " ;
itype . c_in = " %5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL " (in %1); \n " ;
2021-05-03 15:21:06 +02:00
itype . c_out = " %5return " C_METHOD_MANAGED_FROM_SIGNAL " (&%1); \n " ;
itype . c_arg_in = " &%s_in " ;
itype . c_type = " godot_signal " ;
2022-02-27 21:57:46 +01:00
itype . c_type_in = " in " + itype . cs_type ;
2021-05-03 15:21:06 +02:00
itype . c_type_out = itype . cs_type ;
itype . c_type_is_disposable_struct = true ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToSignalInfo(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromSignalInfo(%0) " ;
2020-03-14 19:20:17 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// VarArg (fictitious type to represent variable arguments)
itype = TypeInterface ( ) ;
itype . name = " VarArg " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2022-07-28 17:41:49 +02:00
itype . proxy_name = " Variant[] " ;
itype . cs_type = " params Variant[] " ;
itype . cs_in_expr = " %0 ?? Array.Empty<Variant>() " ;
2021-05-03 15:21:06 +02:00
// c_type, c_in and c_arg_in are hard-coded in the generator.
// c_out and c_type_out are not applicable to VarArg.
itype . c_arg_in = " &%s_in " ;
2022-07-28 17:41:49 +02:00
itype . c_type_in = " Variant[] " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
# define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \
{ \
itype = TypeInterface(); \
itype.name = #m_name; \
itype.cname = itype.name; \
itype.proxy_name = #m_proxy_t "[]"; \
itype.cs_type = itype.proxy_name; \
itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \
2021-12-28 23:25:16 +01:00
itype.c_out = "%5return " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \
2021-05-03 15:21:06 +02:00
itype.c_arg_in = "&%s_in"; \
itype.c_type = #m_managed_type; \
itype.c_type_in = itype.proxy_name; \
itype.c_type_out = itype.proxy_name; \
itype.c_type_is_disposable_struct = true; \
2022-02-27 21:57:46 +01:00
itype.cs_variant_to_managed = "VariantUtils.ConvertAs%2ToSystemArray(%0)"; \
itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
2021-05-03 15:21:06 +02:00
builtin_types.insert(itype.name, itype); \
2017-10-02 23:24:00 +02:00
}
2021-05-03 15:21:06 +02:00
# define INSERT_ARRAY(m_type, m_managed_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_managed_type, m_proxy_t)
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
INSERT_ARRAY ( PackedInt32Array , godot_packed_int32_array , int ) ;
INSERT_ARRAY ( PackedInt64Array , godot_packed_int64_array , long ) ;
INSERT_ARRAY_FULL ( PackedByteArray , PackedByteArray , godot_packed_byte_array , byte ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
INSERT_ARRAY ( PackedFloat32Array , godot_packed_float32_array , float ) ;
INSERT_ARRAY ( PackedFloat64Array , godot_packed_float64_array , double ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
INSERT_ARRAY ( PackedStringArray , godot_packed_string_array , string ) ;
2017-10-02 23:24:00 +02:00
2021-05-03 15:21:06 +02:00
INSERT_ARRAY ( PackedColorArray , godot_packed_color_array , Color ) ;
INSERT_ARRAY ( PackedVector2Array , godot_packed_vector2_array , Vector2 ) ;
INSERT_ARRAY ( PackedVector3Array , godot_packed_vector3_array , Vector3 ) ;
2017-10-02 23:24:00 +02:00
# undef INSERT_ARRAY
2018-07-18 23:07:57 +02:00
// Array
itype = TypeInterface ( ) ;
itype . name = " Array " ;
itype . cname = itype . name ;
2018-08-27 20:39:51 +02:00
itype . proxy_name = itype . name ;
2022-02-09 00:56:10 +01:00
itype . type_parameter_count = 1 ;
2018-08-27 20:39:51 +02:00
itype . cs_type = BINDINGS_NAMESPACE_COLLECTIONS " . " + itype . proxy_name ;
2021-12-28 23:25:16 +01:00
itype . cs_in_expr = " (%1)(%0 ?? new()).NativeValue " ;
2021-05-03 15:21:06 +02:00
itype . c_out = " %5return %0.CreateTakingOwnershipOfDisposableValue(%1); \n " ;
2021-12-28 23:25:16 +01:00
itype . c_arg_in = " &%s " ;
2021-05-03 15:21:06 +02:00
itype . c_type = " godot_array " ;
2021-12-28 23:25:16 +01:00
itype . c_type_in = itype . c_type ;
2021-05-03 15:21:06 +02:00
itype . c_type_out = itype . cs_type ;
itype . c_type_is_disposable_struct = false ; // [c_out] takes ownership
itype . c_ret_needs_default_initialization = true ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToArrayObject(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromArray(%0) " ;
2021-05-03 15:21:06 +02:00
builtin_types . insert ( itype . cname , itype ) ;
// Array_@generic
// Re-use Array's itype
itype . name = " Array_@generic " ;
itype . cname = itype . name ;
itype . cs_out = " %5return new %2(%0(%1)); " ;
2018-07-18 23:07:57 +02:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// Dictionary
itype = TypeInterface ( ) ;
itype . name = " Dictionary " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2018-08-27 20:39:51 +02:00
itype . proxy_name = itype . name ;
2022-02-09 00:56:10 +01:00
itype . type_parameter_count = 2 ;
2018-08-27 20:39:51 +02:00
itype . cs_type = BINDINGS_NAMESPACE_COLLECTIONS " . " + itype . proxy_name ;
2021-12-28 23:25:16 +01:00
itype . cs_in_expr = " (%1)(%0 ?? new()).NativeValue " ;
2021-05-03 15:21:06 +02:00
itype . c_out = " %5return %0.CreateTakingOwnershipOfDisposableValue(%1); \n " ;
2021-12-28 23:25:16 +01:00
itype . c_arg_in = " &%s " ;
2021-05-03 15:21:06 +02:00
itype . c_type = " godot_dictionary " ;
2021-12-28 23:25:16 +01:00
itype . c_type_in = itype . c_type ;
2021-05-03 15:21:06 +02:00
itype . c_type_out = itype . cs_type ;
itype . c_type_is_disposable_struct = false ; // [c_out] takes ownership
itype . c_ret_needs_default_initialization = true ;
2022-02-27 21:57:46 +01:00
itype . cs_variant_to_managed = " VariantUtils.ConvertToDictionaryObject(%0) " ;
itype . cs_managed_to_variant = " VariantUtils.CreateFromDictionary(%0) " ;
2021-05-03 15:21:06 +02:00
builtin_types . insert ( itype . cname , itype ) ;
// Dictionary_@generic
// Re-use Dictionary's itype
itype . name = " Dictionary_@generic " ;
itype . cname = itype . name ;
itype . cs_out = " %5return new %2(%0(%1)); " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// void (fictitious type to represent the return type of methods that do not return anything)
itype = TypeInterface ( ) ;
itype . name = " void " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = itype . name ;
2021-05-03 15:21:06 +02:00
itype . cs_type = itype . proxy_name ;
itype . c_type = itype . proxy_name ;
2017-10-02 23:24:00 +02:00
itype . c_type_in = itype . c_type ;
itype . c_type_out = itype . c_type ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
}
2017-12-24 03:17:48 +01:00
void BindingsGenerator : : _populate_global_constants ( ) {
2020-11-07 19:33:38 -03:00
int global_constants_count = CoreConstants : : get_global_constant_count ( ) ;
2017-12-24 03:17:48 +01:00
if ( global_constants_count > 0 ) {
2022-05-13 15:04:37 +02:00
HashMap < String , DocData : : ClassDoc > : : Iterator match = EditorHelp : : get_doc_data ( ) - > class_list . find ( " @GlobalScope " ) ;
2017-12-24 03:17:48 +01:00
2019-08-09 03:39:45 +02:00
CRASH_COND_MSG ( ! match , " Could not find '@GlobalScope' in DocData. " ) ;
2017-12-24 03:17:48 +01:00
2022-05-13 15:04:37 +02:00
const DocData : : ClassDoc & global_scope_doc = match - > value ;
2017-12-24 03:17:48 +01:00
for ( int i = 0 ; i < global_constants_count ; i + + ) {
2020-11-07 19:33:38 -03:00
String constant_name = CoreConstants : : get_global_constant_name ( i ) ;
2017-12-24 03:17:48 +01:00
2020-04-02 01:20:12 +02:00
const DocData : : ConstantDoc * const_doc = nullptr ;
2018-10-03 00:56:28 +02:00
for ( int j = 0 ; j < global_scope_doc . constants . size ( ) ; j + + ) {
const DocData : : ConstantDoc & curr_const_doc = global_scope_doc . constants [ j ] ;
2017-12-24 03:17:48 +01:00
if ( curr_const_doc . name = = constant_name ) {
const_doc = & curr_const_doc ;
break ;
}
}
2022-05-09 12:47:10 +03:00
int64_t constant_value = CoreConstants : : get_global_constant_value ( i ) ;
2020-11-07 19:33:38 -03:00
StringName enum_name = CoreConstants : : get_global_constant_enum ( i ) ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , constant_value ) ;
2017-12-24 03:17:48 +01:00
iconstant . const_doc = const_doc ;
if ( enum_name ! = StringName ( ) ) {
EnumInterface ienum ( enum_name ) ;
2022-06-24 20:25:30 +02:00
// TODO: ienum.is_flags is always false for core constants since they don't seem to support bitfield enums
2019-02-12 21:10:08 +01:00
List < EnumInterface > : : Element * enum_match = global_enums . find ( ienum ) ;
if ( enum_match ) {
enum_match - > get ( ) . constants . push_back ( iconstant ) ;
2017-12-24 03:17:48 +01:00
} else {
ienum . constants . push_back ( iconstant ) ;
global_enums . push_back ( ienum ) ;
}
} else {
global_constants . push_back ( iconstant ) ;
}
}
2021-07-25 18:45:00 +02:00
for ( EnumInterface & ienum : global_enums ) {
2017-12-24 03:17:48 +01:00
TypeInterface enum_itype ;
2018-04-28 22:25:25 +02:00
enum_itype . is_enum = true ;
enum_itype . name = ienum . cname . operator String ( ) ;
enum_itype . cname = ienum . cname ;
enum_itype . proxy_name = enum_itype . name ;
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2022-02-27 21:57:46 +01:00
enum_itype . cs_variant_to_managed = " (%1)VariantUtils.ConvertToInt32(%0) " ;
enum_itype . cs_managed_to_variant = " VariantUtils.CreateFromInt((int)%0) " ;
2017-12-24 03:17:48 +01:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
2018-10-18 19:41:10 +02:00
int prefix_length = _determine_enum_prefix ( ienum ) ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
// HARDCODED: The Error enum have the prefix 'ERR_' for everything except 'OK' and 'FAILED'.
2017-12-24 03:17:48 +01:00
if ( ienum . cname = = name_cache . enum_Error ) {
2018-10-18 19:41:10 +02:00
if ( prefix_length > 0 ) { // Just in case it ever changes
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Prefix for enum ' " _STR ( Error ) " ' is not empty. " ) ;
2017-12-24 03:17:48 +01:00
}
2018-10-18 19:41:10 +02:00
prefix_length = 1 ; // 'ERR_'
2017-12-24 03:17:48 +01:00
}
2018-10-18 19:41:10 +02:00
_apply_prefix_to_enum_constants ( ienum , prefix_length ) ;
2017-12-24 03:17:48 +01:00
}
}
// HARDCODED
List < StringName > hardcoded_enums ;
2020-03-03 04:42:20 -05:00
hardcoded_enums . push_back ( " Vector2.Axis " ) ;
hardcoded_enums . push_back ( " Vector2i.Axis " ) ;
2017-12-24 03:17:48 +01:00
hardcoded_enums . push_back ( " Vector3.Axis " ) ;
2020-03-03 04:42:20 -05:00
hardcoded_enums . push_back ( " Vector3i.Axis " ) ;
2021-07-25 18:45:00 +02:00
for ( const StringName & enum_cname : hardcoded_enums ) {
2017-12-24 03:17:48 +01:00
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
2018-04-28 22:25:25 +02:00
// Here, we assume core types do not begin with underscore
2017-12-24 03:17:48 +01:00
TypeInterface enum_itype ;
2018-04-28 22:25:25 +02:00
enum_itype . is_enum = true ;
2021-07-25 18:45:00 +02:00
enum_itype . name = enum_cname . operator String ( ) ;
enum_itype . cname = enum_cname ;
2018-04-28 22:25:25 +02:00
enum_itype . proxy_name = enum_itype . name ;
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2022-02-27 21:57:46 +01:00
enum_itype . cs_variant_to_managed = " (%1)VariantUtils.ConvertToInt32(%0) " ;
enum_itype . cs_managed_to_variant = " VariantUtils.CreateFromInt((int)%0) " ;
2017-12-24 03:17:48 +01:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
}
}
2019-05-24 00:40:16 +02:00
void BindingsGenerator : : _initialize_blacklisted_methods ( ) {
blacklisted_methods [ " Object " ] . push_back ( " to_string " ) ; // there is already ToString
blacklisted_methods [ " Object " ] . push_back ( " _to_string " ) ; // override ToString instead
blacklisted_methods [ " Object " ] . push_back ( " _init " ) ; // never called in C# (TODO: implement it)
}
2019-04-25 15:45:12 +02:00
void BindingsGenerator : : _log ( const char * p_format , . . . ) {
if ( log_print_enabled ) {
va_list list ;
va_start ( list , p_format ) ;
OS : : get_singleton ( ) - > print ( " %s " , str_format ( p_format , list ) . utf8 ( ) . get_data ( ) ) ;
va_end ( list ) ;
}
}
void BindingsGenerator : : _initialize ( ) {
2019-09-24 15:09:54 +02:00
initialized = false ;
2017-10-02 23:24:00 +02:00
EditorHelp : : generate_doc ( ) ;
2017-12-24 03:17:48 +01:00
enum_types . clear ( ) ;
2019-05-24 00:40:16 +02:00
_initialize_blacklisted_methods ( ) ;
2019-09-24 15:09:54 +02:00
bool obj_type_ok = _populate_object_type_interfaces ( ) ;
ERR_FAIL_COND_MSG ( ! obj_type_ok , " Failed to generate object type interfaces " ) ;
2017-10-02 23:24:00 +02:00
_populate_builtin_type_interfaces ( ) ;
2017-12-24 03:17:48 +01:00
_populate_global_constants ( ) ;
2018-09-04 05:40:41 +02:00
// Generate internal calls (after populating type interfaces and global constants)
2017-12-24 03:17:48 +01:00
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , TypeInterface > & E : obj_types ) {
2021-05-03 15:21:06 +02:00
const TypeInterface & itype = E . value ;
Error err = _populate_method_icalls_table ( itype ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Failed to generate icalls table for type: " + itype . name ) ;
2020-05-16 04:03:05 +02:00
}
2019-09-24 15:09:54 +02:00
initialized = true ;
2017-10-02 23:24:00 +02:00
}
2021-03-13 01:16:27 +01:00
static String generate_all_glue_option = " --generate-mono-glue " ;
2021-05-03 15:21:06 +02:00
static void handle_cmdline_options ( String glue_dir_path ) {
2021-03-13 01:16:27 +01:00
BindingsGenerator bindings_generator ;
bindings_generator . set_log_print_enabled ( true ) ;
if ( ! bindings_generator . is_initialized ( ) ) {
ERR_PRINT ( " Failed to initialize the bindings generator " ) ;
return ;
}
2021-05-03 15:21:06 +02:00
CRASH_COND ( glue_dir_path . is_empty ( ) ) ;
2021-03-13 01:16:27 +01:00
2022-08-29 19:34:01 -05:00
if ( bindings_generator . generate_cs_api ( glue_dir_path . path_join ( API_SOLUTION_NAME ) ) ! = OK ) {
2021-05-03 15:21:06 +02:00
ERR_PRINT ( generate_all_glue_option + " : Failed to generate the C# API. " ) ;
2021-03-13 01:16:27 +01:00
}
2021-05-03 15:21:06 +02:00
}
2021-03-13 01:16:27 +01:00
2021-05-03 15:21:06 +02:00
static void cleanup_and_exit_godot ( ) {
// Exit once done
Main : : cleanup ( true ) ;
: : exit ( 0 ) ;
2021-03-13 01:16:27 +01:00
}
2017-10-02 23:24:00 +02:00
void BindingsGenerator : : handle_cmdline_args ( const List < String > & p_cmdline_args ) {
2019-07-03 09:44:53 +02:00
String glue_dir_path ;
2021-03-13 01:16:27 +01:00
2017-10-02 23:24:00 +02:00
const List < String > : : Element * elem = p_cmdline_args . front ( ) ;
2021-05-03 15:21:06 +02:00
while ( elem ) {
2019-07-03 09:44:53 +02:00
if ( elem - > get ( ) = = generate_all_glue_option ) {
2017-10-02 23:24:00 +02:00
const List < String > : : Element * path_elem = elem - > next ( ) ;
if ( path_elem ) {
2019-07-03 09:44:53 +02:00
glue_dir_path = path_elem - > get ( ) ;
2017-10-02 23:24:00 +02:00
elem = elem - > next ( ) ;
} else {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( generate_all_glue_option + " : No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue'). " ) ;
2021-05-03 15:21:06 +02:00
// Exit once done with invalid command line arguments
cleanup_and_exit_godot ( ) ;
2019-07-03 09:44:53 +02:00
}
2021-05-03 15:21:06 +02:00
break ;
2017-10-02 23:24:00 +02:00
}
elem = elem - > next ( ) ;
}
2021-05-03 15:21:06 +02:00
if ( glue_dir_path . length ( ) ) {
handle_cmdline_options ( glue_dir_path ) ;
2019-04-25 15:45:12 +02:00
// Exit once done
2021-05-03 15:21:06 +02:00
cleanup_and_exit_godot ( ) ;
2019-04-25 15:45:12 +02:00
}
2017-10-02 23:24:00 +02:00
}
# endif