Mono/C#: Make 'GD.Print' and its variants fallback to 'ToString()'

Up until now, 'GD.Print' would convert parameters first to
Variant and only then to String. This meant parameters that cannot be
converted to Variant would be printed as "Null".
This commit makes 'GD.Print' fallback to 'System.Object.ToString()'
if the parameter could not be converted to Variant.
The same applies to all 'GD.Print' variants:
'GD.PrintS', 'GD.PrintT', 'GD.PrintErr' and 'GD.PrintRaw'.
This commit is contained in:
Ignacio Etcheverry 2020-01-02 13:54:16 +01:00
parent ea75ea50d2
commit 844a8d215b
3 changed files with 153 additions and 33 deletions

View File

@ -71,48 +71,114 @@ MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id) {
}
void godot_icall_GD_print(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++)
str += what[i].operator String();
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = NULL;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
str += elem_str;
}
print_line(str);
}
void godot_icall_GD_printerr(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++)
str += what[i].operator String();
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = NULL;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
str += elem_str;
}
print_error(str);
}
void godot_icall_GD_printraw(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++)
str += what[i].operator String();
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = NULL;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
str += elem_str;
}
OS::get_singleton()->print("%s", str.utf8().get_data());
}
void godot_icall_GD_prints(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++) {
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = NULL;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
if (i)
str += " ";
str += what[i].operator String();
str += elem_str;
}
print_line(str);
}
void godot_icall_GD_printt(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++) {
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = NULL;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
if (i)
str += "\t";
str += what[i].operator String();
str += elem_str;
}
print_line(str);
}

View File

@ -700,15 +700,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
}
Variant mono_object_to_variant(MonoObject *p_obj) {
if (!p_obj)
return Variant();
Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) {
ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
ERR_FAIL_COND_V(!p_type.type_class, Variant());
ERR_FAIL_COND_V(!type.type_class, Variant());
switch (type.type_encoding) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return (bool)unbox<MonoBoolean>(p_obj);
@ -745,7 +741,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
} break;
case MONO_TYPE_VALUETYPE: {
GDMonoClass *vtclass = type.type_class;
GDMonoClass *vtclass = p_type.type_class;
if (vtclass == CACHED_CLASS(Vector2))
return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj));
@ -783,7 +779,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return mono_array_to_Array((MonoArray *)p_obj);
@ -809,11 +805,15 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
if (array_type->eklass == CACHED_CLASS_RAW(Color))
return mono_array_to_PoolColorArray((MonoArray *)p_obj);
ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant.");
if (p_fail_with_err) {
ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant.");
} else {
return Variant();
}
} break;
case MONO_TYPE_CLASS: {
GDMonoClass *type_class = type.type_class;
GDMonoClass *type_class = p_type.type_class;
// GodotObject
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
@ -871,18 +871,18 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
} break;
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
MonoException *exc = NULL;
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return *unbox<Dictionary *>(ret);
}
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
MonoException *exc = NULL;
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return *unbox<Array *>(ret);
}
@ -893,7 +893,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
}
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
}
@ -901,14 +901,62 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
} break;
}
ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" +
type.type_class->get_name() + "' Encoding: " + itos(type.type_encoding) + ".");
if (p_fail_with_err) {
ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" +
p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
} else {
return Variant();
}
}
Variant mono_object_to_variant(MonoObject *p_obj) {
if (!p_obj)
return Variant();
ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
return mono_object_to_variant_impl(p_obj, type);
}
Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) {
if (!p_obj)
return Variant();
return mono_object_to_variant_impl(p_obj, p_type);
}
Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type) {
if (!p_obj)
return Variant();
return mono_object_to_variant_impl(p_obj, p_type, /* fail_with_err: */ false);
}
String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type);
if (var.get_type() == Variant::NIL && p_obj != NULL) {
// Cannot convert MonoObject* to Variant; fallback to 'ToString()'.
MonoException *exc = NULL;
MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc);
if (exc) {
if (r_exc)
*r_exc = exc;
return String();
}
return GDMonoMarshal::mono_string_to_godot(mono_str);
} else {
return var.operator String();
}
}
MonoArray *Array_to_mono_array(const Array &p_array) {

View File

@ -115,6 +115,12 @@ _FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var, const Ma
}
Variant mono_object_to_variant(MonoObject *p_obj);
Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type);
Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type);
/// Tries to convert the MonoObject* to Variant and then convert the Variant to String.
/// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead.
String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc);
// Array