Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
godot
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
community
godot
Commits
9fa4b402
Commit
9fa4b402
authored
May 16, 2020
by
Ignacio Etcheverry
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added tests for ClassDB
parent
6815bf42
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1344 additions
and
218 deletions
+1344
-218
test_class_db.cpp
main/tests/test_class_db.cpp
+882
-0
test_class_db.h
main/tests/test_class_db.h
+42
-0
test_main.cpp
main/tests/test_main.cpp
+6
-0
bindings_generator.cpp
modules/mono/editor/bindings_generator.cpp
+333
-159
bindings_generator.h
modules/mono/editor/bindings_generator.h
+81
-59
No files found.
main/tests/test_class_db.cpp
0 → 100644
View file @
9fa4b402
/*************************************************************************/
/* test_class_db.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#include "test_class_db.h"
#include "core/global_constants.h"
#include "core/ordered_hash_map.h"
#include "core/os/os.h"
#include "core/string_name.h"
#include "core/ustring.h"
#include "core/variant.h"
namespace
TestClassDB
{
enum
class
[[
nodiscard
]]
TestResult
{
FAILED
,
PASS
};
#define TEST_FAIL_COND_FATAL(m_cond, m_msg) \
if (unlikely(m_cond)) { \
ERR_PRINT(m_msg); \
return TestResult::FAILED; \
} else \
((void)0)
#define TEST_FAIL_COND(m_cond, m_msg) \
if (unlikely(m_cond)) { \
ERR_PRINT(m_msg); \
__test_result__ = TestResult::FAILED; \
} else \
((void)0)
#define TEST_CHECK_FATAL(m_test_expr) \
if (unlikely((m_test_expr) == TestResult::FAILED)) { \
return TestResult::FAILED; \
} else \
((void)0)
#define TEST_CHECK(m_test_expr) \
if (unlikely((m_test_expr) == TestResult::FAILED)) { \
__test_result__ = TestResult::FAILED; \
} else \
((void)0)
#define TEST_START() \
TestResult __test_result__ = TestResult::PASS; \
((void)0)
#define TEST_END() return __test_result__;
struct
TypeReference
{
StringName
name
;
bool
is_enum
=
false
;
};
struct
ConstantData
{
String
name
;
int
value
=
0
;
};
struct
EnumData
{
StringName
name
;
List
<
ConstantData
>
constants
;
_FORCE_INLINE_
bool
operator
==
(
const
EnumData
&
p_enum
)
const
{
return
p_enum
.
name
==
name
;
}
};
struct
PropertyData
{
StringName
name
;
int
index
=
0
;
StringName
getter
;
StringName
setter
;
};
struct
ArgumentData
{
TypeReference
type
;
String
name
;
bool
has_defval
=
false
;
Variant
defval
;
};
struct
MethodData
{
StringName
name
;
TypeReference
return_type
;
List
<
ArgumentData
>
arguments
;
bool
is_virtual
=
false
;
bool
is_vararg
=
false
;
};
struct
SignalData
{
StringName
name
;
List
<
ArgumentData
>
arguments
;
};
struct
ExposedClass
{
StringName
name
;
StringName
base
;
bool
is_singleton
=
false
;
bool
is_instantiable
=
false
;
bool
is_reference
=
false
;
ClassDB
::
APIType
api_type
;
List
<
ConstantData
>
constants
;
List
<
EnumData
>
enums
;
List
<
PropertyData
>
properties
;
List
<
MethodData
>
methods
;
List
<
SignalData
>
signals_
;
const
PropertyData
*
find_property_by_name
(
const
StringName
&
p_name
)
const
{
for
(
const
List
<
PropertyData
>::
Element
*
E
=
properties
.
front
();
E
;
E
=
E
->
next
())
{
if
(
E
->
get
().
name
==
p_name
)
{
return
&
E
->
get
();
}
}
return
nullptr
;
}
const
MethodData
*
find_method_by_name
(
const
StringName
&
p_name
)
const
{
for
(
const
List
<
MethodData
>::
Element
*
E
=
methods
.
front
();
E
;
E
=
E
->
next
())
{
if
(
E
->
get
().
name
==
p_name
)
{
return
&
E
->
get
();
}
}
return
nullptr
;
}
};
struct
NamesCache
{
StringName
variant_type
=
StaticCString
::
create
(
"Variant"
);
StringName
object_class
=
StaticCString
::
create
(
"Object"
);
StringName
reference_class
=
StaticCString
::
create
(
"Reference"
);
StringName
string_type
=
StaticCString
::
create
(
"String"
);
StringName
string_name_type
=
StaticCString
::
create
(
"StringName"
);
StringName
node_path_type
=
StaticCString
::
create
(
"NodePath"
);
StringName
bool_type
=
StaticCString
::
create
(
"bool"
);
StringName
int_type
=
StaticCString
::
create
(
"int"
);
StringName
float_type
=
StaticCString
::
create
(
"float"
);
StringName
void_type
=
StaticCString
::
create
(
"void"
);
StringName
vararg_stub_type
=
StaticCString
::
create
(
"@VarArg@"
);
StringName
vector2_type
=
StaticCString
::
create
(
"Vector2"
);
StringName
rect2_type
=
StaticCString
::
create
(
"Rect2"
);
StringName
vector3_type
=
StaticCString
::
create
(
"Vector3"
);
// Object not included as it must be checked for all derived classes
static
constexpr
int
nullable_types_count
=
17
;
StringName
nullable_types
[
nullable_types_count
]
=
{
string_type
,
string_name_type
,
node_path_type
,
StaticCString
::
create
(
_STR
(
Array
)),
StaticCString
::
create
(
_STR
(
Dictionary
)),
StaticCString
::
create
(
_STR
(
Callable
)),
StaticCString
::
create
(
_STR
(
Signal
)),
StaticCString
::
create
(
_STR
(
PackedByteArray
)),
StaticCString
::
create
(
_STR
(
PackedInt32Array
)),
StaticCString
::
create
(
_STR
(
PackedInt64rray
)),
StaticCString
::
create
(
_STR
(
PackedFloat32Array
)),
StaticCString
::
create
(
_STR
(
PackedFloat64Array
)),
StaticCString
::
create
(
_STR
(
PackedStringArray
)),
StaticCString
::
create
(
_STR
(
PackedVector2Array
)),
StaticCString
::
create
(
_STR
(
PackedVector3Array
)),
StaticCString
::
create
(
_STR
(
PackedColorArray
)),
};
bool
is_nullable_type
(
const
StringName
&
p_type
)
const
{
for
(
int
i
=
0
;
i
<
nullable_types_count
;
i
++
)
{
if
(
p_type
==
nullable_types
[
i
])
{
return
true
;
}
}
return
false
;
}
};
typedef
OrderedHashMap
<
StringName
,
ExposedClass
>
ExposedClasses
;
struct
Context
{
Vector
<
StringName
>
enum_types
;
Vector
<
StringName
>
builtin_types
;
ExposedClasses
exposed_classes
;
List
<
EnumData
>
global_enums
;
NamesCache
names_cache
;
const
ExposedClass
*
find_exposed_class
(
const
StringName
&
p_name
)
const
{
ExposedClasses
::
ConstElement
elem
=
exposed_classes
.
find
(
p_name
);
return
elem
?
&
elem
.
value
()
:
nullptr
;
}
const
ExposedClass
*
find_exposed_class
(
const
TypeReference
&
p_type_ref
)
const
{
ExposedClasses
::
ConstElement
elem
=
exposed_classes
.
find
(
p_type_ref
.
name
);
return
elem
?
&
elem
.
value
()
:
nullptr
;
}
bool
has_type
(
const
Context
&
p_context
,
const
TypeReference
&
p_type_ref
)
const
{
if
(
p_context
.
builtin_types
.
find
(
p_type_ref
.
name
)
>=
0
)
{
return
true
;
}
if
(
p_type_ref
.
is_enum
)
{
if
(
p_context
.
enum_types
.
find
(
p_type_ref
.
name
)
>=
0
)
{
return
true
;
}
// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
return
p_context
.
builtin_types
.
find
(
p_context
.
names_cache
.
int_type
);
}
return
false
;
}
};
bool
arg_default_value_is_assignable_to_type
(
const
Context
&
p_context
,
const
Variant
&
p_val
,
const
TypeReference
&
p_arg_type
)
{
if
(
p_arg_type
.
name
==
p_context
.
names_cache
.
variant_type
)
{
// Variant can take anything
return
true
;
}
switch
(
p_val
.
get_type
())
{
case
Variant
:
:
NIL
:
return
p_context
.
find_exposed_class
(
p_arg_type
)
||
p_context
.
names_cache
.
is_nullable_type
(
p_arg_type
.
name
);
case
Variant
:
:
BOOL
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
bool_type
;
case
Variant
:
:
INT
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
int_type
||
p_arg_type
.
name
==
p_context
.
names_cache
.
float_type
||
p_arg_type
.
is_enum
;
case
Variant
:
:
FLOAT
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
float_type
;
case
Variant
:
:
STRING
:
case
Variant
:
:
STRING_NAME
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
string_type
||
p_arg_type
.
name
==
p_context
.
names_cache
.
string_name_type
||
p_arg_type
.
name
==
p_context
.
names_cache
.
node_path_type
;
case
Variant
:
:
NODE_PATH
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
node_path_type
;
case
Variant
:
:
TRANSFORM
:
case
Variant
:
:
TRANSFORM2D
:
case
Variant
:
:
BASIS
:
case
Variant
:
:
QUAT
:
case
Variant
:
:
PLANE
:
case
Variant
:
:
AABB
:
case
Variant
:
:
COLOR
:
case
Variant
:
:
VECTOR2
:
case
Variant
:
:
RECT2
:
case
Variant
:
:
VECTOR3
:
case
Variant
:
:
_RID
:
case
Variant
:
:
ARRAY
:
case
Variant
:
:
DICTIONARY
:
case
Variant
:
:
PACKED_BYTE_ARRAY
:
case
Variant
:
:
PACKED_INT32_ARRAY
:
case
Variant
:
:
PACKED_INT64_ARRAY
:
case
Variant
:
:
PACKED_FLOAT32_ARRAY
:
case
Variant
:
:
PACKED_FLOAT64_ARRAY
:
case
Variant
:
:
PACKED_STRING_ARRAY
:
case
Variant
:
:
PACKED_VECTOR2_ARRAY
:
case
Variant
:
:
PACKED_VECTOR3_ARRAY
:
case
Variant
:
:
PACKED_COLOR_ARRAY
:
case
Variant
:
:
CALLABLE
:
case
Variant
:
:
SIGNAL
:
return
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
case
Variant
:
:
OBJECT
:
return
p_context
.
find_exposed_class
(
p_arg_type
);
case
Variant
:
:
VECTOR2I
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
vector2_type
||
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
case
Variant
:
:
RECT2I
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
rect2_type
||
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
case
Variant
:
:
VECTOR3I
:
return
p_arg_type
.
name
==
p_context
.
names_cache
.
vector3_type
||
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
default:
ERR_PRINT
(
"Unexpected Variant type: "
+
itos
(
p_val
.
get_type
()));
break
;
}
return
false
;
}
TestResult
validate_property
(
const
Context
&
p_context
,
const
ExposedClass
&
p_class
,
const
PropertyData
&
p_prop
)
{
TEST_START
();
const
MethodData
*
setter
=
p_class
.
find_method_by_name
(
p_prop
.
setter
);
// Search it in base classes too
const
ExposedClass
*
top
=
&
p_class
;
while
(
!
setter
&&
top
->
base
!=
StringName
())
{
top
=
p_context
.
find_exposed_class
(
top
->
base
);
TEST_FAIL_COND
(
!
top
,
"Class not found '"
+
top
->
base
+
"'. Inherited by '"
+
top
->
name
+
"'."
);
setter
=
top
->
find_method_by_name
(
p_prop
.
setter
);
}
const
MethodData
*
getter
=
p_class
.
find_method_by_name
(
p_prop
.
getter
);
// Search it in base classes too
top
=
&
p_class
;
while
(
!
getter
&&
top
->
base
!=
StringName
())
{
top
=
p_context
.
find_exposed_class
(
top
->
base
);
TEST_FAIL_COND
(
!
top
,
"Class not found '"
+
top
->
base
+
"'. Inherited by '"
+
top
->
name
+
"'."
);
getter
=
top
->
find_method_by_name
(
p_prop
.
getter
);
}
TEST_FAIL_COND
(
!
setter
&&
!
getter
,
"Couldn't find neither the setter nor the getter for property: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
if
(
setter
)
{
int
setter_argc
=
p_prop
.
index
!=
-
1
?
2
:
1
;
TEST_FAIL_COND
(
setter
->
arguments
.
size
()
!=
setter_argc
,
"Invalid property setter argument count: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
}
if
(
getter
)
{
int
getter_argc
=
p_prop
.
index
!=
-
1
?
1
:
0
;
TEST_FAIL_COND
(
getter
->
arguments
.
size
()
!=
getter_argc
,
"Invalid property setter argument count: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
}
if
(
getter
&&
setter
)
{
const
ArgumentData
&
setter_first_arg
=
setter
->
arguments
.
back
()
->
get
();
if
(
getter
->
return_type
.
name
!=
setter_first_arg
.
type
.
name
)
{
// Special case for Node::set_name
bool
whitelisted
=
getter
->
return_type
.
name
==
p_context
.
names_cache
.
string_name_type
&&
setter_first_arg
.
type
.
name
==
p_context
.
names_cache
.
string_type
;
TEST_FAIL_COND
(
!
whitelisted
,
"Return type from getter doesn't match first argument of setter, for property: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
}
}
const
TypeReference
&
prop_type_ref
=
getter
?
getter
->
return_type
:
setter
->
arguments
.
back
()
->
get
().
type
;
const
ExposedClass
*
prop_class
=
p_context
.
find_exposed_class
(
prop_type_ref
);
if
(
prop_class
)
{
TEST_FAIL_COND
(
prop_class
->
is_singleton
,
"Property type is a singleton: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
}
else
{
TEST_FAIL_COND
(
!
p_context
.
has_type
(
p_context
,
prop_type_ref
),
"Property type '"
+
prop_type_ref
.
name
+
"' not found: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
}
if
(
getter
)
{
if
(
p_prop
.
index
!=
-
1
)
{
const
ArgumentData
&
idx_arg
=
getter
->
arguments
.
front
()
->
get
();
if
(
idx_arg
.
type
.
name
!=
p_context
.
names_cache
.
int_type
)
{
// If not an int, it can be an enum
TEST_FAIL_COND
(
p_context
.
enum_types
.
find
(
idx_arg
.
type
.
name
)
<
0
,
"Invalid type '"
+
idx_arg
.
type
.
name
+
"' for index argument of property getter: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
}
}
}
if
(
setter
)
{
if
(
p_prop
.
index
!=
-
1
)
{
const
ArgumentData
&
idx_arg
=
setter
->
arguments
.
front
()
->
get
();
if
(
idx_arg
.
type
.
name
!=
p_context
.
names_cache
.
int_type
)
{
// Assume the index parameter is an enum
// If not an int, it can be an enum
TEST_FAIL_COND
(
p_context
.
enum_types
.
find
(
idx_arg
.
type
.
name
)
<
0
,
"Invalid type '"
+
idx_arg
.
type
.
name
+
"' for index argument of property setter: '"
+
p_class
.
name
+
"."
+
String
(
p_prop
.
name
)
+
"'."
);
}
}
}
TEST_END
();
}
TestResult
validate_method
(
const
Context
&
p_context
,
const
ExposedClass
&
p_class
,
const
MethodData
&
p_method
)
{
TEST_START
();
const
ExposedClass
*
return_class
=
p_context
.
find_exposed_class
(
p_method
.
return_type
);
if
(
return_class
)
{
TEST_FAIL_COND
(
return_class
->
is_singleton
,
"Method return type is a singleton: '"
+
p_class
.
name
+
"."
+
p_method
.
name
+
"'."
);
}
for
(
const
List
<
ArgumentData
>::
Element
*
F
=
p_method
.
arguments
.
front
();
F
;
F
=
F
->
next
())
{
const
ArgumentData
&
arg
=
F
->
get
();
const
ExposedClass
*
arg_class
=
p_context
.
find_exposed_class
(
arg
.
type
);
if
(
arg_class
)
{
TEST_FAIL_COND
(
arg_class
->
is_singleton
,
"Argument type is a singleton: '"
+
arg
.
name
+
"' of method '"
+
p_class
.
name
+
"."
+
p_method
.
name
+
"'."
);
}
else
{
TEST_FAIL_COND
(
!
p_context
.
has_type
(
p_context
,
arg
.
type
),
"Argument type '"
+
arg
.
type
.
name
+
"' not found: '"
+
arg
.
name
+
"' of method"
+
p_class
.
name
+
"."
+
p_method
.
name
+
"'."
);
}
if
(
arg
.
has_defval
)
{
TEST_FAIL_COND
(
!
arg_default_value_is_assignable_to_type
(
p_context
,
arg
.
defval
,
arg
.
type
),
"Invalid default value for parameter '"
+
arg
.
name
+
"' of method '"
+
p_class
.
name
+
"."
+
p_method
.
name
+
"'."
);
}
}
TEST_END
();
}
TestResult
validate_signal
(
const
Context
&
p_context
,
const
ExposedClass
&
p_class
,
const
SignalData
&
p_signal
)
{
TEST_START
();
for
(
const
List
<
ArgumentData
>::
Element
*
F
=
p_signal
.
arguments
.
front
();
F
;
F
=
F
->
next
())
{
const
ArgumentData
&
arg
=
F
->
get
();
const
ExposedClass
*
arg_class
=
p_context
.
find_exposed_class
(
arg
.
type
);
if
(
arg_class
)
{
TEST_FAIL_COND
(
arg_class
->
is_singleton
,
"Argument class is a singleton: '"
+
arg
.
name
+
"' of signal"
+
p_class
.
name
+
"."
+
p_signal
.
name
+
"'."
);
}
else
{
TEST_FAIL_COND
(
!
p_context
.
has_type
(
p_context
,
arg
.
type
),
"Argument type '"
+
arg
.
type
.
name
+
"' not found: '"
+
arg
.
name
+
"' of signal"
+
p_class
.
name
+
"."
+
p_signal
.
name
+
"'."
);
}
}
TEST_END
();
}
TestResult
validate_class
(
const
Context
&
p_context
,
const
ExposedClass
&
p_exposed_class
)
{
TEST_START
();
bool
is_derived_type
=
p_exposed_class
.
base
!=
StringName
();
if
(
!
is_derived_type
)
{
// Asserts about the base Object class
TEST_FAIL_COND_FATAL
(
p_exposed_class
.
name
!=
p_context
.
names_cache
.
object_class
,
"Class '"
+
p_exposed_class
.
name
+
"' has no base class."
);
TEST_FAIL_COND_FATAL
(
!
p_exposed_class
.
is_instantiable
,
"Object class is not instantiable."
);
TEST_FAIL_COND_FATAL
(
p_exposed_class
.
api_type
!=
ClassDB
::
API_CORE
,
"Object class is API is not API_CORE."
);
TEST_FAIL_COND_FATAL
(
p_exposed_class
.
is_singleton
,
"Object class is registered as a singleton."
);
}
CRASH_COND_MSG
(
p_exposed_class
.
is_singleton
&&
p_exposed_class
.
base
!=
p_context
.
names_cache
.
object_class
,
"Singleton base class '"
+
String
(
p_exposed_class
.
base
)
+
"' is not Object, for class '"
+
p_exposed_class
.
name
+
"'."
);
CRASH_COND_MSG
(
is_derived_type
&&
!
p_context
.
exposed_classes
.
has
(
p_exposed_class
.
base
),
"Base type '"
+
p_exposed_class
.
base
.
operator
String
()
+
"' does not exist, for class '"
+
p_exposed_class
.
name
+
"'."
);
for
(
const
List
<
PropertyData
>::
Element
*
F
=
p_exposed_class
.
properties
.
front
();
F
;
F
=
F
->
next
())
{
TEST_CHECK
(
validate_property
(
p_context
,
p_exposed_class
,
F
->
get
()));
}
for
(
const
List
<
MethodData
>::
Element
*
F
=
p_exposed_class
.
methods
.
front
();
F
;
F
=
F
->
next
())
{
TEST_CHECK
(
validate_method
(
p_context
,
p_exposed_class
,
F
->
get
()));
}
for
(
const
List
<
SignalData
>::
Element
*
F
=
p_exposed_class
.
signals_
.
front
();
F
;
F
=
F
->
next
())
{
TEST_CHECK
(
validate_signal
(
p_context
,
p_exposed_class
,
F
->
get
()));
}
TEST_END
();
}
TestResult
add_exposed_classes
(
Context
&
r_context
)
{
TEST_START
();
List
<
StringName
>
class_list
;
ClassDB
::
get_class_list
(
&
class_list
);
class_list
.
sort_custom
<
StringName
::
AlphCompare
>
();
while
(
class_list
.
size
())
{
StringName
class_name
=
class_list
.
front
()
->
get
();
ClassDB
::
APIType
api_type
=
ClassDB
::
get_api_type
(
class_name
);
if
(
api_type
==
ClassDB
::
API_NONE
)
{
class_list
.
pop_front
();
continue
;
}
if
(
!
ClassDB
::
is_class_exposed
(
class_name
))
{
OS
::
get_singleton
()
->
print
(
"Ignoring class '%s' because it's not exposed
\n
"
,
String
(
class_name
).
utf8
().
get_data
());
class_list
.
pop_front
();
continue
;
}
if
(
!
ClassDB
::
is_class_enabled
(
class_name
))
{
OS
::
get_singleton
()
->
print
(
"Ignoring class '%s' because it's not enabled
\n
"
,
String
(
class_name
).
utf8
().
get_data
());
class_list
.
pop_front
();
continue
;
}
ClassDB
::
ClassInfo
*
class_info
=
ClassDB
::
classes
.
getptr
(
class_name
);
ExposedClass
exposed_class
;
exposed_class
.
name
=
class_name
;
exposed_class
.
api_type
=
api_type
;
exposed_class
.
is_singleton
=
Engine
::
get_singleton
()
->
has_singleton
(
class_name
);
exposed_class
.
is_instantiable
=
class_info
->
creation_func
&&
!
exposed_class
.
is_singleton
;
exposed_class
.
is_reference
=
ClassDB
::
is_parent_class
(
class_name
,
"Reference"
);
exposed_class
.
base
=
ClassDB
::
get_parent_class
(
class_name
);
// Add properties
List
<
PropertyInfo
>
property_list
;
ClassDB
::
get_property_list
(
class_name
,
&
property_list
,
true
);
Map
<
StringName
,
StringName
>
accessor_methods
;
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_SUBGROUP
||
property
.
usage
&
PROPERTY_USAGE_CATEGORY
)
{
continue
;
}
PropertyData
prop
;
prop
.
name
=
property
.
name
;
prop
.
setter
=
ClassDB
::
get_property_setter
(
class_name
,
prop
.
name
);
prop
.
getter
=
ClassDB
::
get_property_getter
(
class_name
,
prop
.
name
);
if
(
prop
.
setter
!=
StringName
())
{
accessor_methods
[
prop
.
setter
]
=
prop
.
name
;
}
if
(
prop
.
getter
!=
StringName
())
{
accessor_methods
[
prop
.
getter
]
=
prop
.
name
;
}
bool
valid
=
false
;
prop
.
index
=
ClassDB
::
get_property_index
(
class_name
,
prop
.
name
,
&
valid
);
TEST_FAIL_COND
(
!
valid
,
"Invalid property: '"
+
exposed_class
.
name
+
"."
+
String
(
prop
.
name
)
+
"'."
);
exposed_class
.
properties
.
push_back
(
prop
);
}
// Add methods
List
<
MethodInfo
>
virtual_method_list
;
ClassDB
::
get_virtual_methods
(
class_name
,
&
virtual_method_list
,
true
);
List
<
MethodInfo
>
method_list
;
ClassDB
::
get_method_list
(
class_name
,
&
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
;
}
MethodData
method
;
method
.
name
=
method_info
.
name
;
if
(
method_info
.
flags
&
METHOD_FLAG_VIRTUAL
)
{
method
.
is_virtual
=
true
;
}
PropertyInfo
return_info
=
method_info
.
return_val
;
MethodBind
*
m
=
method
.
is_virtual
?
nullptr
:
ClassDB
::
get_method
(
class_name
,
method_info
.
name
);
method
.
is_vararg
=
m
&&
m
->
is_vararg
();
if
(
!
m
&&
!
method
.
is_virtual
)
{
TEST_FAIL_COND
(
!
virtual_method_list
.
find
(
method_info
),
"Missing MethodBind for non-virtual method: '"
+
exposed_class
.
name
+
"."
+
method
.
name
+
"'."
);
// A virtual method without the virtual flag. This is a special case.
// 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.
method
.
return_type
.
name
=
r_context
.
names_cache
.
void_type
;
// Actually, more methods like this may be added in the future, which could return
// something different. Let's put this check to notify us if that ever happens.
if
(
exposed_class
.
name
!=
r_context
.
names_cache
.
object_class
||
String
(
method
.
name
)
!=
"free"
)
{
WARN_PRINT
(
"Notification: New unexpected virtual non-overridable method found."
" We only expected Object.free, but found '"
+
exposed_class
.
name
+
"."
+
method
.
name
+
"'."
);
}
}
else
if
(
return_info
.
type
==
Variant
::
INT
&&
return_info
.
usage
&
PROPERTY_USAGE_CLASS_IS_ENUM
)
{
method
.
return_type
.
name
=
return_info
.
class_name
;
method
.
return_type
.
is_enum
=
true
;
}
else
if
(
return_info
.
class_name
!=
StringName
())
{
method
.
return_type
.
name
=
return_info
.
class_name
;
bool
bad_reference_hint
=
!
method
.
is_virtual
&&
return_info
.
hint
!=
PROPERTY_HINT_RESOURCE_TYPE
&&
ClassDB
::
is_parent_class
(
return_info
.
class_name
,
r_context
.
names_cache
.
reference_class
);
TEST_FAIL_COND
(
bad_reference_hint
,
String
()
+
"Return type is reference but hint is not '"
_STR
(
PROPERTY_HINT_RESOURCE_TYPE
)
"'."
+
" Are you returning a reference type by pointer? Method: '"
+
exposed_class
.
name
+
"."
+
method
.
name
+
"'."
);
}
else
if
(
return_info
.
hint
==
PROPERTY_HINT_RESOURCE_TYPE
)
{
method
.
return_type
.
name
=
return_info
.
hint_string
;
}
else
if
(
return_info
.
type
==
Variant
::
NIL
&&
return_info
.
usage
&
PROPERTY_USAGE_NIL_IS_VARIANT
)
{
method
.
return_type
.
name
=
r_context
.
names_cache
.
variant_type
;
}
else
if
(
return_info
.
type
==
Variant
::
NIL
)
{
method
.
return_type
.
name
=
r_context
.
names_cache
.
void_type
;
}
else
{
// NOTE: We don't care about the size and sign of int and float in these tests
method
.
return_type
.
name
=
Variant
::
get_type_name
(
return_info
.
type
);
}
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
PropertyInfo
arg_info
=
method_info
.
arguments
[
i
];
String
orig_arg_name
=
arg_info
.
name
;
ArgumentData
arg
;
arg
.
name
=
orig_arg_name
;
if
(
arg_info
.
type
==
Variant
::
INT
&&
arg_info
.
usage
&
PROPERTY_USAGE_CLASS_IS_ENUM
)
{
arg
.
type
.
name
=
arg_info
.
class_name
;
arg
.
type
.
is_enum
=
true
;
}
else
if
(
arg_info
.
class_name
!=
StringName
())
{
arg
.
type
.
name
=
arg_info
.
class_name
;
}
else
if
(
arg_info
.
hint
==
PROPERTY_HINT_RESOURCE_TYPE
)
{
arg
.
type
.
name
=
arg_info
.
hint_string
;
}
else
if
(
arg_info
.
type
==
Variant
::
NIL
)
{
arg
.
type
.
name
=
r_context
.
names_cache
.
variant_type
;
}
else
{
// NOTE: We don't care about the size and sign of int and float in these tests
arg
.
type
.
name
=
Variant
::
get_type_name
(
arg_info
.
type
);
}
if
(
m
&&
m
->
has_default_argument
(
i
))
{
arg
.
has_defval
=
true
;
arg
.
defval
=
m
->
get_default_argument
(
i
);
}
method
.
arguments
.
push_back
(
arg
);
}
if
(
method
.
is_vararg
)
{
ArgumentData
vararg
;
vararg
.
type
.
name
=
r_context
.
names_cache
.
vararg_stub_type
;
vararg
.
name
=
"@varargs@"
;
method
.
arguments
.
push_back
(
vararg
);
}
TEST_FAIL_COND
(
exposed_class
.
find_property_by_name
(
method
.
name
),
"Method name conflicts with property: '"
+
String
(
class_name
)
+
"."
+
String
(
method
.
name
)
+
"'."
);
// Classes starting with an underscore are ignored unless they're used as a property setter or getter
if
(
!
method
.
is_virtual
&&
String
(
method
.
name
)[
0
]
==
'_'
)
{
for
(
const
List
<
PropertyData
>::
Element
*
F
=
exposed_class
.
properties
.
front
();
F
;
F
=
F
->
next
())
{
const
PropertyData
&
prop
=
F
->
get
();
if
(
prop
.
setter
==
method
.
name
||
prop
.
getter
==
method
.
name
)
{
exposed_class
.
methods
.
push_back
(
method
);
break
;
}
}
}
else
{
exposed_class
.
methods
.
push_back
(
method
);
}
}
// Add signals
const
HashMap
<
StringName
,
MethodInfo
>
&
signal_map
=
class_info
->
signal_map
;
const
StringName
*
k
=
nullptr
;
while
((
k
=
signal_map
.
next
(
k
)))
{
SignalData
signal
;
const
MethodInfo
&
method_info
=
signal_map
.
get
(
*
k
);
signal
.
name
=
method_info
.
name
;
int
argc
=
method_info
.
arguments
.
size
();
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
PropertyInfo
arg_info
=
method_info
.
arguments
[
i
];
String
orig_arg_name
=
arg_info
.
name
;
ArgumentData
arg
;
arg
.
name
=
orig_arg_name
;
if
(
arg_info
.
type
==
Variant
::
INT
&&
arg_info
.
usage
&
PROPERTY_USAGE_CLASS_IS_ENUM
)
{
arg
.
type
.
name
=
arg_info
.
class_name
;
arg
.
type
.
is_enum
=
true
;
}
else
if
(
arg_info
.
class_name
!=
StringName
())
{
arg
.
type
.
name
=
arg_info
.
class_name
;
}
else
if
(
arg_info
.
hint
==
PROPERTY_HINT_RESOURCE_TYPE
)
{
arg
.
type
.
name
=
arg_info
.
hint_string
;
}
else
if
(
arg_info
.
type
==
Variant
::
NIL
)
{
arg
.
type
.
name
=
r_context
.
names_cache
.
variant_type
;
}
else
{
// NOTE: We don't care about the size and sign of int and float in these tests
arg
.
type
.
name
=
Variant
::
get_type_name
(
arg_info
.
type
);
}
signal
.
arguments
.
push_back
(
arg
);
}
bool
method_conflict
=
exposed_class
.
find_property_by_name
(
signal
.
name
);
if
(
method_conflict
||
exposed_class
.
find_method_by_name
(
signal
.
name
))
{
// TODO:
// ClassDB allows signal names that conflict with method or property names.
// However registering a signal with a conflicting name is still considered wrong.
// Unfortunately there are some existing cases that are yet to be fixed.
// Until those are fixed we will print a warning instead of failing the test.
WARN_PRINT
(
"Signal name conflicts with "
+
String
(
method_conflict
?
"method"
:
"property"
)
+
": '"
+
String
(
class_name
)
+
"."
+
String
(
signal
.
name
)
+
"'."
);
}
exposed_class
.
signals_
.
push_back
(
signal
);
}
// Add enums and constants
List
<
String
>
constants
;
ClassDB
::
get_integer_constant_list
(
class_name
,
&
constants
,
true
);
const
HashMap
<
StringName
,
List
<
StringName
>>
&
enum_map
=
class_info
->
enum_map
;
k
=
nullptr
;
while
((
k
=
enum_map
.
next
(
k
)))
{
EnumData
enum_
;
enum_
.
name
=
*
k
;
const
List
<
StringName
>
&
enum_constants
=
enum_map
.
get
(
*
k
);
for
(
const
List
<
StringName
>::
Element
*
E
=
enum_constants
.
front
();
E
;
E
=
E
->
next
())
{
const
StringName
&
constant_name
=
E
->
get
();
int
*
value
=
class_info
->
constant_map
.
getptr
(
constant_name
);
TEST_FAIL_COND
(
!
value
,
"Missing enum constant value: '"
+
String
(
class_name
)
+
"."
+
String
(
enum_
.
name
)
+
"."
+
String
(
constant_name
)
+
"'."
);
constants
.
erase
(
constant_name
);
ConstantData
constant
;
constant
.
name
=
constant_name
;
constant
.
value
=
*
value
;
enum_
.
constants
.
push_back
(
constant
);
}
exposed_class
.
enums
.
push_back
(
enum_
);
r_context
.
enum_types
.
push_back
(
String
(
class_name
)
+
"."
+
String
(
*
k
));
}
for
(
const
List
<
String
>::
Element
*
E
=
constants
.
front
();
E
;
E
=
E
->
next
())
{
const
String
&
constant_name
=
E
->
get
();
int
*
value
=
class_info
->
constant_map
.
getptr
(
StringName
(
E
->
get
()));
TEST_FAIL_COND
(
!
value
,
"Missing enum constant value: '"
+
String
(
class_name
)
+
"."
+
String
(
constant_name
)
+
"'."
);
ConstantData
constant
;
constant
.
name
=
constant_name
;
constant
.
value
=
*
value
;
exposed_class
.
constants
.
push_back
(
constant
);
}
r_context
.
exposed_classes
.
insert
(
class_name
,
exposed_class
);
class_list
.
pop_front
();
}
TEST_END
();
}
void
add_builtin_types
(
Context
&
r_context
)
{
// NOTE: We don't care about the size and sign of int and float in these tests
for
(
int
i
=
0
;
i
<
Variant
::
VARIANT_MAX
;
i
++
)
{
r_context
.
builtin_types
.
push_back
(
Variant
::
get_type_name
(
Variant
::
Type
(
i
)));
}
r_context
.
builtin_types
.
push_back
(
_STR
(
Variant
));
r_context
.
builtin_types
.
push_back
(
r_context
.
names_cache
.
vararg_stub_type
);
r_context
.
builtin_types
.
push_back
(
"void"
);
}
void
add_global_enums
(
Context
&
r_context
)
{
int
global_constants_count
=
GlobalConstants
::
get_global_constant_count
();
if
(
global_constants_count
>
0
)
{
for
(
int
i
=
0
;
i
<
global_constants_count
;
i
++
)
{
StringName
enum_name
=
GlobalConstants
::
get_global_constant_enum
(
i
);
if
(
enum_name
!=
StringName
())
{
ConstantData
constant
;
constant
.
name
=
GlobalConstants
::
get_global_constant_name
(
i
);
constant
.
value
=
GlobalConstants
::
get_global_constant_value
(
i
);
EnumData
enum_
;
enum_
.
name
=
enum_name
;
List
<
EnumData
>::
Element
*
enum_match
=
r_context
.
global_enums
.
find
(
enum_
);
if
(
enum_match
)
{
enum_match
->
get
().
constants
.
push_back
(
constant
);
}
else
{
enum_
.
constants
.
push_back
(
constant
);
r_context
.
global_enums
.
push_back
(
enum_
);
}
}
}
for
(
List
<
EnumData
>::
Element
*
E
=
r_context
.
global_enums
.
front
();
E
;
E
=
E
->
next
())
{
r_context
.
enum_types
.
push_back
(
E
->
get
().
name
);
}
}
// HARDCODED
List
<
StringName
>
hardcoded_enums
;
hardcoded_enums
.
push_back
(
"Vector2.Axis"
);
hardcoded_enums
.
push_back
(
"Vector2i.Axis"
);
hardcoded_enums
.
push_back
(
"Vector3.Axis"
);
hardcoded_enums
.
push_back
(
"Vector3i.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)
// Here, we assume core types do not begin with underscore
r_context
.
enum_types
.
push_back
(
E
->
get
());
}
}
TestResult
run_class_db_tests
()
{
TEST_START
();
Context
context
;
TEST_CHECK_FATAL
(
add_exposed_classes
(
context
));
add_builtin_types
(
context
);
add_global_enums
(
context
);
const
ExposedClass
*
object_class
=
context
.
find_exposed_class
(
context
.
names_cache
.
object_class
);
TEST_FAIL_COND_FATAL
(
!
object_class
,
"Object class not found."
);
TEST_FAIL_COND_FATAL
(
object_class
->
base
!=
StringName
(),
"Object class derives from another class: '"
+
object_class
->
base
+
"'."
);
for
(
ExposedClasses
::
Element
E
=
context
.
exposed_classes
.
front
();
E
;
E
=
E
.
next
())
{
TEST_CHECK
(
validate_class
(
context
,
E
.
value
()));
}
TEST_END
();
}
MainLoop
*
test
()
{
TestResult
pass
=
run_class_db_tests
();
OS
::
get_singleton
()
->
print
(
"ClassDB tests: %s
\n
"
,
pass
==
TestResult
::
PASS
?
"PASS"
:
"FAILED"
);
if
(
pass
==
TestResult
::
FAILED
)
{
OS
::
get_singleton
()
->
set_exit_code
(
pass
==
TestResult
::
PASS
?
0
:
1
);
}
return
nullptr
;
}
}
// namespace TestClassDB
main/tests/test_class_db.h
0 → 100644
View file @
9fa4b402
/*************************************************************************/
/* test_class_db.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef GODOT_TEST_CLASS_DB_H
#define GODOT_TEST_CLASS_DB_H
#include "core/os/main_loop.h"
namespace
TestClassDB
{
MainLoop
*
test
();
}
#endif //GODOT_TEST_CLASS_DB_H
main/tests/test_main.cpp
View file @
9fa4b402
...
...
@@ -35,6 +35,7 @@
#ifdef DEBUG_ENABLED
#include "test_astar.h"
#include "test_class_db.h"
#include "test_gdscript.h"
#include "test_gui.h"
#include "test_math.h"
...
...
@@ -54,6 +55,7 @@ const char **tests_get_names() {
"physics_3d"
,
"render"
,
"oa_hash_map"
,
"class_db"
,
"gui"
,
"shaderlang"
,
"gd_tokenizer"
,
...
...
@@ -93,6 +95,10 @@ MainLoop *test_main(String p_test, const List<String> &p_args) {
return
TestOAHashMap
::
test
();
}
if
(
p_test
==
"class_db"
)
{
return
TestClassDB
::
test
();
}
#ifndef _3D_DISABLED
if
(
p_test
==
"gui"
)
{
return
TestGUI
::
test
();
...
...
modules/mono/editor/bindings_generator.cpp
View file @
9fa4b402
...
...
@@ -123,8 +123,9 @@ static String snake_to_pascal_case(const String &p_identifier, bool p_input_is_u
if
(
part
.
length
())
{
part
[
0
]
=
_find_upper
(
part
[
0
]);
if
(
p_input_is_upper
)
{
for
(
int
j
=
1
;
j
<
part
.
length
();
j
++
)
for
(
int
j
=
1
;
j
<
part
.
length
();
j
++
)
{
part
[
j
]
=
_find_lower
(
part
[
j
]);
}
}
ret
+=
part
;
}
else
{
...
...
@@ -157,8 +158,9 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up
part
[
0
]
=
_find_upper
(
part
[
0
]);
}
if
(
p_input_is_upper
)
{
for
(
int
j
=
i
!=
0
?
1
:
0
;
j
<
part
.
length
();
j
++
)
for
(
int
j
=
i
!=
0
?
1
:
0
;
j
<
part
.
length
();
j
++
)
{
part
[
j
]
=
_find_lower
(
part
[
j
]);
}
}
ret
+=
part
;
}
else
{
...
...
@@ -182,8 +184,9 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up
String
BindingsGenerator
::
bbcode_to_xml
(
const
String
&
p_bbcode
,
const
TypeInterface
*
p_itype
)
{
// Based on the version in EditorHelp
if
(
p_bbcode
.
empty
())
if
(
p_bbcode
.
empty
())
{
return
String
();
}
DocData
*
doc
=
EditorHelp
::
get_doc_data
();
...
...
@@ -200,8 +203,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
while
(
pos
<
bbcode
.
length
())
{
int
brk_pos
=
bbcode
.
find
(
"["
,
pos
);
if
(
brk_pos
<
0
)
if
(
brk_pos
<
0
)
{
brk_pos
=
bbcode
.
length
();
}
if
(
brk_pos
>
pos
)
{
String
text
=
bbcode
.
substr
(
pos
,
brk_pos
-
pos
);
...
...
@@ -210,19 +214,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
}
else
{
Vector
<
String
>
lines
=
text
.
split
(
"
\n
"
);
for
(
int
i
=
0
;
i
<
lines
.
size
();
i
++
)
{
if
(
i
!=
0
)
if
(
i
!=
0
)
{
xml_output
.
append
(
"<para>"
);
}
xml_output
.
append
(
lines
[
i
].
xml_escape
());
if
(
i
!=
lines
.
size
()
-
1
)
if
(
i
!=
lines
.
size
()
-
1
)
{
xml_output
.
append
(
"</para>
\n
"
);
}
}
}
}
if
(
brk_pos
==
bbcode
.
length
())
if
(
brk_pos
==
bbcode
.
length
())
{
break
;
// nothing else to add
}
int
brk_end
=
bbcode
.
find
(
"]"
,
brk_pos
+
1
);
...
...
@@ -233,13 +240,15 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
}
else
{
Vector
<
String
>
lines
=
text
.
split
(
"
\n
"
);
for
(
int
i
=
0
;
i
<
lines
.
size
();
i
++
)
{
if
(
i
!=
0
)
if
(
i
!=
0
)
{
xml_output
.
append
(
"<para>"
);
}
xml_output
.
append
(
lines
[
i
].
xml_escape
());
if
(
i
!=
lines
.
size
()
-
1
)
if
(
i
!=
lines
.
size
()
-
1
)
{
xml_output
.
append
(
"</para>
\n
"
);
}
}
}
...
...
@@ -412,8 +421,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
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
)
if
(
target_iconst
)
{
break
;
}
}
if
(
target_iconst
)
{
...
...
@@ -450,8 +460,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
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
)
if
(
target_iconst
)
{
break
;
}
}
if
(
target_iconst
)
{
...
...
@@ -583,8 +594,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
tag_stack
.
push_front
(
tag
);
}
else
if
(
tag
==
"url"
)
{
int
end
=
bbcode
.
find
(
"["
,
brk_end
);
if
(
end
==
-
1
)
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
);
...
...
@@ -603,8 +615,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
tag_stack
.
push_front
(
"url"
);
}
else
if
(
tag
==
"img"
)
{
int
end
=
bbcode
.
find
(
"["
,
brk_end
);
if
(
end
==
-
1
)
if
(
end
==
-
1
)
{
end
=
bbcode
.
length
();
}
String
image
=
bbcode
.
substr
(
brk_end
+
1
,
end
-
brk_end
-
1
);
// Not supported. Just append the bbcode.
...
...
@@ -640,8 +653,9 @@ int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
Vector
<
String
>
front_parts
=
front_iconstant
.
name
.
split
(
"_"
,
/* p_allow_empty: */
true
);
int
candidate_len
=
front_parts
.
size
()
-
1
;
if
(
candidate_len
==
0
)
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
();
...
...
@@ -653,14 +667,16 @@ int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
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
)
if
(
!
hardcoded_exc
)
{
break
;
}
}
}
candidate_len
=
i
;
if
(
candidate_len
==
0
)
if
(
candidate_len
==
0
)
{
return
0
;
}
}
return
candidate_len
;
...
...
@@ -677,22 +693,25 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
Vector
<
String
>
parts
=
constant_name
.
split
(
"_"
,
/* p_allow_empty: */
true
);
if
(
parts
.
size
()
<=
curr_prefix_length
)
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'
)
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
)
if
(
i
>
curr_prefix_length
)
{
constant_name
+=
"_"
;
}
constant_name
+=
parts
[
i
];
}
...
...
@@ -705,8 +724,9 @@ 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
)
if
(
imethod
.
is_virtual
)
{
continue
;
}
const
TypeInterface
*
return_type
=
_get_type_or_placeholder
(
imethod
.
return_type
);
...
...
@@ -755,8 +775,9 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
List
<
InternalCall
>::
Element
*
match
=
method_icalls
.
find
(
im_icall
);
if
(
match
)
{
if
(
p_itype
.
api_type
!=
ClassDB
::
API_EDITOR
)
if
(
p_itype
.
api_type
!=
ClassDB
::
API_EDITOR
)
{
match
->
get
().
editor_only
=
false
;
}
method_icalls_map
.
insert
(
&
E
->
get
(),
&
match
->
get
());
}
else
{
List
<
InternalCall
>::
Element
*
added
=
method_icalls
.
push_back
(
im_icall
);
...
...
@@ -801,8 +822,9 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output
.
append
(
";"
);
}
if
(
!
global_constants
.
empty
())
if
(
!
global_constants
.
empty
())
{
p_output
.
append
(
"
\n
"
);
}
p_output
.
append
(
INDENT1
CLOSE_BLOCK
);
// end of GD class
...
...
@@ -864,8 +886,9 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output
.
append
(
INDENT1
CLOSE_BLOCK
);
if
(
enum_in_static_class
)
if
(
enum_in_static_class
)
{
p_output
.
append
(
INDENT1
CLOSE_BLOCK
);
}
}
p_output
.
append
(
CLOSE_BLOCK
);
// end of namespace
...
...
@@ -899,8 +922,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
_generate_global_constants
(
constants_source
);
String
output_file
=
path
::
join
(
base_gen_dir
,
BINDINGS_GLOBAL_SCOPE_CLASS
"_constants.cs"
);
Error
save_err
=
_save_file
(
output_file
,
constants_source
);
if
(
save_err
!=
OK
)
if
(
save_err
!=
OK
)
{
return
save_err
;
}
compile_items
.
push_back
(
output_file
);
}
...
...
@@ -908,17 +932,20 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
for
(
OrderedHashMap
<
StringName
,
TypeInterface
>::
Element
E
=
obj_types
.
front
();
E
;
E
=
E
.
next
())
{
const
TypeInterface
&
itype
=
E
.
get
();
if
(
itype
.
api_type
==
ClassDB
::
API_EDITOR
)
if
(
itype
.
api_type
==
ClassDB
::
API_EDITOR
)
{
continue
;
}
String
output_file
=
path
::
join
(
godot_objects_gen_dir
,
itype
.
proxy_name
+
".cs"
);
Error
err
=
_generate_cs_type
(
itype
,
output_file
);
if
(
err
==
ERR_SKIP
)
if
(
err
==
ERR_SKIP
)
{
continue
;
}
if
(
err
!=
OK
)
if
(
err
!=
OK
)
{
return
err
;
}
compile_items
.
push_back
(
output_file
);
}
...
...
@@ -949,10 +976,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
for
(
const
List
<
InternalCall
>::
Element
*
E
=
core_custom_icalls
.
front
();
E
;
E
=
E
->
next
())
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
())
}
for
(
const
List
<
InternalCall
>::
Element
*
E
=
method_icalls
.
front
();
E
;
E
=
E
->
next
())
{
ADD_INTERNAL_CALL
(
E
->
get
());
}
#undef ADD_INTERNAL_CALL
...
...
@@ -961,8 +990,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
String
internal_methods_file
=
path
::
join
(
base_gen_dir
,
BINDINGS_CLASS_NATIVECALLS
".cs"
);
Error
err
=
_save_file
(
internal_methods_file
,
cs_icalls_content
);
if
(
err
!=
OK
)
if
(
err
!=
OK
)
{
return
err
;
}
compile_items
.
push_back
(
internal_methods_file
);
...
...
@@ -981,8 +1011,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
String
includes_props_file
=
path
::
join
(
base_gen_dir
,
"GeneratedIncludes.props"
);
err
=
_save_file
(
includes_props_file
,
includes_props_content
);
if
(
err
!=
OK
)
if
(
err
!=
OK
)
{
return
err
;
}
return
OK
;
}
...
...
@@ -1010,17 +1041,20 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
for
(
OrderedHashMap
<
StringName
,
TypeInterface
>::
Element
E
=
obj_types
.
front
();
E
;
E
=
E
.
next
())
{
const
TypeInterface
&
itype
=
E
.
get
();
if
(
itype
.
api_type
!=
ClassDB
::
API_EDITOR
)
if
(
itype
.
api_type
!=
ClassDB
::
API_EDITOR
)
{
continue
;
}
String
output_file
=
path
::
join
(
godot_objects_gen_dir
,
itype
.
proxy_name
+
".cs"
);
Error
err
=
_generate_cs_type
(
itype
,
output_file
);
if
(
err
==
ERR_SKIP
)
if
(
err
==
ERR_SKIP
)
{
continue
;
}
if
(
err
!=
OK
)
if
(
err
!=
OK
)
{
return
err
;
}
compile_items
.
push_back
(
output_file
);
}
...
...
@@ -1050,10 +1084,12 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
for
(
const
List
<
InternalCall
>::
Element
*
E
=
editor_custom_icalls
.
front
();
E
;
E
=
E
->
next
())
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
())
}
for
(
const
List
<
InternalCall
>::
Element
*
E
=
method_icalls
.
front
();
E
;
E
=
E
->
next
())
{
ADD_INTERNAL_CALL
(
E
->
get
());
}
#undef ADD_INTERNAL_CALL
...
...
@@ -1062,8 +1098,9 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
String
internal_methods_file
=
path
::
join
(
base_gen_dir
,
BINDINGS_CLASS_NATIVECALLS_EDITOR
".cs"
);
Error
err
=
_save_file
(
internal_methods_file
,
cs_icalls_content
);
if
(
err
!=
OK
)
if
(
err
!=
OK
)
{
return
err
;
}
compile_items
.
push_back
(
internal_methods_file
);
...
...
@@ -1082,8 +1119,9 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
String
includes_props_file
=
path
::
join
(
base_gen_dir
,
"GeneratedIncludes.props"
);
err
=
_save_file
(
includes_props_file
,
includes_props_content
);
if
(
err
!=
OK
)
if
(
err
!=
OK
)
{
return
err
;
}
return
OK
;
}
...
...
@@ -1210,92 +1248,89 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output
.
append
(
INDENT1
"{"
);
if
(
class_doc
)
{
// Add constants
for
(
const
List
<
ConstantInterface
>::
Element
*
E
=
itype
.
constants
.
front
();
E
;
E
=
E
->
next
())
{
const
ConstantInterface
&
iconstant
=
E
->
get
();
// Add constants
if
(
iconstant
.
const_doc
&&
iconstant
.
const_doc
->
description
.
size
())
{
String
xml_summary
=
bbcode_to_xml
(
fix_doc_description
(
iconstant
.
const_doc
->
description
),
&
itype
);
Vector
<
String
>
summary_lines
=
xml_summary
.
length
()
?
xml_summary
.
split
(
"
\n
"
)
:
Vector
<
String
>
();
for
(
const
List
<
ConstantInterface
>::
Element
*
E
=
itype
.
constants
.
front
();
E
;
E
=
E
->
next
())
{
const
ConstantInterface
&
iconstant
=
E
->
get
();
if
(
summary_lines
.
size
())
{
output
.
append
(
MEMBER_BEGIN
"/// <summary>
\n
"
);
if
(
iconstant
.
const_doc
&&
iconstant
.
const_doc
->
description
.
size
())
{
String
xml_summary
=
bbcode_to_xml
(
fix_doc_description
(
iconstant
.
const_doc
->
description
),
&
itype
);
Vector
<
String
>
summary_lines
=
xml_summary
.
length
()
?
xml_summary
.
split
(
"
\n
"
)
:
Vector
<
String
>
();
for
(
int
i
=
0
;
i
<
summary_lines
.
size
();
i
++
)
{
output
.
append
(
INDENT2
"/// "
);
output
.
append
(
summary_lines
[
i
]);
output
.
append
(
"
\n
"
);
}
if
(
summary_lines
.
size
())
{
output
.
append
(
MEMBER_BEGIN
"/// <summary>
\n
"
);
output
.
append
(
INDENT2
"/// </summary>"
);
for
(
int
i
=
0
;
i
<
summary_lines
.
size
();
i
++
)
{
output
.
append
(
INDENT2
"/// "
);
output
.
append
(
summary_lines
[
i
]);
output
.
append
(
"
\n
"
);
}
}
output
.
append
(
MEMBER_BEGIN
"public const int "
);
output
.
append
(
iconstant
.
proxy_name
);
output
.
append
(
" = "
);
output
.
append
(
itos
(
iconstant
.
value
));
output
.
append
(
";"
);
output
.
append
(
INDENT2
"/// </summary>"
);
}
}
if
(
itype
.
constants
.
size
())
output
.
append
(
"
\n
"
);
output
.
append
(
MEMBER_BEGIN
"public const int "
);
output
.
append
(
iconstant
.
proxy_name
);
output
.
append
(
" = "
);
output
.
append
(
itos
(
iconstant
.
value
));
output
.
append
(
";"
);
}
// Add enums
if
(
itype
.
constants
.
size
())
{
output
.
append
(
"
\n
"
);
}
for
(
const
List
<
EnumInterface
>::
Element
*
E
=
itype
.
enums
.
front
();
E
;
E
=
E
->
next
())
{
const
EnumInterface
&
ienum
=
E
->
get
();
// Add enums
ERR_FAIL_COND_V
(
ienum
.
constants
.
empty
(),
ERR_BUG
);
for
(
const
List
<
EnumInterface
>::
Element
*
E
=
itype
.
enums
.
front
();
E
;
E
=
E
->
next
())
{
const
EnumInterface
&
ienum
=
E
->
get
();
output
.
append
(
MEMBER_BEGIN
"public enum "
);
output
.
append
(
ienum
.
cname
.
operator
String
());
output
.
append
(
MEMBER_BEGIN
OPEN_BLOCK
);
ERR_FAIL_COND_V
(
ienum
.
constants
.
empty
(),
ERR_BUG
);
for
(
const
List
<
ConstantInterface
>::
Element
*
F
=
ienum
.
constants
.
front
();
F
;
F
=
F
->
next
())
{
const
ConstantInterface
&
iconstant
=
F
->
get
();
output
.
append
(
MEMBER_BEGIN
"public enum "
);
output
.
append
(
ienum
.
cname
.
operator
String
());
output
.
append
(
MEMBER_BEGIN
OPEN_BLOCK
);
if
(
iconstant
.
const_doc
&&
iconstant
.
const_doc
->
description
.
size
())
{
String
xml_summary
=
bbcode_to_xml
(
fix_doc_description
(
iconstant
.
const_doc
->
description
),
&
itype
);
Vector
<
String
>
summary_lines
=
xml_summary
.
length
()
?
xml_summary
.
split
(
"
\n
"
)
:
Vector
<
String
>
();
for
(
const
List
<
ConstantInterface
>::
Element
*
F
=
ienum
.
constants
.
front
();
F
;
F
=
F
->
next
())
{
const
ConstantInterface
&
iconstant
=
F
->
get
();
if
(
summary_lines
.
size
())
{
output
.
append
(
INDENT3
"/// <summary>
\n
"
);
if
(
iconstant
.
const_doc
&&
iconstant
.
const_doc
->
description
.
size
())
{
String
xml_summary
=
bbcode_to_xml
(
fix_doc_description
(
iconstant
.
const_doc
->
description
),
&
itype
);
Vector
<
String
>
summary_lines
=
xml_summary
.
length
()
?
xml_summary
.
split
(
"
\n
"
)
:
Vector
<
String
>
();
for
(
int
i
=
0
;
i
<
summary_lines
.
size
();
i
++
)
{
output
.
append
(
INDENT3
"/// "
);
output
.
append
(
summary_lines
[
i
]);
output
.
append
(
"
\n
"
);
}
if
(
summary_lines
.
size
())
{
output
.
append
(
INDENT3
"/// <summary>
\n
"
);
output
.
append
(
INDENT3
"/// </summary>
\n
"
);
for
(
int
i
=
0
;
i
<
summary_lines
.
size
();
i
++
)
{
output
.
append
(
INDENT3
"/// "
);
output
.
append
(
summary_lines
[
i
]);
output
.
append
(
"
\n
"
);
}
}
output
.
append
(
INDENT3
);
output
.
append
(
iconstant
.
proxy_name
);
output
.
append
(
" = "
);
output
.
append
(
itos
(
iconstant
.
value
));
output
.
append
(
F
!=
ienum
.
constants
.
back
()
?
",
\n
"
:
"
\n
"
);
output
.
append
(
INDENT3
"/// </summary>
\n
"
);
}
}
output
.
append
(
INDENT2
CLOSE_BLOCK
);
output
.
append
(
INDENT3
);
output
.
append
(
iconstant
.
proxy_name
);
output
.
append
(
" = "
);
output
.
append
(
itos
(
iconstant
.
value
));
output
.
append
(
F
!=
ienum
.
constants
.
back
()
?
",
\n
"
:
"
\n
"
);
}
// Add properties
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
);
ERR_FAIL_COND_V_MSG
(
prop_err
!=
OK
,
prop_err
,
"Failed to generate property '"
+
iprop
.
cname
.
operator
String
()
+
"' for class '"
+
itype
.
name
+
"'."
);
}
output
.
append
(
INDENT2
CLOSE_BLOCK
);
}
// TODO: BINDINGS_NATIVE_NAME_FIELD should be StringName, once we support it in C#
// Add properties
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
);
ERR_FAIL_COND_V_MSG
(
prop_err
!=
OK
,
prop_err
,
"Failed to generate property '"
+
iprop
.
cname
.
operator
String
()
+
"' for class '"
+
itype
.
name
+
"'."
);
}
if
(
itype
.
is_singleton
)
{
// Add the type name and the singleton pointer as static fields
...
...
@@ -1368,15 +1403,17 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
if
(
itype
.
is_singleton
)
{
InternalCall
singleton_icall
=
InternalCall
(
itype
.
api_type
,
ICALL_PREFIX
+
itype
.
name
+
SINGLETON_ICALL_SUFFIX
,
"IntPtr"
);
if
(
!
find_icall_by_name
(
singleton_icall
.
name
,
custom_icalls
))
if
(
!
find_icall_by_name
(
singleton_icall
.
name
,
custom_icalls
))
{
custom_icalls
.
push_back
(
singleton_icall
);
}
}
if
(
is_derived_type
&&
itype
.
is_instantiable
)
{
InternalCall
ctor_icall
=
InternalCall
(
itype
.
api_type
,
ctor_method
,
"IntPtr"
,
itype
.
proxy_name
+
" obj"
);
if
(
!
find_icall_by_name
(
ctor_icall
.
name
,
custom_icalls
))
if
(
!
find_icall_by_name
(
ctor_icall
.
name
,
custom_icalls
))
{
custom_icalls
.
push_back
(
ctor_icall
);
}
}
output
.
append
(
INDENT1
CLOSE_BLOCK
/* class */
...
...
@@ -1442,6 +1479,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
const
TypeInterface
*
prop_itype
=
_get_type_or_null
(
proptype_name
);
ERR_FAIL_NULL_V
(
prop_itype
,
ERR_BUG
);
// Property type not found
ERR_FAIL_COND_V_MSG
(
prop_itype
->
is_singleton
,
ERR_BUG
,
"Property type is a singleton: '"
+
p_itype
.
name
+
"."
+
String
(
p_iprop
.
cname
)
+
"'."
);
if
(
p_iprop
.
prop_doc
&&
p_iprop
.
prop_doc
->
description
.
size
())
{
String
xml_summary
=
bbcode_to_xml
(
fix_doc_description
(
p_iprop
.
prop_doc
->
description
),
&
p_itype
);
Vector
<
String
>
summary_lines
=
xml_summary
.
length
()
?
xml_summary
.
split
(
"
\n
"
)
:
Vector
<
String
>
();
...
...
@@ -1461,8 +1501,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output
.
append
(
MEMBER_BEGIN
"public "
);
if
(
p_itype
.
is_singleton
)
if
(
p_itype
.
is_singleton
)
{
p_output
.
append
(
"static "
);
}
p_output
.
append
(
prop_itype
->
cs_type
);
p_output
.
append
(
" "
);
...
...
@@ -1534,6 +1575,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
Error
BindingsGenerator
::
_generate_cs_method
(
const
BindingsGenerator
::
TypeInterface
&
p_itype
,
const
BindingsGenerator
::
MethodInterface
&
p_imethod
,
int
&
p_method_bind_count
,
StringBuilder
&
p_output
)
{
const
TypeInterface
*
return_type
=
_get_type_or_placeholder
(
p_imethod
.
return_type
);
ERR_FAIL_COND_V_MSG
(
return_type
->
is_singleton
,
ERR_BUG
,
"Method return type is a singleton: '"
+
p_itype
.
name
+
"."
+
p_imethod
.
name
+
"'."
);
String
method_bind_field
=
"__method_bind_"
+
itos
(
p_method_bind_count
);
String
arguments_sig
;
...
...
@@ -1549,29 +1593,41 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
const
ArgumentInterface
&
iarg
=
F
->
get
();
const
TypeInterface
*
arg_type
=
_get_type_or_placeholder
(
iarg
.
type
);
ERR_FAIL_COND_V_MSG
(
arg_type
->
is_singleton
,
ERR_BUG
,
"Argument type is a singleton: '"
+
iarg
.
name
+
"' of method '"
+
p_itype
.
name
+
"."
+
p_imethod
.
name
+
"'."
);
if
(
iarg
.
default_argument
.
size
())
{
CRASH_COND_MSG
(
!
_arg_default_value_is_assignable_to_type
(
iarg
.
def_param_value
,
*
arg_type
),
"Invalid default value for parameter '"
+
iarg
.
name
+
"' of method '"
+
p_itype
.
name
+
"."
+
p_imethod
.
name
+
"'."
);
}
// 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
())
if
(
F
!=
p_imethod
.
arguments
.
front
())
{
arguments_sig
+=
", "
;
}
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
{
arguments_sig
+=
"Nullable<"
;
}
arguments_sig
+=
arg_type
->
cs_type
;
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
{
arguments_sig
+=
"> "
;
else
}
else
{
arguments_sig
+=
" "
;
}
arguments_sig
+=
iarg
.
name
;
if
(
iarg
.
default_argument
.
size
())
{
if
(
iarg
.
def_param_mode
!=
ArgumentInterface
::
CONSTANT
)
if
(
iarg
.
def_param_mode
!=
ArgumentInterface
::
CONSTANT
)
{
arguments_sig
+=
" = null"
;
else
}
else
{
arguments_sig
+=
" = "
+
sformat
(
iarg
.
default_argument
,
arg_type
->
cs_type
);
}
}
}
...
...
@@ -1589,17 +1645,19 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_statements
+=
" = "
;
cs_in_statements
+=
iarg
.
name
;
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
{
cs_in_statements
+=
".HasValue ? "
;
else
}
else
{
cs_in_statements
+=
" != null ? "
;
}
cs_in_statements
+=
iarg
.
name
;
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
if
(
iarg
.
def_param_mode
==
ArgumentInterface
::
NULLABLE_VAL
)
{
cs_in_statements
+=
".Value : "
;
else
}
else
{
cs_in_statements
+=
" : "
;
}
String
def_arg
=
sformat
(
iarg
.
default_argument
,
arg_type
->
cs_type
);
...
...
@@ -1659,8 +1717,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
}
if
(
p_imethod
.
is_deprecated
)
{
if
(
p_imethod
.
deprecation_message
.
empty
())
if
(
p_imethod
.
deprecation_message
.
empty
())
{
WARN_PRINT
(
"An empty deprecation message is discouraged. Method: '"
+
p_imethod
.
proxy_name
+
"'."
);
}
p_output
.
append
(
MEMBER_BEGIN
"[Obsolete(
\"
"
);
p_output
.
append
(
p_imethod
.
deprecation_message
);
...
...
@@ -1720,8 +1779,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
im_call
+=
"."
;
im_call
+=
im_icall
->
name
;
if
(
p_imethod
.
arguments
.
size
())
if
(
p_imethod
.
arguments
.
size
())
{
p_output
.
append
(
cs_in_statements
);
}
if
(
return_type
->
cname
==
name_cache
.
type_void
)
{
p_output
.
append
(
im_call
+
"("
+
icall_params
+
");
\n
"
);
...
...
@@ -1748,10 +1808,14 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
const
ArgumentInterface
&
iarg
=
F
->
get
();
const
TypeInterface
*
arg_type
=
_get_type_or_placeholder
(
iarg
.
type
);
ERR_FAIL_COND_V_MSG
(
arg_type
->
is_singleton
,
ERR_BUG
,
"Argument type is a singleton: '"
+
iarg
.
name
+
"' of signal"
+
p_itype
.
name
+
"."
+
p_isignal
.
name
+
"'."
);
// Add the current arguments to the signature
if
(
F
!=
p_isignal
.
arguments
.
front
())
if
(
F
!=
p_isignal
.
arguments
.
front
())
{
arguments_sig
+=
", "
;
}
arguments_sig
+=
arg_type
->
cs_type
;
arguments_sig
+=
" "
;
...
...
@@ -1778,8 +1842,9 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
if
(
p_isignal
.
is_deprecated
)
{
if
(
p_isignal
.
deprecation_message
.
empty
())
if
(
p_isignal
.
deprecation_message
.
empty
())
{
WARN_PRINT
(
"An empty deprecation message is discouraged. Signal: '"
+
p_isignal
.
proxy_name
+
"'."
);
}
p_output
.
append
(
MEMBER_BEGIN
"[Obsolete(
\"
"
);
p_output
.
append
(
p_isignal
.
deprecation_message
);
...
...
@@ -1810,8 +1875,9 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Generate event
p_output
.
append
(
MEMBER_BEGIN
"[Signal]"
MEMBER_BEGIN
"public "
);
if
(
p_itype
.
is_singleton
)
if
(
p_itype
.
is_singleton
)
{
p_output
.
append
(
"static "
);
}
p_output
.
append
(
"event "
);
p_output
.
append
(
delegate_name
);
...
...
@@ -1819,18 +1885,20 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output
.
append
(
p_isignal
.
proxy_name
);
p_output
.
append
(
"
\n
"
OPEN_BLOCK_L2
);
if
(
p_itype
.
is_singleton
)
if
(
p_itype
.
is_singleton
)
{
p_output
.
append
(
"add => Singleton.Connect(__signal_name_"
);
else
}
else
{
p_output
.
append
(
"add => Connect(__signal_name_"
);
}
p_output
.
append
(
p_isignal
.
name
);
p_output
.
append
(
", new Callable(value));
\n
"
);
if
(
p_itype
.
is_singleton
)
if
(
p_itype
.
is_singleton
)
{
p_output
.
append
(
INDENT3
"remove => Singleton.Disconnect(__signal_name_"
);
else
}
else
{
p_output
.
append
(
INDENT3
"remove => Disconnect(__signal_name_"
);
}
p_output
.
append
(
p_isignal
.
name
);
p_output
.
append
(
", new Callable(value));
\n
"
);
...
...
@@ -1864,7 +1932,6 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
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
);
}
...
...
@@ -1885,8 +1952,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
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
))
if
(
!
find_icall_by_name
(
singleton_icall
.
name
,
custom_icalls
))
{
custom_icalls
.
push_back
(
singleton_icall
);
}
output
.
append
(
"Object* "
);
output
.
append
(
singleton_icall_name
);
...
...
@@ -1898,8 +1966,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if
(
is_derived_type
&&
itype
.
is_instantiable
)
{
InternalCall
ctor_icall
=
InternalCall
(
itype
.
api_type
,
ctor_method
,
"IntPtr"
,
itype
.
proxy_name
+
" obj"
);
if
(
!
find_icall_by_name
(
ctor_icall
.
name
,
custom_icalls
))
if
(
!
find_icall_by_name
(
ctor_icall
.
name
,
custom_icalls
))
{
custom_icalls
.
push_back
(
ctor_icall
);
}
output
.
append
(
"Object* "
);
output
.
append
(
ctor_method
);
...
...
@@ -1998,8 +2067,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
output
.
append
(
"
\n
#endif // MONO_GLUE_ENABLED
\n
"
);
Error
save_err
=
_save_file
(
path
::
join
(
p_output_dir
,
"mono_glue.gen.cpp"
),
output
);
if
(
save_err
!=
OK
)
if
(
save_err
!=
OK
)
{
return
save_err
;
}
OS
::
get_singleton
()
->
print
(
"Mono glue generated successfully
\n
"
);
...
...
@@ -2022,8 +2092,9 @@ Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p
}
Error
BindingsGenerator
::
_generate_glue_method
(
const
BindingsGenerator
::
TypeInterface
&
p_itype
,
const
BindingsGenerator
::
MethodInterface
&
p_imethod
,
StringBuilder
&
p_output
)
{
if
(
p_imethod
.
is_virtual
)
if
(
p_imethod
.
is_virtual
)
{
return
OK
;
// Ignore
}
bool
ret_void
=
p_imethod
.
return_type
.
cname
==
name_cache
.
type_void
;
...
...
@@ -2051,10 +2122,12 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
c_in_statements
+=
sformat
(
", &%s_in);
\n
"
,
c_param_name
);
}
}
else
{
if
(
i
>
0
)
if
(
i
>
0
)
{
c_args_var_content
+=
", "
;
if
(
arg_type
->
c_in
.
size
())
}
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
);
}
...
...
@@ -2084,8 +2157,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
if
(
!
generated_icall_funcs
.
find
(
im_icall
))
{
generated_icall_funcs
.
push_back
(
im_icall
);
if
(
im_icall
->
editor_only
)
if
(
im_icall
->
editor_only
)
{
p_output
.
append
(
"#ifdef TOOLS_ENABLED
\n
"
);
}
// Generate icall function
...
...
@@ -2203,8 +2277,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
p_output
.
append
(
CLOSE_BLOCK
"
\n
"
);
if
(
im_icall
->
editor_only
)
if
(
im_icall
->
editor_only
)
{
p_output
.
append
(
"#endif // TOOLS_ENABLED
\n
"
);
}
}
return
OK
;
...
...
@@ -2213,19 +2288,22 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
const
BindingsGenerator
::
TypeInterface
*
BindingsGenerator
::
_get_type_or_null
(
const
TypeReference
&
p_typeref
)
{
const
Map
<
StringName
,
TypeInterface
>::
Element
*
builtin_type_match
=
builtin_types
.
find
(
p_typeref
.
cname
);
if
(
builtin_type_match
)
if
(
builtin_type_match
)
{
return
&
builtin_type_match
->
get
();
}
const
OrderedHashMap
<
StringName
,
TypeInterface
>::
Element
obj_type_match
=
obj_types
.
find
(
p_typeref
.
cname
);
if
(
obj_type_match
)
if
(
obj_type_match
)
{
return
&
obj_type_match
.
get
();
}
if
(
p_typeref
.
is_enum
)
{
const
Map
<
StringName
,
TypeInterface
>::
Element
*
enum_match
=
enum_types
.
find
(
p_typeref
.
cname
);
if
(
enum_match
)
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
);
...
...
@@ -2239,15 +2317,17 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
const
BindingsGenerator
::
TypeInterface
*
BindingsGenerator
::
_get_type_or_placeholder
(
const
TypeReference
&
p_typeref
)
{
const
TypeInterface
*
found
=
_get_type_or_null
(
p_typeref
);
if
(
found
)
if
(
found
)
{
return
found
;
}
ERR_PRINT
(
String
()
+
"Type not found. Creating placeholder: '"
+
p_typeref
.
cname
.
operator
String
()
+
"'."
);
const
Map
<
StringName
,
TypeInterface
>::
Element
*
match
=
placeholder_types
.
find
(
p_typeref
.
cname
);
if
(
match
)
if
(
match
)
{
return
&
match
->
get
();
}
TypeInterface
placeholder
;
TypeInterface
::
create_placeholder_type
(
placeholder
,
p_typeref
.
cname
);
...
...
@@ -2305,6 +2385,84 @@ StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Meta
}
}
bool
BindingsGenerator
::
_arg_default_value_is_assignable_to_type
(
const
Variant
&
p_val
,
const
TypeInterface
&
p_arg_type
)
{
if
(
p_arg_type
.
name
==
name_cache
.
type_Variant
)
{
// Variant can take anything
return
true
;
}
switch
(
p_val
.
get_type
())
{
case
Variant
:
:
NIL
:
return
p_arg_type
.
is_object_type
||
name_cache
.
is_nullable_type
(
p_arg_type
.
name
);
case
Variant
:
:
BOOL
:
return
p_arg_type
.
name
==
name_cache
.
type_bool
;
case
Variant
:
:
INT
:
return
p_arg_type
.
name
==
name_cache
.
type_sbyte
||
p_arg_type
.
name
==
name_cache
.
type_short
||
p_arg_type
.
name
==
name_cache
.
type_int
||
p_arg_type
.
name
==
name_cache
.
type_byte
||
p_arg_type
.
name
==
name_cache
.
type_ushort
||
p_arg_type
.
name
==
name_cache
.
type_uint
||
p_arg_type
.
name
==
name_cache
.
type_long
||
p_arg_type
.
name
==
name_cache
.
type_ulong
||
p_arg_type
.
name
==
name_cache
.
type_float
||
p_arg_type
.
name
==
name_cache
.
type_double
||
p_arg_type
.
is_enum
;
case
Variant
:
:
FLOAT
:
return
p_arg_type
.
name
==
name_cache
.
type_float
||
p_arg_type
.
name
==
name_cache
.
type_double
;
case
Variant
:
:
STRING
:
case
Variant
:
:
STRING_NAME
:
return
p_arg_type
.
name
==
name_cache
.
type_String
||
p_arg_type
.
name
==
name_cache
.
type_StringName
||
p_arg_type
.
name
==
name_cache
.
type_NodePath
;
case
Variant
:
:
NODE_PATH
:
return
p_arg_type
.
name
==
name_cache
.
type_NodePath
;
case
Variant
:
:
TRANSFORM
:
case
Variant
:
:
TRANSFORM2D
:
case
Variant
:
:
BASIS
:
case
Variant
:
:
QUAT
:
case
Variant
:
:
PLANE
:
case
Variant
:
:
AABB
:
case
Variant
:
:
COLOR
:
case
Variant
:
:
VECTOR2
:
case
Variant
:
:
RECT2
:
case
Variant
:
:
VECTOR3
:
case
Variant
:
:
_RID
:
case
Variant
:
:
ARRAY
:
case
Variant
:
:
DICTIONARY
:
case
Variant
:
:
PACKED_BYTE_ARRAY
:
case
Variant
:
:
PACKED_INT32_ARRAY
:
case
Variant
:
:
PACKED_INT64_ARRAY
:
case
Variant
:
:
PACKED_FLOAT32_ARRAY
:
case
Variant
:
:
PACKED_FLOAT64_ARRAY
:
case
Variant
:
:
PACKED_STRING_ARRAY
:
case
Variant
:
:
PACKED_VECTOR2_ARRAY
:
case
Variant
:
:
PACKED_VECTOR3_ARRAY
:
case
Variant
:
:
PACKED_COLOR_ARRAY
:
case
Variant
:
:
CALLABLE
:
case
Variant
:
:
SIGNAL
:
return
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
case
Variant
:
:
OBJECT
:
return
p_arg_type
.
is_object_type
;
case
Variant
:
:
VECTOR2I
:
return
p_arg_type
.
name
==
name_cache
.
type_Vector2
||
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
case
Variant
:
:
RECT2I
:
return
p_arg_type
.
name
==
name_cache
.
type_Rect2
||
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
case
Variant
:
:
VECTOR3I
:
return
p_arg_type
.
name
==
name_cache
.
type_Vector3
||
p_arg_type
.
name
==
Variant
::
get_type_name
(
p_val
.
get_type
());
default
:
CRASH_NOW_MSG
(
"Unexpected Variant type: "
+
itos
(
p_val
.
get_type
()));
break
;
}
return
false
;
}
bool
BindingsGenerator
::
_populate_object_type_interfaces
()
{
obj_types
.
clear
();
...
...
@@ -2367,8 +2525,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
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_SUBGROUP
||
property
.
usage
&
PROPERTY_USAGE_CATEGORY
)
if
(
property
.
usage
&
PROPERTY_USAGE_GROUP
||
property
.
usage
&
PROPERTY_USAGE_SUBGROUP
||
property
.
usage
&
PROPERTY_USAGE_CATEGORY
)
{
continue
;
}
if
(
property
.
name
.
find
(
"/"
)
>=
0
)
{
// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
...
...
@@ -2380,10 +2539,12 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
iprop
.
setter
=
ClassDB
::
get_property_setter
(
type_cname
,
iprop
.
cname
);
iprop
.
getter
=
ClassDB
::
get_property_getter
(
type_cname
,
iprop
.
cname
);
if
(
iprop
.
setter
!=
StringName
())
if
(
iprop
.
setter
!=
StringName
())
{
accessor_methods
[
iprop
.
setter
]
=
iprop
.
cname
;
if
(
iprop
.
getter
!=
StringName
())
}
if
(
iprop
.
getter
!=
StringName
())
{
accessor_methods
[
iprop
.
getter
]
=
iprop
.
cname
;
}
bool
valid
=
false
;
iprop
.
index
=
ClassDB
::
get_property_index
(
type_cname
,
iprop
.
cname
,
&
valid
);
...
...
@@ -2427,20 +2588,23 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
int
argc
=
method_info
.
arguments
.
size
();
if
(
method_info
.
name
.
empty
())
if
(
method_info
.
name
.
empty
())
{
continue
;
}
String
cname
=
method_info
.
name
;
if
(
blacklisted_methods
.
find
(
itype
.
cname
)
&&
blacklisted_methods
[
itype
.
cname
].
find
(
cname
))
if
(
blacklisted_methods
.
find
(
itype
.
cname
)
&&
blacklisted_methods
[
itype
.
cname
].
find
(
cname
))
{
continue
;
}
MethodInterface
imethod
;
imethod
.
name
=
method_info
.
name
;
imethod
.
cname
=
cname
;
if
(
method_info
.
flags
&
METHOD_FLAG_VIRTUAL
)
if
(
method_info
.
flags
&
METHOD_FLAG_VIRTUAL
)
{
imethod
.
is_virtual
=
true
;
}
PropertyInfo
return_info
=
method_info
.
return_val
;
...
...
@@ -2462,9 +2626,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
// We assume the return type is void.
imethod
.
return_type
.
cname
=
name_cache
.
type_void
;
// Actually, more methods like this may be added in the future,
// which could actually will return something different.
// Let's put this to notify us if that ever happens.
// Actually, more methods like this may be added in the future, which could return
// something different. Let's put this check to notify us if that ever happens.
if
(
itype
.
cname
!=
name_cache
.
type_Object
||
imethod
.
name
!=
"free"
)
{
WARN_PRINT
(
"Notification: New unexpected virtual non-overridable method found."
" We only expected Object.free, but found '"
+
...
...
@@ -2475,13 +2638,12 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
imethod
.
return_type
.
is_enum
=
true
;
}
else
if
(
return_info
.
class_name
!=
StringName
())
{
imethod
.
return_type
.
cname
=
return_info
.
class_name
;
if
(
!
imethod
.
is_virtual
&&
ClassDB
::
is_parent_class
(
return_info
.
class_name
,
name_cache
.
type_Reference
)
&&
return_info
.
hint
!=
PROPERTY_HINT_RESOURCE_TYPE
)
{
/* clang-format off */
ERR_PRINT
(
"Return type is reference but hint is not '"
_STR
(
PROPERTY_HINT_RESOURCE_TYPE
)
"'."
" Are you returning a reference type by pointer? Method: '"
+
itype
.
name
+
"."
+
imethod
.
name
+
"'."
);
/* clang-format on */
ERR_FAIL_V
(
false
);
}
bool
bad_reference_hint
=
!
imethod
.
is_virtual
&&
return_info
.
hint
!=
PROPERTY_HINT_RESOURCE_TYPE
&&
ClassDB
::
is_parent_class
(
return_info
.
class_name
,
name_cache
.
type_Reference
);
ERR_FAIL_COND_V_MSG
(
bad_reference_hint
,
false
,
String
()
+
"Return type is reference but hint is not '"
_STR
(
PROPERTY_HINT_RESOURCE_TYPE
)
"'."
+
" Are you returning a reference type by pointer? Method: '"
+
itype
.
name
+
"."
+
imethod
.
name
+
"'."
);
}
else
if
(
return_info
.
hint
==
PROPERTY_HINT_RESOURCE_TYPE
)
{
imethod
.
return_type
.
cname
=
return_info
.
hint_string
;
}
else
if
(
return_info
.
type
==
Variant
::
NIL
&&
return_info
.
usage
&
PROPERTY_USAGE_NIL_IS_VARIANT
)
{
...
...
@@ -2572,6 +2734,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
}
}
ERR_FAIL_COND_V_MSG
(
itype
.
find_property_by_name
(
imethod
.
cname
),
false
,
"Method name conflicts with property: '"
+
itype
.
name
+
"."
+
imethod
.
name
+
"'."
);
// Classes starting with an underscore are ignored unless they're used as a property setter or getter
if
(
!
imethod
.
is_virtual
&&
imethod
.
name
[
0
]
==
'_'
)
{
for
(
const
List
<
PropertyInterface
>::
Element
*
F
=
itype
.
properties
.
front
();
F
;
F
=
F
->
next
())
{
const
PropertyInterface
&
iprop
=
F
->
get
();
...
...
@@ -2751,7 +2917,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
}
bool
BindingsGenerator
::
_arg_default_value_from_variant
(
const
Variant
&
p_val
,
ArgumentInterface
&
r_iarg
)
{
r_iarg
.
default_argument
=
p_val
;
r_iarg
.
def_param_value
=
p_val
;
r_iarg
.
default_argument
=
p_val
.
operator
String
();
switch
(
p_val
.
get_type
())
{
case
Variant
:
:
NIL
:
...
...
@@ -2784,8 +2951,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
}
break
;
case
Variant
:
:
TRANSFORM
:
if
(
p_val
.
operator
Transform
()
==
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
;
...
...
@@ -2851,8 +3019,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
break
;
}
if
(
r_iarg
.
def_param_mode
==
ArgumentInterface
::
CONSTANT
&&
r_iarg
.
type
.
cname
==
name_cache
.
type_Variant
&&
r_iarg
.
default_argument
!=
"null"
)
if
(
r_iarg
.
def_param_mode
==
ArgumentInterface
::
CONSTANT
&&
r_iarg
.
type
.
cname
==
name_cache
.
type_Variant
&&
r_iarg
.
default_argument
!=
"null"
)
{
r_iarg
.
def_param_mode
=
ArgumentInterface
::
NULLABLE_REF
;
}
return
true
;
}
...
...
@@ -3357,8 +3526,9 @@ void BindingsGenerator::_initialize() {
core_custom_icalls
.
clear
();
editor_custom_icalls
.
clear
();
for
(
OrderedHashMap
<
StringName
,
TypeInterface
>::
Element
E
=
obj_types
.
front
();
E
;
E
=
E
.
next
())
for
(
OrderedHashMap
<
StringName
,
TypeInterface
>::
Element
E
=
obj_types
.
front
();
E
;
E
=
E
.
next
())
{
_generate_method_icalls
(
E
.
get
());
}
initialized
=
true
;
}
...
...
@@ -3426,21 +3596,25 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
}
if
(
glue_dir_path
.
length
())
{
if
(
bindings_generator
.
generate_glue
(
glue_dir_path
)
!=
OK
)
if
(
bindings_generator
.
generate_glue
(
glue_dir_path
)
!=
OK
)
{
ERR_PRINT
(
generate_all_glue_option
+
": Failed to generate the C++ glue."
);
}
if
(
bindings_generator
.
generate_cs_api
(
glue_dir_path
.
plus_file
(
API_SOLUTION_NAME
))
!=
OK
)
if
(
bindings_generator
.
generate_cs_api
(
glue_dir_path
.
plus_file
(
API_SOLUTION_NAME
))
!=
OK
)
{
ERR_PRINT
(
generate_all_glue_option
+
": Failed to generate the C# API."
);
}
}
if
(
cs_dir_path
.
length
())
{
if
(
bindings_generator
.
generate_cs_api
(
cs_dir_path
)
!=
OK
)
if
(
bindings_generator
.
generate_cs_api
(
cs_dir_path
)
!=
OK
)
{
ERR_PRINT
(
generate_cs_glue_option
+
": Failed to generate the C# API."
);
}
}
if
(
cpp_dir_path
.
length
())
{
if
(
bindings_generator
.
generate_glue
(
cpp_dir_path
)
!=
OK
)
if
(
bindings_generator
.
generate_glue
(
cpp_dir_path
)
!=
OK
)
{
ERR_PRINT
(
generate_cpp_glue_option
+
": Failed to generate the C++ glue."
);
}
}
// Exit once done
...
...
modules/mono/editor/bindings_generator.h
View file @
9fa4b402
...
...
@@ -102,6 +102,8 @@ class BindingsGenerator {
TypeReference
type
;
String
name
;
Variant
def_param_value
;
DefaultParamMode
def_param_mode
=
CONSTANT
;
/**
...
...
@@ -355,8 +357,9 @@ class BindingsGenerator {
const
MethodInterface
*
find_method_by_name
(
const
StringName
&
p_cname
)
const
{
for
(
const
List
<
MethodInterface
>::
Element
*
E
=
methods
.
front
();
E
;
E
=
E
->
next
())
{
if
(
E
->
get
().
cname
==
p_cname
)
if
(
E
->
get
().
cname
==
p_cname
)
{
return
&
E
->
get
();
}
}
return
nullptr
;
...
...
@@ -364,8 +367,9 @@ class BindingsGenerator {
const
PropertyInterface
*
find_property_by_name
(
const
StringName
&
p_cname
)
const
{
for
(
const
List
<
PropertyInterface
>::
Element
*
E
=
properties
.
front
();
E
;
E
=
E
->
next
())
{
if
(
E
->
get
().
cname
==
p_cname
)
if
(
E
->
get
().
cname
==
p_cname
)
{
return
&
E
->
get
();
}
}
return
nullptr
;
...
...
@@ -373,8 +377,9 @@ class BindingsGenerator {
const
PropertyInterface
*
find_property_by_proxy_name
(
const
String
&
p_proxy_name
)
const
{
for
(
const
List
<
PropertyInterface
>::
Element
*
E
=
properties
.
front
();
E
;
E
=
E
->
next
())
{
if
(
E
->
get
().
proxy_name
==
p_proxy_name
)
if
(
E
->
get
().
proxy_name
==
p_proxy_name
)
{
return
&
E
->
get
();
}
}
return
nullptr
;
...
...
@@ -382,8 +387,9 @@ class BindingsGenerator {
const
MethodInterface
*
find_method_by_proxy_name
(
const
String
&
p_proxy_name
)
const
{
for
(
const
List
<
MethodInterface
>::
Element
*
E
=
methods
.
front
();
E
;
E
=
E
->
next
())
{
if
(
E
->
get
().
proxy_name
==
p_proxy_name
)
if
(
E
->
get
().
proxy_name
==
p_proxy_name
)
{
return
&
E
->
get
();
}
}
return
nullptr
;
...
...
@@ -523,58 +529,70 @@ class BindingsGenerator {
void
_initialize_blacklisted_methods
();
struct
NameCache
{
StringName
type_void
;
StringName
type_Array
;
StringName
type_Dictionary
;
StringName
type_Variant
;
StringName
type_VarArg
;
StringName
type_Object
;
StringName
type_Reference
;
StringName
type_RID
;
StringName
type_String
;
StringName
type_StringName
;
StringName
type_NodePath
;
StringName
type_at_GlobalScope
;
StringName
enum_Error
;
StringName
type_sbyte
;
StringName
type_short
;
StringName
type_int
;
StringName
type_long
;
StringName
type_byte
;
StringName
type_ushort
;
StringName
type_uint
;
StringName
type_ulong
;
StringName
type_float
;
StringName
type_double
;
NameCache
()
{
type_void
=
StaticCString
::
create
(
"void"
);
type_Array
=
StaticCString
::
create
(
"Array"
);
type_Dictionary
=
StaticCString
::
create
(
"Dictionary"
);
type_Variant
=
StaticCString
::
create
(
"Variant"
);
type_VarArg
=
StaticCString
::
create
(
"VarArg"
);
type_Object
=
StaticCString
::
create
(
"Object"
);
type_Reference
=
StaticCString
::
create
(
"Reference"
);
type_RID
=
StaticCString
::
create
(
"RID"
);
type_String
=
StaticCString
::
create
(
"String"
);
type_StringName
=
StaticCString
::
create
(
"StringName"
);
type_NodePath
=
StaticCString
::
create
(
"NodePath"
);
type_at_GlobalScope
=
StaticCString
::
create
(
"@GlobalScope"
);
enum_Error
=
StaticCString
::
create
(
"Error"
);
type_sbyte
=
StaticCString
::
create
(
"sbyte"
);
type_short
=
StaticCString
::
create
(
"short"
);
type_int
=
StaticCString
::
create
(
"int"
);
type_long
=
StaticCString
::
create
(
"long"
);
type_byte
=
StaticCString
::
create
(
"byte"
);
type_ushort
=
StaticCString
::
create
(
"ushort"
);
type_uint
=
StaticCString
::
create
(
"uint"
);
type_ulong
=
StaticCString
::
create
(
"ulong"
);
type_float
=
StaticCString
::
create
(
"float"
);
type_double
=
StaticCString
::
create
(
"double"
);
StringName
type_void
=
StaticCString
::
create
(
"void"
);
StringName
type_Variant
=
StaticCString
::
create
(
"Variant"
);
StringName
type_VarArg
=
StaticCString
::
create
(
"VarArg"
);
StringName
type_Object
=
StaticCString
::
create
(
"Object"
);
StringName
type_Reference
=
StaticCString
::
create
(
"Reference"
);
StringName
type_RID
=
StaticCString
::
create
(
"RID"
);
StringName
type_String
=
StaticCString
::
create
(
"String"
);
StringName
type_StringName
=
StaticCString
::
create
(
"StringName"
);
StringName
type_NodePath
=
StaticCString
::
create
(
"NodePath"
);
StringName
type_at_GlobalScope
=
StaticCString
::
create
(
"@GlobalScope"
);
StringName
enum_Error
=
StaticCString
::
create
(
"Error"
);
StringName
type_sbyte
=
StaticCString
::
create
(
"sbyte"
);
StringName
type_short
=
StaticCString
::
create
(
"short"
);
StringName
type_int
=
StaticCString
::
create
(
"int"
);
StringName
type_byte
=
StaticCString
::
create
(
"byte"
);
StringName
type_ushort
=
StaticCString
::
create
(
"ushort"
);
StringName
type_uint
=
StaticCString
::
create
(
"uint"
);
StringName
type_long
=
StaticCString
::
create
(
"long"
);
StringName
type_ulong
=
StaticCString
::
create
(
"ulong"
);
StringName
type_bool
=
StaticCString
::
create
(
"bool"
);
StringName
type_float
=
StaticCString
::
create
(
"float"
);
StringName
type_double
=
StaticCString
::
create
(
"double"
);
StringName
type_Vector2
=
StaticCString
::
create
(
"Vector2"
);
StringName
type_Rect2
=
StaticCString
::
create
(
"Rect2"
);
StringName
type_Vector3
=
StaticCString
::
create
(
"Vector3"
);
// Object not included as it must be checked for all derived classes
static
constexpr
int
nullable_types_count
=
17
;
StringName
nullable_types
[
nullable_types_count
]
=
{
type_String
,
type_StringName
,
type_NodePath
,
StaticCString
::
create
(
_STR
(
Array
)),
StaticCString
::
create
(
_STR
(
Dictionary
)),
StaticCString
::
create
(
_STR
(
Callable
)),
StaticCString
::
create
(
_STR
(
Signal
)),
StaticCString
::
create
(
_STR
(
PackedByteArray
)),
StaticCString
::
create
(
_STR
(
PackedInt32Array
)),
StaticCString
::
create
(
_STR
(
PackedInt64rray
)),
StaticCString
::
create
(
_STR
(
PackedFloat32Array
)),
StaticCString
::
create
(
_STR
(
PackedFloat64Array
)),
StaticCString
::
create
(
_STR
(
PackedStringArray
)),
StaticCString
::
create
(
_STR
(
PackedVector2Array
)),
StaticCString
::
create
(
_STR
(
PackedVector3Array
)),
StaticCString
::
create
(
_STR
(
PackedColorArray
)),
};
bool
is_nullable_type
(
const
StringName
&
p_type
)
const
{
for
(
int
i
=
0
;
i
<
nullable_types_count
;
i
++
)
{
if
(
p_type
==
nullable_types
[
i
])
{
return
true
;
}
}
return
false
;
}
NameCache
()
{}
private
:
NameCache
(
const
NameCache
&
);
NameCache
&
operator
=
(
const
NameCache
&
);
...
...
@@ -585,8 +603,9 @@ class BindingsGenerator {
const
List
<
InternalCall
>::
Element
*
find_icall_by_name
(
const
String
&
p_name
,
const
List
<
InternalCall
>
&
p_list
)
{
const
List
<
InternalCall
>::
Element
*
it
=
p_list
.
front
();
while
(
it
)
{
if
(
it
->
get
().
name
==
p_name
)
if
(
it
->
get
().
name
==
p_name
)
{
return
it
;
}
it
=
it
->
next
();
}
return
nullptr
;
...
...
@@ -594,20 +613,22 @@ class BindingsGenerator {
const
ConstantInterface
*
find_constant_by_name
(
const
String
&
p_name
,
const
List
<
ConstantInterface
>
&
p_constants
)
const
{
for
(
const
List
<
ConstantInterface
>::
Element
*
E
=
p_constants
.
front
();
E
;
E
=
E
->
next
())
{
if
(
E
->
get
().
name
==
p_name
)
if
(
E
->
get
().
name
==
p_name
)
{
return
&
E
->
get
();
}
}
return
nullptr
;
}
inline
String
get_unique_sig
(
const
TypeInterface
&
p_type
)
{
if
(
p_type
.
is_reference
)
if
(
p_type
.
is_reference
)
{
return
"Ref"
;
else
if
(
p_type
.
is_object_type
)
}
else
if
(
p_type
.
is_object_type
)
{
return
"Obj"
;
else
if
(
p_type
.
is_enum
)
}
else
if
(
p_type
.
is_enum
)
{
return
"int"
;
}
return
p_type
.
name
;
}
...
...
@@ -626,6 +647,7 @@ class BindingsGenerator {
StringName
_get_float_type_name_from_meta
(
GodotTypeInfo
::
Metadata
p_meta
);
bool
_arg_default_value_from_variant
(
const
Variant
&
p_val
,
ArgumentInterface
&
r_iarg
);
bool
_arg_default_value_is_assignable_to_type
(
const
Variant
&
p_val
,
const
TypeInterface
&
p_arg_type
);
bool
_populate_object_type_interfaces
();
void
_populate_builtin_type_interfaces
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment