Browse Source

Implement read-only dictionaries.

* Add ability to set them read only.
* If read-only, it can't be modified.

This is added in order to optionally make const dictionaries (and eventually arrays) properly read-only in GDScript.
reduz 3 years ago
parent
commit
e6c443a353
3 changed files with 71 additions and 5 deletions
  1. 55 5
      core/variant/dictionary.cpp
  2. 3 0
      core/variant/dictionary.h
  3. 13 0
      core/variant/variant_setget.cpp

+ 55 - 5
core/variant/dictionary.cpp

@@ -41,6 +41,7 @@
 
 struct DictionaryPrivate {
 	SafeRefCount refcount;
+	Variant *read_only = nullptr; // If enabled, a pointer is used to a temporary value that is used to return read-only values.
 	HashMap<Variant, Variant, VariantHasher, VariantComparator> variant_map;
 };
 
@@ -79,11 +80,22 @@ Variant Dictionary::get_value_at_index(int p_index) const {
 }
 
 Variant &Dictionary::operator[](const Variant &p_key) {
-	if (p_key.get_type() == Variant::STRING_NAME) {
-		const StringName *sn = VariantInternal::get_string_name(&p_key);
-		return _p->variant_map[sn->operator String()];
+	if (unlikely(_p->read_only)) {
+		if (p_key.get_type() == Variant::STRING_NAME) {
+			const StringName *sn = VariantInternal::get_string_name(&p_key);
+			*_p->read_only = _p->variant_map[sn->operator String()];
+		} else {
+			*_p->read_only = _p->variant_map[p_key];
+		}
+
+		return *_p->read_only;
 	} else {
-		return _p->variant_map[p_key];
+		if (p_key.get_type() == Variant::STRING_NAME) {
+			const StringName *sn = VariantInternal::get_string_name(&p_key);
+			return _p->variant_map[sn->operator String()];
+		} else {
+			return _p->variant_map[p_key];
+		}
 	}
 }
 
@@ -124,7 +136,12 @@ Variant *Dictionary::getptr(const Variant &p_key) {
 	if (!E) {
 		return nullptr;
 	}
-	return &E->value;
+	if (unlikely(_p->read_only != nullptr)) {
+		*_p->read_only = E->value;
+		return _p->read_only;
+	} else {
+		return &E->value;
+	}
 }
 
 Variant Dictionary::get_valid(const Variant &p_key) const {
@@ -179,6 +196,7 @@ bool Dictionary::has_all(const Array &p_keys) const {
 }
 
 bool Dictionary::erase(const Variant &p_key) {
+	ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state.");
 	if (p_key.get_type() == Variant::STRING_NAME) {
 		const StringName *sn = VariantInternal::get_string_name(&p_key);
 		return _p->variant_map.erase(sn->operator String());
@@ -220,6 +238,16 @@ bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_c
 }
 
 void Dictionary::_ref(const Dictionary &p_from) const {
+	if (unlikely(p_from._p->read_only != nullptr)) {
+		// If p_from is a read-only dictionary, just copy the contents to avoid further modification.
+		if (_p) {
+			_unref();
+		}
+		_p = memnew(DictionaryPrivate);
+		_p->refcount.init();
+		_p->variant_map = p_from._p->variant_map;
+		return;
+	}
 	//make a copy first (thread safe)
 	if (!p_from._p->refcount.ref()) {
 		return; // couldn't copy
@@ -237,12 +265,16 @@ void Dictionary::_ref(const Dictionary &p_from) const {
 }
 
 void Dictionary::clear() {
+	ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
 	_p->variant_map.clear();
 }
 
 void Dictionary::_unref() const {
 	ERR_FAIL_COND(!_p);
 	if (_p->refcount.unref()) {
+		if (_p->read_only) {
+			memdelete(_p->read_only);
+		}
 		memdelete(_p);
 	}
 	_p = nullptr;
@@ -330,6 +362,21 @@ Dictionary Dictionary::duplicate(bool p_deep) const {
 	return recursive_duplicate(p_deep, 0);
 }
 
+void Dictionary::set_read_only(bool p_enable) {
+	if (p_enable == bool(_p->read_only != nullptr)) {
+		return;
+	}
+	if (p_enable) {
+		_p->read_only = memnew(Variant);
+	} else {
+		memdelete(_p->read_only);
+		_p->read_only = nullptr;
+	}
+}
+bool Dictionary::is_read_only() const {
+	return _p->read_only != nullptr;
+}
+
 Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) const {
 	Dictionary n;
 
@@ -353,6 +400,9 @@ Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) con
 }
 
 void Dictionary::operator=(const Dictionary &p_dictionary) {
+	if (this == &p_dictionary) {
+		return;
+	}
 	_ref(p_dictionary);
 }
 

+ 3 - 0
core/variant/dictionary.h

@@ -84,6 +84,9 @@ public:
 	Dictionary duplicate(bool p_deep = false) const;
 	Dictionary recursive_duplicate(bool p_deep, int recursion_count) const;
 
+	void set_read_only(bool p_enable);
+	bool is_read_only() const;
+
 	const void *id() const;
 
 	Dictionary(const Dictionary &p_from);

+ 13 - 0
core/variant/variant_setget.cpp

@@ -766,11 +766,20 @@ struct VariantIndexedSetGet_String {
 			PtrToArg<Variant>::encode(*ptr, member);                                                                                \
 		}                                                                                                                           \
 		static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {                               \
+			if (VariantGetInternalPtr<m_base_type>::get_ptr(base)->is_read_only()) {                                                \
+				*valid = false;                                                                                                     \
+				*oob = true;                                                                                                        \
+				return;                                                                                                             \
+			}                                                                                                                       \
 			(*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value;                                                   \
 			*oob = false;                                                                                                           \
 			*valid = true;                                                                                                          \
 		}                                                                                                                           \
 		static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {                                  \
+			if (VariantGetInternalPtr<m_base_type>::get_ptr(base)->is_read_only()) {                                                \
+				*oob = true;                                                                                                        \
+				return;                                                                                                             \
+			}                                                                                                                       \
 			(*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value;                                                   \
 			*oob = false;                                                                                                           \
 		}                                                                                                                           \
@@ -946,6 +955,10 @@ struct VariantKeyedSetGetDictionary {
 		PtrToArg<Variant>::encode(*ptr, value);
 	}
 	static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) {
+		if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
+			*r_valid = false;
+			return;
+		}
 		(*VariantGetInternalPtr<Dictionary>::get_ptr(base))[*key] = *value;
 		*r_valid = true;
 	}