Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Fix operator[] for typed dictionaries #96797

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 49 additions & 10 deletions core/variant/dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,35 +83,64 @@ Variant Dictionary::get_value_at_index(int p_index) const {
return Variant();
}

// WARNING: This operator does not validate the value type. For scripting/extensions this is
// done in `variant_setget.cpp`. Consider using `set()` if the data might be invalid.
Variant &Dictionary::operator[](const Variant &p_key) {
Repiteo marked this conversation as resolved.
Show resolved Hide resolved
if (unlikely(_p->read_only)) {
if (likely(_p->variant_map.has(p_key))) {
*_p->read_only = _p->variant_map[p_key];
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "use `operator[]`"))) {
if (unlikely(!_p->typed_fallback)) {
_p->typed_fallback = memnew(Variant);
}
VariantInternal::initialize(_p->typed_fallback, _p->typed_value.type);
return *_p->typed_fallback;
} else if (unlikely(_p->read_only)) {
if (likely(_p->variant_map.has(key))) {
*_p->read_only = _p->variant_map[key];
} else {
*_p->read_only = Variant();
VariantInternal::initialize(_p->read_only, _p->typed_value.type);
}

return *_p->read_only;
} else {
return _p->variant_map[p_key];
if (unlikely(!_p->variant_map.has(key))) {
VariantInternal::initialize(&_p->variant_map[key], _p->typed_value.type);
}
return _p->variant_map[key];
}
}

const Variant &Dictionary::operator[](const Variant &p_key) const {
// Will not insert key, so no conversion is necessary.
return _p->variant_map[p_key];
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "use `operator[]`"))) {
if (unlikely(!_p->typed_fallback)) {
_p->typed_fallback = memnew(Variant);
}
VariantInternal::initialize(_p->typed_fallback, _p->typed_value.type);
return *_p->typed_fallback;
} else {
// Will not insert key, so no initialization is necessary.
return _p->variant_map[key];
}
}

const Variant *Dictionary::getptr(const Variant &p_key) const {
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(p_key));
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "getptr"))) {
return nullptr;
}
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(key));
if (!E) {
return nullptr;
}
return &E->value;
}

// WARNING: This method does not validate the value type.
Variant *Dictionary::getptr(const Variant &p_key) {
Repiteo marked this conversation as resolved.
Show resolved Hide resolved
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E(_p->variant_map.find(p_key));
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "getptr"))) {
return nullptr;
}
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E(_p->variant_map.find(key));
if (!E) {
return nullptr;
}
Expand Down Expand Up @@ -158,6 +187,16 @@ Variant Dictionary::get_or_add(const Variant &p_key, const Variant &p_default) {
return *result;
}

bool Dictionary::set(const Variant &p_key, const Variant &p_value) {
ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state.");
Variant key = p_key;
ERR_FAIL_COND_V(!_p->typed_key.validate(key, "set"), false);
Variant value = p_value;
ERR_FAIL_COND_V(!_p->typed_value.validate(value, "set"), false);
_p->variant_map[key] = value;
return true;
}

int Dictionary::size() const {
return _p->variant_map.size();
}
Expand Down
1 change: 1 addition & 0 deletions core/variant/dictionary.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class Dictionary {
Variant get_valid(const Variant &p_key) const;
Variant get(const Variant &p_key, const Variant &p_default) const;
Variant get_or_add(const Variant &p_key, const Variant &p_default);
bool set(const Variant &p_key, const Variant &p_value);

int size() const;
bool is_empty() const;
Expand Down
44 changes: 8 additions & 36 deletions core/variant/variant_setget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,20 +252,7 @@ void Variant::set_named(const StringName &p_member, const Variant &p_value, bool
}
} else if (type == Variant::DICTIONARY) {
Dictionary &dict = *VariantGetInternalPtr<Dictionary>::get_ptr(this);

if (dict.is_read_only()) {
r_valid = false;
return;
}

Variant *v = dict.getptr(p_member);
if (v) {
*v = p_value;
} else {
dict[p_member] = p_value;
}

r_valid = true;
r_valid = dict.set(p_member, p_value);
} else {
r_valid = false;
}
Expand Down Expand Up @@ -721,26 +708,16 @@ struct VariantIndexedSetGet_Dictionary {
PtrToArg<Variant>::encode(*ptr, member);
}
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
*valid = false;
*oob = true;
return;
}
(*VariantGetInternalPtr<Dictionary>::get_ptr(base))[index] = *value;
*oob = false;
*valid = true;
*valid = VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(index, *value);
*oob = VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only();
}
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
*oob = true;
return;
}
(*VariantGetInternalPtr<Dictionary>::get_ptr(base))[index] = *value;
*oob = false;
VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(index, *value);
*oob = VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only();
}
static void ptr_set(void *base, int64_t index, const void *member) {
Dictionary &v = *reinterpret_cast<Dictionary *>(base);
v[index] = PtrToArg<Variant>::convert(member);
v.set(index, PtrToArg<Variant>::convert(member));
}
static Variant::Type get_index_type() { return Variant::NIL; }
static uint32_t get_index_usage() { return PROPERTY_USAGE_DEFAULT; }
Expand Down Expand Up @@ -1010,16 +987,11 @@ 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;
*r_valid = VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(*key, *value);
}
static void ptr_set(void *base, const void *key, const void *value) {
Dictionary &v = *reinterpret_cast<Dictionary *>(base);
v[PtrToArg<Variant>::convert(key)] = PtrToArg<Variant>::convert(value);
v.set(PtrToArg<Variant>::convert(key), PtrToArg<Variant>::convert(value));
}

static bool has(const Variant *base, const Variant *key, bool *r_valid) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
func get_key() -> Variant:
return "key"

func test():
var typed: Dictionary[int, int]
typed[get_key()] = 0
print('not ok')
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/typed_dictionary_assign_differently_typed_key.gd
>> 6
>> Invalid assignment of property or key 'key' with value of type 'int' on a base object of type 'Dictionary[int, int]'.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
func get_value() -> Variant:
return "value"

func test():
var typed: Dictionary[int, int]
typed[0] = get_value()
print("not ok")
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/typed_dictionary_assign_differently_typed_value.gd
>> 6
>> Invalid assignment of property or key '0' with value of type 'String' on a base object of type 'Dictionary[int, int]'.
Loading