feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -289,6 +289,38 @@ bool arg_default_value_is_assignable_to_type(const Context &p_context, const Var
|
|||
return false;
|
||||
}
|
||||
|
||||
bool arg_default_value_is_valid_data(const Variant &p_val, String *r_err_msg = nullptr) {
|
||||
switch (p_val.get_type()) {
|
||||
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::PACKED_VECTOR4_ARRAY:
|
||||
case Variant::CALLABLE:
|
||||
case Variant::SIGNAL:
|
||||
case Variant::OBJECT:
|
||||
if (p_val.is_zero()) {
|
||||
return true;
|
||||
}
|
||||
if (r_err_msg) {
|
||||
*r_err_msg = "Must be zero.";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void validate_property(const Context &p_context, const ExposedClass &p_class, const PropertyData &p_prop) {
|
||||
const MethodData *setter = p_class.find_method_by_name(p_prop.setter);
|
||||
|
||||
|
|
@ -411,6 +443,14 @@ void validate_argument(const Context &p_context, const ExposedClass &p_class, co
|
|||
}
|
||||
|
||||
TEST_COND(!arg_defval_assignable_to_type, err_msg);
|
||||
|
||||
bool arg_defval_valid_data = arg_default_value_is_valid_data(p_arg.defval, &type_error_msg);
|
||||
|
||||
if (!type_error_msg.is_empty()) {
|
||||
err_msg += " " + type_error_msg;
|
||||
}
|
||||
|
||||
TEST_COND(!arg_defval_valid_data, err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -563,7 +603,7 @@ void add_exposed_classes(Context &r_context) {
|
|||
|
||||
MethodData method;
|
||||
method.name = method_info.name;
|
||||
TEST_FAIL_COND(!String(method.name).is_valid_identifier(),
|
||||
TEST_FAIL_COND(!String(method.name).is_valid_ascii_identifier(),
|
||||
"Method name is not a valid identifier: '", exposed_class.name, ".", method.name, "'.");
|
||||
|
||||
if (method_info.flags & METHOD_FLAG_VIRTUAL) {
|
||||
|
|
@ -689,7 +729,7 @@ void add_exposed_classes(Context &r_context) {
|
|||
const MethodInfo &method_info = signal_map.get(K.key);
|
||||
|
||||
signal.name = method_info.name;
|
||||
TEST_FAIL_COND(!String(signal.name).is_valid_identifier(),
|
||||
TEST_FAIL_COND(!String(signal.name).is_valid_ascii_identifier(),
|
||||
"Signal name is not a valid identifier: '", exposed_class.name, ".", signal.name, "'.");
|
||||
|
||||
int i = 0;
|
||||
|
|
@ -823,16 +863,19 @@ void add_global_enums(Context &r_context) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 (const StringName &E : hardcoded_enums) {
|
||||
// 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);
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
if (i == Variant::OBJECT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Variant::Type type = Variant::Type(i);
|
||||
|
||||
List<StringName> enum_names;
|
||||
Variant::get_enums_for_type(type, &enum_names);
|
||||
|
||||
for (const StringName &enum_name : enum_names) {
|
||||
r_context.enum_types.push_back(Variant::get_type_name(type) + "." + enum_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,16 @@
|
|||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#ifdef SANITIZERS_ENABLED
|
||||
#ifdef __has_feature
|
||||
#if __has_feature(address_sanitizer) || __has_feature(thread_sanitizer)
|
||||
#define ASAN_OR_TSAN_ENABLED
|
||||
#endif
|
||||
#elif defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
|
||||
#define ASAN_OR_TSAN_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Declared in global namespace because of GDCLASS macro warning (Windows):
|
||||
// "Unqualified friend declaration referring to type outside of the nearest enclosing namespace
|
||||
// is a Microsoft extension; add a nested name specifier".
|
||||
|
|
@ -85,10 +95,10 @@ public:
|
|||
}
|
||||
bool property_can_revert(const StringName &p_name) const override {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
bool property_get_revert(const StringName &p_name, Variant &r_ret) const override {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
void get_method_list(List<MethodInfo> *p_list) const override {
|
||||
}
|
||||
bool has_method(const StringName &p_method) const override {
|
||||
|
|
@ -174,6 +184,31 @@ TEST_CASE("[Object] Metadata") {
|
|||
CHECK_MESSAGE(
|
||||
meta_list2.size() == 0,
|
||||
"The metadata list should contain 0 items after removing all metadata items.");
|
||||
|
||||
Object other;
|
||||
object.set_meta("conflicting_meta", "string");
|
||||
object.set_meta("not_conflicting_meta", 123);
|
||||
other.set_meta("conflicting_meta", Color(0, 1, 0));
|
||||
other.set_meta("other_meta", "other");
|
||||
object.merge_meta_from(&other);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Color(object.get_meta("conflicting_meta")).is_equal_approx(Color(0, 1, 0)),
|
||||
"String meta should be overwritten with Color after merging.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
int(object.get_meta("not_conflicting_meta")) == 123,
|
||||
"Not conflicting meta on destination should be kept intact.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
object.get_meta("other_meta", String()) == "other",
|
||||
"Not conflicting meta name on source should merged.");
|
||||
|
||||
List<StringName> meta_list3;
|
||||
object.get_meta_list(&meta_list3);
|
||||
CHECK_MESSAGE(
|
||||
meta_list3.size() == 3,
|
||||
"The metadata list should contain 3 items after merging meta from two objects.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Object] Construction") {
|
||||
|
|
@ -499,6 +534,74 @@ TEST_CASE("[Object] Notification order") { // GH-52325
|
|||
memdelete(test_notification_object);
|
||||
}
|
||||
|
||||
TEST_CASE("[Object] Destruction at the end of the call chain is safe") {
|
||||
Object *object = memnew(Object);
|
||||
ObjectID obj_id = object->get_instance_id();
|
||||
|
||||
class _SelfDestroyingScriptInstance : public _MockScriptInstance {
|
||||
Object *self = nullptr;
|
||||
|
||||
// This has to be static because ~Object() also destroys the script instance.
|
||||
static void free_self(Object *p_self) {
|
||||
#if defined(ASAN_OR_TSAN_ENABLED)
|
||||
// Regular deletion is enough becausa asan/tsan will catch a potential heap-after-use.
|
||||
memdelete(p_self);
|
||||
#else
|
||||
// Without asan/tsan, try at least to force a crash by replacing the otherwise seemingly good data with garbage.
|
||||
// Operations such as dereferencing pointers or decreasing a refcount would fail.
|
||||
// Unfortunately, we may not poison the memory after the deletion, because the memory would no longer belong to us
|
||||
// and on doing so we may cause a more generalized crash on some platforms (allocator implementations).
|
||||
p_self->~Object();
|
||||
memset((void *)p_self, 0, sizeof(Object));
|
||||
Memory::free_static(p_self, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
|
||||
free_self(self);
|
||||
return Variant();
|
||||
}
|
||||
Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
|
||||
free_self(self);
|
||||
return Variant();
|
||||
}
|
||||
bool has_method(const StringName &p_method) const override {
|
||||
return p_method == "some_method";
|
||||
}
|
||||
|
||||
public:
|
||||
_SelfDestroyingScriptInstance(Object *p_self) :
|
||||
self(p_self) {}
|
||||
};
|
||||
|
||||
_SelfDestroyingScriptInstance *script_instance = memnew(_SelfDestroyingScriptInstance(object));
|
||||
object->set_script_instance(script_instance);
|
||||
|
||||
SUBCASE("Within callp()") {
|
||||
SUBCASE("Through call()") {
|
||||
object->call("some_method");
|
||||
}
|
||||
SUBCASE("Through callv()") {
|
||||
object->callv("some_method", Array());
|
||||
}
|
||||
}
|
||||
SUBCASE("Within call_const()") {
|
||||
Callable::CallError call_error;
|
||||
object->call_const("some_method", nullptr, 0, call_error);
|
||||
}
|
||||
SUBCASE("Within signal handling (from emit_signalp(), through emit_signal())") {
|
||||
Object emitter;
|
||||
emitter.add_user_signal(MethodInfo("some_signal"));
|
||||
emitter.connect("some_signal", Callable(object, "some_method"));
|
||||
emitter.emit_signal("some_signal");
|
||||
}
|
||||
|
||||
CHECK_MESSAGE(
|
||||
ObjectDB::get_instance(obj_id) == nullptr,
|
||||
"Object was tail-deleted without crashes.");
|
||||
}
|
||||
|
||||
} // namespace TestObject
|
||||
|
||||
#endif // TEST_OBJECT_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue