From 09bcee5274f3cbd9eafb986651177f120cf2f5f2 Mon Sep 17 00:00:00 2001 From: DylanCheetah Date: Wed, 17 Jul 2024 16:36:26 -0400 Subject: [PATCH 1/2] Fix invalid string to int cast yields 0. --- core/variant.cpp | 25 +++++++++++++++++++++++++ core/variant.h | 1 + core/variant_call.cpp | 6 +++--- editor/animation_track_editor.cpp | 8 ++++---- modules/gdscript/gdscript_parser.cpp | 2 +- platform/android/api/jni_singleton.h | 2 +- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/core/variant.cpp b/core/variant.cpp index 38dfe1c09a9e..df7b06fbdc73 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -601,6 +601,31 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type return false; } +bool Variant::can_convert(Type p_type_to) const { + // If this is a string variant and we are converting to an integer, check if the contents of the string + // make sense as an integer + if (type == Type::STRING && p_type_to == Type::INT) { + // Get a reference to the string + String s = operator String(); + + // Is the first character a numeral? + if (s.length() > 0 && s[0] >= '0' && s[0] <= '9') { + return true; + } + + // Is the first character '-' and the second character a numeral? + if (s.length() > 1 && s[0] == '-' && s[1] >= '0' && s[1] <= '9') { + return true; + } + + // The string cannot be converted to a valid integer + return false; + } + + // Do other type checks + return Variant::can_convert(type, p_type_to); +} + bool Variant::deep_equal(const Variant &p_variant, int p_recursion_count) const { ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, true, "Max recursion reached"); diff --git a/core/variant.h b/core/variant.h index b5923ea342c2..d91dc8aad48c 100644 --- a/core/variant.h +++ b/core/variant.h @@ -170,6 +170,7 @@ class Variant { static String get_type_name(Variant::Type p_type); static bool can_convert(Type p_type_from, Type p_type_to); static bool can_convert_strict(Type p_type_from, Type p_type_to); + bool can_convert(Type p_type_to) const; bool is_ref() const; _FORCE_INLINE_ bool is_num() const { return type == INT || type == REAL; }; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 83b3fd7ad71a..8b315edab37e 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -70,7 +70,7 @@ struct _VariantCall { if (tptr[i] == Variant::NIL || tptr[i] == p_args[i]->type) { continue; // all good } - if (!Variant::can_convert(p_args[i]->type, tptr[i])) { + if (!p_args[i]->can_convert(tptr[i])) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = i; r_error.expected = tptr[i]; @@ -1335,7 +1335,7 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i } else if (p_argcount == 1 && p_args[0]->type == p_type) { return *p_args[0]; //copy construct - } else if (p_argcount == 1 && (!p_strict || Variant::can_convert(p_args[0]->type, p_type))) { + } else if (p_argcount == 1 && (!p_strict || p_args[0]->can_convert(p_type))) { //near match construct switch (p_type) { @@ -1418,7 +1418,7 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i //validate parameters for (int i = 0; i < cd.arg_count; i++) { - if (!Variant::can_convert(p_args[i]->type, cd.arg_types[i])) { + if (!p_args[i]->can_convert(cd.arg_types[i])) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; //no such constructor r_error.argument = i; r_error.expected = cd.arg_types[i]; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 3d1baa6c97f5..57860a463a61 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -225,7 +225,7 @@ class AnimationTrackKeyEdit : public Object { if (t != args[idx].get_type()) { Variant::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { + if (args[idx].can_convert(t)) { Variant old = args[idx]; Variant *ptrs[1] = { &old }; args.write[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err); @@ -844,7 +844,7 @@ class AnimationMultiTrackKeyEdit : public Object { if (t != args[idx].get_type()) { Variant::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { + if (args[idx].can_convert(t)) { Variant old = args[idx]; Variant *ptrs[1] = { &old }; args.write[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err); @@ -2423,7 +2423,7 @@ bool AnimationTrackEdit::_is_value_key_valid(const Variant &p_key_value, Variant r_valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists); } - return (!prop_exists || Variant::can_convert(p_key_value.get_type(), r_valid_type)); + return (!prop_exists || p_key_value.can_convert(r_valid_type)); } Ref AnimationTrackEdit::_get_key_type_icon() const { @@ -5681,7 +5681,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref p_animation) { for (int j = 0; j < p_animation->track_get_key_count(i); j++) { Variant v = p_animation->track_get_key_value(i, j); - if (!Variant::can_convert(v.get_type(), valid_type)) { + if (!v.can_convert(valid_type)) { p_animation->track_remove_key(i, j); j--; } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index e396c8db0b01..54bbb8025b8b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -5127,7 +5127,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { ConstantNode *cn = static_cast(subexpr); if (cn->value.get_type() != Variant::NIL) { if (member._export.type != Variant::NIL && cn->value.get_type() != member._export.type) { - if (!Variant::can_convert(cn->value.get_type(), member._export.type)) { + if (!cn->value.can_convert(member._export.type)) { _set_error("Can't convert the provided value to the export type."); return; } else if (!member.data_type.has_type) { diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index 6cdd074a161f..4dc2d6878edb 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -61,7 +61,7 @@ class JNISingleton : public Object { bool call_error = !E || E->get().argtypes.size() != p_argcount; if (!call_error) { for (int i = 0; i < p_argcount; i++) { - if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { + if (!p_args[i]->can_convert(E->get().argtypes[i])) { call_error = true; break; } From 4f28ca0e74dcb6afdad833616f53c7eaa2c20b64 Mon Sep 17 00:00:00 2001 From: DylanCheetah Date: Fri, 22 Nov 2024 21:24:02 -0500 Subject: [PATCH 2/2] Use regex for cast to int validation. --- core/variant.cpp | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/core/variant.cpp b/core/variant.cpp index df7b06fbdc73..4a2181e31557 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -39,6 +39,9 @@ #include "core/variant_parser.h" #include "scene/gui/control.h" #include "scene/main/node.h" +#include "modules/regex/regex.h" + +RegEx number("^((-?\\d+)(\\.\\d+)?|true|false)$"); String Variant::get_type_name(Variant::Type p_type) { switch (p_type) { @@ -605,21 +608,9 @@ bool Variant::can_convert(Type p_type_to) const { // If this is a string variant and we are converting to an integer, check if the contents of the string // make sense as an integer if (type == Type::STRING && p_type_to == Type::INT) { - // Get a reference to the string - String s = operator String(); - - // Is the first character a numeral? - if (s.length() > 0 && s[0] >= '0' && s[0] <= '9') { - return true; - } - - // Is the first character '-' and the second character a numeral? - if (s.length() > 1 && s[0] == '-' && s[1] >= '0' && s[1] <= '9') { - return true; - } - - // The string cannot be converted to a valid integer - return false; + // Use regular expression to validate the string + Ref match = number.search(operator String()); + return match != NULL; } // Do other type checks