2017-10-02 23:24:00 +02:00
/*************************************************************************/
/* bindings_generator.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2019-01-01 12:53:14 +01:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2017-10-02 23:24:00 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-10-02 23:24:00 +02:00
# include "bindings_generator.h"
# ifdef DEBUG_METHODS_ENABLED
2018-09-11 18:13:45 +02:00
# include "core/engine.h"
# include "core/global_constants.h"
# include "core/io/compression.h"
# include "core/os/dir_access.h"
# include "core/os/file_access.h"
# include "core/os/os.h"
# include "core/ucaps.h"
2017-10-02 23:24:00 +02:00
# include "../glue/cs_compressed.gen.h"
2018-09-12 02:50:16 +02:00
# include "../glue/cs_glue_version.gen.h"
2017-10-02 23:24:00 +02:00
# include "../godotsharp_defs.h"
# include "../mono_gd/gd_mono_marshal.h"
# include "../utils/path_utils.h"
# include "../utils/string_utils.h"
# include "csharp_project.h"
2018-09-04 05:40:41 +02:00
# define CS_INDENT " " // 4 whitespaces
2017-10-02 23:24:00 +02:00
# define INDENT1 CS_INDENT
# define INDENT2 INDENT1 INDENT1
# define INDENT3 INDENT2 INDENT1
# define INDENT4 INDENT3 INDENT1
# 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 OPEN_BLOCK_L4 INDENT4 OPEN_BLOCK INDENT5
# define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
# define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
# define CLOSE_BLOCK_L4 INDENT4 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"
2018-09-04 05:40:41 +02:00
# define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method"
# define C_LOCAL_RET "ret"
2019-01-18 00:41:41 +01:00
# define C_LOCAL_VARARG_RET "vararg_ret"
2017-10-02 23:24:00 +02:00
# define C_LOCAL_PTRCALL_ARGS "call_args"
# 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"
2019-04-25 17:11:01 +02:00
# define BINDINGS_GENERATOR_VERSION UINT32_C(9)
2018-02-22 13:13:51 +01:00
2019-04-08 13:18:21 +02:00
const char * BindingsGenerator : : TypeInterface : : DEFAULT_VARARG_C_IN ( " \t %0 %1_in = %1; \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
static String fix_doc_description ( const String & p_bbcode ) {
// This seems to be the correct way to do this. It's the same EditorHelp does.
return p_bbcode . dedent ( )
. replace ( " \t " , " " )
. replace ( " \r " , " " )
. strip_edges ( ) ;
}
2017-12-24 03:17:48 +01:00
static String snake_to_pascal_case ( const String & p_identifier , bool p_input_is_upper = false ) {
2017-10-02 23:24:00 +02:00
String ret ;
Vector < String > parts = p_identifier . split ( " _ " , true ) ;
for ( int i = 0 ; i < parts . size ( ) ; i + + ) {
String part = parts [ i ] ;
if ( part . length ( ) ) {
part [ 0 ] = _find_upper ( part [ 0 ] ) ;
2017-12-24 03:17:48 +01:00
if ( p_input_is_upper ) {
for ( int j = 1 ; j < part . length ( ) ; j + + )
part [ j ] = _find_lower ( part [ j ] ) ;
}
2017-10-02 23:24:00 +02:00
ret + = part ;
} else {
if ( i = = 0 | | i = = ( parts . size ( ) - 1 ) ) {
// Preserve underscores at the beginning and end
ret + = " _ " ;
} else {
// Preserve contiguous underscores
if ( parts [ i - 1 ] . length ( ) ) {
ret + = " __ " ;
} else {
ret + = " _ " ;
}
}
}
}
return ret ;
}
2017-12-24 03:17:48 +01:00
static String snake_to_camel_case ( const String & p_identifier , bool p_input_is_upper = false ) {
2017-10-02 23:24:00 +02:00
String ret ;
Vector < String > parts = p_identifier . split ( " _ " , true ) ;
for ( int i = 0 ; i < parts . size ( ) ; i + + ) {
String part = parts [ i ] ;
if ( part . length ( ) ) {
2017-12-24 03:17:48 +01:00
if ( i ! = 0 ) {
2017-10-02 23:24:00 +02:00
part [ 0 ] = _find_upper ( part [ 0 ] ) ;
2017-12-24 03:17:48 +01:00
}
if ( p_input_is_upper ) {
for ( int j = i ! = 0 ? 1 : 0 ; j < part . length ( ) ; j + + )
part [ j ] = _find_lower ( part [ j ] ) ;
}
2017-10-02 23:24:00 +02:00
ret + = part ;
} else {
if ( i = = 0 | | i = = ( parts . size ( ) - 1 ) ) {
// Preserve underscores at the beginning and end
ret + = " _ " ;
} else {
// Preserve contiguous underscores
if ( parts [ i - 1 ] . length ( ) ) {
ret + = " __ " ;
} else {
ret + = " _ " ;
}
}
}
}
return ret ;
}
2019-03-18 21:59:35 +01:00
String BindingsGenerator : : bbcode_to_xml ( const String & p_bbcode , const TypeInterface * p_itype ) {
// Based on the version in EditorHelp
if ( p_bbcode . empty ( ) )
return String ( ) ;
DocData * doc = EditorHelp : : get_doc_data ( ) ;
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 ) ;
if ( brk_pos < 0 )
brk_pos = bbcode . length ( ) ;
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 + + ) {
if ( i ! = 0 )
xml_output . append ( " <para> " ) ;
xml_output . append ( lines [ i ] . xml_escape ( ) ) ;
if ( i ! = lines . size ( ) - 1 )
xml_output . append ( " </para> \n " ) ;
}
}
}
if ( brk_pos = = bbcode . length ( ) )
break ; // nothing else to add
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 + + ) {
if ( i ! = 0 )
xml_output . append ( " <para> " ) ;
xml_output . append ( lines [ i ] . xml_escape ( ) ) ;
if ( i ! = lines . size ( ) - 1 )
xml_output . append ( " </para> \n " ) ;
}
}
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 ;
2019-03-27 20:01:16 +01:00
} else if ( tag . begins_with ( " method " ) | | tag . begins_with ( " member " ) | | tag . begins_with ( " signal " ) | | tag . begins_with ( " enum " ) | | tag . begins_with ( " constant " ) ) {
2019-03-18 21:59:35 +01:00
String link_target = tag . substr ( tag . find ( " " ) + 1 , tag . length ( ) ) ;
String link_tag = tag . substr ( 0 , tag . find ( " " ) ) ;
Vector < String > link_target_parts = link_target . split ( " . " ) ;
if ( link_target_parts . size ( ) < = 0 | | link_target_parts . size ( ) > 2 ) {
ERR_PRINTS ( " Invalid reference format: " + tag ) ;
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 " ) {
if ( ! target_itype | | ! target_itype - > is_object_type ) {
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 {
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 if ( link_tag = = " member " ) {
if ( ! target_itype | | ! target_itype - > is_object_type ) {
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 ( " \" /> " ) ;
}
}
} else if ( link_tag = = " signal " ) {
// We do not declare signals in any way in C#, so there is nothing to reference
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
} else if ( link_tag = = " enum " ) {
StringName search_cname = ! target_itype ? target_cname :
StringName ( target_itype - > name + " . " + ( String ) target_cname ) ;
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 {
ERR_PRINTS ( " Cannot resolve enum reference in documentation: " + link_target ) ;
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
2019-03-27 20:01:16 +01:00
} else if ( link_tag = = " const " ) {
if ( ! target_itype | | ! target_itype - > is_object_type ) {
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> " ) ;
} else if ( ! target_itype & & target_cname = = name_cache . type_at_GlobalScope ) {
String target_name = ( String ) target_cname ;
// 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
const EnumInterface * target_ienum = NULL ;
for ( const List < EnumInterface > : : Element * E = global_enums . front ( ) ; E ; E = E - > next ( ) ) {
target_ienum = & E - > get ( ) ;
target_iconst = find_constant_by_name ( target_name , target_ienum - > constants ) ;
if ( target_iconst )
break ;
}
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 {
ERR_PRINTS ( " Cannot resolve global constant reference in documentation: " + link_target ) ;
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
}
} else {
String target_name = ( String ) target_cname ;
// 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
const EnumInterface * target_ienum = NULL ;
for ( const List < EnumInterface > : : Element * E = target_itype - > enums . front ( ) ; E ; E = E - > next ( ) ) {
target_ienum = & E - > get ( ) ;
target_iconst = find_constant_by_name ( target_name , target_ienum - > constants ) ;
if ( target_iconst )
break ;
}
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 {
ERR_PRINTS ( " Cannot resolve constant reference in documentation: " + link_target ) ;
xml_output . append ( " <c> " ) ;
xml_output . append ( link_target ) ;
xml_output . append ( " </c> " ) ;
}
}
}
2019-03-18 21:59:35 +01:00
}
pos = brk_end + 1 ;
} else if ( doc - > class_list . has ( tag ) ) {
if ( tag = = " Array " | | tag = = " Dictionary " ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE_COLLECTIONS " . " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " \" /> " ) ;
} else if ( tag = = " bool " | | tag = = " int " ) {
xml_output . append ( " <see cref= \" " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " \" /> " ) ;
} else if ( tag = = " float " ) {
xml_output . append ( " <see cref= \" "
# ifdef REAL_T_IS_DOUBLE
" double "
# else
" float "
# endif
" \" /> " ) ;
} else if ( tag = = " Variant " ) {
// We use System.Object for Variant, so there is no Variant type in C#
xml_output . append ( " <c>Variant</c> " ) ;
} else if ( tag = = " String " ) {
xml_output . append ( " <see cref= \" string \" /> " ) ;
} else if ( tag = = " Nil " ) {
xml_output . append ( " <see langword= \" null \" /> " ) ;
} else if ( tag . begins_with ( " @ " ) ) {
2019-03-27 20:01:16 +01:00
// @GlobalScope, @GDScript, etc
2019-03-18 21:59:35 +01:00
xml_output . append ( " <c> " ) ;
xml_output . append ( tag ) ;
xml_output . append ( " </c> " ) ;
} else if ( tag = = " PoolByteArray " ) {
xml_output . append ( " <see cref= \" byte \" /> " ) ;
} else if ( tag = = " PoolIntArray " ) {
xml_output . append ( " <see cref= \" int \" /> " ) ;
} else if ( tag = = " PoolRealArray " ) {
# ifdef REAL_T_IS_DOUBLE
xml_output . append ( " <see cref= \" double \" /> " ) ;
# else
xml_output . append ( " <see cref= \" float \" /> " ) ;
# endif
} else if ( tag = = " PoolStringArray " ) {
xml_output . append ( " <see cref= \" string \" /> " ) ;
} else if ( tag = = " PoolVector2Array " ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " .Vector2 \" /> " ) ;
} else if ( tag = = " PoolVector3Array " ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " .Vector3 \" /> " ) ;
} else if ( tag = = " PoolColorArray " ) {
xml_output . append ( " <see cref= \" " BINDINGS_NAMESPACE " .Color \" /> " ) ;
} 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 {
ERR_PRINTS ( " Cannot resolve type reference in documentation: " + tag ) ;
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 ) ;
} else if ( tag = = " center " ) {
// center is alignment not supported in xml comments
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 ) ;
if ( end = = - 1 )
end = bbcode . length ( ) ;
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 ) ;
if ( end = = - 1 )
end = bbcode . length ( ) ;
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 19:41:10 +02:00
int BindingsGenerator : : _determine_enum_prefix ( const EnumInterface & p_ienum ) {
2017-12-24 03:17:48 +01:00
CRASH_COND ( p_ienum . constants . empty ( ) ) ;
2018-10-18 19:41:10 +02:00
const ConstantInterface & front_iconstant = p_ienum . constants . front ( ) - > get ( ) ;
Vector < String > front_parts = front_iconstant . name . split ( " _ " , /* p_allow_empty: */ true ) ;
int candidate_len = front_parts . size ( ) - 1 ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
if ( candidate_len = = 0 )
return 0 ;
for ( const List < ConstantInterface > : : Element * E = p_ienum . constants . front ( ) - > next ( ) ; E ; E = E - > next ( ) ) {
const ConstantInterface & iconstant = E - > get ( ) ;
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 " ) ) ) ;
if ( ! hardcoded_exc )
break ;
}
2017-12-24 03:17:48 +01:00
}
2018-10-18 19:41:10 +02:00
candidate_len = i ;
if ( candidate_len = = 0 )
return 0 ;
2017-12-24 03:17:48 +01:00
}
2018-10-18 19:41:10 +02:00
return candidate_len ;
}
void BindingsGenerator : : _apply_prefix_to_enum_constants ( BindingsGenerator : : EnumInterface & p_ienum , int p_prefix_length ) {
if ( p_prefix_length > 0 ) {
for ( List < ConstantInterface > : : Element * E = p_ienum . constants . front ( ) ; E ; E = E - > next ( ) ) {
int curr_prefix_length = p_prefix_length ;
ConstantInterface & curr_const = E - > get ( ) ;
String constant_name = curr_const . name ;
Vector < String > parts = constant_name . split ( " _ " , /* p_allow_empty: */ true ) ;
if ( parts . size ( ) < = curr_prefix_length )
continue ;
if ( parts [ curr_prefix_length ] [ 0 ] > = ' 0 ' & & parts [ curr_prefix_length ] [ 0 ] < = ' 9 ' ) {
// 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 - - ) {
if ( parts [ curr_prefix_length ] [ 0 ] < ' 0 ' | | parts [ curr_prefix_length ] [ 0 ] > ' 9 ' )
break ;
}
}
constant_name = " " ;
for ( int i = curr_prefix_length ; i < parts . size ( ) ; i + + ) {
if ( i > curr_prefix_length )
constant_name + = " _ " ;
constant_name + = parts [ i ] ;
}
curr_const . proxy_name = snake_to_pascal_case ( constant_name , true ) ;
}
}
2017-12-24 03:17:48 +01:00
}
2017-10-02 23:24:00 +02:00
void BindingsGenerator : : _generate_method_icalls ( const TypeInterface & p_itype ) {
for ( const List < MethodInterface > : : Element * E = p_itype . methods . front ( ) ; E ; E = E - > next ( ) ) {
const MethodInterface & imethod = E - > get ( ) ;
if ( imethod . is_virtual )
continue ;
2018-04-28 22:25:25 +02:00
const TypeInterface * return_type = _get_type_or_placeholder ( imethod . return_type ) ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
String im_sig = " IntPtr " CS_PARAM_METHODBIND " , " ;
String im_unique_sig = imethod . return_type . cname . operator String ( ) + " ,IntPtr,IntPtr " ;
2018-01-25 23:44:37 +01:00
im_sig + = " IntPtr " CS_PARAM_INSTANCE ;
2017-10-02 23:24:00 +02:00
// Get arguments information
int i = 0 ;
for ( const List < ArgumentInterface > : : Element * F = imethod . arguments . front ( ) ; F ; F = F - > next ( ) ) {
2018-04-28 22:25:25 +02:00
const TypeInterface * arg_type = _get_type_or_placeholder ( F - > get ( ) . type ) ;
2017-10-02 23:24:00 +02:00
im_sig + = " , " ;
im_sig + = arg_type - > im_type_in ;
im_sig + = " arg " ;
im_sig + = itos ( i + 1 ) ;
2018-09-04 05:40:41 +02:00
im_unique_sig + = " , " ;
im_unique_sig + = get_unique_sig ( * arg_type ) ;
2017-10-02 23:24:00 +02:00
i + + ;
}
2018-09-04 05:40:41 +02:00
// godot_icall_{argc}_{icallcount}
2018-01-25 23:44:37 +01:00
String icall_method = ICALL_PREFIX ;
2018-09-04 05:40:41 +02:00
icall_method + = itos ( imethod . arguments . size ( ) ) ;
icall_method + = " _ " ;
icall_method + = itos ( method_icalls . size ( ) ) ;
2017-10-02 23:24:00 +02:00
InternalCall im_icall = InternalCall ( p_itype . api_type , icall_method , return_type - > im_type_out , im_sig , im_unique_sig ) ;
2018-09-04 05:40:41 +02:00
List < InternalCall > : : Element * match = method_icalls . find ( im_icall ) ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
if ( match ) {
if ( p_itype . api_type ! = ClassDB : : API_EDITOR )
match - > get ( ) . editor_only = false ;
method_icalls_map . insert ( & E - > get ( ) , & match - > get ( ) ) ;
2017-10-02 23:24:00 +02:00
} else {
2018-09-04 05:40:41 +02:00
List < InternalCall > : : Element * added = method_icalls . push_back ( im_icall ) ;
2017-10-02 23:24:00 +02:00
method_icalls_map . insert ( & E - > get ( ) , & added - > get ( ) ) ;
}
}
}
2019-04-19 01:10:08 +02:00
void BindingsGenerator : : _generate_global_constants ( StringBuilder & p_output ) {
2017-12-24 03:17:48 +01:00
// Constants (in partial GD class)
2019-04-19 01:10:08 +02:00
p_output . append ( " \n #pragma warning disable CS1591 // Disable warning: "
" 'Missing XML comment for publicly visible type or member' \n " ) ;
2019-03-18 21:59:35 +01:00
2019-04-19 01:10:08 +02: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 03:17:48 +01:00
for ( const List < ConstantInterface > : : Element * E = global_constants . front ( ) ; E ; E = E - > next ( ) ) {
const ConstantInterface & iconstant = E - > get ( ) ;
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , NULL ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// </summary> " ) ;
2019-03-18 21:59:35 +01:00
}
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02: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 03:17:48 +01:00
}
if ( ! global_constants . empty ( ) )
2019-04-19 01:10:08 +02:00
p_output . append ( " \n " ) ;
2017-12-24 03:17:48 +01:00
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT1 CLOSE_BLOCK ) ; // end of GD class
2017-12-24 03:17:48 +01:00
// Enums
for ( List < EnumInterface > : : Element * E = global_enums . front ( ) ; E ; E = E - > next ( ) ) {
const EnumInterface & ienum = E - > get ( ) ;
CRASH_COND ( ienum . constants . empty ( ) ) ;
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-04-25 15:45:12 +02: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 03:17:48 +01:00
2019-04-19 01:10:08 +02: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 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( " \n " INDENT1 " public enum " ) ;
p_output . append ( enum_proxy_name ) ;
p_output . append ( " \n " INDENT1 OPEN_BLOCK ) ;
2017-12-24 03:17:48 +01:00
2019-02-12 21:10:08 +01:00
for ( const List < ConstantInterface > : : Element * F = ienum . constants . front ( ) ; F ; F = F - > next ( ) ) {
const ConstantInterface & iconstant = F - > get ( ) ;
2017-12-24 03:17:48 +01:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , NULL ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// <summary> \n " ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// </summary> \n " ) ;
2019-03-18 21:59:35 +01:00
}
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 ) ;
p_output . append ( iconstant . proxy_name ) ;
p_output . append ( " = " ) ;
p_output . append ( itos ( iconstant . value ) ) ;
p_output . append ( F ! = ienum . constants . back ( ) ? " , \n " : " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT1 CLOSE_BLOCK ) ;
2017-12-24 03:17:48 +01:00
if ( enum_in_static_class )
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT1 CLOSE_BLOCK ) ;
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( CLOSE_BLOCK ) ; // end of namespace
2019-03-18 21:59:35 +01:00
2019-04-19 01:10:08 +02:00
p_output . append ( " \n #pragma warning restore CS1591 \n " ) ;
2017-12-24 03:17:48 +01:00
}
2019-04-25 15:45:12 +02:00
Error BindingsGenerator : : generate_cs_core_project ( const String & p_solution_dir , DotNetSolution & r_solution ) {
2017-10-02 23:24:00 +02:00
2018-11-08 01:05:19 +01:00
String proj_dir = p_solution_dir . plus_file ( CORE_API_ASSEMBLY_NAME ) ;
2017-10-02 23:24:00 +02:00
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
2018-11-08 01:05:19 +01:00
if ( ! DirAccess : : exists ( proj_dir ) ) {
Error err = da - > make_dir_recursive ( proj_dir ) ;
2017-10-02 23:24:00 +02:00
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
2018-11-08 01:05:19 +01:00
da - > change_dir ( proj_dir ) ;
2017-10-02 23:24:00 +02:00
da - > make_dir ( " Core " ) ;
da - > make_dir ( " ObjectType " ) ;
2018-11-08 01:05:19 +01:00
String core_dir = path_join ( proj_dir , " Core " ) ;
String obj_type_dir = path_join ( proj_dir , " ObjectType " ) ;
2017-10-02 23:24:00 +02:00
Vector < String > compile_items ;
2017-12-24 03:17:48 +01:00
// Generate source file for global scope constants and enums
{
2019-04-19 01:10:08 +02:00
StringBuilder constants_source ;
2017-12-24 03:17:48 +01:00
_generate_global_constants ( constants_source ) ;
String output_file = path_join ( core_dir , BINDINGS_GLOBAL_SCOPE_CLASS " _constants.cs " ) ;
Error save_err = _save_file ( output_file , constants_source ) ;
if ( save_err ! = OK )
return save_err ;
compile_items . push_back ( output_file ) ;
}
2018-01-09 19:06:59 +01:00
for ( OrderedHashMap < StringName , TypeInterface > : : Element E = obj_types . front ( ) ; E ; E = E . next ( ) ) {
const TypeInterface & itype = E . get ( ) ;
2017-10-02 23:24:00 +02:00
if ( itype . api_type = = ClassDB : : API_EDITOR )
continue ;
2018-01-09 19:06:59 +01:00
String output_file = path_join ( obj_type_dir , itype . proxy_name + " .cs " ) ;
Error err = _generate_cs_type ( itype , output_file ) ;
2017-10-02 23:24:00 +02:00
if ( err = = ERR_SKIP )
continue ;
if ( err ! = OK )
return err ;
compile_items . push_back ( output_file ) ;
}
// Generate sources from compressed files
Map < String , CompressedFile > compressed_files ;
get_compressed_files ( compressed_files ) ;
for ( Map < String , CompressedFile > : : Element * E = compressed_files . front ( ) ; E ; E = E - > next ( ) ) {
const String & file_name = E - > key ( ) ;
const CompressedFile & file_data = E - > value ( ) ;
String output_file = path_join ( core_dir , file_name ) ;
Vector < uint8_t > data ;
data . resize ( file_data . uncompressed_size ) ;
2017-11-25 12:16:58 +01:00
Compression : : decompress ( data . ptrw ( ) , file_data . uncompressed_size , file_data . data , file_data . compressed_size , Compression : : MODE_DEFLATE ) ;
2017-10-02 23:24:00 +02:00
2018-08-17 13:49:15 +02:00
String output_dir = output_file . get_base_dir ( ) ;
if ( ! DirAccess : : exists ( output_dir ) ) {
Error err = da - > make_dir_recursive ( ProjectSettings : : get_singleton ( ) - > globalize_path ( output_dir ) ) ;
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
2017-10-02 23:24:00 +02:00
FileAccessRef file = FileAccess : : open ( output_file , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V ( ! file , ERR_FILE_CANT_WRITE ) ;
file - > store_buffer ( data . ptr ( ) , data . size ( ) ) ;
file - > close ( ) ;
compile_items . push_back ( output_file ) ;
}
2019-04-19 01:10:08 +02: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 " \n " INDENT1 OPEN_BLOCK ) ;
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 " ) ; \
cs_icalls_content . append ( INDENT2 " internal extern static " ) ; \
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 23:24:00 +02:00
}
for ( const List < InternalCall > : : Element * E = core_custom_icalls . front ( ) ; E ; E = E - > next ( ) )
ADD_INTERNAL_CALL ( E - > get ( ) ) ;
for ( const List < InternalCall > : : Element * E = method_icalls . front ( ) ; E ; E = E - > next ( ) )
ADD_INTERNAL_CALL ( E - > get ( ) ) ;
# undef ADD_INTERNAL_CALL
2019-04-19 01:10:08 +02:00
cs_icalls_content . append ( INDENT1 CLOSE_BLOCK CLOSE_BLOCK ) ;
2017-10-02 23:24:00 +02:00
2018-02-22 13:13:51 +01:00
String internal_methods_file = path_join ( core_dir , BINDINGS_CLASS_NATIVECALLS " .cs " ) ;
2017-10-02 23:24:00 +02:00
Error err = _save_file ( internal_methods_file , cs_icalls_content ) ;
if ( err ! = OK )
return err ;
compile_items . push_back ( internal_methods_file ) ;
2018-11-08 01:05:19 +01:00
String guid = CSharpProject : : generate_core_api_project ( proj_dir , compile_items ) ;
2017-10-02 23:24:00 +02:00
2018-11-08 01:05:19 +01:00
DotNetSolution : : ProjectInfo proj_info ;
proj_info . guid = guid ;
proj_info . relpath = String ( CORE_API_ASSEMBLY_NAME ) . plus_file ( CORE_API_ASSEMBLY_NAME " .csproj " ) ;
proj_info . configs . push_back ( " Debug " ) ;
proj_info . configs . push_back ( " Release " ) ;
2017-10-02 23:24:00 +02:00
2018-11-08 01:05:19 +01:00
r_solution . add_new_project ( CORE_API_ASSEMBLY_NAME , proj_info ) ;
2017-10-02 23:24:00 +02:00
2019-04-25 15:45:12 +02:00
_log ( " The solution and C# project for the Core API was generated successfully \n " ) ;
2017-10-24 16:18:47 +02:00
2017-10-02 23:24:00 +02:00
return OK ;
}
2019-04-25 15:45:12 +02:00
Error BindingsGenerator : : generate_cs_editor_project ( const String & p_solution_dir , DotNetSolution & r_solution ) {
2017-10-02 23:24:00 +02:00
2018-11-08 01:05:19 +01:00
String proj_dir = p_solution_dir . plus_file ( EDITOR_API_ASSEMBLY_NAME ) ;
2017-10-02 23:24:00 +02:00
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
2018-11-08 01:05:19 +01:00
if ( ! DirAccess : : exists ( proj_dir ) ) {
Error err = da - > make_dir_recursive ( proj_dir ) ;
2017-10-02 23:24:00 +02:00
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
2018-11-08 01:05:19 +01:00
da - > change_dir ( proj_dir ) ;
2017-10-02 23:24:00 +02:00
da - > make_dir ( " Core " ) ;
da - > make_dir ( " ObjectType " ) ;
2018-11-08 01:05:19 +01:00
String core_dir = path_join ( proj_dir , " Core " ) ;
String obj_type_dir = path_join ( proj_dir , " ObjectType " ) ;
2017-10-02 23:24:00 +02:00
Vector < String > compile_items ;
2018-01-09 19:06:59 +01:00
for ( OrderedHashMap < StringName , TypeInterface > : : Element E = obj_types . front ( ) ; E ; E = E . next ( ) ) {
const TypeInterface & itype = E . get ( ) ;
2017-10-02 23:24:00 +02:00
if ( itype . api_type ! = ClassDB : : API_EDITOR )
continue ;
2018-01-09 19:06:59 +01:00
String output_file = path_join ( obj_type_dir , itype . proxy_name + " .cs " ) ;
Error err = _generate_cs_type ( itype , output_file ) ;
2017-10-02 23:24:00 +02:00
if ( err = = ERR_SKIP )
continue ;
if ( err ! = OK )
return err ;
compile_items . push_back ( output_file ) ;
}
2019-04-19 01:10:08 +02: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 " ) ; \
cs_icalls_content . append ( INDENT2 " internal extern static " ) ; \
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 23:24:00 +02:00
}
for ( const List < InternalCall > : : Element * E = editor_custom_icalls . front ( ) ; E ; E = E - > next ( ) )
ADD_INTERNAL_CALL ( E - > get ( ) ) ;
for ( const List < InternalCall > : : Element * E = method_icalls . front ( ) ; E ; E = E - > next ( ) )
ADD_INTERNAL_CALL ( E - > get ( ) ) ;
# undef ADD_INTERNAL_CALL
2019-04-19 01:10:08 +02:00
cs_icalls_content . append ( INDENT1 CLOSE_BLOCK CLOSE_BLOCK ) ;
2017-10-02 23:24:00 +02:00
2018-02-22 13:13:51 +01:00
String internal_methods_file = path_join ( core_dir , BINDINGS_CLASS_NATIVECALLS_EDITOR " .cs " ) ;
2017-10-02 23:24:00 +02:00
Error err = _save_file ( internal_methods_file , cs_icalls_content ) ;
if ( err ! = OK )
return err ;
compile_items . push_back ( internal_methods_file ) ;
2018-11-08 01:05:19 +01:00
String guid = CSharpProject : : generate_editor_api_project ( proj_dir , " ../ " CORE_API_ASSEMBLY_NAME " / " CORE_API_ASSEMBLY_NAME " .csproj " , compile_items ) ;
DotNetSolution : : ProjectInfo proj_info ;
proj_info . guid = guid ;
proj_info . relpath = String ( EDITOR_API_ASSEMBLY_NAME ) . plus_file ( EDITOR_API_ASSEMBLY_NAME " .csproj " ) ;
proj_info . configs . push_back ( " Debug " ) ;
proj_info . configs . push_back ( " Release " ) ;
r_solution . add_new_project ( EDITOR_API_ASSEMBLY_NAME , proj_info ) ;
2019-04-25 15:45:12 +02:00
_log ( " The solution and C# project for the Editor API was generated successfully \n " ) ;
2018-11-08 01:05:19 +01:00
return OK ;
}
2017-10-02 23:24:00 +02:00
2019-04-25 15:45:12 +02:00
Error BindingsGenerator : : generate_cs_api ( const String & p_output_dir ) {
2018-11-08 01:05:19 +01:00
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
if ( ! DirAccess : : exists ( p_output_dir ) ) {
Error err = da - > make_dir_recursive ( p_output_dir ) ;
ERR_FAIL_COND_V ( err ! = OK , ERR_CANT_CREATE ) ;
}
DotNetSolution solution ( API_SOLUTION_NAME ) ;
if ( ! solution . set_path ( p_output_dir ) )
return ERR_FILE_NOT_FOUND ;
Error proj_err ;
2019-04-25 15:45:12 +02:00
proj_err = generate_cs_core_project ( p_output_dir , solution ) ;
2018-11-08 01:05:19 +01:00
if ( proj_err ! = OK ) {
ERR_PRINT ( " Generation of the Core API C# project failed " ) ;
return proj_err ;
}
2019-04-25 15:45:12 +02:00
proj_err = generate_cs_editor_project ( p_output_dir , solution ) ;
2018-11-08 01:05:19 +01:00
if ( proj_err ! = OK ) {
ERR_PRINT ( " Generation of the Editor API C# project failed " ) ;
return proj_err ;
}
2017-10-02 23:24:00 +02:00
Error sln_error = solution . save ( ) ;
if ( sln_error ! = OK ) {
2018-11-08 01:05:19 +01:00
ERR_PRINT ( " Failed to save API solution " ) ;
2017-10-02 23:24:00 +02:00
return sln_error ;
}
return OK ;
}
2018-09-04 05:40:41 +02:00
// FIXME: There are some members that hide other inherited members.
// - In the case of both members being the same kind, the new one must be declared
// explicitly as `new` to avoid the warning (and we must print a message about it).
// - In the case of both members being of a different kind, then the new one must
// be renamed to avoid the name collision (and we must print a warning about it).
// - Csc warning e.g.:
// ObjectType/LineEdit.cs(140,38): warning CS0108: 'LineEdit.FocusMode' hides inherited member 'Control.FocusMode'. Use the new keyword if hiding was intended.
2017-10-02 23:24:00 +02:00
Error BindingsGenerator : : _generate_cs_type ( const TypeInterface & itype , const String & p_output_file ) {
2018-09-04 05:40:41 +02:00
CRASH_COND ( ! itype . is_object_type ) ;
2017-12-24 03:17:48 +01:00
bool is_derived_type = itype . base_name ! = StringName ( ) ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
if ( ! is_derived_type ) {
// Some Godot.Object assertions
CRASH_COND ( itype . cname ! = name_cache . type_Object ) ;
CRASH_COND ( ! itype . is_instantiable ) ;
CRASH_COND ( itype . api_type ! = ClassDB : : API_CORE ) ;
CRASH_COND ( itype . is_reference ) ;
CRASH_COND ( itype . is_singleton ) ;
}
2017-10-02 23:24:00 +02:00
List < InternalCall > & custom_icalls = itype . api_type = = ClassDB : : API_EDITOR ? editor_custom_icalls : core_custom_icalls ;
2019-04-25 15:45:12 +02:00
_log ( " Generating %s.cs... \n " , itype . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
String ctor_method ( ICALL_PREFIX + itype . proxy_name + " _Ctor " ) ; // Used only for derived types
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
StringBuilder output ;
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
output . append ( " using System; \n " ) ; // IntPtr
output . append ( " using System.Diagnostics; \n " ) ; // DebuggerBrowsable
2018-10-16 17:22:27 +02:00
2019-04-19 01:10:08 +02:00
output . append ( " \n "
" #pragma warning disable CS1591 // Disable warning: "
" 'Missing XML comment for publicly visible type or member' \n "
" #pragma warning disable CS1573 // Disable warning: "
" 'Parameter has no matching param tag in the XML comment' \n " ) ;
2018-10-16 17:22:27 +02:00
2019-04-19 01:10:08 +02:00
output . append ( " \n namespace " BINDINGS_NAMESPACE " \n " OPEN_BLOCK ) ;
2017-10-02 23:24:00 +02:00
const DocData : : ClassDoc * class_doc = itype . class_doc ;
if ( class_doc & & class_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( class_doc - > description ) , & itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
output . append ( INDENT1 " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-19 01:10:08 +02:00
output . append ( INDENT1 " /// " ) ;
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2017-10-02 23:24:00 +02:00
}
2019-04-19 01:10:08 +02:00
output . append ( INDENT1 " /// </summary> \n " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-02 23:24:00 +02:00
}
2019-04-19 01:10:08 +02:00
output . append ( INDENT1 " public " ) ;
2018-09-04 05:40:41 +02:00
if ( itype . is_singleton ) {
2019-04-19 01:10:08 +02:00
output . append ( " static partial class " ) ;
2018-08-27 20:39:51 +02:00
} else {
2019-04-19 01:10:08 +02:00
output . append ( itype . is_instantiable ? " partial class " : " abstract partial class " ) ;
2018-08-27 20:39:51 +02:00
}
2019-04-19 01:10:08 +02:00
output . append ( itype . proxy_name ) ;
2017-10-02 23:24:00 +02:00
2018-01-25 23:44:37 +01:00
if ( itype . is_singleton ) {
2019-04-19 01:10:08 +02:00
output . append ( " \n " ) ;
2018-09-04 05:40:41 +02:00
} else if ( is_derived_type ) {
if ( obj_types . has ( itype . base_name ) ) {
2019-04-19 01:10:08 +02:00
output . append ( " : " ) ;
output . append ( obj_types [ itype . base_name ] . proxy_name ) ;
output . append ( " \n " ) ;
2018-09-04 05:40:41 +02:00
} else {
ERR_PRINTS ( " Base type ' " + itype . base_name . operator String ( ) + " ' does not exist, for class " + itype . name ) ;
return ERR_INVALID_DATA ;
}
2017-10-02 23:24:00 +02:00
}
2019-04-19 01:10:08 +02:00
output . append ( INDENT1 " { " ) ;
2017-10-02 23:24:00 +02:00
if ( class_doc ) {
// Add constants
2017-12-24 03:17:48 +01:00
for ( const List < ConstantInterface > : : Element * E = itype . constants . front ( ) ; E ; E = E - > next ( ) ) {
const ConstantInterface & iconstant = E - > get ( ) ;
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , & itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-19 01:10:08 +02:00
output . append ( INDENT2 " /// " ) ;
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2017-10-02 23:24:00 +02:00
}
2019-04-19 01:10:08 +02:00
output . append ( INDENT2 " /// </summary> " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-02 23:24:00 +02:00
}
2019-04-19 01:10:08 +02: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 23:24:00 +02:00
}
2017-12-24 03:17:48 +01:00
if ( itype . constants . size ( ) )
2019-04-19 01:10:08 +02:00
output . append ( " \n " ) ;
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
// Add enums
for ( const List < EnumInterface > : : Element * E = itype . enums . front ( ) ; E ; E = E - > next ( ) ) {
const EnumInterface & ienum = E - > get ( ) ;
ERR_FAIL_COND_V ( ienum . constants . empty ( ) , ERR_BUG ) ;
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " public enum " ) ;
output . append ( ienum . cname . operator String ( ) ) ;
output . append ( MEMBER_BEGIN OPEN_BLOCK ) ;
2017-12-24 03:17:48 +01:00
2019-02-12 21:10:08 +01:00
for ( const List < ConstantInterface > : : Element * F = ienum . constants . front ( ) ; F ; F = F - > next ( ) ) {
const ConstantInterface & iconstant = F - > get ( ) ;
2017-12-24 03:17:48 +01:00
if ( iconstant . const_doc & & iconstant . const_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( iconstant . const_doc - > description ) , & itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
output . append ( INDENT3 " /// <summary> \n " ) ;
2017-12-24 03:17:48 +01:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-19 01:10:08 +02:00
output . append ( INDENT3 " /// " ) ;
output . append ( summary_lines [ i ] ) ;
output . append ( " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
output . append ( INDENT3 " /// </summary> \n " ) ;
2019-03-18 21:59:35 +01:00
}
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
output . append ( INDENT3 ) ;
output . append ( iconstant . proxy_name ) ;
output . append ( " = " ) ;
output . append ( itos ( iconstant . value ) ) ;
output . append ( F ! = ienum . constants . back ( ) ? " , \n " : " \n " ) ;
2017-12-24 03:17:48 +01:00
}
2019-04-19 01:10:08 +02:00
output . append ( INDENT2 CLOSE_BLOCK ) ;
2017-12-24 03:17:48 +01:00
}
// Add properties
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
for ( const List < PropertyInterface > : : Element * E = itype . properties . front ( ) ; E ; E = E - > next ( ) ) {
const PropertyInterface & iprop = E - > get ( ) ;
Error prop_err = _generate_cs_property ( itype , iprop , output ) ;
2017-10-29 02:37:13 +01:00
if ( prop_err ! = OK ) {
2017-12-24 03:17:48 +01:00
ERR_EXPLAIN ( " Failed to generate property ' " + iprop . cname . operator String ( ) +
" ' for class ' " + itype . name + " ' " ) ;
2017-10-29 02:37:13 +01:00
ERR_FAIL_V ( prop_err ) ;
2017-10-02 23:24:00 +02:00
}
}
}
2019-02-04 20:39:02 +01:00
// TODO: BINDINGS_NATIVE_NAME_FIELD should be StringName, once we support it in C#
2018-09-04 05:40:41 +02:00
if ( itype . is_singleton ) {
2017-10-02 23:24:00 +02:00
// Add the type name and the singleton pointer as static fields
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " private static Godot.Object singleton; \n " ) ;
output . append ( MEMBER_BEGIN " public static Godot.Object Singleton \n " INDENT2 " { \n " INDENT3
" get \n " INDENT3 " { \n " INDENT4 " if (singleton == null) \n " INDENT5
" singleton = Engine.GetSingleton( " BINDINGS_NATIVE_NAME_FIELD " ); \n " INDENT4
" return singleton; \n " INDENT3 " } \n " INDENT2 " } \n " ) ;
output . append ( MEMBER_BEGIN " private const string " BINDINGS_NATIVE_NAME_FIELD " = \" " ) ;
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 05:40:41 +02:00
} else if ( is_derived_type ) {
2017-10-02 23:24:00 +02:00
// Add member fields
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " private const string " BINDINGS_NATIVE_NAME_FIELD " = \" " ) ;
output . append ( itype . name ) ;
output . append ( " \" ; \n " ) ;
2017-10-02 23:24:00 +02:00
// Add default constructor
if ( itype . is_instantiable ) {
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " public " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " () : this( " ) ;
output . append ( itype . memory_own ? " true " : " false " ) ;
2017-10-02 23:24:00 +02: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-19 01:10:08 +02: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 ) ;
output . append ( " (this); \n " CLOSE_BLOCK_L2 ) ;
2017-10-02 23:24:00 +02:00
} else {
// Hide the constructor
2019-04-19 01:10:08 +02:00
output . append ( MEMBER_BEGIN " internal " ) ;
output . append ( itype . proxy_name ) ;
output . append ( " () {} \n " ) ;
2017-10-02 23:24:00 +02:00
}
// Add.. em.. trick constructor. Sort of.
2019-04-19 01:10:08 +02: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 23:24:00 +02:00
}
2017-10-29 02:37:13 +01:00
int method_bind_count = 0 ;
2017-10-02 23:24:00 +02:00
for ( const List < MethodInterface > : : Element * E = itype . methods . front ( ) ; E ; E = E - > next ( ) ) {
const MethodInterface & imethod = E - > get ( ) ;
2017-10-29 02:37:13 +01:00
Error method_err = _generate_cs_method ( itype , imethod , method_bind_count , output ) ;
if ( method_err ! = OK ) {
ERR_EXPLAIN ( " Failed to generate method ' " + imethod . name + " ' for class ' " + itype . name + " ' " ) ;
ERR_FAIL_V ( method_err ) ;
}
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( itype . is_singleton ) {
InternalCall singleton_icall = InternalCall ( itype . api_type , ICALL_PREFIX + itype . name + SINGLETON_ICALL_SUFFIX , " IntPtr " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( ! find_icall_by_name ( singleton_icall . name , custom_icalls ) )
custom_icalls . push_back ( singleton_icall ) ;
}
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
if ( is_derived_type & & itype . is_instantiable ) {
2017-10-29 02:37:13 +01:00
InternalCall ctor_icall = InternalCall ( itype . api_type , ctor_method , " IntPtr " , itype . proxy_name + " obj " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( ! find_icall_by_name ( ctor_icall . name , custom_icalls ) )
custom_icalls . push_back ( ctor_icall ) ;
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
output . append ( INDENT1 CLOSE_BLOCK /* class */
2018-08-27 20:39:51 +02:00
CLOSE_BLOCK /* namespace */ ) ;
2019-04-19 01:10:08 +02:00
output . append ( " \n "
" #pragma warning restore CS1591 \n "
" #pragma warning restore CS1573 \n " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return _save_file ( p_output_file , output ) ;
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
Error BindingsGenerator : : _generate_cs_property ( const BindingsGenerator : : TypeInterface & p_itype , const PropertyInterface & p_iprop , StringBuilder & p_output ) {
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
const MethodInterface * setter = p_itype . find_method_by_name ( p_iprop . setter ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Search it in base types too
const TypeInterface * current_type = & p_itype ;
2017-12-24 03:17:48 +01:00
while ( ! setter & & current_type - > base_name ! = StringName ( ) ) {
2018-01-09 19:06:59 +01:00
OrderedHashMap < StringName , TypeInterface > : : Element base_match = obj_types . find ( current_type - > base_name ) ;
ERR_FAIL_COND_V ( ! base_match , ERR_BUG ) ;
current_type = & base_match . get ( ) ;
2017-12-24 03:17:48 +01:00
setter = current_type - > find_method_by_name ( p_iprop . setter ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
const MethodInterface * getter = p_itype . find_method_by_name ( p_iprop . getter ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Search it in base types too
current_type = & p_itype ;
2017-12-24 03:17:48 +01:00
while ( ! getter & & current_type - > base_name ! = StringName ( ) ) {
2018-01-09 19:06:59 +01:00
OrderedHashMap < StringName , TypeInterface > : : Element base_match = obj_types . find ( current_type - > base_name ) ;
ERR_FAIL_COND_V ( ! base_match , ERR_BUG ) ;
current_type = & base_match . get ( ) ;
2017-12-24 03:17:48 +01:00
getter = current_type - > find_method_by_name ( p_iprop . getter ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
ERR_FAIL_COND_V ( ! setter & & ! getter , ERR_BUG ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( setter ) {
2017-12-24 03:17:48 +01:00
int setter_argc = p_iprop . index ! = - 1 ? 2 : 1 ;
2017-10-29 02:37:13 +01:00
ERR_FAIL_COND_V ( setter - > arguments . size ( ) ! = setter_argc , ERR_BUG ) ;
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( getter ) {
2017-12-24 03:17:48 +01:00
int getter_argc = p_iprop . index ! = - 1 ? 1 : 0 ;
2017-10-29 02:37:13 +01:00
ERR_FAIL_COND_V ( getter - > arguments . size ( ) ! = getter_argc , ERR_BUG ) ;
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( getter & & setter ) {
2018-04-28 22:25:25 +02:00
ERR_FAIL_COND_V ( getter - > return_type . cname ! = setter - > arguments . back ( ) - > get ( ) . type . cname , ERR_BUG ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
const TypeReference & proptype_name = getter ? getter - > return_type : setter - > arguments . back ( ) - > get ( ) . type ;
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
const TypeInterface * prop_itype = _get_type_or_null ( proptype_name ) ;
2017-12-24 03:17:48 +01:00
ERR_FAIL_NULL_V ( prop_itype , ERR_BUG ) ; // Property type not found
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
if ( p_iprop . prop_doc & & p_iprop . prop_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( p_iprop . prop_doc - > description ) , & p_itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
if ( summary_lines . size ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// </summary> " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " public " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_itype . is_singleton )
2019-04-19 01:10:08 +02:00
p_output . append ( " static " ) ;
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02: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 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( getter ) {
2019-04-25 17:11:01 +02: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-19 01:10:08 +02:00
p_output . append ( " return " ) ;
p_output . append ( getter - > proxy_name + " ( " ) ;
2017-12-24 03:17:48 +01:00
if ( p_iprop . index ! = - 1 ) {
const ArgumentInterface & idx_arg = getter - > arguments . front ( ) - > get ( ) ;
2018-04-28 22:25:25 +02:00
if ( idx_arg . type . cname ! = name_cache . type_int ) {
2017-12-24 03:17:48 +01:00
// Assume the index parameter is an enum
2018-04-28 22:25:25 +02:00
const TypeInterface * idx_arg_type = _get_type_or_null ( idx_arg . type ) ;
2017-12-24 03:17:48 +01:00
CRASH_COND ( idx_arg_type = = NULL ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( " ( " + idx_arg_type - > proxy_name + " ) " + itos ( p_iprop . index ) ) ;
2017-12-24 03:17:48 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( itos ( p_iprop . index ) ) ;
2017-12-24 03:17:48 +01:00
}
}
2019-04-25 17:11:01 +02:00
p_output . append ( " ); \n "
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 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( setter ) {
2019-04-25 17:11:01 +02: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-19 01:10:08 +02:00
p_output . append ( setter - > proxy_name + " ( " ) ;
2017-12-24 03:17:48 +01:00
if ( p_iprop . index ! = - 1 ) {
const ArgumentInterface & idx_arg = setter - > arguments . front ( ) - > get ( ) ;
2018-04-28 22:25:25 +02:00
if ( idx_arg . type . cname ! = name_cache . type_int ) {
2017-12-24 03:17:48 +01:00
// Assume the index parameter is an enum
2018-04-28 22:25:25 +02:00
const TypeInterface * idx_arg_type = _get_type_or_null ( idx_arg . type ) ;
2017-12-24 03:17:48 +01:00
CRASH_COND ( idx_arg_type = = NULL ) ;
2019-04-19 01:10:08 +02:00
p_output . append ( " ( " + idx_arg_type - > proxy_name + " ) " + itos ( p_iprop . index ) + " , " ) ;
2017-12-24 03:17:48 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( itos ( p_iprop . index ) + " , " ) ;
2017-12-24 03:17:48 +01:00
}
}
2019-04-25 17:11:01 +02:00
p_output . append ( " value); \n "
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 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( CLOSE_BLOCK_L2 ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return OK ;
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
Error BindingsGenerator : : _generate_cs_method ( const BindingsGenerator : : TypeInterface & p_itype , const BindingsGenerator : : MethodInterface & p_imethod , int & p_method_bind_count , StringBuilder & p_output ) {
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
const TypeInterface * return_type = _get_type_or_placeholder ( p_imethod . return_type ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
String method_bind_field = " method_bind_ " + itos ( p_method_bind_count ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
String arguments_sig ;
String cs_in_statements ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
String icall_params = method_bind_field + " , " ;
2018-01-25 23:44:37 +01:00
icall_params + = sformat ( p_itype . cs_in , " this " ) ;
2019-04-19 01:10:08 +02:00
StringBuilder default_args_doc ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Retrieve information from the arguments
for ( const List < ArgumentInterface > : : Element * F = p_imethod . arguments . front ( ) ; F ; F = F - > next ( ) ) {
const ArgumentInterface & iarg = F - > get ( ) ;
2018-04-28 22:25:25 +02:00
const TypeInterface * arg_type = _get_type_or_placeholder ( iarg . type ) ;
2017-10-29 02:37:13 +01:00
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
if ( F ! = p_imethod . arguments . front ( ) )
arguments_sig + = " , " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL )
arguments_sig + = " Nullable< " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
arguments_sig + = arg_type - > cs_type ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL )
arguments_sig + = " > " ;
else
arguments_sig + = " " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
arguments_sig + = iarg . name ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . default_argument . size ( ) ) {
if ( iarg . def_param_mode ! = ArgumentInterface : : CONSTANT )
arguments_sig + = " = null " ;
else
arguments_sig + = " = " + sformat ( iarg . default_argument , arg_type - > cs_type ) ;
2017-10-02 23:24:00 +02:00
}
}
2017-10-29 02:37:13 +01:00
icall_params + = " , " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . default_argument . size ( ) & & iarg . def_param_mode ! = ArgumentInterface : : CONSTANT ) {
// The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
// Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
String arg_in = iarg . name ;
arg_in + = " _in " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01: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 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL )
cs_in_statements + = " .HasValue ? " ;
else
cs_in_statements + = " != null ? " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
cs_in_statements + = iarg . name ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( iarg . def_param_mode = = ArgumentInterface : : NULLABLE_VAL )
cs_in_statements + = " .Value : " ;
else
cs_in_statements + = " : " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
String def_arg = sformat ( iarg . default_argument , arg_type - > cs_type ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
cs_in_statements + = def_arg ;
cs_in_statements + = " ; \n " INDENT3 ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
icall_params + = arg_type - > cs_in . empty ( ) ? arg_in : sformat ( arg_type - > cs_in , arg_in ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
// Apparently the name attribute must not include the @
String param_tag_name = iarg . name . begins_with ( " @ " ) ? iarg . name . substr ( 1 , iarg . name . length ( ) ) : iarg . name ;
2019-04-19 01:10:08 +02:00
default_args_doc . append ( INDENT2 " /// <param name= \" " + param_tag_name + " \" >If the parameter is null, then the default value is " + def_arg + " </param> \n " ) ;
2017-10-29 02:37:13 +01:00
} else {
icall_params + = arg_type - > cs_in . empty ( ) ? iarg . name : sformat ( arg_type - > cs_in , iarg . name ) ;
}
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
// Generate method
{
2018-09-04 05:40:41 +02:00
if ( ! p_imethod . is_virtual & & ! p_imethod . requires_object_call ) {
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " [DebuggerBrowsable(DebuggerBrowsableState.Never)] " MEMBER_BEGIN " private static IntPtr " ) ;
p_output . append ( method_bind_field + " = Object. " ICALL_GET_METHODBIND " ( " BINDINGS_NATIVE_NAME_FIELD " , \" " ) ;
p_output . append ( p_imethod . name ) ;
p_output . append ( " \" ); \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_imethod . method_doc & & p_imethod . method_doc - > description . size ( ) ) {
2019-03-18 21:59:35 +01:00
String xml_summary = bbcode_to_xml ( fix_doc_description ( p_imethod . method_doc - > description ) , & p_itype ) ;
2019-04-19 01:10:08 +02:00
Vector < String > summary_lines = xml_summary . length ( ) ? xml_summary . split ( " \n " ) : Vector < String > ( ) ;
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
if ( summary_lines . size ( ) | | default_args_doc . get_string_length ( ) ) {
p_output . append ( MEMBER_BEGIN " /// <summary> \n " ) ;
2017-10-02 23:24:00 +02:00
2019-03-18 21:59:35 +01:00
for ( int i = 0 ; i < summary_lines . size ( ) ; i + + ) {
2019-04-19 01:10:08 +02:00
p_output . append ( INDENT2 " /// " ) ;
p_output . append ( summary_lines [ i ] ) ;
p_output . append ( " \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( default_args_doc . as_string ( ) ) ;
p_output . append ( INDENT2 " /// </summary> " ) ;
2019-03-18 21:59:35 +01:00
}
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( ! p_imethod . is_internal ) {
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN " [GodotMethod( \" " ) ;
p_output . append ( p_imethod . name ) ;
p_output . append ( " \" )] " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-25 17:11:01 +02:00
if ( p_imethod . is_deprecated ) {
if ( p_imethod . deprecation_message . empty ( ) )
WARN_PRINTS ( " An empty deprecation message is discouraged. Method: " + p_imethod . proxy_name ) ;
p_output . append ( MEMBER_BEGIN " [Obsolete( \" " ) ;
p_output . append ( p_imethod . deprecation_message ) ;
p_output . append ( " \" )] " ) ;
}
2019-04-19 01:10:08 +02:00
p_output . append ( MEMBER_BEGIN ) ;
p_output . append ( p_imethod . is_internal ? " internal " : " public " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_itype . is_singleton ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " static " ) ;
2017-10-29 02:37:13 +01:00
} else if ( p_imethod . is_virtual ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " virtual " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02: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 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_imethod . is_virtual ) {
// Godot virtual method must be overridden, therefore we return a default value by default.
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
if ( return_type - > cname = = name_cache . type_void ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " return; \n " CLOSE_BLOCK_L2 ) ;
2017-10-29 02:37:13 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( " return default( " ) ;
p_output . append ( return_type - > cs_type ) ;
p_output . append ( " ); \n " CLOSE_BLOCK_L2 ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return OK ; // Won't increment method bind count
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_imethod . requires_object_call ) {
// Fallback to Godot's object.Call(string, params)
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( CS_METHOD_CALL " ( \" " ) ;
p_output . append ( p_imethod . name ) ;
p_output . append ( " \" " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
for ( const List < ArgumentInterface > : : Element * F = p_imethod . arguments . front ( ) ; F ; F = F - > next ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " , " ) ;
p_output . append ( F - > get ( ) . name ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( " ); \n " CLOSE_BLOCK_L2 ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
return OK ; // Won't increment method bind count
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
const Map < const MethodInterface * , const InternalCall * > : : Element * match = method_icalls_map . find ( & p_imethod ) ;
ERR_FAIL_NULL_V ( match , ERR_BUG ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
const InternalCall * im_icall = match - > value ( ) ;
2017-10-02 23:24:00 +02:00
2018-02-22 13:13:51 +01:00
String im_call = im_icall - > editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS ;
2018-04-28 22:25:25 +02:00
im_call + = " . " + im_icall - > name + " ( " + icall_params + " ) " ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
if ( p_imethod . arguments . size ( ) )
2019-04-19 01:10:08 +02:00
p_output . append ( cs_in_statements ) ;
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
if ( return_type - > cname = = name_cache . type_void ) {
2019-04-19 01:10:08 +02:00
p_output . append ( im_call + " ; \n " ) ;
2017-10-29 02:37:13 +01:00
} else if ( return_type - > cs_out . empty ( ) ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " return " + im_call + " ; \n " ) ;
2017-10-29 02:37:13 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( sformat ( return_type - > cs_out , im_call , return_type - > cs_type , return_type - > im_type_out ) ) ;
p_output . append ( " \n " ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
p_output . append ( CLOSE_BLOCK_L2 ) ;
2017-10-29 02:37:13 +01:00
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
p_method_bind_count + + ;
2019-04-19 01:10:08 +02:00
2017-10-29 02:37:13 +01:00
return OK ;
}
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
Error BindingsGenerator : : generate_glue ( const String & p_output_dir ) {
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
bool dir_exists = DirAccess : : exists ( p_output_dir ) ;
ERR_EXPLAIN ( " The output directory does not exist. " ) ;
ERR_FAIL_COND_V ( ! dir_exists , ERR_FILE_BAD_PATH ) ;
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
StringBuilder output ;
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02: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 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
generated_icall_funcs . clear ( ) ;
2017-10-02 23:24:00 +02:00
2018-01-09 19:06:59 +01: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 23:24:00 +02:00
2018-09-04 05:40:41 +02: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_reference ) ;
CRASH_COND ( itype . is_singleton ) ;
}
2017-10-29 02:37:13 +01:00
List < InternalCall > & custom_icalls = itype . api_type = = ClassDB : : API_EDITOR ? editor_custom_icalls : core_custom_icalls ;
2019-02-22 15:38:49 +01:00
OS : : get_singleton ( ) - > print ( " Generating %s... \n " , itype . name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 23:24:00 +02:00
2018-09-04 05:40:41 +02:00
String ctor_method ( ICALL_PREFIX + itype . proxy_name + " _Ctor " ) ; // Used only for derived types
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
for ( const List < MethodInterface > : : Element * E = itype . methods . front ( ) ; E ; E = E - > next ( ) ) {
const MethodInterface & imethod = E - > get ( ) ;
Error method_err = _generate_glue_method ( itype , imethod , output ) ;
if ( method_err ! = OK ) {
ERR_EXPLAIN ( " Failed to generate method ' " + imethod . name + " ' for class ' " + itype . name + " ' " ) ;
ERR_FAIL_V ( method_err ) ;
2017-10-02 23:24:00 +02: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 " ) ;
if ( ! find_icall_by_name ( singleton_icall . name , custom_icalls ) )
custom_icalls . push_back ( singleton_icall ) ;
2019-04-19 01:10:08 +02: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 23:24:00 +02:00
}
2018-09-04 05:40:41 +02:00
if ( is_derived_type & & itype . is_instantiable ) {
2017-10-02 23:24:00 +02:00
InternalCall ctor_icall = InternalCall ( itype . api_type , ctor_method , " IntPtr " , itype . proxy_name + " obj " ) ;
if ( ! find_icall_by_name ( ctor_icall . name , custom_icalls ) )
custom_icalls . push_back ( ctor_icall ) ;
2019-04-19 01:10:08 +02: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 23:24:00 +02:00
}
}
2019-04-19 01:10:08 +02:00
output . append ( " namespace GodotSharpBindings \n " OPEN_BLOCK " \n " ) ;
2018-02-22 13:13:51 +01:00
2019-04-19 01:10:08 +02: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 13:13:51 +01:00
2019-04-19 01:10:08 +02: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 13:13:51 +01:00
2019-04-19 01:10:08 +02:00
output . append ( " uint32_t get_bindings_version() { return " ) ;
output . append ( String : : num_uint64 ( BINDINGS_GENERATOR_VERSION ) + " ; } \n " ) ;
2018-02-22 13:13:51 +01:00
2019-04-19 01:10:08 +02:00
output . append ( " \n void register_generated_icalls() " OPEN_BLOCK ) ;
output . append ( " \t godot_register_glue_header_icalls(); \n " ) ;
2017-10-02 23:24:00 +02:00
2019-04-19 01:10:08 +02:00
# define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
{ \
output . append ( " \t mono_add_internal_call( " ) ; \
output . append ( " \" " BINDINGS_NAMESPACE " . " ) ; \
output . append ( m_icall . editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS ) ; \
output . append ( " :: " ) ; \
output . append ( m_icall . name ) ; \
output . append ( " \" , (void*) " ) ; \
output . append ( m_icall . name ) ; \
output . append ( " ); \n " ) ; \
2017-10-02 23:24:00 +02:00
}
bool tools_sequence = false ;
for ( const List < InternalCall > : : Element * E = core_custom_icalls . front ( ) ; E ; E = E - > next ( ) ) {
if ( tools_sequence ) {
if ( ! E - > get ( ) . editor_only ) {
tools_sequence = false ;
2019-04-19 01:10:08 +02:00
output . append ( " #endif \n " ) ;
2017-10-02 23:24:00 +02:00
}
} else {
if ( E - > get ( ) . editor_only ) {
2019-04-19 01:10:08 +02:00
output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2017-10-02 23:24:00 +02:00
tools_sequence = true ;
}
}
ADD_INTERNAL_CALL_REGISTRATION ( E - > get ( ) ) ;
}
if ( tools_sequence ) {
tools_sequence = false ;
2019-04-19 01:10:08 +02:00
output . append ( " #endif \n " ) ;
2017-10-02 23:24:00 +02:00
}
2019-04-19 01:10:08 +02:00
output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2017-10-02 23:24:00 +02:00
for ( const List < InternalCall > : : Element * E = editor_custom_icalls . front ( ) ; E ; E = E - > next ( ) )
ADD_INTERNAL_CALL_REGISTRATION ( E - > get ( ) ) ;
2019-04-19 01:10:08 +02:00
output . append ( " #endif // TOOLS_ENABLED \n " ) ;
2017-10-02 23:24:00 +02:00
for ( const List < InternalCall > : : Element * E = method_icalls . front ( ) ; E ; E = E - > next ( ) ) {
if ( tools_sequence ) {
if ( ! E - > get ( ) . editor_only ) {
tools_sequence = false ;
2019-04-19 01:10:08 +02:00
output . append ( " #endif \n " ) ;
2017-10-02 23:24:00 +02:00
}
} else {
if ( E - > get ( ) . editor_only ) {
2019-04-19 01:10:08 +02:00
output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2017-10-02 23:24:00 +02:00
tools_sequence = true ;
}
}
ADD_INTERNAL_CALL_REGISTRATION ( E - > get ( ) ) ;
}
if ( tools_sequence ) {
tools_sequence = false ;
2019-04-19 01:10:08 +02:00
output . append ( " #endif \n " ) ;
2017-10-02 23:24:00 +02:00
}
# undef ADD_INTERNAL_CALL_REGISTRATION
2019-04-19 01:10:08 +02:00
output . append ( CLOSE_BLOCK " \n } // namespace GodotSharpBindings \n " ) ;
2018-09-04 05:40:41 +02:00
2019-04-19 01:10:08 +02:00
output . append ( " \n #endif // MONO_GLUE_ENABLED \n " ) ;
2017-10-02 23:24:00 +02:00
2017-10-29 02:37:13 +01:00
Error save_err = _save_file ( path_join ( p_output_dir , " mono_glue.gen.cpp " ) , output ) ;
2017-10-24 16:18:47 +02:00
if ( save_err ! = OK )
return save_err ;
2017-10-29 02:37:13 +01:00
OS : : get_singleton ( ) - > print ( " Mono glue generated successfully \n " ) ;
2017-10-24 16:18:47 +02:00
return OK ;
2017-10-02 23:24:00 +02:00
}
2018-02-22 13:13:51 +01:00
uint32_t BindingsGenerator : : get_version ( ) {
return BINDINGS_GENERATOR_VERSION ;
}
2019-04-19 01:10:08 +02:00
Error BindingsGenerator : : _save_file ( const String & p_path , const StringBuilder & p_content ) {
2017-10-02 23:24:00 +02:00
FileAccessRef file = FileAccess : : open ( p_path , FileAccess : : WRITE ) ;
2017-10-24 16:18:47 +02:00
ERR_EXPLAIN ( " Cannot open file: " + p_path ) ;
2017-10-02 23:24:00 +02:00
ERR_FAIL_COND_V ( ! file , ERR_FILE_CANT_WRITE ) ;
2019-04-19 01:10:08 +02:00
file - > store_string ( p_content . as_string ( ) ) ;
2017-10-02 23:24:00 +02:00
file - > close ( ) ;
return OK ;
}
2019-04-19 01:10:08 +02:00
Error BindingsGenerator : : _generate_glue_method ( const BindingsGenerator : : TypeInterface & p_itype , const BindingsGenerator : : MethodInterface & p_imethod , StringBuilder & p_output ) {
2017-10-29 02:37:13 +01:00
if ( p_imethod . is_virtual )
return OK ; // Ignore
2018-04-28 22:25:25 +02:00
bool ret_void = p_imethod . return_type . cname = = name_cache . type_void ;
2017-10-29 02:37:13 +01:00
2018-04-28 22:25:25 +02:00
const TypeInterface * return_type = _get_type_or_placeholder ( p_imethod . return_type ) ;
2017-10-29 02:37:13 +01: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 ;
for ( const List < ArgumentInterface > : : Element * F = p_imethod . arguments . front ( ) ; F ; F = F - > next ( ) ) {
const ArgumentInterface & iarg = F - > get ( ) ;
2018-04-28 22:25:25 +02:00
const TypeInterface * arg_type = _get_type_or_placeholder ( iarg . type ) ;
2017-10-29 02:37:13 +01: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 20:03:16 +01: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 02:37:13 +01:00
}
} else {
if ( i > 0 )
c_args_var_content + = " , " ;
if ( arg_type - > c_in . size ( ) )
c_in_statements + = sformat ( arg_type - > c_in , arg_type - > c_type , c_param_name ) ;
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 + + ;
}
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 ) ;
if ( im_icall - > editor_only )
2019-04-19 01:10:08 +02:00
p_output . append ( " #ifdef TOOLS_ENABLED \n " ) ;
2017-10-29 02:37:13 +01:00
// Generate icall function
2019-04-19 01:10:08 +02:00
p_output . append ( ret_void ? " void " : return_type - > c_type_out + " " ) ;
p_output . append ( icall_method ) ;
p_output . append ( " ( " ) ;
p_output . append ( c_func_sig ) ;
p_output . append ( " ) " OPEN_BLOCK ) ;
2017-10-29 02:37:13 +01:00
String fail_ret = ret_void ? " " : " , " + ( return_type - > c_type_out . ends_with ( " * " ) ? " NULL " : return_type - > c_type_out + " () " ) ;
if ( ! ret_void ) {
String ptrcall_return_type ;
String initialization ;
2019-01-18 00:41:41 +01: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-19 01:10:08 +02:00
p_output . append ( " \t Variant " C_LOCAL_VARARG_RET " ; \n " ) ;
2019-01-18 00:41:41 +01:00
}
2017-10-29 02:37:13 +01:00
if ( return_type - > is_object_type ) {
ptrcall_return_type = return_type - > is_reference ? " Ref<Reference> " : return_type - > c_type ;
initialization = return_type - > is_reference ? " " : " = NULL " ;
} else {
ptrcall_return_type = return_type - > c_type ;
}
2019-04-19 01:10:08 +02:00
p_output . append ( " \t " + ptrcall_return_type ) ;
p_output . append ( " " C_LOCAL_RET ) ;
p_output . append ( initialization + " ; \n " ) ;
p_output . append ( " \t ERR_FAIL_NULL_V( " CS_PARAM_INSTANCE ) ;
p_output . append ( fail_ret ) ;
p_output . append ( " ); \n " ) ;
2017-10-29 02:37:13 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( " \t ERR_FAIL_NULL( " CS_PARAM_INSTANCE " ); \n " ) ;
2017-10-29 02:37:13 +01: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-19 01:10:08 +02: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 02:37:13 +01:00
} else {
2019-04-19 01:10:08 +02: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 02:37:13 +01:00
}
}
if ( p_imethod . is_vararg ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " \t Variant::CallError vcall_error; \n \t " ) ;
2017-10-29 02:37:13 +01:00
2019-01-18 00:41:41 +01:00
if ( ! ret_void ) {
// See the comment on the C_LOCAL_VARARG_RET declaration
if ( return_type - > cname ! = name_cache . type_Variant ) {
2019-04-19 01:10:08 +02:00
p_output . append ( C_LOCAL_VARARG_RET " = " ) ;
2019-01-18 00:41:41 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( C_LOCAL_RET " = " ) ;
2019-01-18 00:41:41 +01:00
}
}
2017-10-29 02:37:13 +01:00
2019-04-19 01:10:08 +02:00
p_output . append ( CS_PARAM_METHODBIND " ->call( " CS_PARAM_INSTANCE " , " ) ;
p_output . append ( p_imethod . arguments . size ( ) ? C_LOCAL_PTRCALL_ARGS " .ptr() " : " NULL " ) ;
p_output . append ( " , total_length, vcall_error); \n " ) ;
2019-01-18 00:41:41 +01:00
// See the comment on the C_LOCAL_VARARG_RET declaration
if ( return_type - > cname ! = name_cache . type_Variant ) {
2019-04-19 01:10:08 +02:00
p_output . append ( " \t " C_LOCAL_RET " = " C_LOCAL_VARARG_RET " ; \n " ) ;
2019-01-18 00:41:41 +01:00
}
2017-10-29 02:37:13 +01:00
} else {
2019-04-19 01:10:08 +02:00
p_output . append ( " \t " CS_PARAM_METHODBIND " ->ptrcall( " CS_PARAM_INSTANCE " , " ) ;
p_output . append ( p_imethod . arguments . size ( ) ? C_LOCAL_PTRCALL_ARGS " , " : " NULL, " ) ;
p_output . append ( ! ret_void ? " & " C_LOCAL_RET " ); \n " : " NULL); \n " ) ;
2017-10-29 02:37:13 +01:00
}
if ( ! ret_void ) {
if ( return_type - > c_out . empty ( ) )
2019-04-19 01:10:08 +02:00
p_output . append ( " \t return " C_LOCAL_RET " ; \n " ) ;
2017-10-29 02:37:13 +01:00
else
2019-04-19 01:10:08 +02:00
p_output . append ( sformat ( return_type - > c_out , return_type - > c_type_out , C_LOCAL_RET , return_type - > name ) ) ;
2017-10-29 02:37:13 +01:00
}
2019-04-19 01:10:08 +02:00
p_output . append ( CLOSE_BLOCK " \n " ) ;
2017-10-29 02:37:13 +01:00
if ( im_icall - > editor_only )
2019-04-19 01:10:08 +02:00
p_output . append ( " #endif // TOOLS_ENABLED \n " ) ;
2017-10-29 02:37:13 +01:00
}
return OK ;
}
2018-04-28 22:25:25 +02:00
const BindingsGenerator : : TypeInterface * BindingsGenerator : : _get_type_or_null ( const TypeReference & p_typeref ) {
2017-12-24 03:17:48 +01:00
2018-04-28 22:25:25 +02:00
const Map < StringName , TypeInterface > : : Element * builtin_type_match = builtin_types . find ( p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
2018-01-09 19:06:59 +01:00
if ( builtin_type_match )
return & builtin_type_match - > get ( ) ;
2017-12-24 03:17:48 +01:00
2018-04-28 22:25:25 +02:00
const OrderedHashMap < StringName , TypeInterface > : : Element obj_type_match = obj_types . find ( p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
2018-01-09 19:06:59 +01:00
if ( obj_type_match )
return & obj_type_match . get ( ) ;
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
if ( p_typeref . is_enum ) {
const Map < StringName , TypeInterface > : : Element * enum_match = enum_types . find ( p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
if ( enum_match )
return & enum_match - > get ( ) ;
// 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 ) ;
ERR_FAIL_NULL_V ( int_match , NULL ) ;
return & int_match - > get ( ) ;
}
2017-10-02 23:24:00 +02:00
return NULL ;
}
2018-04-28 22:25:25 +02:00
const BindingsGenerator : : TypeInterface * BindingsGenerator : : _get_type_or_placeholder ( const TypeReference & p_typeref ) {
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
const TypeInterface * found = _get_type_or_null ( p_typeref ) ;
2017-10-02 23:24:00 +02:00
if ( found )
return found ;
2018-04-28 22:25:25 +02:00
ERR_PRINTS ( String ( ) + " Type not found. Creating placeholder: " + p_typeref . cname . operator String ( ) ) ;
2017-10-02 23:24:00 +02:00
2018-04-28 22:25:25 +02:00
const Map < StringName , TypeInterface > : : Element * match = placeholder_types . find ( p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
if ( match )
return & match - > get ( ) ;
TypeInterface placeholder ;
2018-04-28 22:25:25 +02:00
TypeInterface : : create_placeholder_type ( placeholder , p_typeref . cname ) ;
2017-10-02 23:24:00 +02:00
2017-12-24 03:17:48 +01:00
return & placeholder_types . insert ( placeholder . cname , placeholder ) - > get ( ) ;
}
2019-04-25 20:24:48 +02:00
StringName BindingsGenerator : : _get_int_type_name_from_meta ( GodotTypeInfo : : Metadata p_meta ) {
switch ( p_meta ) {
case GodotTypeInfo : : METADATA_INT_IS_INT8 :
return " sbyte " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_INT16 :
return " short " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_INT32 :
return " int " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_INT64 :
return " long " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT8 :
return " byte " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT16 :
return " ushort " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT32 :
return " uint " ;
break ;
case GodotTypeInfo : : METADATA_INT_IS_UINT64 :
return " ulong " ;
break ;
default :
// 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
}
}
2017-10-02 23:24:00 +02:00
void BindingsGenerator : : _populate_object_type_interfaces ( ) {
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 03:17:48 +01:00
if ( ! ClassDB : : is_class_exposed ( type_cname ) ) {
2019-04-25 15:45:12 +02:00
_log ( " Ignoring type `%s` because it's not exposed \n " , String ( type_cname ) . utf8 ( ) . get_data ( ) ) ;
2017-12-24 03:17:48 +01:00
class_list . pop_front ( ) ;
continue ;
}
2018-07-18 21:15:41 +02:00
if ( ! ClassDB : : is_class_enabled ( type_cname ) ) {
2019-04-25 15:45:12 +02:00
_log ( " Ignoring type `%s` because it's not enabled \n " , String ( type_cname ) . utf8 ( ) . get_data ( ) ) ;
2018-07-18 21:15:41 +02:00
class_list . pop_front ( ) ;
continue ;
}
2017-12-24 03:17:48 +01:00
ClassDB : : ClassInfo * class_info = ClassDB : : classes . getptr ( type_cname ) ;
2017-10-02 23:24:00 +02:00
TypeInterface itype = TypeInterface : : create_object_type ( type_cname , api_type ) ;
itype . base_name = ClassDB : : get_parent_class ( type_cname ) ;
2017-11-13 21:46:57 +01:00
itype . is_singleton = Engine : : get_singleton ( ) - > has_singleton ( itype . proxy_name ) ;
2017-10-02 23:24:00 +02:00
itype . is_instantiable = ClassDB : : can_instance ( type_cname ) & & ! itype . is_singleton ;
2017-12-24 03:17:48 +01:00
itype . is_reference = ClassDB : : is_parent_class ( type_cname , name_cache . type_Reference ) ;
2017-10-02 23:24:00 +02:00
itype . memory_own = itype . is_reference ;
itype . c_out = " \t return " ;
itype . c_out + = C_METHOD_UNMANAGED_GET_MANAGED ;
itype . c_out + = itype . is_reference ? " (%1.ptr()); \n " : " (%1); \n " ;
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 17:11:01 +02:00
// Populate properties
2017-12-29 02:18:46 +01:00
List < PropertyInfo > property_list ;
ClassDB : : get_property_list ( type_cname , & property_list , true ) ;
2019-04-25 17:11:01 +02:00
Map < StringName , StringName > accessor_methods ;
2017-12-29 02:18:46 +01:00
for ( const List < PropertyInfo > : : Element * E = property_list . front ( ) ; E ; E = E - > next ( ) ) {
const PropertyInfo & property = E - > get ( ) ;
if ( property . usage & PROPERTY_USAGE_GROUP | | property . usage & PROPERTY_USAGE_CATEGORY )
continue ;
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 ) ;
2019-04-25 17:11:01 +02:00
if ( iprop . setter ! = StringName ( ) )
accessor_methods [ iprop . setter ] = iprop . cname ;
if ( iprop . getter ! = StringName ( ) )
accessor_methods [ iprop . getter ] = iprop . cname ;
2017-12-29 02:18:46 +01:00
bool valid = false ;
iprop . index = ClassDB : : get_property_index ( type_cname , iprop . cname , & valid ) ;
ERR_FAIL_COND ( ! valid ) ;
2019-03-18 21:59:35 +01:00
iprop . proxy_name = escape_csharp_keyword ( snake_to_pascal_case ( iprop . cname ) ) ;
2019-04-25 15:45:12 +02:00
// Prevent the property and its enclosing type from sharing the same name
2017-12-29 02:18:46 +01:00
if ( iprop . proxy_name = = itype . proxy_name ) {
2019-04-25 15:45:12 +02:00
_log ( " Name of property `%s` is ambiguous with the name of its enclosing class `%s`. Renaming property to `%s_` \n " ,
iprop . proxy_name . utf8 ( ) . get_data ( ) , itype . proxy_name . utf8 ( ) . get_data ( ) , iprop . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-12-29 02:18:46 +01:00
iprop . proxy_name + = " _ " ;
}
2019-03-18 21:59:35 +01:00
iprop . proxy_name = iprop . proxy_name . replace ( " / " , " __ " ) ; // Some members have a slash...
2017-12-29 02:18:46 +01:00
iprop . prop_doc = NULL ;
for ( int i = 0 ; i < itype . class_doc - > properties . size ( ) ; i + + ) {
const DocData : : PropertyDoc & prop_doc = itype . class_doc - > properties [ i ] ;
if ( prop_doc . name = = iprop . cname ) {
iprop . prop_doc = & prop_doc ;
break ;
}
}
itype . properties . push_back ( iprop ) ;
}
2017-12-24 03:17:48 +01:00
// Populate methods
2017-10-02 23:24:00 +02:00
List < MethodInfo > virtual_method_list ;
ClassDB : : get_virtual_methods ( type_cname , & virtual_method_list , true ) ;
List < MethodInfo > method_list ;
ClassDB : : get_method_list ( type_cname , & method_list , true ) ;
method_list . sort ( ) ;
for ( List < MethodInfo > : : Element * E = method_list . front ( ) ; E ; E = E - > next ( ) ) {
const MethodInfo & method_info = E - > get ( ) ;
int argc = method_info . arguments . size ( ) ;
if ( method_info . name . empty ( ) )
continue ;
MethodInterface imethod ;
imethod . name = method_info . name ;
2017-12-24 03:17:48 +01:00
imethod . cname = imethod . name ;
2017-10-02 23:24:00 +02:00
if ( method_info . flags & METHOD_FLAG_VIRTUAL )
imethod . is_virtual = true ;
PropertyInfo return_info = method_info . return_val ;
MethodBind * m = imethod . is_virtual ? NULL : ClassDB : : get_method ( type_cname , method_info . name ) ;
imethod . is_vararg = m & & m - > is_vararg ( ) ;
if ( ! m & & ! imethod . is_virtual ) {
if ( virtual_method_list . find ( method_info ) ) {
// 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.
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = name_cache . type_void ;
2017-10-02 23:24:00 +02:00
// Actually, more methods like this may be added in the future,
2018-01-18 21:37:17 +01:00
// which could actually will return something different.
2017-10-02 23:24:00 +02:00
// Let's put this to notify us if that ever happens.
2017-12-24 03:17:48 +01:00
if ( itype . cname ! = name_cache . type_Object | | imethod . name ! = " free " ) {
2019-04-25 15:45:12 +02:00
ERR_PRINTS ( " Notification: New unexpected virtual non-overridable method found. \n "
" We only expected Object.free, but found " +
itype . name + " . " + imethod . name ) ;
2017-10-02 23:24:00 +02:00
}
} else {
2019-04-25 15:45:12 +02:00
ERR_EXPLAIN ( " Missing MethodBind for non-virtual method: " + itype . name + " . " + imethod . name ) ;
ERR_FAIL ( ) ;
2017-10-02 23:24:00 +02:00
}
2018-04-28 22:25:25 +02: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 23:24:00 +02:00
} else if ( return_info . class_name ! = StringName ( ) ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = return_info . class_name ;
2017-10-02 23:24:00 +02:00
} else if ( return_info . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = return_info . hint_string ;
2017-10-02 23:24:00 +02:00
} else if ( return_info . type = = Variant : : NIL & & return_info . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = name_cache . type_Variant ;
2017-10-02 23:24:00 +02:00
} else if ( return_info . type = = Variant : : NIL ) {
2018-04-28 22:25:25 +02:00
imethod . return_type . cname = name_cache . type_void ;
2017-10-02 23:24:00 +02:00
} else {
2019-04-25 20:24:48 +02: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 ) ;
} else if ( return_info . type = = Variant : : REAL ) {
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 23:24:00 +02:00
}
for ( int i = 0 ; i < argc ; i + + ) {
PropertyInfo arginfo = method_info . arguments [ i ] ;
ArgumentInterface iarg ;
iarg . name = arginfo . name ;
2018-04-28 22:25:25 +02: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 23:24:00 +02:00
} else if ( arginfo . class_name ! = StringName ( ) ) {
2018-04-28 22:25:25 +02:00
iarg . type . cname = arginfo . class_name ;
2017-10-02 23:24:00 +02:00
} else if ( arginfo . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
2018-04-28 22:25:25 +02:00
iarg . type . cname = arginfo . hint_string ;
2017-10-02 23:24:00 +02:00
} else if ( arginfo . type = = Variant : : NIL ) {
2018-04-28 22:25:25 +02:00
iarg . type . cname = name_cache . type_Variant ;
2017-10-02 23:24:00 +02:00
} else {
2019-04-25 20:24:48 +02:00
if ( arginfo . type = = Variant : : INT ) {
iarg . type . cname = _get_int_type_name_from_meta ( m ? m - > get_argument_meta ( i ) : GodotTypeInfo : : METADATA_NONE ) ;
} else if ( arginfo . type = = Variant : : REAL ) {
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 23:24:00 +02:00
}
iarg . name = escape_csharp_keyword ( snake_to_camel_case ( iarg . name ) ) ;
if ( m & & m - > has_default_argument ( i ) ) {
_default_argument_from_variant ( m - > get_default_argument ( i ) , iarg ) ;
}
imethod . add_argument ( iarg ) ;
}
if ( imethod . is_vararg ) {
ArgumentInterface ivararg ;
2018-04-28 22:25:25 +02:00
ivararg . type . cname = name_cache . type_VarArg ;
2017-10-02 23:24:00 +02:00
ivararg . name = " @args " ;
imethod . add_argument ( ivararg ) ;
}
imethod . proxy_name = escape_csharp_keyword ( snake_to_pascal_case ( imethod . name ) ) ;
2019-04-25 15:45:12 +02:00
// Prevent the method and its enclosing type from sharing the same name
2017-10-02 23:24:00 +02:00
if ( imethod . proxy_name = = itype . proxy_name ) {
2019-04-25 15:45:12 +02:00
_log ( " Name of method `%s` is ambiguous with the name of its enclosing class `%s`. Renaming method to `%s_` \n " ,
imethod . proxy_name . utf8 ( ) . get_data ( ) , itype . proxy_name . utf8 ( ) . get_data ( ) , imethod . proxy_name . utf8 ( ) . get_data ( ) ) ;
2017-10-02 23:24:00 +02:00
imethod . proxy_name + = " _ " ;
}
2019-04-25 17:11:01 +02: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 23:24:00 +02:00
if ( itype . class_doc ) {
for ( int i = 0 ; i < itype . class_doc - > methods . size ( ) ; i + + ) {
if ( itype . class_doc - > methods [ i ] . name = = imethod . name ) {
imethod . method_doc = & itype . class_doc - > methods [ i ] ;
break ;
}
}
}
if ( ! imethod . is_virtual & & imethod . name [ 0 ] = = ' _ ' ) {
2019-02-12 21:10:08 +01:00
for ( const List < PropertyInterface > : : Element * F = itype . properties . front ( ) ; F ; F = F - > next ( ) ) {
const PropertyInterface & iprop = F - > get ( ) ;
2017-10-02 23:24:00 +02:00
2017-12-29 02:18:46 +01:00
if ( iprop . setter = = imethod . name | | iprop . getter = = imethod . name ) {
2017-10-02 23:24:00 +02:00
imethod . is_internal = true ;
itype . methods . push_back ( imethod ) ;
break ;
}
}
} else {
itype . methods . push_back ( imethod ) ;
}
}
2017-12-24 03:17:48 +01:00
// Populate enums and constants
2019-04-19 01:10:08 +02:00
List < String > constants ;
ClassDB : : get_integer_constant_list ( type_cname , & constants , true ) ;
2017-12-24 03:17:48 +01:00
const HashMap < StringName , List < StringName > > & enum_map = class_info - > enum_map ;
const StringName * k = NULL ;
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-19 01:10:08 +02:00
const List < StringName > & enum_constants = enum_map . get ( * k ) ;
for ( const List < StringName > : : Element * E = enum_constants . front ( ) ; E ; E = E - > next ( ) ) {
2018-10-18 19:41:10 +02:00
const StringName & constant_cname = E - > get ( ) ;
String constant_name = constant_cname . operator String ( ) ;
int * value = class_info - > constant_map . getptr ( constant_cname ) ;
2017-12-24 03:17:48 +01:00
ERR_FAIL_NULL ( value ) ;
2019-04-19 01:10:08 +02:00
constants . erase ( constant_name ) ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , * value ) ;
2017-12-24 03:17:48 +01:00
iconstant . const_doc = NULL ;
for ( int i = 0 ; i < itype . class_doc - > constants . size ( ) ; i + + ) {
const DocData : : ConstantDoc & const_doc = itype . class_doc - > constants [ i ] ;
if ( const_doc . name = = iconstant . name ) {
iconstant . const_doc = & const_doc ;
break ;
}
}
ienum . constants . push_back ( iconstant ) ;
}
2018-10-18 19:41:10 +02:00
int prefix_length = _determine_enum_prefix ( ienum ) ;
_apply_prefix_to_enum_constants ( ienum , prefix_length ) ;
2017-12-24 03:17:48 +01:00
itype . enums . push_back ( ienum ) ;
TypeInterface enum_itype ;
2018-04-28 22:25:25 +02:00
enum_itype . is_enum = true ;
2017-12-24 03:17:48 +01: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 22:25:25 +02:00
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2017-12-24 03:17:48 +01:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
}
2019-04-19 01:10:08 +02:00
for ( const List < String > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
2018-10-18 19:41:10 +02:00
const String & constant_name = E - > get ( ) ;
int * value = class_info - > constant_map . getptr ( StringName ( E - > get ( ) ) ) ;
2017-12-24 03:17:48 +01:00
ERR_FAIL_NULL ( value ) ;
2018-10-18 19:41:10 +02:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , * value ) ;
2017-12-24 03:17:48 +01:00
iconstant . const_doc = NULL ;
for ( int i = 0 ; i < itype . class_doc - > constants . size ( ) ; i + + ) {
const DocData : : ConstantDoc & const_doc = itype . class_doc - > constants [ i ] ;
if ( const_doc . name = = iconstant . name ) {
iconstant . const_doc = & const_doc ;
break ;
}
}
itype . constants . push_back ( iconstant ) ;
}
obj_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
class_list . pop_front ( ) ;
}
}
void BindingsGenerator : : _default_argument_from_variant ( const Variant & p_val , ArgumentInterface & r_iarg ) {
r_iarg . default_argument = p_val ;
switch ( p_val . get_type ( ) ) {
case Variant : : NIL :
2018-04-28 22:25:25 +02:00
if ( ClassDB : : class_exists ( r_iarg . type . cname ) ) {
2017-10-02 23:24:00 +02:00
// Object type
r_iarg . default_argument = " null " ;
} else {
// Variant
r_iarg . default_argument = " null " ;
}
break ;
// Atomic types
case Variant : : BOOL :
r_iarg . default_argument = bool ( p_val ) ? " true " : " false " ;
break ;
case Variant : : INT :
2018-04-28 22:25:25 +02:00
if ( r_iarg . type . cname ! = name_cache . type_int ) {
2017-12-24 03:17:48 +01:00
r_iarg . default_argument = " (%s) " + r_iarg . default_argument ;
}
break ;
2017-10-02 23:24:00 +02:00
case Variant : : REAL :
# ifndef REAL_T_IS_DOUBLE
r_iarg . default_argument + = " f " ;
# endif
break ;
case Variant : : STRING :
case Variant : : NODE_PATH :
r_iarg . default_argument = " \" " + r_iarg . default_argument + " \" " ;
break ;
case Variant : : TRANSFORM :
if ( p_val . operator Transform ( ) = = Transform ( ) )
r_iarg . default_argument . clear ( ) ;
r_iarg . default_argument = " new %s( " + r_iarg . default_argument + " ) " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
break ;
case Variant : : PLANE :
2017-11-17 03:09:00 +01:00
case Variant : : AABB :
2017-10-02 23:24:00 +02:00
case Variant : : COLOR :
r_iarg . default_argument = " new Color(1, 1, 1, 1) " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
break ;
case Variant : : VECTOR2 :
case Variant : : RECT2 :
case Variant : : VECTOR3 :
r_iarg . default_argument = " new %s " + r_iarg . default_argument ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
break ;
case Variant : : OBJECT :
if ( p_val . is_zero ( ) ) {
r_iarg . default_argument = " null " ;
break ;
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-10-02 23:24:00 +02:00
case Variant : : DICTIONARY :
case Variant : : _RID :
r_iarg . default_argument = " new %s() " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
break ;
case Variant : : ARRAY :
case Variant : : POOL_BYTE_ARRAY :
case Variant : : POOL_INT_ARRAY :
case Variant : : POOL_REAL_ARRAY :
case Variant : : POOL_STRING_ARRAY :
case Variant : : POOL_VECTOR2_ARRAY :
case Variant : : POOL_VECTOR3_ARRAY :
case Variant : : POOL_COLOR_ARRAY :
r_iarg . default_argument = " new %s {} " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
break ;
case Variant : : TRANSFORM2D :
case Variant : : BASIS :
case Variant : : QUAT :
r_iarg . default_argument = Variant : : get_type_name ( p_val . get_type ( ) ) + " .Identity " ;
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_VAL ;
break ;
2019-04-09 17:08:36 +02:00
default : {
}
2017-10-02 23:24:00 +02:00
}
2018-04-28 22:25:25 +02: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 23:24:00 +02:00
r_iarg . def_param_mode = ArgumentInterface : : NULLABLE_REF ;
}
void BindingsGenerator : : _populate_builtin_type_interfaces ( ) {
builtin_types . clear ( ) ;
TypeInterface itype ;
2018-10-17 21:37:57 +02:00
# define INSERT_STRUCT_TYPE(m_type, m_type_in) \
{ \
itype = TypeInterface : : create_value_type ( String ( # m_type ) ) ; \
itype . c_in = " \t %0 %1_in = MARSHALLED_IN( " # m_type " , %1); \n " ; \
itype . c_out = " \t return MARSHALLED_OUT( " # m_type " , %1); \n " ; \
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 " ; \
itype . cs_out = " return (%1)%0; " ; \
itype . im_type_out = itype . cs_type ; \
builtin_types . insert ( itype . cname , itype ) ; \
2017-10-02 23:24:00 +02:00
}
INSERT_STRUCT_TYPE ( Vector2 , " real_t* " )
INSERT_STRUCT_TYPE ( Rect2 , " real_t* " )
INSERT_STRUCT_TYPE ( Transform2D , " real_t* " )
INSERT_STRUCT_TYPE ( Vector3 , " real_t* " )
INSERT_STRUCT_TYPE ( Basis , " real_t* " )
INSERT_STRUCT_TYPE ( Quat , " real_t* " )
INSERT_STRUCT_TYPE ( Transform , " real_t* " )
2017-11-17 03:09:00 +01:00
INSERT_STRUCT_TYPE ( AABB , " real_t* " )
2017-10-02 23:24:00 +02:00
INSERT_STRUCT_TYPE ( Color , " real_t* " )
INSERT_STRUCT_TYPE ( Plane , " real_t* " )
# undef INSERT_STRUCT_TYPE
2018-01-25 23:44:37 +01:00
// bool
itype = TypeInterface : : create_value_type ( String ( " bool " ) ) ;
2018-10-17 21:37:57 +02: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 23:44:37 +01:00
itype . im_type_in = itype . name ;
itype . im_type_out = itype . name ;
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
2019-04-25 20:24:48 +02:00
// Integer types
2018-10-17 21:37:57 +02:00
{
2019-04-25 20:24:48 +02:00
// C interface for 'uint32_t' is the same as that of enums. Remember to apply
// any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
# 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 21:37:57 +02:00
}
2017-10-02 23:24:00 +02:00
2019-04-25 20:24:48 +02:00
// The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
INSERT_INT_TYPE ( " sbyte " , int8_t , int64_t ) ;
INSERT_INT_TYPE ( " short " , int16_t , int64_t ) ;
INSERT_INT_TYPE ( " int " , int32_t , int64_t ) ;
INSERT_INT_TYPE ( " long " , int64_t , int64_t ) ;
INSERT_INT_TYPE ( " byte " , uint8_t , int64_t ) ;
INSERT_INT_TYPE ( " ushort " , uint16_t , int64_t ) ;
INSERT_INT_TYPE ( " uint " , uint32_t , int64_t ) ;
INSERT_INT_TYPE ( " ulong " , uint64_t , int64_t ) ;
}
// Floating point types
2018-10-17 21:37:57 +02:00
{
2019-04-25 20:24:48 +02:00
// float
itype = TypeInterface ( ) ;
itype . name = " float " ;
itype . cname = itype . name ;
itype . proxy_name = " float " ;
{
// The expected type for 'float' in ptrcall is 'double'
itype . c_in = " \t %0 %1_in = (%0)%1; \n " ;
itype . c_out = " \t return (%0)%1; \n " ;
itype . c_type = " double " ;
itype . c_type_in = " float " ;
itype . c_type_out = " float " ;
itype . c_arg_in = " &%s_in " ;
}
itype . cs_type = itype . proxy_name ;
itype . im_type_in = itype . proxy_name ;
itype . im_type_out = itype . proxy_name ;
builtin_types . insert ( itype . cname , itype ) ;
// double
itype = TypeInterface ( ) ;
itype . name = " double " ;
itype . cname = itype . name ;
itype . proxy_name = " double " ;
2018-10-17 21:37:57 +02:00
itype . c_type = " double " ;
2019-04-25 20:24:48 +02:00
itype . c_type_in = " double " ;
itype . c_type_out = " double " ;
itype . c_arg_in = " &%s " ;
itype . cs_type = itype . proxy_name ;
itype . im_type_in = itype . proxy_name ;
itype . im_type_out = itype . proxy_name ;
builtin_types . insert ( itype . cname , itype ) ;
2018-10-17 21:37:57 +02:00
}
2017-10-02 23:24:00 +02:00
// String
itype = TypeInterface ( ) ;
itype . name = " String " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = " string " ;
itype . 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 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// NodePath
itype = TypeInterface ( ) ;
itype . name = " NodePath " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = " NodePath " ;
itype . 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) " ;
2018-04-28 22:25:25 +02:00
itype . cs_out = " return new %1(%0); " ;
2017-10-02 23:24:00 +02:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// RID
itype = TypeInterface ( ) ;
itype . name = " RID " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = " RID " ;
itype . 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) " ;
2018-04-28 22:25:25 +02:00
itype . cs_out = " return new %1(%0); " ;
2017-10-02 23:24:00 +02:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// Variant
itype = TypeInterface ( ) ;
itype . name = " Variant " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02: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 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// VarArg (fictitious type to represent variable arguments)
itype = TypeInterface ( ) ;
itype . name = " VarArg " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02: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 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
# define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t) \
{ \
itype = TypeInterface ( ) ; \
itype . name = # m_name ; \
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ; \
2017-10-02 23:24:00 +02: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)
INSERT_ARRAY ( PoolIntArray , int ) ;
INSERT_ARRAY_FULL ( PoolByteArray , PoolByteArray , byte ) ;
# ifdef REAL_T_IS_DOUBLE
INSERT_ARRAY ( PoolRealArray , double ) ;
# else
INSERT_ARRAY ( PoolRealArray , float ) ;
# endif
INSERT_ARRAY ( PoolStringArray , string ) ;
INSERT_ARRAY ( PoolColorArray , Color ) ;
INSERT_ARRAY ( PoolVector2Array , Vector2 ) ;
INSERT_ARRAY ( PoolVector3Array , Vector3 ) ;
# undef INSERT_ARRAY
2018-07-18 23:07:57 +02:00
// Array
itype = TypeInterface ( ) ;
itype . name = " Array " ;
itype . cname = itype . name ;
2018-08-27 20:39:51 +02:00
itype . proxy_name = itype . name ;
2018-07-18 23:07:57 +02: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 20:39:51 +02:00
itype . cs_type = BINDINGS_NAMESPACE_COLLECTIONS " . " + itype . proxy_name ;
2018-07-18 23:07:57 +02:00
itype . cs_in = " %0. " CS_SMETHOD_GETINSTANCE " () " ;
2018-08-27 20:39:51 +02:00
itype . cs_out = " return new " + itype . cs_type + " (%0); " ;
2018-07-18 23:07:57 +02:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// Dictionary
itype = TypeInterface ( ) ;
itype . name = " Dictionary " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2018-08-27 20:39:51 +02:00
itype . proxy_name = itype . name ;
2018-07-18 23:07:57 +02:00
itype . c_out = " \t return memnew(Dictionary(%1)); \n " ;
2017-10-02 23:24:00 +02:00
itype . c_type = itype . name ;
2018-07-18 23:07:57 +02:00
itype . c_type_in = itype . c_type + " * " ;
itype . c_type_out = itype . c_type + " * " ;
2018-08-27 20:39:51 +02:00
itype . cs_type = BINDINGS_NAMESPACE_COLLECTIONS " . " + itype . proxy_name ;
2018-07-18 23:07:57 +02:00
itype . cs_in = " %0. " CS_SMETHOD_GETINSTANCE " () " ;
2018-08-27 20:39:51 +02:00
itype . cs_out = " return new " + itype . cs_type + " (%0); " ;
2018-07-18 23:07:57 +02:00
itype . im_type_in = " IntPtr " ;
itype . im_type_out = " IntPtr " ;
2017-12-24 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
// void (fictitious type to represent the return type of methods that do not return anything)
itype = TypeInterface ( ) ;
itype . name = " void " ;
2017-12-24 03:17:48 +01:00
itype . cname = itype . name ;
2017-10-02 23:24:00 +02:00
itype . proxy_name = itype . name ;
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 03:17:48 +01:00
builtin_types . insert ( itype . cname , itype ) ;
2017-10-02 23:24:00 +02:00
}
2017-12-24 03:17:48 +01:00
void BindingsGenerator : : _populate_global_constants ( ) {
int global_constants_count = GlobalConstants : : get_global_constant_count ( ) ;
if ( global_constants_count > 0 ) {
Map < String , DocData : : ClassDoc > : : Element * match = EditorHelp : : get_doc_data ( ) - > class_list . find ( " @GlobalScope " ) ;
ERR_EXPLAIN ( " Could not find `@GlobalScope` in DocData " ) ;
CRASH_COND ( ! match ) ;
const DocData : : ClassDoc & global_scope_doc = match - > value ( ) ;
for ( int i = 0 ; i < global_constants_count ; i + + ) {
String constant_name = GlobalConstants : : get_global_constant_name ( i ) ;
const DocData : : ConstantDoc * const_doc = NULL ;
2018-10-03 00:56:28 +02:00
for ( int j = 0 ; j < global_scope_doc . constants . size ( ) ; j + + ) {
const DocData : : ConstantDoc & curr_const_doc = global_scope_doc . constants [ j ] ;
2017-12-24 03:17:48 +01:00
if ( curr_const_doc . name = = constant_name ) {
const_doc = & curr_const_doc ;
break ;
}
}
int constant_value = GlobalConstants : : get_global_constant_value ( i ) ;
StringName enum_name = GlobalConstants : : get_global_constant_enum ( i ) ;
2018-10-18 19:41:10 +02:00
ConstantInterface iconstant ( constant_name , snake_to_pascal_case ( constant_name , true ) , constant_value ) ;
2017-12-24 03:17:48 +01:00
iconstant . const_doc = const_doc ;
if ( enum_name ! = StringName ( ) ) {
EnumInterface ienum ( enum_name ) ;
2019-02-12 21:10:08 +01:00
List < EnumInterface > : : Element * enum_match = global_enums . find ( ienum ) ;
if ( enum_match ) {
enum_match - > get ( ) . constants . push_back ( iconstant ) ;
2017-12-24 03:17:48 +01:00
} else {
ienum . constants . push_back ( iconstant ) ;
global_enums . push_back ( ienum ) ;
}
} else {
global_constants . push_back ( iconstant ) ;
}
}
for ( List < EnumInterface > : : Element * E = global_enums . front ( ) ; E ; E = E - > next ( ) ) {
EnumInterface & ienum = E - > get ( ) ;
TypeInterface enum_itype ;
2018-04-28 22:25:25 +02:00
enum_itype . is_enum = true ;
enum_itype . name = ienum . cname . operator String ( ) ;
enum_itype . cname = ienum . cname ;
enum_itype . proxy_name = enum_itype . name ;
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2017-12-24 03:17:48 +01:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
2018-10-18 19:41:10 +02:00
int prefix_length = _determine_enum_prefix ( ienum ) ;
2017-12-24 03:17:48 +01:00
2018-10-18 19:41:10 +02:00
// HARDCODED: The Error enum have the prefix 'ERR_' for everything except 'OK' and 'FAILED'.
2017-12-24 03:17:48 +01:00
if ( ienum . cname = = name_cache . enum_Error ) {
2018-10-18 19:41:10 +02:00
if ( prefix_length > 0 ) { // Just in case it ever changes
2017-12-24 03:17:48 +01:00
ERR_PRINTS ( " Prefix for enum 'Error' is not empty " ) ;
}
2018-10-18 19:41:10 +02:00
prefix_length = 1 ; // 'ERR_'
2017-12-24 03:17:48 +01:00
}
2018-10-18 19:41:10 +02:00
_apply_prefix_to_enum_constants ( ienum , prefix_length ) ;
2017-12-24 03:17:48 +01:00
}
}
// HARDCODED
List < StringName > hardcoded_enums ;
hardcoded_enums . push_back ( " Vector3.Axis " ) ;
for ( List < StringName > : : Element * E = hardcoded_enums . front ( ) ; E ; E = E - > next ( ) ) {
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
2018-04-28 22:25:25 +02:00
// Here, we assume core types do not begin with underscore
2017-12-24 03:17:48 +01:00
TypeInterface enum_itype ;
2018-04-28 22:25:25 +02:00
enum_itype . is_enum = true ;
enum_itype . name = E - > get ( ) . operator String ( ) ;
enum_itype . cname = E - > get ( ) ;
enum_itype . proxy_name = enum_itype . name ;
TypeInterface : : postsetup_enum_type ( enum_itype ) ;
2017-12-24 03:17:48 +01:00
enum_types . insert ( enum_itype . cname , enum_itype ) ;
}
}
2019-04-25 15:45:12 +02:00
void BindingsGenerator : : _log ( const char * p_format , . . . ) {
if ( log_print_enabled ) {
va_list list ;
va_start ( list , p_format ) ;
OS : : get_singleton ( ) - > print ( " %s " , str_format ( p_format , list ) . utf8 ( ) . get_data ( ) ) ;
va_end ( list ) ;
}
}
void BindingsGenerator : : _initialize ( ) {
2017-10-02 23:24:00 +02:00
EditorHelp : : generate_doc ( ) ;
2017-12-24 03:17:48 +01:00
enum_types . clear ( ) ;
2017-10-02 23:24:00 +02:00
_populate_object_type_interfaces ( ) ;
_populate_builtin_type_interfaces ( ) ;
2017-12-24 03:17:48 +01:00
_populate_global_constants ( ) ;
2018-09-04 05:40:41 +02:00
// Generate internal calls (after populating type interfaces and global constants)
2017-12-24 03:17:48 +01:00
2018-09-04 05:40:41 +02:00
core_custom_icalls . clear ( ) ;
editor_custom_icalls . clear ( ) ;
2017-10-02 23:24:00 +02:00
2018-01-09 19:06:59 +01:00
for ( OrderedHashMap < StringName , TypeInterface > : : Element E = obj_types . front ( ) ; E ; E = E . next ( ) )
_generate_method_icalls ( E . get ( ) ) ;
2017-10-02 23:24:00 +02:00
}
void BindingsGenerator : : handle_cmdline_args ( const List < String > & p_cmdline_args ) {
2018-11-08 01:05:19 +01:00
const int NUM_OPTIONS = 2 ;
2017-10-02 23:24:00 +02:00
String mono_glue_option = " --generate-mono-glue " ;
2018-11-08 01:05:19 +01:00
String cs_api_option = " --generate-cs-api " ;
2017-10-02 23:24:00 +02:00
2019-04-25 15:45:12 +02:00
String mono_glue_path ;
String cs_api_path ;
int options_left = NUM_OPTIONS ;
2017-10-02 23:24:00 +02:00
const List < String > : : Element * elem = p_cmdline_args . front ( ) ;
2017-10-24 16:18:47 +02:00
while ( elem & & options_left ) {
2017-10-02 23:24:00 +02:00
if ( elem - > get ( ) = = mono_glue_option ) {
const List < String > : : Element * path_elem = elem - > next ( ) ;
if ( path_elem ) {
2019-04-25 15:45:12 +02:00
mono_glue_path = path_elem - > get ( ) ;
2017-10-02 23:24:00 +02:00
elem = elem - > next ( ) ;
} else {
2018-11-08 01:05:19 +01:00
ERR_PRINTS ( mono_glue_option + " : No output directory specified " ) ;
2017-10-02 23:24:00 +02:00
}
2017-10-24 16:18:47 +02:00
- - options_left ;
2018-11-08 01:05:19 +01:00
} else if ( elem - > get ( ) = = cs_api_option ) {
2017-10-02 23:24:00 +02:00
const List < String > : : Element * path_elem = elem - > next ( ) ;
if ( path_elem ) {
2019-04-25 15:45:12 +02:00
cs_api_path = path_elem - > get ( ) ;
2017-10-02 23:24:00 +02:00
elem = elem - > next ( ) ;
} else {
2018-11-08 01:05:19 +01:00
ERR_PRINTS ( cs_api_option + " : No output directory specified " ) ;
2017-10-02 23:24:00 +02:00
}
2017-10-24 16:18:47 +02:00
- - options_left ;
2017-10-02 23:24:00 +02:00
}
elem = elem - > next ( ) ;
}
2019-04-25 15:45:12 +02:00
if ( mono_glue_path . length ( ) | | cs_api_path . length ( ) ) {
BindingsGenerator bindings_generator ;
bindings_generator . set_log_print_enabled ( true ) ;
2017-10-24 16:18:47 +02:00
2019-04-25 15:45:12 +02:00
if ( mono_glue_path . length ( ) ) {
if ( bindings_generator . generate_glue ( mono_glue_path ) ! = OK )
ERR_PRINTS ( mono_glue_option + " : Failed to generate mono glue " ) ;
}
if ( cs_api_path . length ( ) ) {
if ( bindings_generator . generate_cs_api ( cs_api_path ) ! = OK )
ERR_PRINTS ( cs_api_option + " : Failed to generate the C# API " ) ;
}
// Exit once done
2018-11-08 01:05:19 +01:00
: : exit ( 0 ) ;
2019-04-25 15:45:12 +02:00
}
2017-10-02 23:24:00 +02:00
}
# endif