diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index fcbfdd47416..37eb16f9b2d 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3515,6 +3515,17 @@ bool Variant::is_shared() const { return is_type_shared(type); } +bool Variant::is_read_only() const { + switch (type) { + case ARRAY: + return reinterpret_cast(_data._mem)->is_read_only(); + case DICTIONARY: + return reinterpret_cast(_data._mem)->is_read_only(); + default: + return false; + } +} + void Variant::_variant_call_error(const String &p_method, Callable::CallError &error) { switch (error.error) { case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { diff --git a/core/variant/variant.h b/core/variant/variant.h index ea6ae02c1ed..93953c4e0e1 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -349,6 +349,7 @@ public: bool is_zero() const; bool is_one() const; bool is_null() const; + bool is_read_only() const; // Make sure Variant is not implicitly cast when accessing it with bracket notation (GH-49469). Variant &operator[](const Variant &p_key) = delete; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 163ffcb35b9..4e76965889f 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -884,23 +884,27 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif #ifdef DEBUG_ENABLED if (!valid) { - Object *obj = dst->get_validated_object(); - String v = index->operator String(); - bool read_only_property = false; - if (obj) { - read_only_property = ClassDB::has_property(obj->get_class_name(), v) && (ClassDB::get_property_setter(obj->get_class_name(), v) == StringName()); - } - if (read_only_property) { - err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", v, _get_var_type(dst)); + if (dst->is_read_only()) { + err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "')."; } else { - if (!v.is_empty()) { - v = "'" + v + "'"; - } else { - v = "of type '" + _get_var_type(index) + "'"; + Object *obj = dst->get_validated_object(); + String v = index->operator String(); + bool read_only_property = false; + if (obj) { + read_only_property = ClassDB::has_property(obj->get_class_name(), v) && (ClassDB::get_property_setter(obj->get_class_name(), v) == StringName()); } - err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; - if (err_code == Variant::VariantSetError::SET_INDEXED_ERR) { - err_text = "Invalid assignment of index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + if (read_only_property) { + err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", v, _get_var_type(dst)); + } else { + if (!v.is_empty()) { + v = "'" + v + "'"; + } else { + v = "of type '" + _get_var_type(index) + "'"; + } + err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; + if (err_code == Variant::VariantSetError::SET_INDEXED_ERR) { + err_text = "Invalid assignment of index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + } } } OPCODE_BREAK; @@ -926,13 +930,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (!valid) { - String v = index->operator String(); - if (!v.is_empty()) { - v = "'" + v + "'"; + if (dst->is_read_only()) { + err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "')."; } else { - v = "of type '" + _get_var_type(index) + "'"; + String v = index->operator String(); + if (!v.is_empty()) { + v = "'" + v + "'"; + } else { + v = "of type '" + _get_var_type(index) + "'"; + } + err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; } - err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; OPCODE_BREAK; } #endif @@ -958,13 +966,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (oob) { - String v = index->operator String(); - if (!v.is_empty()) { - v = "'" + v + "'"; + if (dst->is_read_only()) { + err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "')."; } else { - v = "of type '" + _get_var_type(index) + "'"; + String v = index->operator String(); + if (!v.is_empty()) { + v = "'" + v + "'"; + } else { + v = "of type '" + _get_var_type(index) + "'"; + } + err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')"; } - err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')"; OPCODE_BREAK; } #endif @@ -1092,15 +1104,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (!valid) { - Object *obj = dst->get_validated_object(); - bool read_only_property = false; - if (obj) { - read_only_property = ClassDB::has_property(obj->get_class_name(), *index) && (ClassDB::get_property_setter(obj->get_class_name(), *index) == StringName()); - } - if (read_only_property) { - err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", String(*index), _get_var_type(dst)); + if (dst->is_read_only()) { + err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "')."; } else { - err_text = "Invalid assignment of property or key '" + String(*index) + "' with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; + Object *obj = dst->get_validated_object(); + bool read_only_property = false; + if (obj) { + read_only_property = ClassDB::has_property(obj->get_class_name(), *index) && (ClassDB::get_property_setter(obj->get_class_name(), *index) == StringName()); + } + if (read_only_property) { + err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", String(*index), _get_var_type(dst)); + } else { + err_text = "Invalid assignment of property or key '" + String(*index) + "' with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; + } } OPCODE_BREAK; } diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out index c524a1ae6b9..350d5d1d458 100644 --- a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out @@ -3,4 +3,4 @@ GDTEST_RUNTIME_ERROR >> on function: test() >> runtime/errors/constant_array_is_deep.gd >> 6 ->> Invalid assignment of property or key '0' with value of type 'int' on a base object of type 'Dictionary'. +>> Invalid assignment on read-only value (on base: 'Dictionary'). diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out index cf51b0262d0..5f1f372b0a3 100644 --- a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out @@ -3,4 +3,4 @@ GDTEST_RUNTIME_ERROR >> on function: test() >> runtime/errors/constant_dictionary_is_deep.gd >> 6 ->> Invalid assignment of index '0' (on base: 'Array') with value of type 'int'. +>> Invalid assignment on read-only value (on base: 'Array'). diff --git a/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out b/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out index da7ce58d733..f7d531e1192 100644 --- a/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out +++ b/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out @@ -3,4 +3,4 @@ GDTEST_RUNTIME_ERROR >> on function: test() >> runtime/errors/read_only_dictionary.gd >> 4 ->> Invalid assignment of property or key 'a' with value of type 'int' on a base object of type 'Dictionary'. +>> Invalid assignment on read-only value (on base: 'Dictionary').