diff options
author | reduz <reduzio@gmail.com> | 2022-05-16 15:44:43 +0200 |
---|---|---|
committer | reduz <reduzio@gmail.com> | 2022-05-16 23:31:46 +0200 |
commit | e6c443a353cb3923b80f4fa112667a08159ee971 (patch) | |
tree | 238260992b1c448293d039a3b66b47e5d20cbd34 | |
parent | c41f62c3df85fd71539ae09bd97964e5e367d897 (diff) |
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.
-rw-r--r-- | core/variant/dictionary.cpp | 60 | ||||
-rw-r--r-- | core/variant/dictionary.h | 3 | ||||
-rw-r--r-- | core/variant/variant_setget.cpp | 13 |
3 files changed, 71 insertions, 5 deletions
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index 46543da2c2..bda8c93a79 100644 --- a/core/variant/dictionary.cpp +++ b/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); } diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index 16cf0c2bf8..1224a4ff6f 100644 --- a/core/variant/dictionary.h +++ b/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); diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 6023e4d129..6514922d01 100644 --- a/core/variant/variant_setget.cpp +++ b/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; } |