From 6d3e1ec7a68c7411cec1443ecaaa4e55bf7a134a Mon Sep 17 00:00:00 2001 From: DylanCheetah Date: Wed, 17 Jul 2024 16:36:26 -0400 Subject: [PATCH] Fix invalid string to numeric type cast yields 0. --- core/variant.cpp | 54 ++++++++++++++++++++++++++++ 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, 64 insertions(+), 9 deletions(-) diff --git a/core/variant.cpp b/core/variant.cpp index 38dfe1c09a9e..aeaad7b9af21 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -37,6 +37,7 @@ #include "core/print_string.h" #include "core/resource.h" #include "core/variant_parser.h" +#include "modules/regex/regex.h" #include "scene/gui/control.h" #include "scene/main/node.h" @@ -601,6 +602,59 @@ 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 a numeric type, check if the contents of the + // string make sense as a number + if (type == Type::STRING && (p_type_to == Type::INT || p_type_to == Type::REAL)) { + // Get string + String s = operator String(); + + // Skip first character if it is '-' + int i = 0; + + if (s[i] == '-') { + i++; + + // If the first character is '-', there must be at least one character afterwards + if (s.length() < 2) { + return false; + } + } + + // Scan for invalid characters in whole portion of numeric literal + while (i < s.length()) { + // Period? + if (s[i] == '.') { + // Proceed to processing of decimal portion of numeric literal + i++; + break; + } else if (s[i] < '0' || s[i] > '9') { // Invalid character? + return false; + } + + // Next character + i++; + } + + // Scan for invalid characters in decimal portion of numeric literal + while (i < s.length()) { + // Invalid character? + if (s[i] < '0' || s[i] > '9') { + return false; + } + + // Next character + i++; + } + + // The string is a valid numeric literal + return true; + } + + // 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; }