2017-10-02 21:24:00 +00:00
/*************************************************************************/
/* bindings_generator.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 20:27:34 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-10-02 21:24:00 +00: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-04 23:50:27 +00:00
2017-10-02 21:24:00 +00:00
# include "bindings_generator.h"
2019-09-27 23:07:57 +00:00
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
2017-10-02 21:24:00 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/engine.h"
# include "core/core_constants.h"
2018-09-11 16:13:45 +00:00
# include "core/io/compression.h"
2021-06-11 12:51:48 +00:00
# include "core/io/dir_access.h"
# include "core/io/file_access.h"
2018-09-11 16:13:45 +00:00
# include "core/os/os.h"
2020-11-07 22:33:38 +00:00
# include "core/string/ucaps.h"
2021-02-22 10:06:33 +00:00
# include "main/main.h"
2017-10-02 21:24:00 +00:00
2018-09-12 00:50:16 +00:00
# include "../glue/cs_glue_version.gen.h"
2017-10-02 21:24:00 +00:00
# include "../godotsharp_defs.h"
# include "../mono_gd/gd_mono_marshal.h"
# include "../utils/path_utils.h"
# include "../utils/string_utils.h"
2018-09-04 03:40:41 +00:00
# define CS_INDENT " " // 4 whitespaces
2017-10-02 21:24:00 +00:00
# define INDENT1 CS_INDENT
# define INDENT2 INDENT1 INDENT1
# define INDENT3 INDENT2 INDENT1
# define INDENT4 INDENT3 INDENT1
# define INDENT5 INDENT4 INDENT1
# define MEMBER_BEGIN "\n" INDENT2
# define OPEN_BLOCK "{\n"
# define CLOSE_BLOCK "}\n"
# define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3
# define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4
# define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
# define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
# define CS_FIELD_MEMORYOWN "memoryOwn"
# define CS_PARAM_METHODBIND "method"
# define CS_PARAM_INSTANCE "ptr"
# define CS_SMETHOD_GETINSTANCE "GetPtr"
# define CS_METHOD_CALL "Call"
# define GLUE_HEADER_FILE "glue_header.h"
# define ICALL_PREFIX "godot_icall_"
# define SINGLETON_ICALL_SUFFIX "_get_singleton"
2020-03-14 18:20:17 +00:00
# define ICALL_GET_METHODBIND "__ClassDB_get_method"
2018-09-04 03:40:41 +00:00
# define C_LOCAL_RET "ret"
2019-01-17 23:41:41 +00:00
# define C_LOCAL_VARARG_RET "vararg_ret"
2017-10-02 21:24:00 +00:00
# define C_LOCAL_PTRCALL_ARGS "call_args"
# define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT"
# define C_NS_MONOUTILS "GDMonoUtils"
# define C_NS_MONOINTERNALS "GDMonoInternals"
# define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOINTERNALS "::tie_managed_to_unmanaged"
# define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS "::unmanaged_get_managed"
# define C_NS_MONOMARSHAL "GDMonoMarshal"
# define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL "::mono_object_to_variant"
# define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL "::variant_to_mono_object"
# define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL "::mono_string_to_godot"
# define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
# define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
# define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
2020-03-14 18:20:17 +00:00
# define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable"
# define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed"
# define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
# define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
2017-10-02 21:24:00 +00:00
2020-12-04 23:05:01 +00:00
# define BINDINGS_GENERATOR_VERSION UINT32_C(13)
2018-02-22 12:13:51 +00:00
2019-04-08 11:18:21 +00:00
const char * BindingsGenerator : : TypeInterface : : DEFAULT_VARARG_C_IN ( " \t %0 %1_in = %1; \n " ) ;
2017-10-02 21:24:00 +00:00
2019-03-18 20:59:35 +00: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 02:17:48 +00:00
static String snake_to_pascal_case ( const String & p_identifier , bool p_input_is_upper = false ) {
2017-10-02 21:24:00 +00: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 02:17:48 +00:00
if ( p_input_is_upper ) {
2020-05-16 02:03:05 +00:00
for ( int j = 1 ; j < part . length ( ) ; j + + ) {
2017-12-24 02:17:48 +00:00
part [ j ] = _find_lower ( part [ j ] ) ;
2020-05-16 02:03:05 +00:00
}
2017-12-24 02:17:48 +00:00
}
2017-10-02 21:24:00 +00: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 02:17:48 +00:00
static String snake_to_camel_case ( const String & p_identifier , bool p_input_is_upper = false ) {
2017-10-02 21:24:00 +00: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 02:17:48 +00:00
if ( i ! = 0 ) {
2017-10-02 21:24:00 +00:00
part [ 0 ] = _find_upper ( part [ 0 ] ) ;
2017-12-24 02:17:48 +00:00
}
if ( p_input_is_upper ) {
2020-05-16 02:03:05 +00:00
for ( int j = i ! = 0 ? 1 : 0 ; j < part . length ( ) ; j + + ) {
2017-12-24 02:17:48 +00:00
part [ j ] = _find_lower ( part [ j ] ) ;
2020-05-16 02:03:05 +00:00
}
2017-12-24 02:17:48 +00:00
}
2017-10-02 21:24:00 +00: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 20:59:35 +00: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 20:59:35 +00:00
return String ( ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00:00
2020-11-29 03:42:06 +00:00
DocTools * doc = EditorHelp : : get_doc_data ( ) ;
2019-03-18 20:59:35 +00: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 02:03:05 +00:00
if ( brk_pos < 0 ) {
2019-03-18 20:59:35 +00:00
brk_pos = bbcode . length ( ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00: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 02:03:05 +00:00
if ( i ! = 0 ) {
2019-03-18 20:59:35 +00:00
xml_output . append ( " <para> " ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00:00
xml_output . append ( lines [ i ] . xml_escape ( ) ) ;
2020-05-16 02:03:05 +00:00
if ( i ! = lines . size ( ) - 1 ) {
2019-03-18 20:59:35 +00:00
xml_output . append ( " </para> \n " ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00:00
}
}
}
2020-05-16 02:03:05 +00:00
if ( brk_pos = = bbcode . length ( ) ) {
2019-03-18 20:59:35 +00:00
break ; // nothing else to add
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00: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 02:03:05 +00:00
if ( i ! = 0 ) {
2019-03-18 20:59:35 +00:00
xml_output . append ( " <para> " ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00:00
xml_output . append ( lines [ i ] . xml_escape ( ) ) ;
2020-05-16 02:03:05 +00:00
if ( i ! = lines . size ( ) - 1 ) {
2019-03-18 20:59:35 +00:00
xml_output . append ( " </para> \n " ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00: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 15:24:35 +00: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 18:01:18 +00: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 20:59:35 +00:00
Vector < String > link_target_parts = link_target . split ( " . " ) ;
if ( link_target_parts . size ( ) < = 0 | | link_target_parts . size ( ) > 2 ) {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Invalid reference format: ' " + tag + " '. " ) ;
2019-03-18 20:59:35 +00: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-02-11 16:21:47 +00:00
if ( 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 " , link_target . utf8 ( ) . get_data ( ) ) ;
}
// TODO Map what we can
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
} else if ( ! target_itype | | ! target_itype - > is_object_type ) {
2019-03-18 20:59:35 +00:00
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve method reference for non-Godot.Object type in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from method reference in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
} else {
2022-02-10 15:01:22 +00:00
if ( target_cname = = " _init " ) {
// The _init method is not declared in C#, reference the constructor instead
2019-03-18 20:59:35 +00:00
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " . " ) ;
2022-02-10 15:01:22 +00:00
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " () \" /> " ) ;
} else {
const MethodInterface * target_imethod = target_itype - > find_method_by_name ( target_cname ) ;
if ( target_imethod ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " . " ) ;
xml_output . append ( target_imethod - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
} else {
ERR_PRINT ( " Cannot resolve method reference in documentation: ' " + link_target + " '. " ) ;
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
2019-03-18 20:59:35 +00:00
}
}
} else if ( link_tag = = " member " ) {
2022-02-10 15:01:22 +00:00
if ( link_target . find ( " / " ) > = 0 ) {
// Properties with '/' (slash) in the name are not declared in C#, so there is nothing to reference.
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
} else if ( ! target_itype | | ! target_itype - > is_object_type ) {
2019-03-18 20:59:35 +00:00
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve member reference for non-Godot.Object type in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from member reference in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
} else {
const PropertyInterface * target_iprop = target_itype - > find_property_by_name ( target_cname ) ;
if ( target_iprop ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " . " ) ;
xml_output . append ( target_iprop - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
2022-02-10 15:01:22 +00:00
} else {
ERR_PRINT ( " Cannot resolve member reference in documentation: ' " + link_target + " '. " ) ;
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
2019-03-18 20:59:35 +00:00
}
}
} else if ( link_tag = = " signal " ) {
2022-02-09 18:01:18 +00:00
if ( ! target_itype | | ! target_itype - > is_object_type ) {
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve signal reference for non-Godot.Object type in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from signal reference in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
} else {
const SignalInterface * target_isignal = target_itype - > find_signal_by_name ( target_cname ) ;
if ( target_isignal ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " . " ) ;
xml_output . append ( target_isignal - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
} else {
ERR_PRINT ( " Cannot resolve signal reference in documentation: ' " + link_target + " '. " ) ;
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
}
2019-03-18 20:59:35 +00:00
} else if ( link_tag = = " enum " ) {
2022-02-09 18:01:18 +00:00
const StringName search_cname = ! target_itype ? target_cname : StringName ( target_itype - > name + " . " + ( String ) target_cname ) ;
2019-03-18 20:59:35 +00:00
const Map < StringName , TypeInterface > : : Element * enum_match = enum_types . find ( search_cname ) ;
if ( ! enum_match & & search_cname ! = target_cname ) {
enum_match = enum_types . find ( target_cname ) ;
}
if ( enum_match ) {
const TypeInterface & target_enum_itype = enum_match - > value ( ) ;
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_enum_itype . proxy_name ) ; // Includes nesting class if any
xml_output . append ( " \" /> " ) ;
} else {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Cannot resolve enum reference in documentation: ' " + link_target + " '. " ) ;
2019-03-18 20:59:35 +00:00
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
2021-08-04 19:17:16 +00:00
} else if ( link_tag = = " constant " ) {
2022-02-11 16:21:47 +00:00
if ( link_target_parts [ 0 ] = = name_cache . type_at_GlobalScope ) {
2022-02-09 17:59:22 +00:00
find_constant_in_global_scope :
2022-02-09 18:01:18 +00:00
const String target_name = ( String ) target_cname ;
2019-03-27 19:01:16 +00:00
// Try to find as a global constant
const ConstantInterface * target_iconst = find_constant_by_name ( target_name , global_constants ) ;
if ( target_iconst ) {
// Found global constant
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " BINDINGS_GLOBAL_SCOPE_CLASS " . " ) ;
xml_output . append ( target_iconst - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
} else {
// Try to find as global enum constant
2020-04-01 23:20:12 +00:00
const EnumInterface * target_ienum = nullptr ;
2019-03-27 19:01:16 +00:00
2021-07-25 16:45:00 +00:00
for ( const EnumInterface & ienum : global_enums ) {
target_ienum = & ienum ;
2019-03-27 19:01:16 +00:00
target_iconst = find_constant_by_name ( target_name , target_ienum - > constants ) ;
2020-05-16 02:03:05 +00:00
if ( target_iconst ) {
2019-03-27 19:01:16 +00:00
break ;
2020-05-16 02:03:05 +00:00
}
2019-03-27 19:01:16 +00:00
}
if ( target_iconst ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_ienum - > cname ) ;
xml_output . append ( " . " ) ;
xml_output . append ( target_iconst - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
} else {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Cannot resolve global constant reference in documentation: ' " + link_target + " '. " ) ;
2019-03-27 19:01:16 +00:00
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
}
2022-02-11 16:21:47 +00:00
} else if ( ! target_itype | | ! target_itype - > is_object_type ) {
// Search in @GlobalScope as a last resort if no class was specified
if ( link_target_parts . size ( ) = = 1 ) {
goto find_constant_in_global_scope ;
}
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( target_itype ) {
OS : : get_singleton ( ) - > print ( " Cannot resolve constant reference for non-Godot.Object type in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
} else {
OS : : get_singleton ( ) - > print ( " Cannot resolve type from constant reference in documentation: %s \n " , link_target . utf8 ( ) . get_data ( ) ) ;
}
}
// TODO Map what we can
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
2019-03-27 19:01:16 +00:00
} else {
2022-02-09 18:01:18 +00:00
const String target_name = ( String ) target_cname ;
2019-03-27 19:01:16 +00:00
// Try to find the constant in the current class
const ConstantInterface * target_iconst = find_constant_by_name ( target_name , target_itype - > constants ) ;
if ( target_iconst ) {
// Found constant in current class
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " . " ) ;
xml_output . append ( target_iconst - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
} else {
// Try to find as enum constant in the current class
2020-04-01 23:20:12 +00:00
const EnumInterface * target_ienum = nullptr ;
2019-03-27 19:01:16 +00:00
2021-07-25 16:45:00 +00:00
for ( const EnumInterface & ienum : target_itype - > enums ) {
target_ienum = & ienum ;
2019-03-27 19:01:16 +00:00
target_iconst = find_constant_by_name ( target_name , target_ienum - > constants ) ;
2020-05-16 02:03:05 +00:00
if ( target_iconst ) {
2019-03-27 19:01:16 +00:00
break ;
2020-05-16 02:03:05 +00:00
}
2019-03-27 19:01:16 +00:00
}
if ( target_iconst ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " . " ) ;
xml_output . append ( target_itype - > proxy_name ) ;
xml_output . append ( " . " ) ;
xml_output . append ( target_ienum - > cname ) ;
xml_output . append ( " . " ) ;
xml_output . append ( target_iconst - > proxy_name ) ;
xml_output . append ( " \" /> " ) ;
} else {
2022-02-09 17:59:22 +00:00
// Also search in @GlobalScope as a last resort if no class was specified
if ( link_target_parts . size ( ) = = 1 ) {
goto find_constant_in_global_scope ;
}
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Cannot resolve constant reference in documentation: ' " + link_target + " '. " ) ;
2019-03-27 19:01:16 +00:00
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
}
}
2022-02-14 15:24:35 +00:00
} else if ( link_tag = = " theme_item " ) {
// We do not declare theme_items in any way in C#, so there is nothing to reference
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
2019-03-18 20:59:35 +00: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 19:01:16 +00:00
// @GlobalScope, @GDScript, etc
2019-03-18 20:59:35 +00:00
xml_output . append ( " <c> " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " </c> " ) ;
2020-02-17 21:06:54 +00:00
} else if ( tag = = " PackedByteArray " ) {
2020-03-14 18:20:17 +00:00
xml_output . append ( " <see cref= \" T:byte[] \" /> " ) ;
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
} else if ( tag = = " PackedInt32Array " ) {
2020-03-14 18:20:17 +00:00
xml_output . append ( " <see cref= \" T:int[] \" /> " ) ;
} else if ( tag = = " PackedInt64Array " ) {
xml_output . append ( " <see cref= \" T:long[] \" /> " ) ;
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
} else if ( tag = = " PackedFloat32Array " ) {
2020-03-14 18:20:17 +00:00
xml_output . append ( " <see cref= \" T:float[] \" /> " ) ;
} else if ( tag = = " PackedFloat64Array " ) {
xml_output . append ( " <see cref= \" T:double[] \" /> " ) ;
2020-02-17 21:06:54 +00:00
} else if ( tag = = " PackedStringArray " ) {
2020-03-14 18:20:17 +00:00
xml_output . append ( " <see cref= \" T:string[] \" /> " ) ;
2020-02-17 21:06:54 +00:00
} else if ( tag = = " PackedVector2Array " ) {
2020-03-14 18:20:17 +00:00
xml_output . append ( " <see cref= \" T: " BINDINGS_NAMESPACE " .Vector2[] \" /> " ) ;
2020-02-17 21:06:54 +00:00
} else if ( tag = = " PackedVector3Array " ) {
2020-03-14 18:20:17 +00:00
xml_output . append ( " <see cref= \" T: " BINDINGS_NAMESPACE " .Vector3[] \" /> " ) ;
2020-02-17 21:06:54 +00:00
} else if ( tag = = " PackedColorArray " ) {
2020-03-14 18:20:17 +00:00
xml_output . append ( " <see cref= \" T: " BINDINGS_NAMESPACE " .Color[] \" /> " ) ;
2019-03-18 20:59:35 +00: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 16:03:04 +00:00
ERR_PRINT ( " Cannot resolve type reference in documentation: ' " + tag + " '. " ) ;
2019-03-18 20:59:35 +00: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 10:41:36 +00: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 20:59:35 +00:00
} else if ( tag = = " center " ) {
2020-03-10 10:41:36 +00:00
// center alignment is not supported in xml comments
2019-03-18 20:59:35 +00: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 02:03:05 +00:00
if ( end = = - 1 ) {
2019-03-18 20:59:35 +00:00
end = bbcode . length ( ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00: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 02:03:05 +00:00
if ( end = = - 1 ) {
2019-03-18 20:59:35 +00:00
end = bbcode . length ( ) ;
2020-05-16 02:03:05 +00:00
}
2019-03-18 20:59:35 +00: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 ( ) ;
}
2018-10-18 17:41:10 +00: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 02:17:48 +00:00
2018-10-18 17:41:10 +00: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 02:17:48 +00:00
2020-05-16 02:03:05 +00:00
if ( candidate_len = = 0 ) {
2018-10-18 17:41:10 +00:00
return 0 ;
2020-05-16 02:03:05 +00:00
}
2018-10-18 17:41:10 +00:00
2021-07-26 19:31:17 +00:00
for ( const ConstantInterface & iconstant : p_ienum . constants ) {
2018-10-18 17:41:10 +00: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 02:03:05 +00:00
if ( ! hardcoded_exc ) {
2018-10-18 17:41:10 +00:00
break ;
2020-05-16 02:03:05 +00:00
}
2018-10-18 17:41:10 +00:00
}
2017-12-24 02:17:48 +00:00
}
2018-10-18 17:41:10 +00:00
candidate_len = i ;
2020-05-16 02:03:05 +00:00
if ( candidate_len = = 0 ) {
2018-10-18 17:41:10 +00:00
return 0 ;
2020-05-16 02:03:05 +00:00
}
2017-12-24 02:17:48 +00:00
}
2018-10-18 17:41:10 +00: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 16:45:00 +00:00
for ( ConstantInterface & iconstant : p_ienum . constants ) {
2018-10-18 17:41:10 +00:00
int curr_prefix_length = p_prefix_length ;
2021-07-25 16:45:00 +00:00
String constant_name = iconstant . name ;
2018-10-18 17:41:10 +00:00
Vector < String > parts = constant_name . split ( " _ " , /* p_allow_empty: */ true ) ;
2020-05-16 02:03:05 +00:00
if ( parts . size ( ) < = curr_prefix_length ) {
2018-10-18 17:41:10 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2018-10-18 17:41:10 +00:00
2022-02-04 08:32:20 +00:00
if ( is_digit ( parts [ curr_prefix_length ] [ 0 ] ) ) {
2018-10-18 17:41:10 +00: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 08:32:20 +00:00
if ( ! is_digit ( parts [ curr_prefix_length ] [ 0 ] ) ) {
2018-10-18 17:41:10 +00:00
break ;
2020-05-16 02:03:05 +00:00
}
2018-10-18 17:41:10 +00:00
}
}
constant_name = " " ;
for ( int i = curr_prefix_length ; i < parts . size ( ) ; i + + ) {
2020-05-16 02:03:05 +00:00
if ( i > curr_prefix_length ) {
2018-10-18 17:41:10 +00:00
constant_name + = " _ " ;
2020-05-16 02:03:05 +00:00
}
2018-10-18 17:41:10 +00:00
constant_name + = parts [ i ] ;
}
2021-07-25 16:45:00 +00:00
iconstant . proxy_name = snake_to_pascal_case ( constant_name , true ) ;
2018-10-18 17:41:10 +00:00
}
}
2017-12-24 02:17:48 +00:00
}
2017-10-02 21:24:00 +00:00
void BindingsGenerator : : _generate_method_icalls ( const TypeInterface & p_itype ) {
2021-07-16 03:45:57 +00:00
for ( const MethodInterface & imethod : p_itype . methods ) {
2020-05-16 02:03:05 +00:00
if ( imethod . is_virtual ) {
2017-10-02 21:24:00 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2018-04-28 20:25:25 +00:00
const TypeInterface * return_type = _get_type_or_placeholder ( imethod . return_type ) ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
String im_sig = " IntPtr " CS_PARAM_METHODBIND " , " ;
String im_unique_sig = imethod . return_type . cname . operator String ( ) + " ,IntPtr,IntPtr " ;
2018-01-25 22:44:37 +00:00
im_sig + = " IntPtr " CS_PARAM_INSTANCE ;
2017-10-02 21:24:00 +00:00
// Get arguments information
int i = 0 ;
2021-07-25 16:45:00 +00:00
for ( const ArgumentInterface & iarg : imethod . arguments ) {
const TypeInterface * arg_type = _get_type_or_placeholder ( iarg . type ) ;
2017-10-02 21:24:00 +00:00
im_sig + = " , " ;
im_sig + = arg_type - > im_type_in ;
im_sig + = " arg " ;
im_sig + = itos ( i + 1 ) ;
2018-09-04 03:40:41 +00:00
im_unique_sig + = " , " ;
im_unique_sig + = get_unique_sig ( * arg_type ) ;
2017-10-02 21:24:00 +00:00
i + + ;
}
2019-11-14 20:48:49 +00:00
String im_type_out = return_type - > im_type_out ;
if ( return_type - > ret_as_byref_arg ) {
// Doesn't affect the unique signature
im_type_out = " void " ;
im_sig + = " , " ;
im_sig + = return_type - > im_type_out ;
im_sig + = " argRet " ;
i + + ;
}
2018-09-04 03:40:41 +00:00
// godot_icall_{argc}_{icallcount}
2018-01-25 22:44:37 +00:00
String icall_method = ICALL_PREFIX ;
2018-09-04 03:40:41 +00:00
icall_method + = itos ( imethod . arguments . size ( ) ) ;
icall_method + = " _ " ;
icall_method + = itos ( method_icalls . size ( ) ) ;
2017-10-02 21:24:00 +00:00
2019-11-14 20:48:49 +00:00
InternalCall im_icall = InternalCall ( p_itype . api_type , icall_method , im_type_out , im_sig , im_unique_sig ) ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
List < InternalCall > : : Element * match = method_icalls . find ( im_icall ) ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
if ( match ) {
2020-05-16 02:03:05 +00:00
if ( p_itype . api_type ! = ClassDB : : API_EDITOR ) {
2018-09-04 03:40:41 +00:00
match - > get ( ) . editor_only = false ;
2020-05-16 02:03:05 +00:00
}
2021-07-25 16:45:00 +00:00
method_icalls_map . insert ( & imethod , & match - > get ( ) ) ;
2017-10-02 21:24:00 +00:00
} else {
2018-09-04 03:40:41 +00:00
List < InternalCall > : : Element * added = method_icalls . push_back ( im_icall ) ;
2021-07-25 16:45:00 +00:00
method_icalls_map . insert ( & imethod , & added - > get ( ) ) ;
2017-10-02 21:24:00 +00:00
}
}
}
2021-01-13 10:17:39 +00:00
void BindingsGenerator : : _generate_array_extensions ( StringBuilder & p_output ) {
p_output . append ( " using System; \n \n " ) ;
p_output . append ( " namespace " BINDINGS_NAMESPACE " \n " OPEN_BLOCK ) ;
// The class where we put the extensions doesn't matter, so just use "GD".
p_output . append ( INDENT1 " public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS " \n " INDENT1 " { " ) ;
# define ARRAY_IS_EMPTY(m_type) \
p_output . append ( " \n " INDENT2 " /// <summary> \n " ) ; \
p_output . append ( INDENT2 " /// Returns true if this " # m_type " array is empty or doesn't exist. \n " ) ; \
p_output . append ( INDENT2 " /// </summary> \n " ) ; \
p_output . append ( INDENT2 " /// <param name= \" instance \" >The " # m_type " array check.</param> \n " ) ; \
p_output . append ( INDENT2 " /// <returns>Whether or not the array is empty.</returns> \n " ) ; \
p_output . append ( INDENT2 " public static bool IsEmpty(this " # m_type " [] instance) \n " ) ; \
p_output . append ( INDENT2 OPEN_BLOCK ) ; \
p_output . append ( INDENT3 " return instance == null || instance.Length == 0; \n " ) ; \
p_output . append ( INDENT2 CLOSE_BLOCK ) ;
# define ARRAY_JOIN(m_type) \
p_output . append ( " \n " INDENT2 " /// <summary> \n " ) ; \
p_output . append ( INDENT2 " /// Converts this " # m_type " array to a string delimited by the given string. \n " ) ; \
p_output . append ( INDENT2 " /// </summary> \n " ) ; \
p_output . append ( INDENT2 " /// <param name= \" instance \" >The " # m_type " array to convert.</param> \n " ) ; \
p_output . append ( INDENT2 " /// <param name= \" delimiter \" >The delimiter to use between items.</param> \n " ) ; \
p_output . append ( INDENT2 " /// <returns>A single string with all items.</returns> \n " ) ; \
p_output . append ( INDENT2 " public static string Join(this " # m_type " [] instance, string delimiter = \" , \" ) \n " ) ; \
p_output . append ( INDENT2 OPEN_BLOCK ) ; \
p_output . append ( INDENT3 " return String.Join(delimiter, instance); \n " ) ; \
p_output . append ( INDENT2 CLOSE_BLOCK ) ;
# define ARRAY_STRINGIFY(m_type) \
p_output . append ( " \n " INDENT2 " /// <summary> \n " ) ; \
p_output . append ( INDENT2 " /// Converts this " # m_type " array to a string with brackets. \n " ) ; \
p_output . append ( INDENT2 " /// </summary> \n " ) ; \
p_output . append ( INDENT2 " /// <param name= \" instance \" >The " # m_type " array to convert.</param> \n " ) ; \
p_output . append ( INDENT2 " /// <returns>A single string with all items.</returns> \n " ) ; \
p_output . append ( INDENT2 " public static string Stringify(this " # m_type " [] instance) \n " ) ; \
p_output . append ( INDENT2 OPEN_BLOCK ) ; \
p_output . append ( INDENT3 " return \" [ \" + instance.Join() + \" ] \" ; \n " ) ; \
p_output . append ( INDENT2 CLOSE_BLOCK ) ;
# 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 ) ;
# undef ARRAY_ALL
# undef ARRAY_IS_EMPTY
# undef ARRAY_JOIN
# undef ARRAY_STRINGIFY
p_output . append ( INDENT1 CLOSE_BLOCK ) ; // End of GD class.
p_output . append ( CLOSE_BLOCK ) ; // End of namespace.
}
2019-04-18 23:10:08 +00:00
void BindingsGenerator : : _generate_global_constants ( StringBuilder & p_output ) {
2017-12-24 02:17:48 +00:00
// Constants (in partial GD class)
2019-04-18 23:10:08 +00:00
p_output . append ( " \n #pragma warning disable CS1591 // Disable warning: "
" 'Missing XML comment for publicly visible type or member' \n " ) ;
2019-03-18 20:59:35 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( " namespace " BINDINGS_NAMESPACE " \n " OPEN_BLOCK ) ;
p_output . append ( INDENT1 " public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS " \n " INDENT1 " { " ) ;
2017-12-24 02:17:48 +00:00
2021-07-16 03:45:57 +00:00
for ( const ConstantInterface & iconstant : global_constants ) {
2017-12-24 02:17:48 +00:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2020-04-01 23:20:12 +00:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , nullptr ) ;
2019-04-18 23:10:08 +00:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 02:17:48 +00:00
2019-03-18 20:59:35 +00:00
if ( summary_lines . size ( ) ) {
2019-04-18 23:10:08 +00:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-12-24 02:17:48 +00:00
2019-03-18 20:59:35 +00:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-12-24 02:17:48 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// </summary> " ) ;
2019-03-18 20:59:35 +00:00
}
2017-12-24 02:17:48 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( MEMBER_BEGIN " public const int " ) ;
p_output . append ( iconstant . proxy_name ) ;
p_output . append ( " = " ) ;
p_output . append ( itos ( iconstant . value ) ) ;
p_output . append ( " ; " ) ;
2017-12-24 02:17:48 +00:00
}
2020-12-15 12:04:21 +00:00
if ( ! global_constants . is_empty ( ) ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " \n " ) ;
2020-05-16 02:03:05 +00:00
}
2017-12-24 02:17:48 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT1 CLOSE_BLOCK ) ; // end of GD class
2017-12-24 02:17:48 +00:00
// Enums
2021-07-24 13:46:25 +00:00
for ( const EnumInterface & ienum : global_enums ) {
2020-12-15 12:04:21 +00:00
CRASH_COND ( ienum . constants . is_empty ( ) ) ;
2017-12-24 02:17:48 +00: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...
2019-08-09 01:39:45 +00:00
_log ( " Declaring global enum '%s' inside static class '%s' \n " , enum_proxy_name . utf8 ( ) . get_data ( ) , enum_class_name . utf8 ( ) . get_data ( ) ) ;
2017-12-24 02:17:48 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( " \n " INDENT1 " public static partial class " ) ;
p_output . append ( enum_class_name ) ;
p_output . append ( " \n " INDENT1 OPEN_BLOCK ) ;
2017-12-24 02:17:48 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( " \n " INDENT1 " public enum " ) ;
p_output . append ( enum_proxy_name ) ;
p_output . append ( " \n " INDENT1 OPEN_BLOCK ) ;
2017-12-24 02:17:48 +00:00
2021-07-25 16:45:00 +00:00
const ConstantInterface & last = ienum . constants . back ( ) - > get ( ) ;
for ( const ConstantInterface & iconstant : ienum . constants ) {
2017-12-24 02:17:48 +00:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2020-04-01 23:20:12 +00:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , nullptr ) ;
2019-04-18 23:10:08 +00:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 02:17:48 +00:00
2019-03-18 20:59:35 +00:00
if ( summary_lines . size ( ) ) {
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// <summary> \n " ) ;
2017-12-24 02:17:48 +00:00
2019-03-18 20:59:35 +00:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-12-24 02:17:48 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// </summary> \n " ) ;
2019-03-18 20:59:35 +00:00
}
2017-12-24 02:17:48 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 ) ;
p_output . append ( iconstant . proxy_name ) ;
p_output . append ( " = " ) ;
p_output . append ( itos ( iconstant . value ) ) ;
2021-07-25 16:45:00 +00:00
p_output . append ( & iconstant ! = & last ? " , \n " : " \n " ) ;
2017-12-24 02:17:48 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT1 CLOSE_BLOCK ) ;
2017-12-24 02:17:48 +00:00
2020-05-16 02:03:05 +00:00
if ( enum_in_static_class ) {
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT1 CLOSE_BLOCK ) ;
2020-05-16 02:03:05 +00:00
}
2017-12-24 02:17:48 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( CLOSE_BLOCK ) ; // end of namespace
2019-03-18 20:59:35 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( " \n #pragma warning restore CS1591 \n " ) ;
2017-12-24 02:17:48 +00:00
}
2019-12-28 18:12:32 +00:00
Error BindingsGenerator : : generate_cs_core_project ( const String & p_proj_dir ) {
2019-09-24 13:09:54 +00:00
ERR_FAIL_COND_V ( ! initialized , ERR_UNCONFIGURED ) ;
2017-10-02 21:24:00 +00:00
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
2019-07-03 07:44:53 +00:00
if ( ! DirAccess : : exists ( p_proj_dir ) ) {
Error err = da - > make_dir_recursive ( p_proj_dir ) ;
2019-09-25 08:28:50 +00:00
ERR_FAIL_COND_V_MSG ( err ! = OK , ERR_CANT_CREATE , " Cannot create directory ' " + p_proj_dir + " '. " ) ;
2017-10-02 21:24:00 +00:00
}
2019-07-03 07:44:53 +00:00
da - > change_dir ( p_proj_dir ) ;
2019-12-28 18:12:32 +00:00
da - > make_dir ( " Generated " ) ;
da - > make_dir ( " Generated/GodotObjects " ) ;
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00: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 21:24:00 +00:00
2017-12-24 02:17:48 +00:00
// Generate source file for global scope constants and enums
{
2019-04-18 23:10:08 +00:00
StringBuilder constants_source ;
2017-12-24 02:17:48 +00:00
_generate_global_constants ( constants_source ) ;
2019-12-28 18:12:32 +00:00
String output_file = path : : join ( base_gen_dir , BINDINGS_GLOBAL_SCOPE_CLASS " _constants.cs " ) ;
2017-12-24 02:17:48 +00:00
Error save_err = _save_file ( output_file , constants_source ) ;
2020-05-16 02:03:05 +00:00
if ( save_err ! = OK ) {
2017-12-24 02:17:48 +00:00
return save_err ;
2020-05-16 02:03:05 +00:00
}
2017-12-24 02:17:48 +00:00
2019-12-28 18:12:32 +00:00
compile_items . push_back ( output_file ) ;
2017-12-24 02:17:48 +00:00
}
2021-01-13 10:17:39 +00: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 18:12:32 +00:00
compile_items . push_back ( output_file ) ;
2017-12-24 02:17:48 +00:00
}
2018-01-09 18:06:59 +00:00
for ( OrderedHashMap < StringName , TypeInterface > : : Element E = obj_types . front ( ) ; E ; E = E . next ( ) ) {
const TypeInterface & itype = E . get ( ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( itype . api_type = = ClassDB : : API_EDITOR ) {
2017-10-02 21:24:00 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
String output_file = path : : join ( godot_objects_gen_dir , itype . proxy_name + " .cs " ) ;
2018-01-09 18:06:59 +00:00
Error err = _generate_cs_type ( itype , output_file ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( err = = ERR_SKIP ) {
2017-10-02 21:24:00 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( err ! = OK ) {
2017-10-02 21:24:00 +00:00
return err ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
compile_items . push_back ( output_file ) ;
2017-10-02 21:24:00 +00:00
}
// Generate sources from compressed files
2019-04-18 23:10:08 +00:00
StringBuilder cs_icalls_content ;
cs_icalls_content . append ( " using System; \n "
" using System.Runtime.CompilerServices; \n "
" \n " ) ;
cs_icalls_content . append ( " namespace " BINDINGS_NAMESPACE " \n " OPEN_BLOCK ) ;
2020-03-14 18:20:17 +00:00
cs_icalls_content . append ( INDENT1 " internal static class " BINDINGS_CLASS_NATIVECALLS " \n " INDENT1 " { " ) ;
2019-04-18 23:10:08 +00:00
cs_icalls_content . append ( MEMBER_BEGIN " internal static ulong godot_api_hash = " ) ;
cs_icalls_content . append ( String : : num_uint64 ( GDMono : : get_singleton ( ) - > get_api_core_hash ( ) ) + " ; \n " ) ;
cs_icalls_content . append ( MEMBER_BEGIN " internal static uint bindings_version = " ) ;
cs_icalls_content . append ( String : : num_uint64 ( BINDINGS_GENERATOR_VERSION ) + " ; \n " ) ;
cs_icalls_content . append ( MEMBER_BEGIN " internal static uint cs_glue_version = " ) ;
cs_icalls_content . append ( String : : num_uint64 ( CS_GLUE_VERSION ) + " ; \n " ) ;
# define ADD_INTERNAL_CALL(m_icall) \
if ( ! m_icall . editor_only ) { \
cs_icalls_content . append ( MEMBER_BEGIN " [MethodImpl(MethodImplOptions.InternalCall)] \n " ) ; \
2020-03-14 18:20:17 +00:00
cs_icalls_content . append ( INDENT2 " internal static extern " ) ; \
2019-04-18 23:10:08 +00:00
cs_icalls_content . append ( m_icall . im_type_out + " " ) ; \
cs_icalls_content . append ( m_icall . name + " ( " ) ; \
cs_icalls_content . append ( m_icall . im_sig + " ); \n " ) ; \
2017-10-02 21:24:00 +00:00
}
2021-07-25 16:45:00 +00:00
for ( const InternalCall & internal_call : core_custom_icalls ) {
ADD_INTERNAL_CALL ( internal_call ) ;
2020-05-16 02:03:05 +00:00
}
2021-07-25 16:45:00 +00:00
for ( const InternalCall & internal_call : method_icalls ) {
ADD_INTERNAL_CALL ( internal_call ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
# undef ADD_INTERNAL_CALL
2019-04-18 23:10:08 +00:00
cs_icalls_content . append ( INDENT1 CLOSE_BLOCK CLOSE_BLOCK ) ;
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
String internal_methods_file = path : : join ( base_gen_dir , BINDINGS_CLASS_NATIVECALLS " .cs " ) ;
2017-10-02 21:24:00 +00:00
Error err = _save_file ( internal_methods_file , cs_icalls_content ) ;
2020-05-16 02:03:05 +00:00
if ( err ! = OK ) {
2017-10-02 21:24:00 +00:00
return err ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
compile_items . push_back ( internal_methods_file ) ;
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 02:03:05 +00:00
if ( err ! = OK ) {
2019-12-28 18:12:32 +00:00
return err ;
2020-05-16 02:03:05 +00:00
}
2017-10-24 14:18:47 +00:00
2017-10-02 21:24:00 +00:00
return OK ;
}
2019-12-28 18:12:32 +00:00
Error BindingsGenerator : : generate_cs_editor_project ( const String & p_proj_dir ) {
2019-09-24 13:09:54 +00:00
ERR_FAIL_COND_V ( ! initialized , ERR_UNCONFIGURED ) ;
2017-10-02 21:24:00 +00:00
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
2019-07-03 07:44:53 +00:00
if ( ! DirAccess : : exists ( p_proj_dir ) ) {
Error err = da - > make_dir_recursive ( p_proj_dir ) ;
2017-10-02 21:24:00 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
2019-07-03 07:44:53 +00:00
da - > change_dir ( p_proj_dir ) ;
2019-12-28 18:12:32 +00: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 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
Vector < String > compile_items ;
2017-10-02 21:24:00 +00:00
2018-01-09 18:06:59 +00:00
for ( OrderedHashMap < StringName , TypeInterface > : : Element E = obj_types . front ( ) ; E ; E = E . next ( ) ) {
const TypeInterface & itype = E . get ( ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( itype . api_type ! = ClassDB : : API_EDITOR ) {
2017-10-02 21:24:00 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
String output_file = path : : join ( godot_objects_gen_dir , itype . proxy_name + " .cs " ) ;
2018-01-09 18:06:59 +00:00
Error err = _generate_cs_type ( itype , output_file ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( err = = ERR_SKIP ) {
2017-10-02 21:24:00 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( err ! = OK ) {
2017-10-02 21:24:00 +00:00
return err ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
compile_items . push_back ( output_file ) ;
2017-10-02 21:24:00 +00:00
}
2019-04-18 23:10:08 +00:00
StringBuilder cs_icalls_content ;
cs_icalls_content . append ( " using System; \n "
" using System.Runtime.CompilerServices; \n "
" \n " ) ;
cs_icalls_content . append ( " namespace " BINDINGS_NAMESPACE " \n " OPEN_BLOCK ) ;
cs_icalls_content . append ( INDENT1 " internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR " \n " INDENT1 OPEN_BLOCK ) ;
cs_icalls_content . append ( INDENT2 " internal static ulong godot_api_hash = " ) ;
cs_icalls_content . append ( String : : num_uint64 ( GDMono : : get_singleton ( ) - > get_api_editor_hash ( ) ) + " ; \n " ) ;
cs_icalls_content . append ( INDENT2 " internal static uint bindings_version = " ) ;
cs_icalls_content . append ( String : : num_uint64 ( BINDINGS_GENERATOR_VERSION ) + " ; \n " ) ;
cs_icalls_content . append ( INDENT2 " internal static uint cs_glue_version = " ) ;
cs_icalls_content . append ( String : : num_uint64 ( CS_GLUE_VERSION ) + " ; \n " ) ;
cs_icalls_content . append ( " \n " ) ;
# define ADD_INTERNAL_CALL(m_icall) \
if ( m_icall . editor_only ) { \
cs_icalls_content . append ( INDENT2 " [MethodImpl(MethodImplOptions.InternalCall)] \n " ) ; \
2020-03-14 18:20:17 +00:00
cs_icalls_content . append ( INDENT2 " internal static extern " ) ; \
2019-04-18 23:10:08 +00:00
cs_icalls_content . append ( m_icall . im_type_out + " " ) ; \
cs_icalls_content . append ( m_icall . name + " ( " ) ; \
cs_icalls_content . append ( m_icall . im_sig + " ); \n " ) ; \
2017-10-02 21:24:00 +00:00
}
2021-07-25 16:45:00 +00:00
for ( const InternalCall & internal_call : editor_custom_icalls ) {
ADD_INTERNAL_CALL ( internal_call ) ;
2020-05-16 02:03:05 +00:00
}
2021-07-25 16:45:00 +00:00
for ( const InternalCall & internal_call : method_icalls ) {
ADD_INTERNAL_CALL ( internal_call ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
# undef ADD_INTERNAL_CALL
2019-04-18 23:10:08 +00:00
cs_icalls_content . append ( INDENT1 CLOSE_BLOCK CLOSE_BLOCK ) ;
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
String internal_methods_file = path : : join ( base_gen_dir , BINDINGS_CLASS_NATIVECALLS_EDITOR " .cs " ) ;
2017-10-02 21:24:00 +00:00
Error err = _save_file ( internal_methods_file , cs_icalls_content ) ;
2020-05-16 02:03:05 +00:00
if ( err ! = OK ) {
2017-10-02 21:24:00 +00:00
return err ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
compile_items . push_back ( internal_methods_file ) ;
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 02:03:05 +00:00
if ( err ! = OK ) {
2019-12-28 18:12:32 +00:00
return err ;
2020-05-16 02:03:05 +00:00
}
2018-11-08 00:05:19 +00:00
return OK ;
}
2017-10-02 21:24:00 +00:00
2019-04-25 13:45:12 +00:00
Error BindingsGenerator : : generate_cs_api ( const String & p_output_dir ) {
2019-09-24 13:09:54 +00:00
ERR_FAIL_COND_V ( ! initialized , ERR_UNCONFIGURED ) ;
2019-07-08 13:16:35 +00:00
String output_dir = path : : abspath ( path : : realpath ( p_output_dir ) ) ;
2019-07-03 07:44:53 +00:00
2018-11-08 00:05:19 +00:00
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
2019-07-03 07:44:53 +00:00
if ( ! DirAccess : : exists ( output_dir ) ) {
Error err = da - > make_dir_recursive ( output_dir ) ;
2018-11-08 00:05:19 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
2019-07-03 07:44:53 +00:00
Error proj_err ;
2018-11-08 00:05:19 +00:00
2019-07-03 07:44:53 +00:00
// Generate GodotSharp source files
2018-11-08 00:05:19 +00:00
2019-07-03 07:44:53 +00:00
String core_proj_dir = output_dir . plus_file ( CORE_API_ASSEMBLY_NAME ) ;
2018-11-08 00:05:19 +00:00
2019-12-28 18:12:32 +00:00
proj_err = generate_cs_core_project ( core_proj_dir ) ;
2018-11-08 00:05:19 +00:00
if ( proj_err ! = OK ) {
2019-08-09 01:39:45 +00:00
ERR_PRINT ( " Generation of the Core API C# project failed. " ) ;
2018-11-08 00:05:19 +00:00
return proj_err ;
}
2019-07-03 07:44:53 +00:00
// Generate GodotSharpEditor source files
String editor_proj_dir = output_dir . plus_file ( EDITOR_API_ASSEMBLY_NAME ) ;
2019-12-28 18:12:32 +00:00
proj_err = generate_cs_editor_project ( editor_proj_dir ) ;
2018-11-08 00:05:19 +00:00
if ( proj_err ! = OK ) {
2019-08-09 01:39:45 +00:00
ERR_PRINT ( " Generation of the Editor API C# project failed. " ) ;
2018-11-08 00:05:19 +00:00
return proj_err ;
}
2017-10-02 21:24:00 +00:00
2019-12-28 18:12:32 +00:00
_log ( " The Godot API sources were successfully generated \n " ) ;
2019-07-03 07:44:53 +00:00
2017-10-02 21:24:00 +00:00
return OK ;
}
2018-09-04 03:40:41 +00: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 01:39:45 +00:00
// explicitly as 'new' to avoid the warning (and we must print a message about it).
2018-09-04 03:40:41 +00: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 21:24:00 +00:00
Error BindingsGenerator : : _generate_cs_type ( const TypeInterface & itype , const String & p_output_file ) {
2018-09-04 03:40:41 +00:00
CRASH_COND ( ! itype . is_object_type ) ;
2017-12-24 02:17:48 +00:00
bool is_derived_type = itype . base_name ! = StringName ( ) ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00: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 16:03:15 +00:00
CRASH_COND ( itype . is_ref_counted ) ;
2018-09-04 03:40:41 +00:00
CRASH_COND ( itype . is_singleton ) ;
}
2017-10-02 21:24:00 +00:00
List < InternalCall > & custom_icalls = itype . api_type = = ClassDB : : API_EDITOR ? editor_custom_icalls : core_custom_icalls ;
2019-04-25 13:45:12 +00:00
_log ( " Generating %s.cs... \n " , itype . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
String ctor_method ( ICALL_PREFIX + itype . proxy_name + " _Ctor " ) ; // Used only for derived types
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
StringBuilder output ;
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " using System; \n " ) ; // IntPtr
output . append ( " using System.Diagnostics; \n " ) ; // DebuggerBrowsable
2018-10-16 15:22:27 +00:00
2019-04-18 23:10:08 +00: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 15:22:27 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " \n namespace " BINDINGS_NAMESPACE " \n " OPEN_BLOCK ) ;
2017-10-02 21:24:00 +00:00
const DocData : : ClassDoc * class_doc = itype . class_doc ;
if ( class_doc & & class_doc - > description . size ( ) ) {
2019-03-18 20:59:35 +00:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( class_doc - > description ) , & itype ) ;
2019-04-18 23:10:08 +00:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 21:24:00 +00:00
2019-03-18 20:59:35 +00:00
if ( summary_lines . size ( ) ) {
2019-04-18 23:10:08 +00:00
output . append ( INDENT1 " /// <summary> \n " ) ;
2017-10-02 21:24:00 +00:00
2019-03-18 20:59:35 +00:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-18 23:10:08 +00:00
output . append ( INDENT1 " /// " ) ;
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2017-10-02 21:24:00 +00:00
}
2019-04-18 23:10:08 +00:00
output . append ( INDENT1 " /// </summary> \n " ) ;
2019-03-18 20:59:35 +00:00
}
2017-10-02 21:24:00 +00:00
}
2019-04-18 23:10:08 +00:00
output . append ( INDENT1 " public " ) ;
2018-09-04 03:40:41 +00:00
if ( itype . is_singleton ) {
2019-04-18 23:10:08 +00:00
output . append ( " static partial class " ) ;
2018-08-27 18:39:51 +00:00
} else {
2019-04-18 23:10:08 +00:00
output . append ( itype . is_instantiable ? " partial class " : " abstract partial class " ) ;
2018-08-27 18:39:51 +00:00
}
2019-04-18 23:10:08 +00:00
output . append ( itype . proxy_name ) ;
2017-10-02 21:24:00 +00:00
2018-01-25 22:44:37 +00:00
if ( itype . is_singleton ) {
2019-04-18 23:10:08 +00:00
output . append ( " \n " ) ;
2018-09-04 03:40:41 +00:00
} else if ( is_derived_type ) {
if ( obj_types . has ( itype . base_name ) ) {
2019-04-18 23:10:08 +00:00
output . append ( " : " ) ;
output . append ( obj_types [ itype . base_name ] . proxy_name ) ;
output . append ( " \n " ) ;
2018-09-04 03:40:41 +00:00
} else {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Base type ' " + itype . base_name . operator String ( ) + " ' does not exist, for class ' " + itype . name + " '. " ) ;
2018-09-04 03:40:41 +00:00
return ERR_INVALID_DATA ;
}
2017-10-02 21:24:00 +00:00
}
2019-04-18 23:10:08 +00:00
output . append ( INDENT1 " { " ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
// Add constants
2017-10-02 21:24:00 +00:00
2021-07-16 03:45:57 +00:00
for ( const ConstantInterface & iconstant : itype . constants ) {
2020-05-16 02:03:05 +00: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 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( summary_lines . size ( ) ) {
output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
output . append ( INDENT2 " /// " ) ;
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2019-03-18 20:59:35 +00:00
}
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
output . append ( INDENT2 " /// </summary> " ) ;
}
2017-10-02 21:24:00 +00:00
}
2020-05-16 02:03:05 +00:00
output . append ( MEMBER_BEGIN " public const int " ) ;
output . append ( iconstant . proxy_name ) ;
output . append ( " = " ) ;
output . append ( itos ( iconstant . value ) ) ;
output . append ( " ; " ) ;
}
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( itype . constants . size ( ) ) {
output . append ( " \n " ) ;
}
2017-12-24 02:17:48 +00:00
2020-05-16 02:03:05 +00:00
// Add enums
2017-12-24 02:17:48 +00:00
2021-07-16 03:45:57 +00: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 02:17:48 +00:00
2020-05-16 02:03:05 +00:00
output . append ( MEMBER_BEGIN " public enum " ) ;
output . append ( ienum . cname . operator String ( ) ) ;
output . append ( MEMBER_BEGIN OPEN_BLOCK ) ;
2017-12-24 02:17:48 +00:00
2021-07-25 16:45:00 +00:00
const ConstantInterface & last = ienum . constants . back ( ) - > get ( ) ;
for ( const ConstantInterface & iconstant : ienum . constants ) {
2020-05-16 02:03:05 +00: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 02:17:48 +00:00
2020-05-16 02:03:05 +00:00
if ( summary_lines . size ( ) ) {
output . append ( INDENT3 " /// <summary> \n " ) ;
2017-12-24 02:17:48 +00:00
2020-05-16 02:03:05 +00:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
output . append ( INDENT3 " /// " ) ;
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2019-03-18 20:59:35 +00:00
}
2017-12-24 02:17:48 +00:00
2020-05-16 02:03:05 +00:00
output . append ( INDENT3 " /// </summary> \n " ) ;
}
2017-12-24 02:17:48 +00:00
}
2020-05-16 02:03:05 +00:00
output . append ( INDENT3 ) ;
output . append ( iconstant . proxy_name ) ;
output . append ( " = " ) ;
output . append ( itos ( iconstant . value ) ) ;
2021-07-25 16:45:00 +00:00
output . append ( & iconstant ! = & last ? " , \n " : " \n " ) ;
2017-12-24 02:17:48 +00:00
}
2020-05-16 02:03:05 +00:00
output . append ( INDENT2 CLOSE_BLOCK ) ;
2017-10-02 21:24:00 +00:00
}
2020-05-16 02:03:05 +00:00
// Add properties
2021-07-16 03:45:57 +00:00
for ( const PropertyInterface & iprop : itype . properties ) {
2020-05-16 02:03:05 +00: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 19:39:02 +00:00
2018-09-04 03:40:41 +00:00
if ( itype . is_singleton ) {
2017-10-02 21:24:00 +00:00
// Add the type name and the singleton pointer as static fields
2019-04-18 23:10:08 +00:00
output . append ( MEMBER_BEGIN " private static Godot.Object singleton; \n " ) ;
output . append ( MEMBER_BEGIN " public static Godot.Object Singleton \n " INDENT2 " { \n " INDENT3
" get \n " INDENT3 " { \n " INDENT4 " if (singleton == null) \n " INDENT5
2019-07-03 07:44:53 +00:00
" singleton = Engine.GetSingleton(typeof( " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " ).Name); \n " INDENT4 " return singleton; \n " INDENT3 " } \n " INDENT2 " } \n " ) ;
2019-04-18 23:10:08 +00:00
2020-03-14 18:20:17 +00:00
output . append ( MEMBER_BEGIN " private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \" " ) ;
2019-04-18 23:10:08 +00:00
output . append ( itype . name ) ;
output . append ( " \" ; \n " ) ;
output . append ( INDENT2 " internal static IntPtr " BINDINGS_PTR_FIELD " = " ) ;
output . append ( itype . api_type = = ClassDB : : API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS ) ;
output . append ( " . " ICALL_PREFIX ) ;
output . append ( itype . name ) ;
output . append ( SINGLETON_ICALL_SUFFIX " (); \n " ) ;
2018-09-04 03:40:41 +00:00
} else if ( is_derived_type ) {
2017-10-02 21:24:00 +00:00
// Add member fields
2020-03-14 18:20:17 +00:00
output . append ( MEMBER_BEGIN " private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \" " ) ;
2019-04-18 23:10:08 +00:00
output . append ( itype . name ) ;
output . append ( " \" ; \n " ) ;
2017-10-02 21:24:00 +00:00
// Add default constructor
if ( itype . is_instantiable ) {
2019-04-18 23:10:08 +00:00
output . append ( MEMBER_BEGIN " public " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " () : this( " ) ;
output . append ( itype . memory_own ? " true " : " false " ) ;
2017-10-02 21:24:00 +00:00
// The default constructor may also be called by the engine when instancing existing native objects
// The engine will initialize the pointer field of the managed side before calling the constructor
// This is why we only allocate a new native object from the constructor if the pointer field is not set
2019-04-18 23:10:08 +00:00
output . append ( " ) \n " OPEN_BLOCK_L2 " if ( " BINDINGS_PTR_FIELD " == IntPtr.Zero) \n " INDENT4 BINDINGS_PTR_FIELD " = " ) ;
output . append ( itype . api_type = = ClassDB : : API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS ) ;
output . append ( " . " + ctor_method ) ;
2020-10-26 05:59:08 +00:00
output . append ( " (this); \n " INDENT3 " _InitializeGodotScriptInstanceInternals(); \n " CLOSE_BLOCK_L2 ) ;
2017-10-02 21:24:00 +00:00
} else {
// Hide the constructor
2019-04-18 23:10:08 +00:00
output . append ( MEMBER_BEGIN " internal " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " () {} \n " ) ;
2017-10-02 21:24:00 +00:00
}
// Add.. em.. trick constructor. Sort of.
2019-04-18 23:10:08 +00:00
output . append ( MEMBER_BEGIN " internal " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " (bool " CS_FIELD_MEMORYOWN " ) : base( " CS_FIELD_MEMORYOWN " ) {} \n " ) ;
2017-10-02 21:24:00 +00:00
}
2017-10-29 01:37:13 +00:00
int method_bind_count = 0 ;
2021-07-16 03:45:57 +00:00
for ( const MethodInterface & imethod : itype . methods ) {
2017-10-29 01:37:13 +00:00
Error method_err = _generate_cs_method ( itype , imethod , method_bind_count , output ) ;
2019-08-09 01:39:45 +00:00
ERR_FAIL_COND_V_MSG ( method_err ! = OK , method_err ,
" Failed to generate method ' " + imethod . name + " ' for class ' " + itype . name + " '. " ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2021-07-16 03:45:57 +00:00
for ( const SignalInterface & isignal : itype . signals_ ) {
2020-03-14 18:20:17 +00: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 + " '. " ) ;
}
2017-10-29 01:37:13 +00:00
if ( itype . is_singleton ) {
InternalCall singleton_icall = InternalCall ( itype . api_type , ICALL_PREFIX + itype . name + SINGLETON_ICALL_SUFFIX , " IntPtr " ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( ! find_icall_by_name ( singleton_icall . name , custom_icalls ) ) {
2017-10-29 01:37:13 +00:00
custom_icalls . push_back ( singleton_icall ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
if ( is_derived_type & & itype . is_instantiable ) {
2017-10-29 01:37:13 +00:00
InternalCall ctor_icall = InternalCall ( itype . api_type , ctor_method , " IntPtr " , itype . proxy_name + " obj " ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( ! find_icall_by_name ( ctor_icall . name , custom_icalls ) ) {
2017-10-29 01:37:13 +00:00
custom_icalls . push_back ( ctor_icall ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
output . append ( INDENT1 CLOSE_BLOCK /* class */
2018-08-27 18:39:51 +00:00
CLOSE_BLOCK /* namespace */ ) ;
2019-04-18 23:10:08 +00:00
output . append ( " \n "
" #pragma warning restore CS1591 \n "
" #pragma warning restore CS1573 \n " ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
return _save_file ( p_output_file , output ) ;
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
Error BindingsGenerator : : _generate_cs_property ( const BindingsGenerator : : TypeInterface & p_itype , const PropertyInterface & p_iprop , StringBuilder & p_output ) {
2017-12-24 02:17:48 +00:00
const MethodInterface * setter = p_itype . find_method_by_name ( p_iprop . setter ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
// Search it in base types too
const TypeInterface * current_type = & p_itype ;
2017-12-24 02:17:48 +00:00
while ( ! setter & & current_type - > base_name ! = StringName ( ) ) {
2018-01-09 18:06:59 +00:00
OrderedHashMap < StringName , TypeInterface > : : Element base_match = obj_types . find ( current_type - > base_name ) ;
2020-04-02 23:38:48 +00:00
ERR_FAIL_COND_V_MSG ( ! base_match , ERR_BUG , " Type not found ' " + current_type - > base_name + " '. Inherited by ' " + current_type - > name + " '. " ) ;
2018-01-09 18:06:59 +00:00
current_type = & base_match . get ( ) ;
2017-12-24 02:17:48 +00:00
setter = current_type - > find_method_by_name ( p_iprop . setter ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2017-12-24 02:17:48 +00:00
const MethodInterface * getter = p_itype . find_method_by_name ( p_iprop . getter ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
// Search it in base types too
current_type = & p_itype ;
2017-12-24 02:17:48 +00:00
while ( ! getter & & current_type - > base_name ! = StringName ( ) ) {
2018-01-09 18:06:59 +00:00
OrderedHashMap < StringName , TypeInterface > : : Element base_match = obj_types . find ( current_type - > base_name ) ;
2020-04-02 23:38:48 +00:00
ERR_FAIL_COND_V_MSG ( ! base_match , ERR_BUG , " Type not found ' " + current_type - > base_name + " '. Inherited by ' " + current_type - > name + " '. " ) ;
2018-01-09 18:06:59 +00:00
current_type = & base_match . get ( ) ;
2017-12-24 02:17:48 +00:00
getter = current_type - > find_method_by_name ( p_iprop . getter ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
ERR_FAIL_COND_V ( ! setter & & ! getter , ERR_BUG ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( setter ) {
2017-12-24 02:17:48 +00:00
int setter_argc = p_iprop . index ! = - 1 ? 2 : 1 ;
2017-10-29 01:37:13 +00:00
ERR_FAIL_COND_V ( setter - > arguments . size ( ) ! = setter_argc , ERR_BUG ) ;
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( getter ) {
2017-12-24 02:17:48 +00:00
int getter_argc = p_iprop . index ! = - 1 ? 1 : 0 ;
2017-10-29 01:37:13 +00:00
ERR_FAIL_COND_V ( getter - > arguments . size ( ) ! = getter_argc , ERR_BUG ) ;
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( getter & & setter ) {
2020-03-14 18:20:17 +00: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 13:19:35 +00:00
setter_first_arg . type . cname = = name_cache . type_String ;
2020-03-14 18:20:17 +00: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 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2018-04-28 20:25:25 +00:00
const TypeReference & proptype_name = getter ? getter - > return_type : setter - > arguments . back ( ) - > get ( ) . type ;
2017-10-02 21:24:00 +00:00
2018-04-28 20:25:25 +00:00
const TypeInterface * prop_itype = _get_type_or_null ( proptype_name ) ;
2017-12-24 02:17:48 +00:00
ERR_FAIL_NULL_V ( prop_itype , ERR_BUG ) ; // Property type not found
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00: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-08 23:14:25 +00: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 02:17:48 +00:00
if ( p_iprop . prop_doc & & p_iprop . prop_doc - > description . size ( ) ) {
2019-03-18 20:59:35 +00:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( p_iprop . prop_doc - > description ) , & p_itype ) ;
2019-04-18 23:10:08 +00:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 21:24:00 +00:00
2019-03-18 20:59:35 +00:00
if ( summary_lines . size ( ) ) {
2019-04-18 23:10:08 +00:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 21:24:00 +00:00
2019-03-18 20:59:35 +00:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// </summary> " ) ;
2019-03-18 20:59:35 +00:00
}
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( MEMBER_BEGIN " public " ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( p_itype . is_singleton ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " static " ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( prop_itype - > cs_type ) ;
p_output . append ( " " ) ;
p_output . append ( p_iprop . proxy_name ) ;
p_output . append ( " \n " INDENT2 OPEN_BLOCK ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( getter ) {
2019-04-25 15:11:01 +00:00
p_output . append ( INDENT3 " get \n "
// 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 "
OPEN_BLOCK_L3 ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( " return " ) ;
p_output . append ( getter - > proxy_name + " ( " ) ;
2017-12-24 02:17:48 +00:00
if ( p_iprop . index ! = - 1 ) {
const ArgumentInterface & idx_arg = getter - > arguments . front ( ) - > get ( ) ;
2018-04-28 20:25:25 +00:00
if ( idx_arg . type . cname ! = name_cache . type_int ) {
2017-12-24 02:17:48 +00:00
// Assume the index parameter is an enum
2018-04-28 20:25:25 +00:00
const TypeInterface * idx_arg_type = _get_type_or_null ( idx_arg . type ) ;
2020-04-01 23:20:12 +00:00
CRASH_COND ( idx_arg_type = = nullptr ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( " ( " + idx_arg_type - > proxy_name + " ) " + itos ( p_iprop . index ) ) ;
2017-12-24 02:17:48 +00:00
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( itos ( p_iprop . index ) ) ;
2017-12-24 02:17:48 +00:00
}
}
2019-04-25 15:11:01 +00:00
p_output . append ( " ); \n "
CLOSE_BLOCK_L3
// 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 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( setter ) {
2019-04-25 15:11:01 +00:00
p_output . append ( INDENT3 " set \n "
// 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 "
OPEN_BLOCK_L3 ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( setter - > proxy_name + " ( " ) ;
2017-12-24 02:17:48 +00:00
if ( p_iprop . index ! = - 1 ) {
const ArgumentInterface & idx_arg = setter - > arguments . front ( ) - > get ( ) ;
2018-04-28 20:25:25 +00:00
if ( idx_arg . type . cname ! = name_cache . type_int ) {
2017-12-24 02:17:48 +00:00
// Assume the index parameter is an enum
2018-04-28 20:25:25 +00:00
const TypeInterface * idx_arg_type = _get_type_or_null ( idx_arg . type ) ;
2020-04-01 23:20:12 +00:00
CRASH_COND ( idx_arg_type = = nullptr ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( " ( " + idx_arg_type - > proxy_name + " ) " + itos ( p_iprop . index ) + " , " ) ;
2017-12-24 02:17:48 +00:00
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( itos ( p_iprop . index ) + " , " ) ;
2017-12-24 02:17:48 +00:00
}
}
2019-04-25 15:11:01 +00:00
p_output . append ( " value); \n "
CLOSE_BLOCK_L3
// 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 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( CLOSE_BLOCK_L2 ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
return OK ;
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
Error BindingsGenerator : : _generate_cs_method ( const BindingsGenerator : : TypeInterface & p_itype , const BindingsGenerator : : MethodInterface & p_imethod , int & p_method_bind_count , StringBuilder & p_output ) {
2018-04-28 20:25:25 +00:00
const TypeInterface * return_type = _get_type_or_placeholder ( p_imethod . return_type ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00: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-08 23:14:25 +00: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. " ) ;
}
2020-03-14 18:20:17 +00:00
String method_bind_field = " __method_bind_ " + itos ( p_method_bind_count ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
String arguments_sig ;
String cs_in_statements ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
String icall_params = method_bind_field + " , " ;
2018-01-25 22:44:37 +00:00
icall_params + = sformat ( p_itype . cs_in , " this " ) ;
2019-04-18 23:10:08 +00:00
StringBuilder default_args_doc ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
// Retrieve information from the arguments
2021-07-25 16:45:00 +00:00
const ArgumentInterface & first = p_imethod . arguments . front ( ) - > get ( ) ;
for ( const ArgumentInterface & iarg : p_imethod . arguments ) {
2018-04-28 20:25:25 +00:00
const TypeInterface * arg_type = _get_type_or_placeholder ( iarg . type ) ;
2017-10-29 01:37:13 +00:00
2020-05-16 02:03:05 +00: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-08 23:14:25 +00: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 02:03:05 +00: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 + " '. " ) ;
}
2017-10-29 01:37:13 +00: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 16:45:00 +00:00
if ( & iarg ! = & first ) {
2017-10-29 01:37:13 +00:00
arguments_sig + = " , " ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2017-10-29 01:37:13 +00:00
arguments_sig + = " Nullable< " ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
arguments_sig + = arg_type - > cs_type ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2017-10-29 01:37:13 +00:00
arguments_sig + = " > " ;
2020-05-16 02:03:05 +00:00
} else {
2017-10-29 01:37:13 +00:00
arguments_sig + = " " ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
arguments_sig + = iarg . name ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( iarg . default_argument . size ( ) ) {
2020-05-16 02:03:05 +00:00
if ( iarg . def_param_mode ! = ArgumentInterface : : CONSTANT ) {
2017-10-29 01:37:13 +00:00
arguments_sig + = " = null " ;
2020-05-16 02:03:05 +00:00
} else {
2017-10-29 01:37:13 +00:00
arguments_sig + = " = " + sformat ( iarg . default_argument , arg_type - > cs_type ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
}
}
2017-10-29 01:37:13 +00:00
icall_params + = " , " ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00: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>;
String arg_in = iarg . name ;
arg_in + = " _in " ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
cs_in_statements + = arg_type - > cs_type ;
cs_in_statements + = " " ;
cs_in_statements + = arg_in ;
cs_in_statements + = " = " ;
cs_in_statements + = iarg . name ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2017-10-29 01:37:13 +00:00
cs_in_statements + = " .HasValue ? " ;
2020-05-16 02:03:05 +00:00
} else {
2017-10-29 01:37:13 +00:00
cs_in_statements + = " != null ? " ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
cs_in_statements + = iarg . name ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL ) {
2017-10-29 01:37:13 +00:00
cs_in_statements + = " .Value : " ;
2020-05-16 02:03:05 +00:00
} else {
2017-10-29 01:37:13 +00:00
cs_in_statements + = " : " ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2021-07-25 11:46:31 +00:00
String cs_type = arg_type - > cs_type ;
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 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
cs_in_statements + = def_arg ;
cs_in_statements + = " ; \n " INDENT3 ;
2017-10-02 21:24:00 +00:00
2020-12-15 12:04:21 +00:00
icall_params + = arg_type - > cs_in . is_empty ( ) ? arg_in : sformat ( arg_type - > cs_in , arg_in ) ;
2017-10-02 21:24:00 +00:00
2019-03-18 20:59:35 +00: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 11:46:31 +00:00
// Escape < and > in the attribute default value
String param_def_arg = def_arg . replacen ( " < " , " < " ) . replacen ( " > " , " > " ) ;
2019-03-18 20:59:35 +00:00
2021-07-25 11:46:31 +00:00
default_args_doc . append ( MEMBER_BEGIN " /// <param name= \" " + param_tag_name + " \" >If the parameter is null, then the default value is " + param_def_arg + " </param> " ) ;
2017-10-29 01:37:13 +00:00
} else {
2020-12-15 12:04:21 +00:00
icall_params + = arg_type - > cs_in . is_empty ( ) ? iarg . name : sformat ( arg_type - > cs_in , iarg . name ) ;
2017-10-29 01:37:13 +00:00
}
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
// Generate method
{
2018-09-04 03:40:41 +00:00
if ( ! p_imethod . is_virtual & & ! p_imethod . requires_object_call ) {
2020-04-02 23:38:48 +00:00
p_output . append ( MEMBER_BEGIN " [DebuggerBrowsable(DebuggerBrowsableState.Never)] " MEMBER_BEGIN " private static readonly IntPtr " ) ;
2020-03-14 18:20:17 +00:00
p_output . append ( method_bind_field ) ;
p_output . append ( " = Object. " ICALL_GET_METHODBIND " ( " BINDINGS_NATIVE_NAME_FIELD " , \" " ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( p_imethod . name ) ;
p_output . append ( " \" ); \n " ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( p_imethod . method_doc & & p_imethod . method_doc - > description . size ( ) ) {
2019-03-18 20:59:35 +00:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( p_imethod . method_doc - > description ) , & p_itype ) ;
2019-04-18 23:10:08 +00:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 21:24:00 +00:00
2020-01-02 17:09:59 +00:00
if ( summary_lines . size ( ) ) {
2019-04-18 23:10:08 +00:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 21:24:00 +00:00
2019-03-18 20:59:35 +00:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( INDENT2 " /// </summary> " ) ;
2019-03-18 20:59:35 +00:00
}
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2020-01-02 17:09:59 +00:00
if ( default_args_doc . get_string_length ( ) ) {
p_output . append ( default_args_doc . as_string ( ) ) ;
}
2017-10-29 01:37:13 +00:00
if ( ! p_imethod . is_internal ) {
2020-05-09 18:54:07 +00:00
// TODO: This alone adds ~0.2 MB of bloat to the core API assembly. It would be
// better to generate a table in the C++ glue instead. That way the strings wouldn't
// add that much extra bloat as they're already used in engine code. Also, it would
// probably be much faster than looking up the attributes when fetching methods.
2019-04-18 23:10:08 +00:00
p_output . append ( MEMBER_BEGIN " [GodotMethod( \" " ) ;
p_output . append ( p_imethod . name ) ;
p_output . append ( " \" )] " ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-25 15:11:01 +00:00
if ( p_imethod . is_deprecated ) {
2020-12-15 12:04:21 +00:00
if ( p_imethod . deprecation_message . is_empty ( ) ) {
2019-11-07 08:44:15 +00:00
WARN_PRINT ( " An empty deprecation message is discouraged. Method: ' " + p_imethod . proxy_name + " '. " ) ;
2020-05-16 02:03:05 +00:00
}
2019-04-25 15:11:01 +00:00
p_output . append ( MEMBER_BEGIN " [Obsolete( \" " ) ;
p_output . append ( p_imethod . deprecation_message ) ;
p_output . append ( " \" )] " ) ;
}
2019-04-18 23:10:08 +00:00
p_output . append ( MEMBER_BEGIN ) ;
p_output . append ( p_imethod . is_internal ? " internal " : " public " ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( p_itype . is_singleton ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " static " ) ;
2017-10-29 01:37:13 +00:00
} else if ( p_imethod . is_virtual ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " virtual " ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( return_type - > cs_type + " " ) ;
p_output . append ( p_imethod . proxy_name + " ( " ) ;
p_output . append ( arguments_sig + " ) \n " OPEN_BLOCK_L2 ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( p_imethod . is_virtual ) {
// Godot virtual method must be overridden, therefore we return a default value by default.
2017-10-02 21:24:00 +00:00
2017-12-24 02:17:48 +00:00
if ( return_type - > cname = = name_cache . type_void ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " return; \n " CLOSE_BLOCK_L2 ) ;
2017-10-29 01:37:13 +00:00
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( " return default( " ) ;
p_output . append ( return_type - > cs_type ) ;
p_output . append ( " ); \n " CLOSE_BLOCK_L2 ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
return OK ; // Won't increment method bind count
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
if ( p_imethod . requires_object_call ) {
// Fallback to Godot's object.Call(string, params)
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( CS_METHOD_CALL " ( \" " ) ;
p_output . append ( p_imethod . name ) ;
p_output . append ( " \" " ) ;
2017-10-02 21:24:00 +00:00
2021-07-25 16:45:00 +00:00
for ( const ArgumentInterface & iarg : p_imethod . arguments ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " , " ) ;
2021-07-25 16:45:00 +00:00
p_output . append ( iarg . name ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( " ); \n " CLOSE_BLOCK_L2 ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
return OK ; // Won't increment method bind count
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
const Map < const MethodInterface * , const InternalCall * > : : Element * match = method_icalls_map . find ( & p_imethod ) ;
ERR_FAIL_NULL_V ( match , ERR_BUG ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
const InternalCall * im_icall = match - > value ( ) ;
2017-10-02 21:24:00 +00:00
2018-02-22 12:13:51 +00:00
String im_call = im_icall - > editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS ;
2019-11-14 20:48:49 +00:00
im_call + = " . " ;
im_call + = im_icall - > name ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( p_imethod . arguments . size ( ) ) {
2019-04-18 23:10:08 +00:00
p_output . append ( cs_in_statements ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2017-12-24 02:17:48 +00:00
if ( return_type - > cname = = name_cache . type_void ) {
2019-11-14 20:48:49 +00:00
p_output . append ( im_call + " ( " + icall_params + " ); \n " ) ;
2020-12-15 12:04:21 +00:00
} else if ( return_type - > cs_out . is_empty ( ) ) {
2019-11-14 20:48:49 +00:00
p_output . append ( " return " + im_call + " ( " + icall_params + " ); \n " ) ;
2017-10-29 01:37:13 +00:00
} else {
2019-11-14 20:48:49 +00:00
p_output . append ( sformat ( return_type - > cs_out , im_call , icall_params , return_type - > cs_type , return_type - > im_type_out ) ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( " \n " ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( CLOSE_BLOCK_L2 ) ;
2017-10-29 01:37:13 +00:00
}
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
p_method_bind_count + + ;
2019-04-18 23:10:08 +00:00
2017-10-29 01:37:13 +00:00
return OK ;
}
2017-10-02 21:24:00 +00:00
2020-03-14 18:20:17 +00: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 16:45:00 +00:00
const ArgumentInterface & first = p_isignal . arguments . front ( ) - > get ( ) ;
for ( const ArgumentInterface & iarg : p_isignal . arguments ) {
2020-03-14 18:20:17 +00:00
const TypeInterface * arg_type = _get_type_or_placeholder ( iarg . type ) ;
2020-05-16 02:03:05 +00:00
ERR_FAIL_COND_V_MSG ( arg_type - > is_singleton , ERR_BUG ,
2021-01-08 23:14:25 +00: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 02:03:05 +00:00
2020-03-14 18:20:17 +00:00
// Add the current arguments to the signature
2021-07-25 16:45:00 +00:00
if ( & iarg ! = & first ) {
2020-03-14 18:20:17 +00:00
arguments_sig + = " , " ;
2020-05-16 02:03:05 +00:00
}
2020-03-14 18:20:17 +00: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 + + ) {
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
}
p_output . append ( INDENT2 " /// </summary> " ) ;
}
}
if ( p_isignal . is_deprecated ) {
2020-12-15 12:04:21 +00:00
if ( p_isignal . deprecation_message . is_empty ( ) ) {
2020-03-14 18:20:17 +00:00
WARN_PRINT ( " An empty deprecation message is discouraged. Signal: ' " + p_isignal . proxy_name + " '. " ) ;
2020-05-16 02:03:05 +00:00
}
2020-03-14 18:20:17 +00:00
p_output . append ( MEMBER_BEGIN " [Obsolete( \" " ) ;
p_output . append ( p_isignal . deprecation_message ) ;
p_output . append ( " \" )] " ) ;
}
String delegate_name = p_isignal . proxy_name ;
delegate_name + = " Handler " ; // Delegate name is [SignalName]Handler
// 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.
// Cached signal name (StringName)
p_output . append ( MEMBER_BEGIN " [DebuggerBrowsable(DebuggerBrowsableState.Never)] " MEMBER_BEGIN " private static StringName __signal_name_ " ) ;
p_output . append ( p_isignal . name ) ;
p_output . append ( " = \" " ) ;
p_output . append ( p_isignal . name ) ;
p_output . append ( " \" ; \n " ) ;
// Generate event
p_output . append ( MEMBER_BEGIN " [Signal] " MEMBER_BEGIN " public " ) ;
2020-05-16 02:03:05 +00:00
if ( p_itype . is_singleton ) {
2020-03-14 18:20:17 +00:00
p_output . append ( " static " ) ;
2020-05-16 02:03:05 +00:00
}
2020-03-14 18:20:17 +00:00
p_output . append ( " event " ) ;
p_output . append ( delegate_name ) ;
p_output . append ( " " ) ;
p_output . append ( p_isignal . proxy_name ) ;
p_output . append ( " \n " OPEN_BLOCK_L2 ) ;
2020-05-16 02:03:05 +00:00
if ( p_itype . is_singleton ) {
2020-03-14 18:20:17 +00:00
p_output . append ( " add => Singleton.Connect(__signal_name_ " ) ;
2020-05-16 02:03:05 +00:00
} else {
2020-03-14 18:20:17 +00:00
p_output . append ( " add => Connect(__signal_name_ " ) ;
2020-05-16 02:03:05 +00:00
}
2020-03-14 18:20:17 +00:00
p_output . append ( p_isignal . name ) ;
p_output . append ( " , new Callable(value)); \n " ) ;
2020-05-16 02:03:05 +00:00
if ( p_itype . is_singleton ) {
2020-03-14 18:20:17 +00:00
p_output . append ( INDENT3 " remove => Singleton.Disconnect(__signal_name_ " ) ;
2020-05-16 02:03:05 +00:00
} else {
2020-03-14 18:20:17 +00:00
p_output . append ( INDENT3 " remove => Disconnect(__signal_name_ " ) ;
2020-05-16 02:03:05 +00:00
}
2020-03-14 18:20:17 +00:00
p_output . append ( p_isignal . name ) ;
p_output . append ( " , new Callable(value)); \n " ) ;
p_output . append ( CLOSE_BLOCK_L2 ) ;
}
return OK ;
}
2017-10-29 01:37:13 +00:00
Error BindingsGenerator : : generate_glue ( const String & p_output_dir ) {
2019-09-24 13:09:54 +00:00
ERR_FAIL_COND_V ( ! initialized , ERR_UNCONFIGURED ) ;
2017-10-29 01:37:13 +00:00
bool dir_exists = DirAccess : : exists ( p_output_dir ) ;
2019-08-09 01:39:45 +00:00
ERR_FAIL_COND_V_MSG ( ! dir_exists , ERR_FILE_BAD_PATH , " The output directory does not exist. " ) ;
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
StringBuilder output ;
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " /* THIS FILE IS GENERATED DO NOT EDIT */ \n " ) ;
output . append ( " #include \" " GLUE_HEADER_FILE " \" \n " ) ;
output . append ( " \n #ifdef MONO_GLUE_ENABLED \n " ) ;
2017-10-02 21:24:00 +00:00
2017-10-29 01:37:13 +00:00
generated_icall_funcs . clear ( ) ;
2017-10-02 21:24:00 +00:00
2018-01-09 18:06:59 +00:00
for ( OrderedHashMap < StringName , TypeInterface > : : Element type_elem = obj_types . front ( ) ; type_elem ; type_elem = type_elem . next ( ) ) {
const TypeInterface & itype = type_elem . get ( ) ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
bool is_derived_type = itype . base_name ! = StringName ( ) ;
if ( ! is_derived_type ) {
// Some Object assertions
CRASH_COND ( itype . cname ! = name_cache . type_Object ) ;
CRASH_COND ( ! itype . is_instantiable ) ;
CRASH_COND ( itype . api_type ! = ClassDB : : API_CORE ) ;
CRASH_COND ( itype . is_singleton ) ;
}
2017-10-29 01:37:13 +00:00
List < InternalCall > & custom_icalls = itype . api_type = = ClassDB : : API_EDITOR ? editor_custom_icalls : core_custom_icalls ;
2019-02-22 14:38:49 +00:00
OS : : get_singleton ( ) - > print ( " Generating %s... \n " , itype . name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 21:24:00 +00:00
2018-09-04 03:40:41 +00:00
String ctor_method ( ICALL_PREFIX + itype . proxy_name + " _Ctor " ) ; // Used only for derived types
2017-10-02 21:24:00 +00:00
2021-07-16 03:45:57 +00:00
for ( const MethodInterface & imethod : itype . methods ) {
2017-10-29 01:37:13 +00:00
Error method_err = _generate_glue_method ( itype , imethod , output ) ;
2019-08-09 01:39:45 +00:00
ERR_FAIL_COND_V_MSG ( method_err ! = OK , method_err ,
" Failed to generate method ' " + imethod . name + " ' for class ' " + itype . name + " '. " ) ;
2017-10-02 21:24:00 +00:00
}
if ( itype . is_singleton ) {
String singleton_icall_name = ICALL_PREFIX + itype . name + SINGLETON_ICALL_SUFFIX ;
InternalCall singleton_icall = InternalCall ( itype . api_type , singleton_icall_name , " IntPtr " ) ;
2020-05-16 02:03:05 +00:00
if ( ! find_icall_by_name ( singleton_icall . name , custom_icalls ) ) {
2017-10-02 21:24:00 +00:00
custom_icalls . push_back ( singleton_icall ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " Object* " ) ;
output . append ( singleton_icall_name ) ;
output . append ( " () " OPEN_BLOCK " \t return Engine::get_singleton()->get_singleton_object( \" " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " \" ); \n " CLOSE_BLOCK " \n " ) ;
2017-10-02 21:24:00 +00:00
}
2018-09-04 03:40:41 +00:00
if ( is_derived_type & & itype . is_instantiable ) {
2017-10-02 21:24:00 +00:00
InternalCall ctor_icall = InternalCall ( itype . api_type , ctor_method , " IntPtr " , itype . proxy_name + " obj " ) ;
2020-05-16 02:03:05 +00:00
if ( ! find_icall_by_name ( ctor_icall . name , custom_icalls ) ) {
2017-10-02 21:24:00 +00:00
custom_icalls . push_back ( ctor_icall ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " Object* " ) ;
output . append ( ctor_method ) ;
output . append ( " (MonoObject* obj) " OPEN_BLOCK
" \t " C_MACRO_OBJECT_CONSTRUCT " (instance, \" " ) ;
output . append ( itype . name ) ;
output . append ( " \" ); \n "
" \t " C_METHOD_TIE_MANAGED_TO_UNMANAGED " (obj, instance); \n "
" \t return instance; \n " CLOSE_BLOCK " \n " ) ;
2017-10-02 21:24:00 +00:00
}
}
2019-04-18 23:10:08 +00:00
output . append ( " namespace GodotSharpBindings \n " OPEN_BLOCK " \n " ) ;
2018-02-22 12:13:51 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " uint64_t get_core_api_hash() { return " ) ;
output . append ( String : : num_uint64 ( GDMono : : get_singleton ( ) - > get_api_core_hash ( ) ) + " U; } \n " ) ;
2018-02-22 12:13:51 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " #ifdef TOOLS_ENABLED \n "
" uint64_t get_editor_api_hash() { return " ) ;
output . append ( String : : num_uint64 ( GDMono : : get_singleton ( ) - > get_api_editor_hash ( ) ) + " U; } \n " ) ;
output . append ( " #endif // TOOLS_ENABLED \n " ) ;
2018-02-22 12:13:51 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " uint32_t get_bindings_version() { return " ) ;
output . append ( String : : num_uint64 ( BINDINGS_GENERATOR_VERSION ) + " ; } \n " ) ;
2018-02-22 12:13:51 +00:00
2019-09-20 15:34:44 +00:00
output . append ( " uint32_t get_cs_glue_version() { return " ) ;
output . append ( String : : num_uint64 ( CS_GLUE_VERSION ) + " ; } \n " ) ;
2019-04-18 23:10:08 +00:00
output . append ( " \n void register_generated_icalls() " OPEN_BLOCK ) ;
output . append ( " \t godot_register_glue_header_icalls(); \n " ) ;
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
# define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
{ \
2020-12-04 23:05:01 +00:00
output . append ( " \t GDMonoUtils::add_internal_call( " ) ; \
2019-04-18 23:10:08 +00:00
output . append ( " \" " BINDINGS_NAMESPACE " . " ) ; \
output . append ( m_icall . editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS ) ; \
output . append ( " :: " ) ; \
output . append ( m_icall . name ) ; \
2020-12-04 23:05:01 +00:00
output . append ( " \" , " ) ; \
2019-04-18 23:10:08 +00:00
output . append ( m_icall . name ) ; \
output . append ( " ); \n " ) ; \
2017-10-02 21:24:00 +00:00
}
bool tools_sequence = false ;
2021-07-25 16:45:00 +00:00
for ( const InternalCall & internal_call : core_custom_icalls ) {
2017-10-02 21:24:00 +00:00
if ( tools_sequence ) {
2021-07-25 16:45:00 +00:00
if ( ! internal_call . editor_only ) {
2017-10-02 21:24:00 +00:00
tools_sequence = false ;
2019-04-18 23:10:08 +00:00
output . append ( " #endif \n " ) ;
2017-10-02 21:24:00 +00:00
}
} else {
2021-07-25 16:45:00 +00:00
if ( internal_call . editor_only ) {
2019-04-18 23:10:08 +00:00
output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2017-10-02 21:24:00 +00:00
tools_sequence = true ;
}
}
2021-07-25 16:45:00 +00:00
ADD_INTERNAL_CALL_REGISTRATION ( internal_call ) ;
2017-10-02 21:24:00 +00:00
}
if ( tools_sequence ) {
tools_sequence = false ;
2019-04-18 23:10:08 +00:00
output . append ( " #endif \n " ) ;
2017-10-02 21:24:00 +00:00
}
2019-04-18 23:10:08 +00:00
output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2022-02-16 12:56:32 +00:00
for ( const InternalCall & internal_call : editor_custom_icalls ) {
2021-07-25 16:45:00 +00:00
ADD_INTERNAL_CALL_REGISTRATION ( internal_call ) ;
2022-02-16 12:56:32 +00:00
}
2019-04-18 23:10:08 +00:00
output . append ( " #endif // TOOLS_ENABLED \n " ) ;
2017-10-02 21:24:00 +00:00
2021-07-25 16:45:00 +00:00
for ( const InternalCall & internal_call : method_icalls ) {
2017-10-02 21:24:00 +00:00
if ( tools_sequence ) {
2021-07-25 16:45:00 +00:00
if ( ! internal_call . editor_only ) {
2017-10-02 21:24:00 +00:00
tools_sequence = false ;
2019-04-18 23:10:08 +00:00
output . append ( " #endif \n " ) ;
2017-10-02 21:24:00 +00:00
}
} else {
2021-07-25 16:45:00 +00:00
if ( internal_call . editor_only ) {
2019-04-18 23:10:08 +00:00
output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2017-10-02 21:24:00 +00:00
tools_sequence = true ;
}
}
2021-07-25 16:45:00 +00:00
ADD_INTERNAL_CALL_REGISTRATION ( internal_call ) ;
2017-10-02 21:24:00 +00:00
}
if ( tools_sequence ) {
tools_sequence = false ;
2019-04-18 23:10:08 +00:00
output . append ( " #endif \n " ) ;
2017-10-02 21:24:00 +00:00
}
# undef ADD_INTERNAL_CALL_REGISTRATION
2019-04-18 23:10:08 +00:00
output . append ( CLOSE_BLOCK " \n } // namespace GodotSharpBindings \n " ) ;
2018-09-04 03:40:41 +00:00
2019-04-18 23:10:08 +00:00
output . append ( " \n #endif // MONO_GLUE_ENABLED \n " ) ;
2017-10-02 21:24:00 +00:00
2019-07-08 13:16:35 +00:00
Error save_err = _save_file ( path : : join ( p_output_dir , " mono_glue.gen.cpp " ) , output ) ;
2020-05-16 02:03:05 +00:00
if ( save_err ! = OK ) {
2017-10-24 14:18:47 +00:00
return save_err ;
2020-05-16 02:03:05 +00:00
}
2017-10-24 14:18:47 +00:00
2017-10-29 01:37:13 +00:00
OS : : get_singleton ( ) - > print ( " Mono glue generated successfully \n " ) ;
2017-10-24 14:18:47 +00:00
return OK ;
2017-10-02 21:24:00 +00:00
}
2018-02-22 12:13:51 +00:00
uint32_t BindingsGenerator : : get_version ( ) {
return BINDINGS_GENERATOR_VERSION ;
}
2019-04-18 23:10:08 +00:00
Error BindingsGenerator : : _save_file ( const String & p_path , const StringBuilder & p_content ) {
2017-10-02 21:24:00 +00:00
FileAccessRef file = FileAccess : : open ( p_path , FileAccess : : WRITE ) ;
2019-08-09 01:39:45 +00:00
ERR_FAIL_COND_V_MSG ( ! file , ERR_FILE_CANT_WRITE , " Cannot open file: ' " + p_path + " '. " ) ;
2017-10-02 21:24:00 +00:00
2019-04-18 23:10:08 +00:00
file - > store_string ( p_content . as_string ( ) ) ;
2017-10-02 21:24:00 +00:00
file - > close ( ) ;
return OK ;
}
2019-04-18 23:10:08 +00:00
Error BindingsGenerator : : _generate_glue_method ( const BindingsGenerator : : TypeInterface & p_itype , const BindingsGenerator : : MethodInterface & p_imethod , StringBuilder & p_output ) {
2020-05-16 02:03:05 +00:00
if ( p_imethod . is_virtual ) {
2017-10-29 01:37:13 +00:00
return OK ; // Ignore
2020-05-16 02:03:05 +00:00
}
2017-10-29 01:37:13 +00:00
2018-04-28 20:25:25 +00:00
bool ret_void = p_imethod . return_type . cname = = name_cache . type_void ;
2017-10-29 01:37:13 +00:00
2018-04-28 20:25:25 +00:00
const TypeInterface * return_type = _get_type_or_placeholder ( p_imethod . return_type ) ;
2017-10-29 01:37:13 +00:00
String argc_str = itos ( p_imethod . arguments . size ( ) ) ;
String c_func_sig = " MethodBind* " CS_PARAM_METHODBIND " , " + p_itype . c_type_in + " " CS_PARAM_INSTANCE ;
String c_in_statements ;
String c_args_var_content ;
// Get arguments information
int i = 0 ;
2021-07-25 16:45:00 +00:00
for ( const ArgumentInterface & iarg : p_imethod . arguments ) {
2018-04-28 20:25:25 +00:00
const TypeInterface * arg_type = _get_type_or_placeholder ( iarg . type ) ;
2017-10-29 01:37:13 +00:00
String c_param_name = " arg " + itos ( i + 1 ) ;
if ( p_imethod . is_vararg ) {
if ( i < p_imethod . arguments . size ( ) - 1 ) {
c_in_statements + = sformat ( arg_type - > c_in . size ( ) ? arg_type - > c_in : TypeInterface : : DEFAULT_VARARG_C_IN , " Variant " , c_param_name ) ;
2018-02-24 19:03:16 +00:00
c_in_statements + = " \t " C_LOCAL_PTRCALL_ARGS " .set( " ;
c_in_statements + = itos ( i ) ;
c_in_statements + = sformat ( " , &%s_in); \n " , c_param_name ) ;
2017-10-29 01:37:13 +00:00
}
} else {
2020-05-16 02:03:05 +00:00
if ( i > 0 ) {
2017-10-29 01:37:13 +00:00
c_args_var_content + = " , " ;
2020-05-16 02:03:05 +00:00
}
if ( arg_type - > c_in . size ( ) ) {
2017-10-29 01:37:13 +00:00
c_in_statements + = sformat ( arg_type - > c_in , arg_type - > c_type , c_param_name ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-29 01:37:13 +00:00
c_args_var_content + = sformat ( arg_type - > c_arg_in , c_param_name ) ;
}
c_func_sig + = " , " ;
c_func_sig + = arg_type - > c_type_in ;
c_func_sig + = " " ;
c_func_sig + = c_param_name ;
i + + ;
}
2019-11-14 20:48:49 +00:00
if ( return_type - > ret_as_byref_arg ) {
c_func_sig + = " , " ;
c_func_sig + = return_type - > c_type_in ;
c_func_sig + = " " ;
c_func_sig + = " arg_ret " ;
i + + ;
}
2017-10-29 01:37:13 +00:00
const Map < const MethodInterface * , const InternalCall * > : : Element * match = method_icalls_map . find ( & p_imethod ) ;
ERR_FAIL_NULL_V ( match , ERR_BUG ) ;
const InternalCall * im_icall = match - > value ( ) ;
String icall_method = im_icall - > name ;
if ( ! generated_icall_funcs . find ( im_icall ) ) {
generated_icall_funcs . push_back ( im_icall ) ;
2020-05-16 02:03:05 +00:00
if ( im_icall - > editor_only ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-29 01:37:13 +00:00
// Generate icall function
2019-11-14 20:48:49 +00:00
p_output . append ( ( ret_void | | return_type - > ret_as_byref_arg ) ? " void " : return_type - > c_type_out + " " ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( icall_method ) ;
p_output . append ( " ( " ) ;
p_output . append ( c_func_sig ) ;
p_output . append ( " ) " OPEN_BLOCK ) ;
2017-10-29 01:37:13 +00:00
if ( ! ret_void ) {
String ptrcall_return_type ;
String initialization ;
2019-01-17 23:41:41 +00:00
if ( p_imethod . is_vararg & & return_type - > cname ! = name_cache . type_Variant ) {
// 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.
2019-04-18 23:10:08 +00:00
p_output . append ( " \t Variant " C_LOCAL_VARARG_RET " ; \n " ) ;
2019-01-17 23:41:41 +00:00
}
2017-10-29 01:37:13 +00:00
if ( return_type - > is_object_type ) {
2021-06-04 16:03:15 +00:00
ptrcall_return_type = return_type - > is_ref_counted ? " Ref<RefCounted> " : return_type - > c_type ;
initialization = return_type - > is_ref_counted ? " " : " = nullptr " ;
2017-10-29 01:37:13 +00:00
} else {
ptrcall_return_type = return_type - > c_type ;
}
2019-04-18 23:10:08 +00:00
p_output . append ( " \t " + ptrcall_return_type ) ;
p_output . append ( " " C_LOCAL_RET ) ;
p_output . append ( initialization + " ; \n " ) ;
2019-11-14 20:48:49 +00:00
2020-04-01 23:20:12 +00:00
String fail_ret = return_type - > c_type_out . ends_with ( " * " ) & & ! return_type - > ret_as_byref_arg ? " nullptr " : return_type - > c_type_out + " () " ;
2019-11-14 20:48:49 +00:00
if ( return_type - > ret_as_byref_arg ) {
2020-04-01 23:20:12 +00:00
p_output . append ( " \t if ( " CS_PARAM_INSTANCE " == nullptr) { *arg_ret = " ) ;
2019-11-14 20:48:49 +00:00
p_output . append ( fail_ret ) ;
2020-05-09 18:54:07 +00:00
p_output . append ( " ; ERR_FAIL_MSG( \" Parameter ' " CS_PARAM_INSTANCE " ' is null. \" ); } \n " ) ;
2019-11-14 20:48:49 +00:00
} else {
p_output . append ( " \t ERR_FAIL_NULL_V( " CS_PARAM_INSTANCE " , " ) ;
p_output . append ( fail_ret ) ;
p_output . append ( " ); \n " ) ;
}
2017-10-29 01:37:13 +00:00
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( " \t ERR_FAIL_NULL( " CS_PARAM_INSTANCE " ); \n " ) ;
2017-10-29 01:37:13 +00:00
}
if ( p_imethod . arguments . size ( ) ) {
if ( p_imethod . is_vararg ) {
String vararg_arg = " arg " + argc_str ;
String real_argc_str = itos ( p_imethod . arguments . size ( ) - 1 ) ; // Arguments count without vararg
2019-04-18 23:10:08 +00:00
p_output . append ( " \t int vararg_length = mono_array_length( " ) ;
p_output . append ( vararg_arg ) ;
p_output . append ( " ); \n \t int total_length = " ) ;
p_output . append ( real_argc_str ) ;
p_output . append ( " + vararg_length; \n "
" \t ArgumentsVector<Variant> varargs(vararg_length); \n "
" \t ArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS " (total_length); \n " ) ;
p_output . append ( c_in_statements ) ;
p_output . append ( " \t for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
" \t \t MonoObject* elem = mono_array_get( " ) ;
p_output . append ( vararg_arg ) ;
p_output . append ( " , MonoObject*, i); \n "
" \t \t varargs.set(i, GDMonoMarshal::mono_object_to_variant(elem)); \n "
" \t \t " C_LOCAL_PTRCALL_ARGS " .set( " ) ;
p_output . append ( real_argc_str ) ;
p_output . append ( " + i, &varargs.get(i)); \n \t " CLOSE_BLOCK ) ;
2017-10-29 01:37:13 +00:00
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( c_in_statements ) ;
p_output . append ( " \t const void* " C_LOCAL_PTRCALL_ARGS " [ " ) ;
p_output . append ( argc_str + " ] = { " ) ;
p_output . append ( c_args_var_content + " }; \n " ) ;
2017-10-29 01:37:13 +00:00
}
}
if ( p_imethod . is_vararg ) {
2020-02-19 19:27:19 +00:00
p_output . append ( " \t Callable::CallError vcall_error; \n \t " ) ;
2017-10-29 01:37:13 +00:00
2019-01-17 23:41:41 +00:00
if ( ! ret_void ) {
// See the comment on the C_LOCAL_VARARG_RET declaration
if ( return_type - > cname ! = name_cache . type_Variant ) {
2019-04-18 23:10:08 +00:00
p_output . append ( C_LOCAL_VARARG_RET " = " ) ;
2019-01-17 23:41:41 +00:00
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( C_LOCAL_RET " = " ) ;
2019-01-17 23:41:41 +00:00
}
}
2017-10-29 01:37:13 +00:00
2019-04-18 23:10:08 +00:00
p_output . append ( CS_PARAM_METHODBIND " ->call( " CS_PARAM_INSTANCE " , " ) ;
2020-04-02 12:41:58 +00:00
p_output . append ( p_imethod . arguments . size ( ) ? C_LOCAL_PTRCALL_ARGS " .ptr() " : " nullptr " ) ;
2019-04-18 23:10:08 +00:00
p_output . append ( " , total_length, vcall_error); \n " ) ;
2019-01-17 23:41:41 +00:00
2020-01-02 17:02:45 +00:00
if ( ! ret_void ) {
// See the comment on the C_LOCAL_VARARG_RET declaration
if ( return_type - > cname ! = name_cache . type_Variant ) {
p_output . append ( " \t " C_LOCAL_RET " = " C_LOCAL_VARARG_RET " ; \n " ) ;
}
2019-01-17 23:41:41 +00:00
}
2017-10-29 01:37:13 +00:00
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( " \t " CS_PARAM_METHODBIND " ->ptrcall( " CS_PARAM_INSTANCE " , " ) ;
2020-04-01 23:20:12 +00:00
p_output . append ( p_imethod . arguments . size ( ) ? C_LOCAL_PTRCALL_ARGS " , " : " nullptr, " ) ;
p_output . append ( ! ret_void ? " & " C_LOCAL_RET " ); \n " : " nullptr); \n " ) ;
2017-10-29 01:37:13 +00:00
}
if ( ! ret_void ) {
2020-12-15 12:04:21 +00:00
if ( return_type - > c_out . is_empty ( ) ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " \t return " C_LOCAL_RET " ; \n " ) ;
2019-11-14 20:48:49 +00:00
} else if ( return_type - > ret_as_byref_arg ) {
p_output . append ( sformat ( return_type - > c_out , return_type - > c_type_out , C_LOCAL_RET , return_type - > name , " arg_ret " ) ) ;
} else {
2019-04-18 23:10:08 +00:00
p_output . append ( sformat ( return_type - > c_out , return_type - > c_type_out , C_LOCAL_RET , return_type - > name ) ) ;
2019-11-14 20:48:49 +00:00
}
2017-10-29 01:37:13 +00:00
}
2019-04-18 23:10:08 +00:00
p_output . append ( CLOSE_BLOCK " \n " ) ;
2017-10-29 01:37:13 +00:00
2020-05-16 02:03:05 +00:00
if ( im_icall - > editor_only ) {
2019-04-18 23:10:08 +00:00
p_output . append ( " #endif // TOOLS_ENABLED \n " ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-29 01:37:13 +00:00
}
return OK ;
}
2018-04-28 20:25:25 +00:00
const BindingsGenerator : : TypeInterface * BindingsGenerator : : _get_type_or_null ( const TypeReference & p_typeref ) {
const Map < StringName , TypeInterface > : : Element * builtin_type_match = builtin_types . find ( p_typeref . cname ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( builtin_type_match ) {
2018-01-09 18:06:59 +00:00
return & builtin_type_match - > get ( ) ;
2020-05-16 02:03:05 +00:00
}
2017-12-24 02:17:48 +00:00
2018-04-28 20:25:25 +00:00
const OrderedHashMap < StringName , TypeInterface > : : Element obj_type_match = obj_types . find ( p_typeref . cname ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( obj_type_match ) {
2018-01-09 18:06:59 +00:00
return & obj_type_match . get ( ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2018-04-28 20:25:25 +00:00
if ( p_typeref . is_enum ) {
const Map < StringName , TypeInterface > : : Element * enum_match = enum_types . find ( p_typeref . cname ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( enum_match ) {
2018-04-28 20:25:25 +00:00
return & enum_match - > get ( ) ;
2020-05-16 02:03:05 +00:00
}
2018-04-28 20:25:25 +00:00
// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
const Map < StringName , TypeInterface > : : Element * int_match = builtin_types . find ( name_cache . type_int ) ;
2020-04-01 23:20:12 +00:00
ERR_FAIL_NULL_V ( int_match , nullptr ) ;
2018-04-28 20:25:25 +00:00
return & int_match - > get ( ) ;
}
2017-10-02 21:24:00 +00:00
2020-04-01 23:20:12 +00:00
return nullptr ;
2017-10-02 21:24:00 +00:00
}
2018-04-28 20:25:25 +00:00
const BindingsGenerator : : TypeInterface * BindingsGenerator : : _get_type_or_placeholder ( const TypeReference & p_typeref ) {
const TypeInterface * found = _get_type_or_null ( p_typeref ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( found ) {
2017-10-02 21:24:00 +00:00
return found ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-11-06 16:03:04 +00:00
ERR_PRINT ( String ( ) + " Type not found. Creating placeholder: ' " + p_typeref . cname . operator String ( ) + " '. " ) ;
2017-10-02 21:24:00 +00:00
2018-04-28 20:25:25 +00:00
const Map < StringName , TypeInterface > : : Element * match = placeholder_types . find ( p_typeref . cname ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( match ) {
2017-10-02 21:24:00 +00:00
return & match - > get ( ) ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
TypeInterface placeholder ;
2018-04-28 20:25:25 +00:00
TypeInterface : : create_placeholder_type ( placeholder , p_typeref . cname ) ;
2017-10-02 21:24:00 +00:00
2017-12-24 02:17:48 +00:00
return & placeholder_types . insert ( placeholder . cname , placeholder ) - > get ( ) ;
}
2019-04-25 18:24:48 +00: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 :
// Assume INT32
return " int " ;
}
}
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 :
// Assume real_t (float or double depending of REAL_T_IS_DOUBLE)
# ifdef REAL_T_IS_DOUBLE
return " double " ;
# else
return " float " ;
# endif
}
}
2020-05-16 02:03:05 +00: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 13:19:35 +00:00
name_cache . is_nullable_type ( p_arg_type . name ) ;
2020-05-16 02:03:05 +00: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 13:19:35 +00: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 02:03:05 +00:00
case Variant : : FLOAT :
return p_arg_type . name = = name_cache . type_float | |
2021-10-28 13:19:35 +00:00
p_arg_type . name = = name_cache . type_double ;
2020-05-16 02:03:05 +00:00
case Variant : : STRING :
case Variant : : STRING_NAME :
return p_arg_type . name = = name_cache . type_String | |
2021-10-28 13:19:35 +00:00
p_arg_type . name = = name_cache . type_StringName | |
p_arg_type . name = = name_cache . type_NodePath ;
2020-05-16 02:03:05 +00:00
case Variant : : NODE_PATH :
return p_arg_type . name = = name_cache . type_NodePath ;
case Variant : : TRANSFORM2D :
2021-04-28 07:36:08 +00:00
case Variant : : TRANSFORM3D :
2020-05-16 02:03:05 +00:00
case Variant : : BASIS :
2021-01-20 07:02:02 +00:00
case Variant : : QUATERNION :
2020-05-16 02:03:05 +00:00
case Variant : : PLANE :
case Variant : : AABB :
case Variant : : COLOR :
case Variant : : VECTOR2 :
case Variant : : RECT2 :
case Variant : : VECTOR3 :
2020-11-09 13:53:05 +00:00
case Variant : : RID :
2020-05-16 02:03:05 +00:00
case Variant : : ARRAY :
case Variant : : DICTIONARY :
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 ( ) ) ;
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 13:19:35 +00:00
p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) ;
2020-05-16 02:03:05 +00:00
case Variant : : RECT2I :
return p_arg_type . name = = name_cache . type_Rect2 | |
2021-10-28 13:19:35 +00:00
p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) ;
2020-05-16 02:03:05 +00:00
case Variant : : VECTOR3I :
return p_arg_type . name = = name_cache . type_Vector3 | |
2021-10-28 13:19:35 +00:00
p_arg_type . name = = Variant : : get_type_name ( p_val . get_type ( ) ) ;
2020-05-16 02:03:05 +00:00
default :
CRASH_NOW_MSG ( " Unexpected Variant type: " + itos ( p_val . get_type ( ) ) ) ;
break ;
}
return false ;
}
2019-09-24 13:09:54 +00:00
bool BindingsGenerator : : _populate_object_type_interfaces ( ) {
2017-10-02 21:24:00 +00: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 ;
}
2017-12-24 02:17:48 +00:00
if ( ! ClassDB : : is_class_exposed ( type_cname ) ) {
2019-08-09 01:39:45 +00:00
_log ( " Ignoring type '%s' because it's not exposed \n " , String ( type_cname ) . utf8 ( ) . get_data ( ) ) ;
2017-12-24 02:17:48 +00:00
class_list . pop_front ( ) ;
continue ;
}
2018-07-18 19:15:41 +00:00
if ( ! ClassDB : : is_class_enabled ( type_cname ) ) {
2019-08-09 01:39:45 +00:00
_log ( " Ignoring type '%s' because it's not enabled \n " , String ( type_cname ) . utf8 ( ) . get_data ( ) ) ;
2018-07-18 19:15:41 +00:00
class_list . pop_front ( ) ;
continue ;
}
2017-12-24 02:17:48 +00:00
ClassDB : : ClassInfo * class_info = ClassDB : : classes . getptr ( type_cname ) ;
2017-10-02 21:24:00 +00:00
TypeInterface itype = TypeInterface : : create_object_type ( type_cname , api_type ) ;
itype . base_name = ClassDB : : get_parent_class ( type_cname ) ;
2017-11-13 20:46:57 +00:00
itype . is_singleton = Engine : : get_singleton ( ) - > has_singleton ( itype . proxy_name ) ;
2019-07-08 13:13:57 +00:00
itype . is_instantiable = class_info - > creation_func & & ! itype . is_singleton ;
2021-06-04 16:03:15 +00: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 21:24:00 +00:00
itype . c_out = " \t return " ;
itype . c_out + = C_METHOD_UNMANAGED_GET_MANAGED ;
2021-06-04 16:03:15 +00:00
itype . c_out + = itype . is_ref_counted ? " (%1.ptr()); \n " : " (%1); \n " ;
2017-10-02 21:24:00 +00:00
itype . cs_in = itype . is_singleton ? BINDINGS_PTR_FIELD : " Object. " CS_SMETHOD_GETINSTANCE " (%0) " ;
itype . c_type = " Object* " ;
itype . c_type_in = itype . c_type ;
itype . c_type_out = " MonoObject* " ;
itype . cs_type = itype . proxy_name ;
itype . im_type_in = " IntPtr " ;
itype . im_type_out = itype . proxy_name ;
2019-04-25 15:11:01 +00:00
// Populate properties
2017-12-29 01:18:46 +00:00
List < PropertyInfo > property_list ;
ClassDB : : get_property_list ( type_cname , & property_list , true ) ;
2019-04-25 15:11:01 +00:00
Map < StringName , StringName > accessor_methods ;
2017-12-29 01:18:46 +00:00
2021-07-16 03:45:57 +00:00
for ( const PropertyInfo & property : property_list ) {
2021-09-08 10:51:45 +00: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 01:18:46 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2017-12-29 01:18:46 +00:00
2020-05-09 18:54:07 +00: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 01:18:46 +00: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 02:03:05 +00:00
if ( iprop . setter ! = StringName ( ) ) {
2019-04-25 15:11:01 +00:00
accessor_methods [ iprop . setter ] = iprop . cname ;
2020-05-16 02:03:05 +00:00
}
if ( iprop . getter ! = StringName ( ) ) {
2019-04-25 15:11:01 +00:00
accessor_methods [ iprop . getter ] = iprop . cname ;
2020-05-16 02:03:05 +00:00
}
2019-04-25 15:11:01 +00:00
2017-12-29 01:18:46 +00:00
bool valid = false ;
iprop . index = ClassDB : : get_property_index ( type_cname , iprop . cname , & valid ) ;
2020-05-09 18:54:07 +00:00
ERR_FAIL_COND_V_MSG ( ! valid , false , " Invalid property: ' " + itype . name + " . " + String ( iprop . cname ) + " '. " ) ;
2017-12-29 01:18:46 +00:00
2019-03-18 20:59:35 +00:00
iprop . proxy_name = escape_csharp_keyword ( snake_to_pascal_case ( iprop . cname ) ) ;
2019-04-25 13:45:12 +00:00
// Prevent the property and its enclosing type from sharing the same name
2017-12-29 01:18:46 +00:00
if ( iprop . proxy_name = = itype . proxy_name ) {
2019-08-09 01:39:45 +00:00
_log ( " Name of property '%s' is ambiguous with the name of its enclosing class '%s'. Renaming property to '%s_' \n " ,
2019-04-25 13:45:12 +00:00
iprop . proxy_name . utf8 ( ) . get_data ( ) , itype . proxy_name . utf8 ( ) . get_data ( ) , iprop . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-12-29 01:18:46 +00:00
iprop . proxy_name + = " _ " ;
}
2020-04-01 23:20:12 +00:00
iprop . prop_doc = nullptr ;
2017-12-29 01:18:46 +00: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 02:17:48 +00:00
// Populate methods
2017-10-02 21:24:00 +00: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 16:45:00 +00:00
for ( const MethodInfo & method_info : method_list ) {
2017-10-02 21:24:00 +00:00
int argc = method_info . arguments . size ( ) ;
2020-12-15 12:04:21 +00:00
if ( method_info . name . is_empty ( ) ) {
2017-10-02 21:24:00 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
2019-05-23 22:40:16 +00:00
String cname = method_info . name ;
2020-05-16 02:03:05 +00:00
if ( blacklisted_methods . find ( itype . cname ) & & blacklisted_methods [ itype . cname ] . find ( cname ) ) {
2019-05-23 22:40:16 +00:00
continue ;
2020-05-16 02:03:05 +00:00
}
2019-05-23 22:40:16 +00:00
2017-10-02 21:24:00 +00:00
MethodInterface imethod ;
imethod . name = method_info . name ;
2019-05-23 22:40:16 +00:00
imethod . cname = cname ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
if ( method_info . flags & METHOD_FLAG_VIRTUAL ) {
2017-10-02 21:24:00 +00:00
imethod . is_virtual = true ;
2020-05-16 02:03:05 +00:00
}
2017-10-02 21:24:00 +00:00
PropertyInfo return_info = method_info . return_val ;
2020-04-01 23:20:12 +00:00
MethodBind * m = imethod . is_virtual ? nullptr : ClassDB : : get_method ( type_cname , method_info . name ) ;
2017-10-02 21:24:00 +00:00
imethod . is_vararg = m & & m - > is_vararg ( ) ;
if ( ! m & & ! imethod . is_virtual ) {
2019-09-24 13:09:54 +00:00
ERR_FAIL_COND_V_MSG ( ! virtual_method_list . find ( method_info ) , false ,
2019-08-09 01:39:45 +00: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 02:03:05 +00: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 01:39:45 +00:00
if ( itype . cname ! = name_cache . type_Object | | imethod . name ! = " free " ) {
2019-11-07 08:44:15 +00:00
WARN_PRINT ( " Notification: New unexpected virtual non-overridable method found. "
" We only expected Object.free, but found ' " +
2021-10-28 13:19:35 +00:00
itype . name + " . " + imethod . name + " '. " ) ;
2017-10-02 21:24:00 +00:00
}
2018-04-28 20:25:25 +00:00
} else if ( return_info . type = = Variant : : INT & & return_info . usage & PROPERTY_USAGE_CLASS_IS_ENUM ) {
imethod . return_type . cname = return_info . class_name ;
imethod . return_type . is_enum = true ;
2017-10-02 21:24:00 +00:00
} else if ( return_info . class_name ! = StringName ( ) ) {
2018-04-28 20:25:25 +00:00
imethod . return_type . cname = return_info . class_name ;
2020-05-16 02:03:05 +00:00
bool bad_reference_hint = ! imethod . is_virtual & & return_info . hint ! = PROPERTY_HINT_RESOURCE_TYPE & &
2021-10-28 13:19:35 +00:00
ClassDB : : is_parent_class ( return_info . class_name , name_cache . type_RefCounted ) ;
2020-05-16 02:03:05 +00: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 + " '. " ) ;
2017-10-02 21:24:00 +00:00
} else if ( return_info . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
2018-04-28 20:25:25 +00:00
imethod . return_type . cname = return_info . hint_string ;
2017-10-02 21:24:00 +00:00
} else if ( return_info . type = = Variant : : NIL & & return_info . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) {
2018-04-28 20:25:25 +00:00
imethod . return_type . cname = name_cache . type_Variant ;
2017-10-02 21:24:00 +00:00
} else if ( return_info . type = = Variant : : NIL ) {
2018-04-28 20:25:25 +00:00
imethod . return_type . cname = name_cache . type_void ;
2017-10-02 21:24:00 +00:00
} else {
2019-04-25 18:24:48 +00:00
if ( return_info . type = = Variant : : INT ) {
imethod . return_type . cname = _get_int_type_name_from_meta ( m ? m - > get_argument_meta ( - 1 ) : GodotTypeInfo : : METADATA_NONE ) ;
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
} else if ( return_info . type = = Variant : : FLOAT ) {
2019-04-25 18:24:48 +00:00
imethod . return_type . cname = _get_float_type_name_from_meta ( m ? m - > get_argument_meta ( - 1 ) : GodotTypeInfo : : METADATA_NONE ) ;
} else {
imethod . return_type . cname = Variant : : get_type_name ( return_info . type ) ;
}
2017-10-02 21:24:00 +00:00
}
for ( int i = 0 ; i < argc ; i + + ) {
PropertyInfo arginfo = method_info . arguments [ i ] ;
2019-09-24 13:09:54 +00:00
String orig_arg_name = arginfo . name ;
2017-10-02 21:24:00 +00:00
ArgumentInterface iarg ;
2019-09-24 13:09:54 +00:00
iarg . name = orig_arg_name ;
2017-10-02 21:24:00 +00:00
2018-04-28 20:25:25 +00:00
if ( arginfo . type = = Variant : : INT & & arginfo . usage & PROPERTY_USAGE_CLASS_IS_ENUM ) {
iarg . type . cname = arginfo . class_name ;
iarg . type . is_enum = true ;
2017-10-02 21:24:00 +00:00
} else if ( arginfo . class_name ! = StringName ( ) ) {
2018-04-28 20:25:25 +00:00
iarg . type . cname = arginfo . class_name ;
2017-10-02 21:24:00 +00:00
} else if ( arginfo . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
2018-04-28 20:25:25 +00:00
iarg . type . cname = arginfo . hint_string ;
2017-10-02 21:24:00 +00:00
} else if ( arginfo . type = = Variant : : NIL ) {
2018-04-28 20:25:25 +00:00
iarg . type . cname = name_cache . type_Variant ;
2017-10-02 21:24:00 +00:00
} else {
2019-04-25 18:24:48 +00:00
if ( arginfo . type = = Variant : : INT ) {
iarg . type . cname = _get_int_type_name_from_meta ( m ? m - > get_argument_meta ( i ) : GodotTypeInfo : : METADATA_NONE ) ;
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
} else if ( arginfo . type = = Variant : : FLOAT ) {
2019-04-25 18:24:48 +00:00
iarg . type . cname = _get_float_type_name_from_meta ( m ? m - > get_argument_meta ( i ) : GodotTypeInfo : : METADATA_NONE ) ;
} else {
iarg . type . cname = Variant : : get_type_name ( arginfo . type ) ;
}
2017-10-02 21:24:00 +00:00
}
iarg . name = escape_csharp_keyword ( snake_to_camel_case ( iarg . name ) ) ;
if ( m & & m - > has_default_argument ( i ) ) {
2019-09-24 13:09:54 +00: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 21:24:00 +00:00
}
imethod . add_argument ( iarg ) ;
}
if ( imethod . is_vararg ) {
ArgumentInterface ivararg ;
2018-04-28 20:25:25 +00:00
ivararg . type . cname = name_cache . type_VarArg ;
2017-10-02 21:24:00 +00:00
ivararg . name = " @args " ;
imethod . add_argument ( ivararg ) ;
}
imethod . proxy_name = escape_csharp_keyword ( snake_to_pascal_case ( imethod . name ) ) ;
2019-04-25 13:45:12 +00:00
// Prevent the method and its enclosing type from sharing the same name
2017-10-02 21:24:00 +00:00
if ( imethod . proxy_name = = itype . proxy_name ) {
2019-08-09 01:39:45 +00:00
_log ( " Name of method '%s' is ambiguous with the name of its enclosing class '%s'. Renaming method to '%s_' \n " ,
2019-04-25 13:45:12 +00:00
imethod . proxy_name . utf8 ( ) . get_data ( ) , itype . proxy_name . utf8 ( ) . get_data ( ) , imethod . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 21:24:00 +00:00
imethod . proxy_name + = " _ " ;
}
2019-04-25 15:11:01 +00:00
Map < StringName , StringName > : : Element * accessor = accessor_methods . find ( imethod . cname ) ;
if ( accessor ) {
const PropertyInterface * accessor_property = itype . find_property_by_name ( accessor - > value ( ) ) ;
// 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 21:24:00 +00: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 02:03:05 +00:00
ERR_FAIL_COND_V_MSG ( itype . find_property_by_name ( imethod . cname ) , false ,
" Method name conflicts with property: ' " + itype . name + " . " + imethod . name + " '. " ) ;
// Classes starting with an underscore are ignored unless they're used as a property setter or getter
2017-10-02 21:24:00 +00:00
if ( ! imethod . is_virtual & & imethod . name [ 0 ] = = ' _ ' ) {
2021-07-25 16:45:00 +00:00
for ( const PropertyInterface & iprop : itype . properties ) {
2017-12-29 01:18:46 +00:00
if ( iprop . setter = = imethod . name | | iprop . getter = = imethod . name ) {
2017-10-02 21:24:00 +00:00
imethod . is_internal = true ;
itype . methods . push_back ( imethod ) ;
break ;
}
}
} else {
itype . methods . push_back ( imethod ) ;
}
}
2020-03-14 18:20:17 +00:00
// Populate signals
const HashMap < StringName , MethodInfo > & signal_map = class_info - > signal_map ;
2020-04-01 23:20:12 +00:00
const StringName * k = nullptr ;
2020-03-14 18:20:17 +00:00
while ( ( k = signal_map . next ( k ) ) ) {
SignalInterface isignal ;
const MethodInfo & method_info = signal_map . get ( * k ) ;
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 ;
if ( arginfo . type = = Variant : : INT & & arginfo . usage & PROPERTY_USAGE_CLASS_IS_ENUM ) {
iarg . type . cname = arginfo . class_name ;
iarg . type . is_enum = true ;
} else if ( arginfo . class_name ! = StringName ( ) ) {
iarg . type . cname = arginfo . class_name ;
} 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 {
if ( arginfo . type = = Variant : : INT ) {
iarg . type . cname = _get_int_type_name_from_meta ( GodotTypeInfo : : METADATA_NONE ) ;
} else if ( arginfo . type = = Variant : : FLOAT ) {
iarg . type . cname = _get_float_type_name_from_meta ( GodotTypeInfo : : METADATA_NONE ) ;
} else {
iarg . type . cname = Variant : : get_type_name ( arginfo . type ) ;
}
}
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 02:17:48 +00:00
// Populate enums and constants
2019-04-18 23:10:08 +00:00
List < String > constants ;
ClassDB : : get_integer_constant_list ( type_cname , & constants , true ) ;
2017-12-24 02:17:48 +00:00
2020-03-17 06:33:00 +00:00
const HashMap < StringName , List < StringName > > & enum_map = class_info - > enum_map ;
2020-04-01 23:20:12 +00:00
k = nullptr ;
2017-12-24 02:17:48 +00:00
while ( ( k = enum_map . next ( k ) ) ) {
StringName enum_proxy_cname = * k ;
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 ) ;
2019-04-18 23:10:08 +00:00
const List < StringName > & enum_constants = enum_map . get ( * k ) ;
2021-07-25 16:45:00 +00:00
for ( const StringName & constant_cname : enum_constants ) {
2018-10-18 17:41:10 +00:00
String constant_name = constant_cname . operator String ( ) ;
int * value = class_info - > constant_map . getptr ( constant_cname ) ;
2019-09-24 13:09:54 +00:00
ERR_FAIL_NULL_V ( value , false ) ;
2019-04-18 23:10:08 +00:00
constants . erase ( constant_name ) ;
2017-12-24 02:17:48 +00:00
2018-10-18 17:41:10 +00:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , * value ) ;
2017-12-24 02:17:48 +00:00
2020-04-01 23:20:12 +00:00
iconstant . const_doc = nullptr ;
2017-12-24 02:17:48 +00: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 17:41:10 +00:00
int prefix_length = _determine_enum_prefix ( ienum ) ;
_apply_prefix_to_enum_constants ( ienum , prefix_length ) ;
2017-12-24 02:17:48 +00:00
itype . enums . push_back ( ienum ) ;
TypeInterface enum_itype ;
2018-04-28 20:25:25 +00:00
enum_itype . is_enum = true ;
2017-12-24 02:17:48 +00:00
enum_itype . name = itype . name + " . " + String ( * k ) ;
enum_itype . cname = StringName ( enum_itype . name ) ;
enum_itype . proxy_name = itype . proxy_name + " . " + enum_proxy_name ;
2018-04-28 20:25:25 +00:00
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2017-12-24 02:17:48 +00:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
}
2021-07-25 16:45:00 +00:00
for ( const String & constant_name : constants ) {
int * value = class_info - > constant_map . getptr ( StringName ( constant_name ) ) ;
2019-09-24 13:09:54 +00:00
ERR_FAIL_NULL_V ( value , false ) ;
2017-12-24 02:17:48 +00:00
2018-10-18 17:41:10 +00:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , * value ) ;
2017-12-24 02:17:48 +00:00
2020-04-01 23:20:12 +00:00
iconstant . const_doc = nullptr ;
2017-12-24 02:17:48 +00: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 21:24:00 +00:00
class_list . pop_front ( ) ;
}
2019-09-24 13:09:54 +00:00
return true ;
2017-10-02 21:24:00 +00:00
}
2019-09-24 13:09:54 +00:00
bool BindingsGenerator : : _arg_default_value_from_variant ( const Variant & p_val , ArgumentInterface & r_iarg ) {
2020-05-16 02:03:05 +00:00
r_iarg . def_param_value = p_val ;
r_iarg . default_argument = p_val . operator String ( ) ;
2017-10-02 21:24:00 +00:00
switch ( p_val . get_type ( ) ) {
case Variant : : NIL :
2019-06-15 15:34:07 +00:00
// Either Object type or Variant
r_iarg . default_argument = " null " ;
2017-10-02 21:24:00 +00:00
break ;
// Atomic types
case Variant : : BOOL :
r_iarg . default_argument = bool ( p_val ) ? " true " : " false " ;
break ;
case Variant : : INT :
2018-04-28 20:25:25 +00:00
if ( r_iarg . type . cname ! = name_cache . type_int ) {
2017-12-24 02:17:48 +00:00
r_iarg . default_argument = " (%s) " + r_iarg . default_argument ;
}
break ;
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : FLOAT :
2020-07-24 07:48:21 +00:00
if ( r_iarg . type . cname = = name_cache . type_float ) {
r_iarg . default_argument + = " f " ;
}
2017-10-02 21:24:00 +00:00
break ;
case Variant : : STRING :
2020-03-14 18:20:17 +00:00
case Variant : : STRING_NAME :
2017-10-02 21:24:00 +00:00
case Variant : : NODE_PATH :
2020-03-14 18:20:17 +00:00
if ( r_iarg . type . cname = = name_cache . type_StringName | | r_iarg . type . cname = = name_cache . type_NodePath ) {
r_iarg . default_argument = " (%s) \" " + r_iarg . default_argument + " \" " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
} else {
CRASH_COND ( r_iarg . type . cname ! = name_cache . type_String ) ;
r_iarg . default_argument = " \" " + r_iarg . default_argument + " \" " ;
}
2017-10-02 21:24:00 +00:00
break ;
2020-07-24 07:48:21 +00:00
case Variant : : PLANE : {
Plane plane = p_val . operator Plane ( ) ;
2021-02-25 14:54:50 +00:00
r_iarg . default_argument = " new Plane(new Vector3 " + plane . normal . operator String ( ) + " , " + rtos ( plane . d ) + " ) " ;
2017-10-02 21:24:00 +00:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
2020-07-24 07:48:21 +00:00
} break ;
case Variant : : AABB : {
AABB aabb = p_val . operator : : AABB ( ) ;
2021-06-18 05:13:00 +00:00
r_iarg . default_argument = " new AABB(new Vector3 " + aabb . position . operator String ( ) + " , new Vector3 " + aabb . size . operator String ( ) + " ) " ;
2020-07-24 07:48:21 +00:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
case Variant : : RECT2 : {
Rect2 rect = p_val . operator Rect2 ( ) ;
2021-06-18 05:13:00 +00:00
r_iarg . default_argument = " new Rect2(new Vector2 " + rect . position . operator String ( ) + " , new Vector2 " + rect . size . operator String ( ) + " ) " ;
2020-07-24 07:48:21 +00:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
case Variant : : RECT2I : {
Rect2i rect = p_val . operator Rect2i ( ) ;
2021-06-18 05:13:00 +00:00
r_iarg . default_argument = " new Rect2i(new Vector2i " + rect . position . operator String ( ) + " , new Vector2i " + rect . size . operator String ( ) + " ) " ;
2020-07-24 07:48:21 +00:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
2017-10-02 21:24:00 +00:00
case Variant : : COLOR :
case Variant : : VECTOR2 :
2020-03-03 09:42:20 +00:00
case Variant : : VECTOR2I :
2017-10-02 21:24:00 +00:00
case Variant : : VECTOR3 :
2020-03-03 09:42:20 +00:00
case Variant : : VECTOR3I :
2017-10-02 21:24:00 +00:00
r_iarg . default_argument = " new %s " + r_iarg . default_argument ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
break ;
case Variant : : OBJECT :
2019-09-24 13:09:54 +00: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 21:24:00 +00:00
case Variant : : DICTIONARY :
r_iarg . default_argument = " new %s() " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
break ;
2020-11-09 13:53:05 +00:00
case Variant : : RID :
2019-09-24 13:09:54 +00: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. " ) ;
r_iarg . default_argument = " null " ;
break ;
2017-10-02 21:24:00 +00:00
case Variant : : ARRAY :
2021-07-25 11:46:31 +00:00
r_iarg . default_argument = " new %s { } " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
break ;
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_BYTE_ARRAY :
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
2020-03-14 18:20:17 +00:00
case Variant : : PACKED_FLOAT32_ARRAY :
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : PACKED_FLOAT64_ARRAY :
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_STRING_ARRAY :
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : PACKED_VECTOR3_ARRAY :
case Variant : : PACKED_COLOR_ARRAY :
2021-07-25 11:46:31 +00:00
r_iarg . default_argument = " Array.Empty<%s>() " ;
2017-10-02 21:24:00 +00:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
break ;
2020-07-24 07:48:21 +00:00
case Variant : : TRANSFORM2D : {
Transform2D transform = p_val . operator Transform2D ( ) ;
if ( transform = = Transform2D ( ) ) {
r_iarg . default_argument = " Transform2D.Identity " ;
} else {
r_iarg . default_argument = " new Transform2D(new Vector2 " + transform . elements [ 0 ] . operator String ( ) + " , new Vector2 " + transform . elements [ 1 ] . operator String ( ) + " , new Vector2 " + transform . elements [ 2 ] . operator String ( ) + " ) " ;
}
2017-10-02 21:24:00 +00:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
2020-07-24 07:48:21 +00:00
} break ;
2021-04-28 07:36:08 +00:00
case Variant : : TRANSFORM3D : {
2020-10-17 05:08:21 +00:00
Transform3D transform = p_val . operator Transform3D ( ) ;
if ( transform = = Transform3D ( ) ) {
2021-02-27 04:26:56 +00:00
r_iarg . default_argument = " Transform3D.Identity " ;
2020-07-24 07:48:21 +00:00
} else {
Basis basis = transform . basis ;
2021-02-27 04:26:56 +00: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 07:48:21 +00: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 07:48:21 +00:00
} else {
2021-01-20 07:02:02 +00:00
r_iarg . default_argument = " new Quaternion " + quaternion . operator String ( ) ;
2020-07-24 07:48:21 +00:00
}
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
} break ;
2020-03-14 18:20:17 +00:00
case Variant : : CALLABLE :
2021-08-20 09:18:04 +00: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 18:20:17 +00:00
case Variant : : SIGNAL :
2021-08-20 09:18:04 +00: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 18:20:17 +00:00
break ;
default :
CRASH_NOW_MSG ( " Unexpected Variant type: " + itos ( p_val . get_type ( ) ) ) ;
break ;
2017-10-02 21:24:00 +00:00
}
2020-05-16 02:03:05 +00:00
if ( r_iarg . def_param_mode = = ArgumentInterface : : CONSTANT & & r_iarg . type . cname = = name_cache . type_Variant & & r_iarg . default_argument ! = " null " ) {
2017-10-02 21:24:00 +00:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
2020-05-16 02:03:05 +00:00
}
2019-09-24 13:09:54 +00:00
return true ;
2017-10-02 21:24:00 +00:00
}
void BindingsGenerator : : _populate_builtin_type_interfaces ( ) {
builtin_types . clear ( ) ;
TypeInterface itype ;
2019-11-14 20:48:49 +00:00
# define INSERT_STRUCT_TYPE(m_type) \
2018-10-17 19:37:57 +00:00
{ \
itype = TypeInterface : : create_value_type ( String ( # m_type ) ) ; \
itype . c_in = " \t %0 %1_in = MARSHALLED_IN( " # m_type " , %1); \n " ; \
2019-11-14 20:48:49 +00:00
itype . c_out = " \t *%3 = MARSHALLED_OUT( " # m_type " , %1); \n " ; \
2018-10-17 19:37:57 +00:00
itype . c_arg_in = " &%s_in " ; \
itype . c_type_in = " GDMonoMarshal::M_ " # m_type " * " ; \
itype . c_type_out = " GDMonoMarshal::M_ " # m_type ; \
itype . cs_in = " ref %s " ; \
2019-11-14 20:48:49 +00:00
/* in cs_out, im_type_out (%3) includes the 'out ' part */ \
2020-03-14 18:20:17 +00:00
itype . cs_out = " %0(%1, %3 argRet); return argRet; " ; \
2019-11-14 20:48:49 +00:00
itype . im_type_out = " out " + itype . cs_type ; \
itype . ret_as_byref_arg = true ; \
2018-10-17 19:37:57 +00:00
builtin_types . insert ( itype . cname , itype ) ; \
2017-10-02 21:24:00 +00:00
}
2019-11-14 20:48:49 +00:00
INSERT_STRUCT_TYPE ( Vector2 )
2020-03-03 09:42:20 +00:00
INSERT_STRUCT_TYPE ( Vector2i )
2019-11-14 20:48:49 +00:00
INSERT_STRUCT_TYPE ( Rect2 )
2020-03-03 09:42:20 +00:00
INSERT_STRUCT_TYPE ( Rect2i )
2019-11-14 20:48:49 +00:00
INSERT_STRUCT_TYPE ( Transform2D )
INSERT_STRUCT_TYPE ( Vector3 )
2020-03-03 09:42:20 +00:00
INSERT_STRUCT_TYPE ( Vector3i )
2019-11-14 20:48:49 +00:00
INSERT_STRUCT_TYPE ( Basis )
2021-01-20 07:02:02 +00:00
INSERT_STRUCT_TYPE ( Quaternion )
2020-10-17 05:08:21 +00:00
INSERT_STRUCT_TYPE ( Transform3D )
2019-11-14 20:48:49 +00:00
INSERT_STRUCT_TYPE ( AABB )
INSERT_STRUCT_TYPE ( Color )
INSERT_STRUCT_TYPE ( Plane )
2017-10-02 21:24:00 +00:00
# undef INSERT_STRUCT_TYPE
2018-01-25 22:44:37 +00:00
// bool
itype = TypeInterface : : create_value_type ( String ( " bool " ) ) ;
2018-10-17 19:37:57 +00:00
{
// MonoBoolean <---> bool
itype . c_in = " \t %0 %1_in = (%0)%1; \n " ;
itype . c_out = " \t return (%0)%1; \n " ;
itype . c_type = " bool " ;
itype . c_type_in = " MonoBoolean " ;
itype . c_type_out = itype . c_type_in ;
itype . c_arg_in = " &%s_in " ;
}
2018-01-25 22:44:37 +00:00
itype . im_type_in = itype . name ;
itype . im_type_out = itype . name ;
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
2019-04-25 18:24:48 +00:00
// Integer types
2018-10-17 19:37:57 +00:00
{
2019-04-25 18:24:48 +00: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.
# define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \
{ \
itype = TypeInterface : : create_value_type ( String ( m_name ) ) ; \
{ \
itype . c_in = " \t %0 %1_in = (%0)%1; \n " ; \
itype . c_out = " \t return (%0)%1; \n " ; \
itype . c_type = # m_c_type ; \
itype . c_arg_in = " &%s_in " ; \
} \
itype . c_type_in = # m_c_type_in_out ; \
itype . c_type_out = itype . c_type_in ; \
itype . im_type_in = itype . name ; \
itype . im_type_out = itype . name ; \
builtin_types . insert ( itype . cname , itype ) ; \
2018-10-17 19:37:57 +00:00
}
2017-10-02 21:24:00 +00:00
2019-04-25 18:24:48 +00:00
// The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
INSERT_INT_TYPE ( " sbyte " , int8_t , int64_t ) ;
INSERT_INT_TYPE ( " short " , int16_t , int64_t ) ;
INSERT_INT_TYPE ( " int " , int32_t , int64_t ) ;
2020-12-04 23:05:01 +00:00
INSERT_INT_TYPE ( " long " , int64_t , int64_t ) ;
2019-04-25 18:24:48 +00:00
INSERT_INT_TYPE ( " byte " , uint8_t , int64_t ) ;
INSERT_INT_TYPE ( " ushort " , uint16_t , int64_t ) ;
INSERT_INT_TYPE ( " uint " , uint32_t , int64_t ) ;
2020-12-04 23:05:01 +00:00
INSERT_INT_TYPE ( " ulong " , uint64_t , int64_t ) ;
2019-04-25 18:24:48 +00:00
}
// Floating point types
2018-10-17 19:37:57 +00:00
{
2019-04-25 18:24:48 +00:00
// float
itype = TypeInterface ( ) ;
itype . name = " float " ;
itype . cname = itype . name ;
itype . proxy_name = " float " ;
{
// The expected type for 'float' in ptrcall is 'double'
2020-12-04 23:05:01 +00:00
itype . c_in = " \t %0 %1_in = (%0)%1; \n " ;
itype . c_out = " \t return (%0)%1; \n " ;
2019-04-25 18:24:48 +00:00
itype . c_type = " double " ;
2020-12-04 23:05:01 +00:00
itype . c_type_in = " float " ;
2019-04-25 18:24:48 +00:00
itype . c_type_out = " float " ;
itype . c_arg_in = " &%s_in " ;
}
itype . cs_type = itype . proxy_name ;
2020-12-04 23:05:01 +00:00
itype . im_type_in = itype . proxy_name ;
itype . im_type_out = itype . proxy_name ;
2019-04-25 18:24:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
// double
itype = TypeInterface ( ) ;
itype . name = " double " ;
itype . cname = itype . name ;
itype . proxy_name = " double " ;
2019-11-14 20:48:49 +00:00
{
itype . c_type = " double " ;
2020-12-04 23:05:01 +00:00
itype . c_type_in = " double " ;
2019-11-14 20:48:49 +00:00
itype . c_type_out = " double " ;
2020-12-04 23:05:01 +00:00
itype . c_arg_in = " &%s " ;
2019-11-14 20:48:49 +00:00
}
2019-04-25 18:24:48 +00:00
itype . cs_type = itype . proxy_name ;
2020-12-04 23:05:01 +00:00
itype . im_type_in = itype . proxy_name ;
itype . im_type_out = itype . proxy_name ;
2019-04-25 18:24:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2018-10-17 19:37:57 +00:00
}
2017-10-02 21:24:00 +00:00
// String
itype = TypeInterface ( ) ;
itype . name = " String " ;
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ;
2017-10-02 21:24:00 +00:00
itype . proxy_name = " string " ;
itype . c_in = " \t %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT " (%1); \n " ;
itype . c_out = " \t return " C_METHOD_MONOSTR_FROM_GODOT " (%1); \n " ;
itype . c_arg_in = " &%s_in " ;
itype . c_type = itype . name ;
itype . c_type_in = " MonoString* " ;
itype . c_type_out = " MonoString* " ;
itype . cs_type = itype . proxy_name ;
itype . im_type_in = itype . proxy_name ;
itype . im_type_out = itype . proxy_name ;
2017-12-24 02:17:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
2020-03-14 18:20:17 +00:00
// StringName
itype = TypeInterface ( ) ;
itype . name = " StringName " ;
itype . cname = itype . name ;
itype . proxy_name = " StringName " ;
itype . c_in = " \t %0 %1_in = %1 ? *%1 : StringName(); \n " ;
itype . c_out = " \t return memnew(StringName(%1)); \n " ;
itype . c_arg_in = " &%s_in " ;
itype . c_type = itype . name ;
itype . c_type_in = itype . c_type + " * " ;
itype . c_type_out = itype . c_type + " * " ;
itype . cs_type = itype . proxy_name ;
itype . cs_in = " StringName. " CS_SMETHOD_GETINSTANCE " (%0) " ;
itype . cs_out = " return new %2(%0(%1)); " ;
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
// NodePath
itype = TypeInterface ( ) ;
itype . name = " NodePath " ;
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ;
2017-10-02 21:24:00 +00:00
itype . proxy_name = " NodePath " ;
itype . c_out = " \t return memnew(NodePath(%1)); \n " ;
itype . c_type = itype . name ;
itype . c_type_in = itype . c_type + " * " ;
itype . c_type_out = itype . c_type + " * " ;
itype . cs_type = itype . proxy_name ;
itype . cs_in = " NodePath. " CS_SMETHOD_GETINSTANCE " (%0) " ;
2019-11-14 20:48:49 +00:00
itype . cs_out = " return new %2(%0(%1)); " ;
2017-10-02 21:24:00 +00:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
2017-12-24 02:17:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
// RID
itype = TypeInterface ( ) ;
itype . name = " RID " ;
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ;
2017-10-02 21:24:00 +00:00
itype . proxy_name = " RID " ;
itype . c_out = " \t return memnew(RID(%1)); \n " ;
itype . c_type = itype . name ;
itype . c_type_in = itype . c_type + " * " ;
itype . c_type_out = itype . c_type + " * " ;
itype . cs_type = itype . proxy_name ;
itype . cs_in = " RID. " CS_SMETHOD_GETINSTANCE " (%0) " ;
2019-11-14 20:48:49 +00:00
itype . cs_out = " return new %2(%0(%1)); " ;
2017-10-02 21:24:00 +00:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
2017-12-24 02:17:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
// Variant
itype = TypeInterface ( ) ;
itype . name = " Variant " ;
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ;
2017-10-02 21:24:00 +00:00
itype . proxy_name = " object " ;
itype . c_in = " \t %0 %1_in = " C_METHOD_MANAGED_TO_VARIANT " (%1); \n " ;
itype . c_out = " \t return " C_METHOD_MANAGED_FROM_VARIANT " (%1); \n " ;
itype . c_arg_in = " &%s_in " ;
itype . c_type = itype . name ;
itype . c_type_in = " MonoObject* " ;
itype . c_type_out = " MonoObject* " ;
itype . cs_type = itype . proxy_name ;
itype . im_type_in = " object " ;
itype . im_type_out = itype . proxy_name ;
2017-12-24 02:17:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
2020-03-14 18:20:17 +00:00
// Callable
itype = TypeInterface : : create_value_type ( String ( " Callable " ) ) ;
itype . c_in = " \t %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE " (*%1); \n " ;
itype . c_out = " \t *%3 = " C_METHOD_MANAGED_FROM_CALLABLE " (%1); \n " ;
itype . c_arg_in = " &%s_in " ;
itype . c_type_in = " GDMonoMarshal::M_Callable* " ;
itype . c_type_out = " GDMonoMarshal::M_Callable " ;
itype . cs_in = " ref %s " ;
/* in cs_out, im_type_out (%3) includes the 'out ' part */
itype . cs_out = " %0(%1, %3 argRet); return argRet; " ;
itype . im_type_out = " out " + itype . cs_type ;
itype . ret_as_byref_arg = true ;
builtin_types . insert ( itype . cname , itype ) ;
// Signal
itype = TypeInterface ( ) ;
itype . name = " Signal " ;
itype . cname = itype . name ;
itype . proxy_name = " SignalInfo " ;
itype . c_in = " \t %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL " (*%1); \n " ;
itype . c_out = " \t *%3 = " C_METHOD_MANAGED_FROM_SIGNAL " (%1); \n " ;
itype . c_arg_in = " &%s_in " ;
itype . c_type = itype . name ;
itype . c_type_in = " GDMonoMarshal::M_SignalInfo* " ;
itype . c_type_out = " GDMonoMarshal::M_SignalInfo " ;
itype . cs_in = " ref %s " ;
/* in cs_out, im_type_out (%3) includes the 'out ' part */
itype . cs_out = " %0(%1, %3 argRet); return argRet; " ;
itype . cs_type = itype . proxy_name ;
itype . im_type_in = " ref " + itype . cs_type ;
itype . im_type_out = " out " + itype . cs_type ;
itype . ret_as_byref_arg = true ;
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
// VarArg (fictitious type to represent variable arguments)
itype = TypeInterface ( ) ;
itype . name = " VarArg " ;
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ;
2017-10-02 21:24:00 +00:00
itype . proxy_name = " object[] " ;
itype . c_in = " \t %0 %1_in = " C_METHOD_MONOARRAY_TO ( Array ) " (%1); \n " ;
itype . c_arg_in = " &%s_in " ;
itype . c_type = " Array " ;
itype . c_type_in = " MonoArray* " ;
itype . cs_type = " params object[] " ;
itype . im_type_in = " object[] " ;
2017-12-24 02:17:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
# define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t) \
{ \
itype = TypeInterface ( ) ; \
itype . name = # m_name ; \
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ; \
2017-10-02 21:24:00 +00:00
itype . proxy_name = # m_proxy_t " [] " ; \
itype . c_in = " \t %0 %1_in = " C_METHOD_MONOARRAY_TO ( m_type ) " (%1); \n " ; \
itype . c_out = " \t return " C_METHOD_MONOARRAY_FROM ( m_type ) " (%1); \n " ; \
itype . c_arg_in = " &%s_in " ; \
itype . c_type = # m_type ; \
itype . c_type_in = " MonoArray* " ; \
itype . c_type_out = " MonoArray* " ; \
itype . cs_type = itype . proxy_name ; \
itype . im_type_in = itype . proxy_name ; \
itype . im_type_out = itype . proxy_name ; \
builtin_types . insert ( itype . name , itype ) ; \
}
# define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
INSERT_ARRAY ( PackedInt32Array , int ) ;
2020-03-14 18:20:17 +00:00
INSERT_ARRAY ( PackedInt64Array , long ) ;
2020-02-17 21:06:54 +00:00
INSERT_ARRAY_FULL ( PackedByteArray , PackedByteArray , byte ) ;
2017-10-02 21:24:00 +00:00
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
INSERT_ARRAY ( PackedFloat32Array , float ) ;
2020-03-14 18:20:17 +00:00
INSERT_ARRAY ( PackedFloat64Array , double ) ;
2017-10-02 21:24:00 +00:00
2020-02-17 21:06:54 +00:00
INSERT_ARRAY ( PackedStringArray , string ) ;
2017-10-02 21:24:00 +00:00
2020-02-17 21:06:54 +00:00
INSERT_ARRAY ( PackedColorArray , Color ) ;
INSERT_ARRAY ( PackedVector2Array , Vector2 ) ;
INSERT_ARRAY ( PackedVector3Array , Vector3 ) ;
2017-10-02 21:24:00 +00:00
# undef INSERT_ARRAY
2018-07-18 21:07:57 +00:00
// Array
itype = TypeInterface ( ) ;
itype . name = " Array " ;
itype . cname = itype . name ;
2018-08-27 18:39:51 +00:00
itype . proxy_name = itype . name ;
2018-07-18 21:07:57 +00:00
itype . c_out = " \t return memnew(Array(%1)); \n " ;
itype . c_type = itype . name ;
itype . c_type_in = itype . c_type + " * " ;
itype . c_type_out = itype . c_type + " * " ;
2018-08-27 18:39:51 +00:00
itype . cs_type = BINDINGS_NAMESPACE_COLLECTIONS " . " + itype . proxy_name ;
2018-07-18 21:07:57 +00:00
itype . cs_in = " %0. " CS_SMETHOD_GETINSTANCE " () " ;
2019-11-14 20:48:49 +00:00
itype . cs_out = " return new " + itype . cs_type + " (%0(%1)); " ;
2018-07-18 21:07:57 +00:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
// Dictionary
itype = TypeInterface ( ) ;
itype . name = " Dictionary " ;
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ;
2018-08-27 18:39:51 +00:00
itype . proxy_name = itype . name ;
2018-07-18 21:07:57 +00:00
itype . c_out = " \t return memnew(Dictionary(%1)); \n " ;
2017-10-02 21:24:00 +00:00
itype . c_type = itype . name ;
2018-07-18 21:07:57 +00:00
itype . c_type_in = itype . c_type + " * " ;
itype . c_type_out = itype . c_type + " * " ;
2018-08-27 18:39:51 +00:00
itype . cs_type = BINDINGS_NAMESPACE_COLLECTIONS " . " + itype . proxy_name ;
2018-07-18 21:07:57 +00:00
itype . cs_in = " %0. " CS_SMETHOD_GETINSTANCE " () " ;
2019-11-14 20:48:49 +00:00
itype . cs_out = " return new " + itype . cs_type + " (%0(%1)); " ;
2018-07-18 21:07:57 +00:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
2017-12-24 02:17:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
// void (fictitious type to represent the return type of methods that do not return anything)
itype = TypeInterface ( ) ;
itype . name = " void " ;
2017-12-24 02:17:48 +00:00
itype . cname = itype . name ;
2017-10-02 21:24:00 +00:00
itype . proxy_name = itype . name ;
itype . c_type = itype . name ;
itype . c_type_in = itype . c_type ;
itype . c_type_out = itype . c_type ;
itype . cs_type = itype . proxy_name ;
itype . im_type_in = itype . proxy_name ;
itype . im_type_out = itype . proxy_name ;
2017-12-24 02:17:48 +00:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 21:24:00 +00:00
}
2017-12-24 02:17:48 +00:00
void BindingsGenerator : : _populate_global_constants ( ) {
2020-11-07 22:33:38 +00:00
int global_constants_count = CoreConstants : : get_global_constant_count ( ) ;
2017-12-24 02:17:48 +00:00
if ( global_constants_count > 0 ) {
Map < String , DocData : : ClassDoc > : : Element * match = EditorHelp : : get_doc_data ( ) - > class_list . find ( " @GlobalScope " ) ;
2019-08-09 01:39:45 +00:00
CRASH_COND_MSG ( ! match , " Could not find '@GlobalScope' in DocData. " ) ;
2017-12-24 02:17:48 +00:00
const DocData : : ClassDoc & global_scope_doc = match - > value ( ) ;
for ( int i = 0 ; i < global_constants_count ; i + + ) {
2020-11-07 22:33:38 +00:00
String constant_name = CoreConstants : : get_global_constant_name ( i ) ;
2017-12-24 02:17:48 +00:00
2020-04-01 23:20:12 +00:00
const DocData : : ConstantDoc * const_doc = nullptr ;
2018-10-02 22:56:28 +00: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 02:17:48 +00:00
if ( curr_const_doc . name = = constant_name ) {
const_doc = & curr_const_doc ;
break ;
}
}
2020-11-07 22:33:38 +00:00
int constant_value = CoreConstants : : get_global_constant_value ( i ) ;
StringName enum_name = CoreConstants : : get_global_constant_enum ( i ) ;
2017-12-24 02:17:48 +00:00
2018-10-18 17:41:10 +00:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , constant_value ) ;
2017-12-24 02:17:48 +00:00
iconstant . const_doc = const_doc ;
if ( enum_name ! = StringName ( ) ) {
EnumInterface ienum ( enum_name ) ;
2019-02-12 20:10:08 +00:00
List < EnumInterface > : : Element * enum_match = global_enums . find ( ienum ) ;
if ( enum_match ) {
enum_match - > get ( ) . constants . push_back ( iconstant ) ;
2017-12-24 02:17:48 +00:00
} else {
ienum . constants . push_back ( iconstant ) ;
global_enums . push_back ( ienum ) ;
}
} else {
global_constants . push_back ( iconstant ) ;
}
}
2021-07-25 16:45:00 +00:00
for ( EnumInterface & ienum : global_enums ) {
2017-12-24 02:17:48 +00:00
TypeInterface enum_itype ;
2018-04-28 20:25:25 +00: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 ) ;
2017-12-24 02:17:48 +00:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
2018-10-18 17:41:10 +00:00
int prefix_length = _determine_enum_prefix ( ienum ) ;
2017-12-24 02:17:48 +00:00
2018-10-18 17:41:10 +00:00
// HARDCODED: The Error enum have the prefix 'ERR_' for everything except 'OK' and 'FAILED'.
2017-12-24 02:17:48 +00:00
if ( ienum . cname = = name_cache . enum_Error ) {
2018-10-18 17:41:10 +00:00
if ( prefix_length > 0 ) { // Just in case it ever changes
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Prefix for enum ' " _STR ( Error ) " ' is not empty. " ) ;
2017-12-24 02:17:48 +00:00
}
2018-10-18 17:41:10 +00:00
prefix_length = 1 ; // 'ERR_'
2017-12-24 02:17:48 +00:00
}
2018-10-18 17:41:10 +00:00
_apply_prefix_to_enum_constants ( ienum , prefix_length ) ;
2017-12-24 02:17:48 +00:00
}
}
// HARDCODED
List < StringName > hardcoded_enums ;
2020-03-03 09:42:20 +00:00
hardcoded_enums . push_back ( " Vector2.Axis " ) ;
hardcoded_enums . push_back ( " Vector2i.Axis " ) ;
2017-12-24 02:17:48 +00:00
hardcoded_enums . push_back ( " Vector3.Axis " ) ;
2020-03-03 09:42:20 +00:00
hardcoded_enums . push_back ( " Vector3i.Axis " ) ;
2021-07-25 16:45:00 +00:00
for ( const StringName & enum_cname : hardcoded_enums ) {
2017-12-24 02:17:48 +00:00
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
2018-04-28 20:25:25 +00:00
// Here, we assume core types do not begin with underscore
2017-12-24 02:17:48 +00:00
TypeInterface enum_itype ;
2018-04-28 20:25:25 +00:00
enum_itype . is_enum = true ;
2021-07-25 16:45:00 +00:00
enum_itype . name = enum_cname . operator String ( ) ;
enum_itype . cname = enum_cname ;
2018-04-28 20:25:25 +00:00
enum_itype . proxy_name = enum_itype . name ;
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2017-12-24 02:17:48 +00:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
}
}
2019-05-23 22:40:16 +00: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 13:45:12 +00: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 13:09:54 +00:00
initialized = false ;
2017-10-02 21:24:00 +00:00
EditorHelp : : generate_doc ( ) ;
2017-12-24 02:17:48 +00:00
enum_types . clear ( ) ;
2019-05-23 22:40:16 +00:00
_initialize_blacklisted_methods ( ) ;
2019-09-24 13:09:54 +00: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 21:24:00 +00:00
_populate_builtin_type_interfaces ( ) ;
2017-12-24 02:17:48 +00:00
_populate_global_constants ( ) ;
2018-09-04 03:40:41 +00:00
// Generate internal calls (after populating type interfaces and global constants)
2017-12-24 02:17:48 +00:00
2018-09-04 03:40:41 +00:00
core_custom_icalls . clear ( ) ;
editor_custom_icalls . clear ( ) ;
2017-10-02 21:24:00 +00:00
2020-05-16 02:03:05 +00:00
for ( OrderedHashMap < StringName , TypeInterface > : : Element E = obj_types . front ( ) ; E ; E = E . next ( ) ) {
2018-01-09 18:06:59 +00:00
_generate_method_icalls ( E . get ( ) ) ;
2020-05-16 02:03:05 +00:00
}
2019-09-24 13:09:54 +00:00
initialized = true ;
2017-10-02 21:24:00 +00:00
}
2021-03-13 00:16:27 +00:00
static String generate_all_glue_option = " --generate-mono-glue " ;
static String generate_cs_glue_option = " --generate-mono-cs-glue " ;
static String generate_cpp_glue_option = " --generate-mono-cpp-glue " ;
static void handle_cmdline_options ( String glue_dir_path , String cs_dir_path , String cpp_dir_path ) {
BindingsGenerator bindings_generator ;
bindings_generator . set_log_print_enabled ( true ) ;
if ( ! bindings_generator . is_initialized ( ) ) {
ERR_PRINT ( " Failed to initialize the bindings generator " ) ;
return ;
}
if ( glue_dir_path . length ( ) ) {
if ( bindings_generator . generate_glue ( glue_dir_path ) ! = OK ) {
ERR_PRINT ( generate_all_glue_option + " : Failed to generate the C++ glue. " ) ;
}
if ( bindings_generator . generate_cs_api ( glue_dir_path . plus_file ( API_SOLUTION_NAME ) ) ! = OK ) {
ERR_PRINT ( generate_all_glue_option + " : Failed to generate the C# API. " ) ;
}
}
if ( cs_dir_path . length ( ) ) {
if ( bindings_generator . generate_cs_api ( cs_dir_path ) ! = OK ) {
ERR_PRINT ( generate_cs_glue_option + " : Failed to generate the C# API. " ) ;
}
}
if ( cpp_dir_path . length ( ) ) {
if ( bindings_generator . generate_glue ( cpp_dir_path ) ! = OK ) {
ERR_PRINT ( generate_cpp_glue_option + " : Failed to generate the C++ glue. " ) ;
}
}
}
2017-10-02 21:24:00 +00:00
void BindingsGenerator : : handle_cmdline_args ( const List < String > & p_cmdline_args ) {
2018-11-08 00:05:19 +00:00
const int NUM_OPTIONS = 2 ;
2017-10-02 21:24:00 +00:00
2019-07-03 07:44:53 +00:00
String glue_dir_path ;
String cs_dir_path ;
String cpp_dir_path ;
2019-04-25 13:45:12 +00:00
int options_left = NUM_OPTIONS ;
2017-10-02 21:24:00 +00:00
2021-03-13 00:16:27 +00:00
bool exit_godot = false ;
2017-10-02 21:24:00 +00:00
const List < String > : : Element * elem = p_cmdline_args . front ( ) ;
2017-10-24 14:18:47 +00:00
while ( elem & & options_left ) {
2019-07-03 07:44:53 +00:00
if ( elem - > get ( ) = = generate_all_glue_option ) {
2017-10-02 21:24:00 +00:00
const List < String > : : Element * path_elem = elem - > next ( ) ;
if ( path_elem ) {
2019-07-03 07:44:53 +00:00
glue_dir_path = path_elem - > get ( ) ;
2017-10-02 21:24:00 +00:00
elem = elem - > next ( ) ;
} else {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( generate_all_glue_option + " : No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue'). " ) ;
2021-03-13 00:16:27 +00:00
exit_godot = true ;
2017-10-02 21:24:00 +00:00
}
2017-10-24 14:18:47 +00:00
- - options_left ;
2019-07-03 07:44:53 +00:00
} else if ( elem - > get ( ) = = generate_cs_glue_option ) {
2017-10-02 21:24:00 +00:00
const List < String > : : Element * path_elem = elem - > next ( ) ;
if ( path_elem ) {
2019-07-03 07:44:53 +00:00
cs_dir_path = path_elem - > get ( ) ;
2017-10-02 21:24:00 +00:00
elem = elem - > next ( ) ;
} else {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( generate_cs_glue_option + " : No output directory specified. " ) ;
2021-03-13 00:16:27 +00:00
exit_godot = true ;
2019-07-03 07:44:53 +00:00
}
- - options_left ;
} else if ( elem - > get ( ) = = generate_cpp_glue_option ) {
const List < String > : : Element * path_elem = elem - > next ( ) ;
if ( path_elem ) {
cpp_dir_path = path_elem - > get ( ) ;
elem = elem - > next ( ) ;
} else {
2019-11-06 16:03:04 +00:00
ERR_PRINT ( generate_cpp_glue_option + " : No output directory specified. " ) ;
2021-03-13 00:16:27 +00:00
exit_godot = true ;
2017-10-02 21:24:00 +00:00
}
2017-10-24 14:18:47 +00:00
- - options_left ;
2017-10-02 21:24:00 +00:00
}
elem = elem - > next ( ) ;
}
2019-07-03 07:44:53 +00:00
if ( glue_dir_path . length ( ) | | cs_dir_path . length ( ) | | cpp_dir_path . length ( ) ) {
2021-03-13 00:16:27 +00:00
handle_cmdline_options ( glue_dir_path , cs_dir_path , cpp_dir_path ) ;
exit_godot = true ;
}
2019-04-25 13:45:12 +00:00
2021-03-13 00:16:27 +00:00
if ( exit_godot ) {
2019-04-25 13:45:12 +00:00
// Exit once done
2021-02-22 10:06:33 +00:00
Main : : cleanup ( true ) ;
2018-11-08 00:05:19 +00:00
: : exit ( 0 ) ;
2019-04-25 13:45:12 +00:00
}
2017-10-02 21:24:00 +00:00
}
# endif