summaryrefslogtreecommitdiffstats
path: root/core/variant
diff options
context:
space:
mode:
Diffstat (limited to 'core/variant')
-rw-r--r--core/variant/array.cpp18
-rw-r--r--core/variant/array.h2
-rw-r--r--core/variant/binder_common.h4
-rw-r--r--core/variant/callable.cpp2
-rw-r--r--core/variant/dictionary.cpp293
-rw-r--r--core/variant/dictionary.h16
-rw-r--r--core/variant/type_info.h8
-rw-r--r--core/variant/typed_dictionary.h342
-rw-r--r--core/variant/variant.cpp11
-rw-r--r--core/variant/variant.h3
-rw-r--r--core/variant/variant_call.cpp18
-rw-r--r--core/variant/variant_construct.cpp1
-rw-r--r--core/variant/variant_construct.h123
-rw-r--r--core/variant/variant_internal.h4
-rw-r--r--core/variant/variant_op.h32
-rw-r--r--core/variant/variant_parser.cpp244
-rw-r--r--core/variant/variant_setget.cpp95
-rw-r--r--core/variant/variant_utility.cpp10
18 files changed, 1085 insertions, 141 deletions
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 3685515db5..869499e668 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -235,7 +235,7 @@ void Array::assign(const Array &p_array) {
for (int i = 0; i < size; i++) {
const Variant &element = source[i];
if (element.get_type() != Variant::NIL && (element.get_type() != Variant::OBJECT || !typed.validate_object(element, "assign"))) {
- ERR_FAIL_MSG(vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(element.get_type()), Variant::get_type_name(typed.type)));
+ ERR_FAIL_MSG(vformat(R"(Unable to convert array index %d from "%s" to "%s".)", i, Variant::get_type_name(element.get_type()), Variant::get_type_name(typed.type)));
}
}
_p->array = p_array._p->array;
@@ -258,11 +258,11 @@ void Array::assign(const Array &p_array) {
continue;
}
if (!Variant::can_convert_strict(value->get_type(), typed.type)) {
- ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(value->get_type()) + "' to '" + Variant::get_type_name(typed.type) + "'.");
+ ERR_FAIL_MSG(vformat(R"(Unable to convert array index %d from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
}
Callable::CallError ce;
Variant::construct(typed.type, data[i], &value, 1, ce);
- ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %d from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
}
} else if (Variant::can_convert_strict(source_typed.type, typed.type)) {
// from primitives to different convertible primitives
@@ -270,7 +270,7 @@ void Array::assign(const Array &p_array) {
const Variant *value = source + i;
Callable::CallError ce;
Variant::construct(typed.type, data[i], &value, 1, ce);
- ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %d from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
}
} else {
ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Array[%s]" to "Array[%s]".)", Variant::get_type_name(source_typed.type), Variant::get_type_name(typed.type)));
@@ -803,6 +803,10 @@ bool Array::is_same_typed(const Array &p_other) const {
return _p->typed == p_other._p->typed;
}
+bool Array::is_same_instance(const Array &p_other) const {
+ return _p == p_other._p;
+}
+
uint32_t Array::get_typed_builtin() const {
return _p->typed.type;
}
@@ -815,6 +819,12 @@ Variant Array::get_typed_script() const {
return _p->typed.script;
}
+Array Array::create_read_only() {
+ Array array;
+ array.make_read_only();
+ return array;
+}
+
void Array::make_read_only() {
if (_p->read_only == nullptr) {
_p->read_only = memnew(Variant);
diff --git a/core/variant/array.h b/core/variant/array.h
index 3aa957b312..12824ee3f6 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -186,12 +186,14 @@ public:
void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
bool is_typed() const;
bool is_same_typed(const Array &p_other) const;
+ bool is_same_instance(const Array &p_other) const;
uint32_t get_typed_builtin() const;
StringName get_typed_class_name() const;
Variant get_typed_script() const;
void make_read_only();
bool is_read_only() const;
+ static Array create_read_only();
Array(const Array &p_base, uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
Array(const Array &p_from);
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 61b90e2a26..fa49767d46 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -214,11 +214,11 @@ struct VariantCaster<char32_t> {
template <>
struct PtrToArg<char32_t> {
_FORCE_INLINE_ static char32_t convert(const void *p_ptr) {
- return char32_t(*reinterpret_cast<const int *>(p_ptr));
+ return char32_t(*reinterpret_cast<const int64_t *>(p_ptr));
}
typedef int64_t EncodeT;
_FORCE_INLINE_ static void encode(char32_t p_val, const void *p_ptr) {
- *(int *)p_ptr = p_val;
+ *(int64_t *)p_ptr = p_val;
}
};
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 667aae879c..9dff5c1e91 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -112,7 +112,7 @@ Error Callable::rpcp(int p_id, const Variant **p_arguments, int p_argcount, Call
argptrs[i + 2] = p_arguments[i];
}
- CallError tmp;
+ CallError tmp; // TODO: Check `tmp`?
Error err = (Error)obj->callp(SNAME("rpc_id"), argptrs, argcount, tmp).operator int64_t();
r_call_error.error = Callable::CallError::CALL_OK;
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 7416101d51..0754814d35 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -32,6 +32,7 @@
#include "core/templates/hash_map.h"
#include "core/templates/safe_refcount.h"
+#include "core/variant/container_type_validate.h"
#include "core/variant/variant.h"
// required in this order by VariantInternal, do not remove this comment.
#include "core/object/class_db.h"
@@ -43,6 +44,9 @@ 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, StringLikeVariantComparator> variant_map;
+ ContainerTypeValidate typed_key;
+ ContainerTypeValidate typed_value;
+ Variant *typed_fallback = nullptr; // Allows a typed dictionary to return dummy values when attempting an invalid access.
};
void Dictionary::get_key_list(List<Variant> *p_keys) const {
@@ -81,15 +85,7 @@ Variant Dictionary::get_value_at_index(int p_index) const {
Variant &Dictionary::operator[](const Variant &p_key) {
if (unlikely(_p->read_only)) {
- if (p_key.get_type() == Variant::STRING_NAME) {
- const StringName *sn = VariantInternal::get_string_name(&p_key);
- const String &key = sn->operator String();
- if (likely(_p->variant_map.has(key))) {
- *_p->read_only = _p->variant_map[key];
- } else {
- *_p->read_only = Variant();
- }
- } else if (likely(_p->variant_map.has(p_key))) {
+ if (likely(_p->variant_map.has(p_key))) {
*_p->read_only = _p->variant_map[p_key];
} else {
*_p->read_only = Variant();
@@ -97,12 +93,7 @@ Variant &Dictionary::operator[](const Variant &p_key) {
return *_p->read_only;
} else {
- 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];
- }
+ return _p->variant_map[p_key];
}
}
@@ -133,7 +124,9 @@ Variant *Dictionary::getptr(const Variant &p_key) {
}
Variant Dictionary::get_valid(const Variant &p_key) const {
- HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(p_key));
+ Variant key = p_key;
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "get_valid"), Variant());
+ HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(key));
if (!E) {
return Variant();
@@ -142,7 +135,9 @@ Variant Dictionary::get_valid(const Variant &p_key) const {
}
Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const {
- const Variant *result = getptr(p_key);
+ Variant key = p_key;
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "get"), p_default);
+ const Variant *result = getptr(key);
if (!result) {
return p_default;
}
@@ -151,10 +146,14 @@ Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const {
}
Variant Dictionary::get_or_add(const Variant &p_key, const Variant &p_default) {
- const Variant *result = getptr(p_key);
+ Variant key = p_key;
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "get"), p_default);
+ const Variant *result = getptr(key);
if (!result) {
- operator[](p_key) = p_default;
- return p_default;
+ Variant value = p_default;
+ ERR_FAIL_COND_V(!_p->typed_value.validate(value, "add"), value);
+ operator[](key) = value;
+ return value;
}
return *result;
}
@@ -168,12 +167,16 @@ bool Dictionary::is_empty() const {
}
bool Dictionary::has(const Variant &p_key) const {
+ Variant key = p_key;
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "use 'has'"), false);
return _p->variant_map.has(p_key);
}
bool Dictionary::has_all(const Array &p_keys) const {
for (int i = 0; i < p_keys.size(); i++) {
- if (!has(p_keys[i])) {
+ Variant key = p_keys[i];
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "use 'has_all'"), false);
+ if (!has(key)) {
return false;
}
}
@@ -181,8 +184,10 @@ bool Dictionary::has_all(const Array &p_keys) const {
}
Variant Dictionary::find_key(const Variant &p_value) const {
+ Variant value = p_value;
+ ERR_FAIL_COND_V(!_p->typed_value.validate(value, "find_key"), Variant());
for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
- if (E.value == p_value) {
+ if (E.value == value) {
return E.key;
}
}
@@ -190,8 +195,10 @@ Variant Dictionary::find_key(const Variant &p_value) const {
}
bool Dictionary::erase(const Variant &p_key) {
+ Variant key = p_key;
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "erase"), false);
ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state.");
- return _p->variant_map.erase(p_key);
+ return _p->variant_map.erase(key);
}
bool Dictionary::operator==(const Dictionary &p_dictionary) const {
@@ -251,8 +258,12 @@ void Dictionary::clear() {
void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) {
ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
- if (p_overwrite || !has(E.key)) {
- operator[](E.key) = E.value;
+ Variant key = E.key;
+ Variant value = E.value;
+ ERR_FAIL_COND(!_p->typed_key.validate(key, "merge"));
+ ERR_FAIL_COND(!_p->typed_value.validate(value, "merge"));
+ if (p_overwrite || !has(key)) {
+ operator[](key) = value;
}
}
}
@@ -269,6 +280,9 @@ void Dictionary::_unref() const {
if (_p->read_only) {
memdelete(_p->read_only);
}
+ if (_p->typed_fallback) {
+ memdelete(_p->typed_fallback);
+ }
memdelete(_p);
}
_p = nullptr;
@@ -297,6 +311,9 @@ uint32_t Dictionary::recursive_hash(int recursion_count) const {
Array Dictionary::keys() const {
Array varr;
+ if (is_typed_key()) {
+ varr.set_typed(get_typed_key_builtin(), get_typed_key_class_name(), get_typed_key_script());
+ }
if (_p->variant_map.is_empty()) {
return varr;
}
@@ -314,6 +331,9 @@ Array Dictionary::keys() const {
Array Dictionary::values() const {
Array varr;
+ if (is_typed_value()) {
+ varr.set_typed(get_typed_value_builtin(), get_typed_value_class_name(), get_typed_value_script());
+ }
if (_p->variant_map.is_empty()) {
return varr;
}
@@ -329,6 +349,146 @@ Array Dictionary::values() const {
return varr;
}
+void Dictionary::assign(const Dictionary &p_dictionary) {
+ const ContainerTypeValidate &typed_key = _p->typed_key;
+ const ContainerTypeValidate &typed_key_source = p_dictionary._p->typed_key;
+
+ const ContainerTypeValidate &typed_value = _p->typed_value;
+ const ContainerTypeValidate &typed_value_source = p_dictionary._p->typed_value;
+
+ if ((typed_key == typed_key_source || typed_key.type == Variant::NIL || (typed_key_source.type == Variant::OBJECT && typed_key.can_reference(typed_key_source))) &&
+ (typed_value == typed_value_source || typed_value.type == Variant::NIL || (typed_value_source.type == Variant::OBJECT && typed_value.can_reference(typed_value_source)))) {
+ // From same to same or,
+ // from anything to variants or,
+ // from subclasses to base classes.
+ _p->variant_map = p_dictionary._p->variant_map;
+ return;
+ }
+
+ int size = p_dictionary._p->variant_map.size();
+ HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator> variant_map = HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>(size);
+
+ Vector<Variant> key_array;
+ key_array.resize(size);
+ Variant *key_data = key_array.ptrw();
+
+ Vector<Variant> value_array;
+ value_array.resize(size);
+ Variant *value_data = value_array.ptrw();
+
+ if (typed_key == typed_key_source || typed_key.type == Variant::NIL || (typed_key_source.type == Variant::OBJECT && typed_key.can_reference(typed_key_source))) {
+ // From same to same or,
+ // from anything to variants or,
+ // from subclasses to base classes.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *key = &E.key;
+ key_data[i++] = *key;
+ }
+ } else if ((typed_key_source.type == Variant::NIL && typed_key.type == Variant::OBJECT) || (typed_key_source.type == Variant::OBJECT && typed_key_source.can_reference(typed_key))) {
+ // From variants to objects or,
+ // from base classes to subclasses.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *key = &E.key;
+ if (key->get_type() != Variant::NIL && (key->get_type() != Variant::OBJECT || !typed_key.validate_object(*key, "assign"))) {
+ ERR_FAIL_MSG(vformat(R"(Unable to convert key from "%s" to "%s".)", Variant::get_type_name(key->get_type()), Variant::get_type_name(typed_key.type)));
+ }
+ key_data[i++] = *key;
+ }
+ } else if (typed_key.type == Variant::OBJECT || typed_key_source.type == Variant::OBJECT) {
+ ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Dictionary[%s, %s]" to "Dictionary[%s, %s]".)", Variant::get_type_name(typed_key_source.type), Variant::get_type_name(typed_value_source.type),
+ Variant::get_type_name(typed_key.type), Variant::get_type_name(typed_value.type)));
+ } else if (typed_key_source.type == Variant::NIL && typed_key.type != Variant::OBJECT) {
+ // From variants to primitives.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *key = &E.key;
+ if (key->get_type() == typed_key.type) {
+ key_data[i++] = *key;
+ continue;
+ }
+ if (!Variant::can_convert_strict(key->get_type(), typed_key.type)) {
+ ERR_FAIL_MSG(vformat(R"(Unable to convert key from "%s" to "%s".)", Variant::get_type_name(key->get_type()), Variant::get_type_name(typed_key.type)));
+ }
+ Callable::CallError ce;
+ Variant::construct(typed_key.type, key_data[i++], &key, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert key from "%s" to "%s".)", Variant::get_type_name(key->get_type()), Variant::get_type_name(typed_key.type)));
+ }
+ } else if (Variant::can_convert_strict(typed_key_source.type, typed_key.type)) {
+ // From primitives to different convertible primitives.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *key = &E.key;
+ Callable::CallError ce;
+ Variant::construct(typed_key.type, key_data[i++], &key, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert key from "%s" to "%s".)", Variant::get_type_name(key->get_type()), Variant::get_type_name(typed_key.type)));
+ }
+ } else {
+ ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Dictionary[%s, %s]" to "Dictionary[%s, %s].)", Variant::get_type_name(typed_key_source.type), Variant::get_type_name(typed_value_source.type),
+ Variant::get_type_name(typed_key.type), Variant::get_type_name(typed_value.type)));
+ }
+
+ if (typed_value == typed_value_source || typed_value.type == Variant::NIL || (typed_value_source.type == Variant::OBJECT && typed_value.can_reference(typed_value_source))) {
+ // From same to same or,
+ // from anything to variants or,
+ // from subclasses to base classes.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *value = &E.value;
+ value_data[i++] = *value;
+ }
+ } else if (((typed_value_source.type == Variant::NIL && typed_value.type == Variant::OBJECT) || (typed_value_source.type == Variant::OBJECT && typed_value_source.can_reference(typed_value)))) {
+ // From variants to objects or,
+ // from base classes to subclasses.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *value = &E.value;
+ if (value->get_type() != Variant::NIL && (value->get_type() != Variant::OBJECT || !typed_value.validate_object(*value, "assign"))) {
+ ERR_FAIL_MSG(vformat(R"(Unable to convert value at key "%s" from "%s" to "%s".)", key_data[i], Variant::get_type_name(value->get_type()), Variant::get_type_name(typed_value.type)));
+ }
+ value_data[i++] = *value;
+ }
+ } else if (typed_value.type == Variant::OBJECT || typed_value_source.type == Variant::OBJECT) {
+ ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Dictionary[%s, %s]" to "Dictionary[%s, %s]".)", Variant::get_type_name(typed_key_source.type), Variant::get_type_name(typed_value_source.type),
+ Variant::get_type_name(typed_key.type), Variant::get_type_name(typed_value.type)));
+ } else if (typed_value_source.type == Variant::NIL && typed_value.type != Variant::OBJECT) {
+ // From variants to primitives.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *value = &E.value;
+ if (value->get_type() == typed_value.type) {
+ value_data[i++] = *value;
+ continue;
+ }
+ if (!Variant::can_convert_strict(value->get_type(), typed_value.type)) {
+ ERR_FAIL_MSG(vformat(R"(Unable to convert value at key "%s" from "%s" to "%s".)", key_data[i], Variant::get_type_name(value->get_type()), Variant::get_type_name(typed_value.type)));
+ }
+ Callable::CallError ce;
+ Variant::construct(typed_value.type, value_data[i++], &value, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert value at key "%s" from "%s" to "%s".)", key_data[i - 1], Variant::get_type_name(value->get_type()), Variant::get_type_name(typed_value.type)));
+ }
+ } else if (Variant::can_convert_strict(typed_value_source.type, typed_value.type)) {
+ // From primitives to different convertible primitives.
+ int i = 0;
+ for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
+ const Variant *value = &E.value;
+ Callable::CallError ce;
+ Variant::construct(typed_value.type, value_data[i++], &value, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert value at key "%s" from "%s" to "%s".)", key_data[i - 1], Variant::get_type_name(value->get_type()), Variant::get_type_name(typed_value.type)));
+ }
+ } else {
+ ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Dictionary[%s, %s]" to "Dictionary[%s, %s].)", Variant::get_type_name(typed_key_source.type), Variant::get_type_name(typed_value_source.type),
+ Variant::get_type_name(typed_key.type), Variant::get_type_name(typed_value.type)));
+ }
+
+ for (int i = 0; i < size; i++) {
+ variant_map.insert(key_data[i], value_data[i]);
+ }
+
+ _p->variant_map = variant_map;
+}
+
const Variant *Dictionary::next(const Variant *p_key) const {
if (p_key == nullptr) {
// caller wants to get the first element
@@ -337,7 +497,9 @@ const Variant *Dictionary::next(const Variant *p_key) const {
}
return nullptr;
}
- HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E = _p->variant_map.find(*p_key);
+ Variant key = *p_key;
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "next"), nullptr);
+ HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E = _p->variant_map.find(key);
if (!E) {
return nullptr;
@@ -367,6 +529,8 @@ bool Dictionary::is_read_only() const {
Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) const {
Dictionary n;
+ n._p->typed_key = _p->typed_key;
+ n._p->typed_value = _p->typed_value;
if (recursion_count > MAX_RECURSION) {
ERR_PRINT("Max recursion reached");
@@ -387,6 +551,76 @@ Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) con
return n;
}
+void Dictionary::set_typed(uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
+ ERR_FAIL_COND_MSG(_p->variant_map.size() > 0, "Type can only be set when dictionary is empty.");
+ ERR_FAIL_COND_MSG(_p->refcount.get() > 1, "Type can only be set when dictionary has no more than one user.");
+ ERR_FAIL_COND_MSG(_p->typed_key.type != Variant::NIL || _p->typed_value.type != Variant::NIL, "Type can only be set once.");
+ ERR_FAIL_COND_MSG((p_key_class_name != StringName() && p_key_type != Variant::OBJECT) || (p_value_class_name != StringName() && p_value_type != Variant::OBJECT), "Class names can only be set for type OBJECT.");
+ Ref<Script> key_script = p_key_script;
+ ERR_FAIL_COND_MSG(key_script.is_valid() && p_key_class_name == StringName(), "Script class can only be set together with base class name.");
+ Ref<Script> value_script = p_value_script;
+ ERR_FAIL_COND_MSG(value_script.is_valid() && p_value_class_name == StringName(), "Script class can only be set together with base class name.");
+
+ _p->typed_key.type = Variant::Type(p_key_type);
+ _p->typed_key.class_name = p_key_class_name;
+ _p->typed_key.script = key_script;
+ _p->typed_key.where = "TypedDictionary.Key";
+
+ _p->typed_value.type = Variant::Type(p_value_type);
+ _p->typed_value.class_name = p_value_class_name;
+ _p->typed_value.script = value_script;
+ _p->typed_value.where = "TypedDictionary.Value";
+}
+
+bool Dictionary::is_typed() const {
+ return is_typed_key() || is_typed_value();
+}
+
+bool Dictionary::is_typed_key() const {
+ return _p->typed_key.type != Variant::NIL;
+}
+
+bool Dictionary::is_typed_value() const {
+ return _p->typed_value.type != Variant::NIL;
+}
+
+bool Dictionary::is_same_typed(const Dictionary &p_other) const {
+ return is_same_typed_key(p_other) && is_same_typed_value(p_other);
+}
+
+bool Dictionary::is_same_typed_key(const Dictionary &p_other) const {
+ return _p->typed_key == p_other._p->typed_key;
+}
+
+bool Dictionary::is_same_typed_value(const Dictionary &p_other) const {
+ return _p->typed_value == p_other._p->typed_value;
+}
+
+uint32_t Dictionary::get_typed_key_builtin() const {
+ return _p->typed_key.type;
+}
+
+uint32_t Dictionary::get_typed_value_builtin() const {
+ return _p->typed_value.type;
+}
+
+StringName Dictionary::get_typed_key_class_name() const {
+ return _p->typed_key.class_name;
+}
+
+StringName Dictionary::get_typed_value_class_name() const {
+ return _p->typed_value.class_name;
+}
+
+Variant Dictionary::get_typed_key_script() const {
+ return _p->typed_key.script;
+}
+
+Variant Dictionary::get_typed_value_script() const {
+ return _p->typed_value.script;
+}
+
void Dictionary::operator=(const Dictionary &p_dictionary) {
if (this == &p_dictionary) {
return;
@@ -398,6 +632,13 @@ const void *Dictionary::id() const {
return _p;
}
+Dictionary::Dictionary(const Dictionary &p_base, uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script) {
+ _p = memnew(DictionaryPrivate);
+ _p->refcount.init();
+ set_typed(p_key_type, p_key_class_name, p_key_script, p_value_type, p_value_class_name, p_value_script);
+ assign(p_base);
+}
+
Dictionary::Dictionary(const Dictionary &p_from) {
_p = nullptr;
_ref(p_from);
diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h
index 67178ee7b7..57fbefc8f2 100644
--- a/core/variant/dictionary.h
+++ b/core/variant/dictionary.h
@@ -80,6 +80,7 @@ public:
uint32_t recursive_hash(int recursion_count) const;
void operator=(const Dictionary &p_dictionary);
+ void assign(const Dictionary &p_dictionary);
const Variant *next(const Variant *p_key = nullptr) const;
Array keys() const;
@@ -88,11 +89,26 @@ public:
Dictionary duplicate(bool p_deep = false) const;
Dictionary recursive_duplicate(bool p_deep, int recursion_count) const;
+ void set_typed(uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script);
+ bool is_typed() const;
+ bool is_typed_key() const;
+ bool is_typed_value() const;
+ bool is_same_typed(const Dictionary &p_other) const;
+ bool is_same_typed_key(const Dictionary &p_other) const;
+ bool is_same_typed_value(const Dictionary &p_other) const;
+ uint32_t get_typed_key_builtin() const;
+ uint32_t get_typed_value_builtin() const;
+ StringName get_typed_key_class_name() const;
+ StringName get_typed_value_class_name() const;
+ Variant get_typed_key_script() const;
+ Variant get_typed_value_script() const;
+
void make_read_only();
bool is_read_only() const;
const void *id() const;
+ Dictionary(const Dictionary &p_base, uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script);
Dictionary(const Dictionary &p_from);
Dictionary();
~Dictionary();
diff --git a/core/variant/type_info.h b/core/variant/type_info.h
index d51c80eebe..6bb703f2dd 100644
--- a/core/variant/type_info.h
+++ b/core/variant/type_info.h
@@ -47,7 +47,9 @@ enum Metadata {
METADATA_INT_IS_UINT32,
METADATA_INT_IS_UINT64,
METADATA_REAL_IS_FLOAT,
- METADATA_REAL_IS_DOUBLE
+ METADATA_REAL_IS_DOUBLE,
+ METADATA_INT_IS_CHAR16,
+ METADATA_INT_IS_CHAR32,
};
}
@@ -104,8 +106,8 @@ MAKE_TYPE_INFO_WITH_META(uint32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_
MAKE_TYPE_INFO_WITH_META(int32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT32)
MAKE_TYPE_INFO_WITH_META(uint64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT64)
MAKE_TYPE_INFO_WITH_META(int64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT64)
-MAKE_TYPE_INFO(char16_t, Variant::INT)
-MAKE_TYPE_INFO(char32_t, Variant::INT)
+MAKE_TYPE_INFO_WITH_META(char16_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_CHAR16)
+MAKE_TYPE_INFO_WITH_META(char32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_CHAR32)
MAKE_TYPE_INFO_WITH_META(float, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_FLOAT)
MAKE_TYPE_INFO_WITH_META(double, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_DOUBLE)
diff --git a/core/variant/typed_dictionary.h b/core/variant/typed_dictionary.h
new file mode 100644
index 0000000000..67fc33b4fc
--- /dev/null
+++ b/core/variant/typed_dictionary.h
@@ -0,0 +1,342 @@
+/**************************************************************************/
+/* typed_dictionary.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TYPED_DICTIONARY_H
+#define TYPED_DICTIONARY_H
+
+#include "core/object/object.h"
+#include "core/variant/binder_common.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/method_ptrcall.h"
+#include "core/variant/type_info.h"
+#include "core/variant/variant.h"
+
+template <typename K, typename V>
+class TypedDictionary : public Dictionary {
+public:
+ _FORCE_INLINE_ void operator=(const Dictionary &p_dictionary) {
+ ERR_FAIL_COND_MSG(!is_same_typed(p_dictionary), "Cannot assign a dictionary with a different element type.");
+ Dictionary::operator=(p_dictionary);
+ }
+ _FORCE_INLINE_ TypedDictionary(const Variant &p_variant) :
+ TypedDictionary(Dictionary(p_variant)) {
+ }
+ _FORCE_INLINE_ TypedDictionary(const Dictionary &p_dictionary) {
+ set_typed(Variant::OBJECT, K::get_class_static(), Variant(), Variant::OBJECT, V::get_class_static(), Variant());
+ if (is_same_typed(p_dictionary)) {
+ Dictionary::operator=(p_dictionary);
+ } else {
+ assign(p_dictionary);
+ }
+ }
+ _FORCE_INLINE_ TypedDictionary() {
+ set_typed(Variant::OBJECT, K::get_class_static(), Variant(), Variant::OBJECT, V::get_class_static(), Variant());
+ }
+};
+
+template <typename K, typename V>
+struct VariantInternalAccessor<TypedDictionary<K, V>> {
+ static _FORCE_INLINE_ TypedDictionary<K, V> get(const Variant *v) { return *VariantInternal::get_dictionary(v); }
+ static _FORCE_INLINE_ void set(Variant *v, const TypedDictionary<K, V> &p_dictionary) { *VariantInternal::get_dictionary(v) = p_dictionary; }
+};
+
+template <typename K, typename V>
+struct VariantInternalAccessor<const TypedDictionary<K, V> &> {
+ static _FORCE_INLINE_ TypedDictionary<K, V> get(const Variant *v) { return *VariantInternal::get_dictionary(v); }
+ static _FORCE_INLINE_ void set(Variant *v, const TypedDictionary<K, V> &p_dictionary) { *VariantInternal::get_dictionary(v) = p_dictionary; }
+};
+
+template <typename K, typename V>
+struct PtrToArg<TypedDictionary<K, V>> {
+ _FORCE_INLINE_ static TypedDictionary<K, V> convert(const void *p_ptr) {
+ return TypedDictionary<K, V>(*reinterpret_cast<const Dictionary *>(p_ptr));
+ }
+ typedef Dictionary EncodeT;
+ _FORCE_INLINE_ static void encode(TypedDictionary<K, V> p_val, void *p_ptr) {
+ *(Dictionary *)p_ptr = p_val;
+ }
+};
+
+template <typename K, typename V>
+struct PtrToArg<const TypedDictionary<K, V> &> {
+ typedef Dictionary EncodeT;
+ _FORCE_INLINE_ static TypedDictionary<K, V>
+ convert(const void *p_ptr) {
+ return TypedDictionary<K, V>(*reinterpret_cast<const Dictionary *>(p_ptr));
+ }
+};
+
+template <typename K, typename V>
+struct GetTypeInfo<TypedDictionary<K, V>> {
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY;
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+ static inline PropertyInfo get_class_info() {
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, vformat("%s;%s", K::get_class_static(), V::get_class_static()));
+ }
+};
+
+template <typename K, typename V>
+struct GetTypeInfo<const TypedDictionary<K, V> &> {
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY;
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+ static inline PropertyInfo get_class_info() {
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, vformat("%s;%s", K::get_class_static(), V::get_class_static()));
+ }
+};
+
+// Specialization for the rest of the Variant types.
+
+#define MAKE_TYPED_DICTIONARY_WITH_OBJECT(m_type, m_variant_type) \
+ template <typename T> \
+ class TypedDictionary<T, m_type> : public Dictionary { \
+ public: \
+ _FORCE_INLINE_ void operator=(const Dictionary &p_dictionary) { \
+ ERR_FAIL_COND_MSG(!is_same_typed(p_dictionary), "Cannot assign an dictionary with a different element type."); \
+ Dictionary::operator=(p_dictionary); \
+ } \
+ _FORCE_INLINE_ TypedDictionary(const Variant &p_variant) : \
+ TypedDictionary(Dictionary(p_variant)) { \
+ } \
+ _FORCE_INLINE_ TypedDictionary(const Dictionary &p_dictionary) { \
+ set_typed(Variant::OBJECT, T::get_class_static(), Variant(), m_variant_type, StringName(), Variant()); \
+ if (is_same_typed(p_dictionary)) { \
+ Dictionary::operator=(p_dictionary); \
+ } else { \
+ assign(p_dictionary); \
+ } \
+ } \
+ _FORCE_INLINE_ TypedDictionary() { \
+ set_typed(Variant::OBJECT, T::get_class_static(), Variant(), m_variant_type, StringName(), Variant()); \
+ } \
+ }; \
+ template <typename T> \
+ struct GetTypeInfo<TypedDictionary<T, m_type>> { \
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY; \
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
+ static inline PropertyInfo get_class_info() { \
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, \
+ vformat("%s;%s", T::get_class_static(), m_variant_type == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type))); \
+ } \
+ }; \
+ template <typename T> \
+ struct GetTypeInfo<const TypedDictionary<T, m_type> &> { \
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY; \
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
+ static inline PropertyInfo get_class_info() { \
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, \
+ vformat("%s;%s", T::get_class_static(), m_variant_type == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type))); \
+ } \
+ }; \
+ template <typename T> \
+ class TypedDictionary<m_type, T> : public Dictionary { \
+ public: \
+ _FORCE_INLINE_ void operator=(const Dictionary &p_dictionary) { \
+ ERR_FAIL_COND_MSG(!is_same_typed(p_dictionary), "Cannot assign an dictionary with a different element type."); \
+ Dictionary::operator=(p_dictionary); \
+ } \
+ _FORCE_INLINE_ TypedDictionary(const Variant &p_variant) : \
+ TypedDictionary(Dictionary(p_variant)) { \
+ } \
+ _FORCE_INLINE_ TypedDictionary(const Dictionary &p_dictionary) { \
+ set_typed(m_variant_type, StringName(), Variant(), Variant::OBJECT, T::get_class_static(), Variant()); \
+ if (is_same_typed(p_dictionary)) { \
+ Dictionary::operator=(p_dictionary); \
+ } else { \
+ assign(p_dictionary); \
+ } \
+ } \
+ _FORCE_INLINE_ TypedDictionary() { \
+ set_typed(m_variant_type, StringName(), Variant(), Variant::OBJECT, T::get_class_static(), Variant()); \
+ } \
+ }; \
+ template <typename T> \
+ struct GetTypeInfo<TypedDictionary<m_type, T>> { \
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY; \
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
+ static inline PropertyInfo get_class_info() { \
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, \
+ vformat("%s;%s", m_variant_type == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type), T::get_class_static())); \
+ } \
+ }; \
+ template <typename T> \
+ struct GetTypeInfo<const TypedDictionary<m_type, T> &> { \
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY; \
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
+ static inline PropertyInfo get_class_info() { \
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, \
+ vformat("%s;%s", m_variant_type == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type), T::get_class_static())); \
+ } \
+ };
+
+#define MAKE_TYPED_DICTIONARY_EXPANDED(m_type_key, m_variant_type_key, m_type_value, m_variant_type_value) \
+ template <> \
+ class TypedDictionary<m_type_key, m_type_value> : public Dictionary { \
+ public: \
+ _FORCE_INLINE_ void operator=(const Dictionary &p_dictionary) { \
+ ERR_FAIL_COND_MSG(!is_same_typed(p_dictionary), "Cannot assign an dictionary with a different element type."); \
+ Dictionary::operator=(p_dictionary); \
+ } \
+ _FORCE_INLINE_ TypedDictionary(const Variant &p_variant) : \
+ TypedDictionary(Dictionary(p_variant)) { \
+ } \
+ _FORCE_INLINE_ TypedDictionary(const Dictionary &p_dictionary) { \
+ set_typed(m_variant_type_key, StringName(), Variant(), m_variant_type_value, StringName(), Variant()); \
+ if (is_same_typed(p_dictionary)) { \
+ Dictionary::operator=(p_dictionary); \
+ } else { \
+ assign(p_dictionary); \
+ } \
+ } \
+ _FORCE_INLINE_ TypedDictionary() { \
+ set_typed(m_variant_type_key, StringName(), Variant(), m_variant_type_value, StringName(), Variant()); \
+ } \
+ }; \
+ template <> \
+ struct GetTypeInfo<TypedDictionary<m_type_key, m_type_value>> { \
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY; \
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
+ static inline PropertyInfo get_class_info() { \
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, \
+ vformat("%s;%s", m_variant_type_key == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type_key), \
+ m_variant_type_value == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type_value))); \
+ } \
+ }; \
+ template <> \
+ struct GetTypeInfo<const TypedDictionary<m_type_key, m_type_value> &> { \
+ static const Variant::Type VARIANT_TYPE = Variant::DICTIONARY; \
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
+ static inline PropertyInfo get_class_info() { \
+ return PropertyInfo(Variant::DICTIONARY, String(), PROPERTY_HINT_DICTIONARY_TYPE, \
+ vformat("%s;%s", m_variant_type_key == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type_key), \
+ m_variant_type_value == Variant::NIL ? "Variant" : Variant::get_type_name(m_variant_type_value))); \
+ } \
+ };
+
+#define MAKE_TYPED_DICTIONARY_NIL(m_type, m_variant_type) \
+ MAKE_TYPED_DICTIONARY_WITH_OBJECT(m_type, m_variant_type) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, bool, Variant::BOOL) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, uint8_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, int8_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, uint16_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, int16_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, uint32_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, int32_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, uint64_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, int64_t, Variant::INT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, float, Variant::FLOAT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, double, Variant::FLOAT) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, String, Variant::STRING) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Vector2, Variant::VECTOR2) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Vector2i, Variant::VECTOR2I) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Rect2, Variant::RECT2) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Rect2i, Variant::RECT2I) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Vector3, Variant::VECTOR3) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Vector3i, Variant::VECTOR3I) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Transform2D, Variant::TRANSFORM2D) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Plane, Variant::PLANE) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Quaternion, Variant::QUATERNION) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, AABB, Variant::AABB) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Basis, Variant::BASIS) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Transform3D, Variant::TRANSFORM3D) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Color, Variant::COLOR) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, StringName, Variant::STRING_NAME) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, NodePath, Variant::NODE_PATH) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, RID, Variant::RID) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Callable, Variant::CALLABLE) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Signal, Variant::SIGNAL) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Dictionary, Variant::DICTIONARY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Array, Variant::ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedByteArray, Variant::PACKED_BYTE_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedInt32Array, Variant::PACKED_INT32_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedInt64Array, Variant::PACKED_INT64_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedFloat32Array, Variant::PACKED_FLOAT32_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedFloat64Array, Variant::PACKED_FLOAT64_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedStringArray, Variant::PACKED_STRING_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedColorArray, Variant::PACKED_COLOR_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, PackedVector4Array, Variant::PACKED_VECTOR4_ARRAY) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, IPAddress, Variant::STRING)
+
+#define MAKE_TYPED_DICTIONARY(m_type, m_variant_type) \
+ MAKE_TYPED_DICTIONARY_EXPANDED(m_type, m_variant_type, Variant, Variant::NIL) \
+ MAKE_TYPED_DICTIONARY_NIL(m_type, m_variant_type)
+
+MAKE_TYPED_DICTIONARY_NIL(Variant, Variant::NIL)
+MAKE_TYPED_DICTIONARY(bool, Variant::BOOL)
+MAKE_TYPED_DICTIONARY(uint8_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(int8_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(uint16_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(int16_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(uint32_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(int32_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(uint64_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(int64_t, Variant::INT)
+MAKE_TYPED_DICTIONARY(float, Variant::FLOAT)
+MAKE_TYPED_DICTIONARY(double, Variant::FLOAT)
+MAKE_TYPED_DICTIONARY(String, Variant::STRING)
+MAKE_TYPED_DICTIONARY(Vector2, Variant::VECTOR2)
+MAKE_TYPED_DICTIONARY(Vector2i, Variant::VECTOR2I)
+MAKE_TYPED_DICTIONARY(Rect2, Variant::RECT2)
+MAKE_TYPED_DICTIONARY(Rect2i, Variant::RECT2I)
+MAKE_TYPED_DICTIONARY(Vector3, Variant::VECTOR3)
+MAKE_TYPED_DICTIONARY(Vector3i, Variant::VECTOR3I)
+MAKE_TYPED_DICTIONARY(Transform2D, Variant::TRANSFORM2D)
+MAKE_TYPED_DICTIONARY(Plane, Variant::PLANE)
+MAKE_TYPED_DICTIONARY(Quaternion, Variant::QUATERNION)
+MAKE_TYPED_DICTIONARY(AABB, Variant::AABB)
+MAKE_TYPED_DICTIONARY(Basis, Variant::BASIS)
+MAKE_TYPED_DICTIONARY(Transform3D, Variant::TRANSFORM3D)
+MAKE_TYPED_DICTIONARY(Color, Variant::COLOR)
+MAKE_TYPED_DICTIONARY(StringName, Variant::STRING_NAME)
+MAKE_TYPED_DICTIONARY(NodePath, Variant::NODE_PATH)
+MAKE_TYPED_DICTIONARY(RID, Variant::RID)
+MAKE_TYPED_DICTIONARY(Callable, Variant::CALLABLE)
+MAKE_TYPED_DICTIONARY(Signal, Variant::SIGNAL)
+MAKE_TYPED_DICTIONARY(Dictionary, Variant::DICTIONARY)
+MAKE_TYPED_DICTIONARY(Array, Variant::ARRAY)
+MAKE_TYPED_DICTIONARY(PackedByteArray, Variant::PACKED_BYTE_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedInt32Array, Variant::PACKED_INT32_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedInt64Array, Variant::PACKED_INT64_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedFloat32Array, Variant::PACKED_FLOAT32_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedFloat64Array, Variant::PACKED_FLOAT64_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedStringArray, Variant::PACKED_STRING_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPED_DICTIONARY(PackedVector4Array, Variant::PACKED_VECTOR4_ARRAY)
+MAKE_TYPED_DICTIONARY(IPAddress, Variant::STRING)
+
+#undef MAKE_TYPED_DICTIONARY
+#undef MAKE_TYPED_DICTIONARY_NIL
+#undef MAKE_TYPED_DICTIONARY_EXPANDED
+#undef MAKE_TYPED_DICTIONARY_WITH_OBJECT
+
+#endif // TYPED_DICTIONARY_H
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 30a8facd67..186643b024 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -951,7 +951,7 @@ bool Variant::is_zero() const {
return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID();
}
case OBJECT: {
- return get_validated_object() == nullptr;
+ return _get_obj().obj == nullptr;
}
case CALLABLE: {
return reinterpret_cast<const Callable *>(_data._mem)->is_null();
@@ -1072,13 +1072,6 @@ bool Variant::is_null() const {
}
}
-bool Variant::initialize_ref(Object *p_object) {
- RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_object));
- if (!ref_counted->init_ref()) {
- return false;
- }
- return true;
-}
void Variant::reference(const Variant &p_variant) {
switch (type) {
case NIL:
@@ -2120,7 +2113,7 @@ Variant::operator ::RID() const {
}
#endif
Callable::CallError ce;
- Variant ret = _get_obj().obj->callp(CoreStringName(get_rid), nullptr, 0, ce);
+ const Variant ret = _get_obj().obj->callp(CoreStringName(get_rid), nullptr, 0, ce);
if (ce.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::RID) {
return ret;
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index f352af24da..d4e4b330cd 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -254,7 +254,6 @@ private:
} _data alignas(8);
void reference(const Variant &p_variant);
- static bool initialize_ref(Object *p_object);
void _clear_internal();
@@ -857,7 +856,7 @@ String vformat(const String &p_text, const VarArgs... p_args) {
bool error = false;
String fmt = p_text.sprintf(args_array, &error);
- ERR_FAIL_COND_V_MSG(error, String(), fmt);
+ ERR_FAIL_COND_V_MSG(error, String(), String("Formatting error in string \"") + p_text + "\": " + fmt + ".");
return fmt;
}
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 5e402937cf..2da94de875 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1724,6 +1724,8 @@ static void _register_variant_builtin_methods_string() {
bind_string_method(validate_node_name, sarray(), varray());
bind_string_method(validate_filename, sarray(), varray());
+ bind_string_method(is_valid_ascii_identifier, sarray(), varray());
+ bind_string_method(is_valid_unicode_identifier, sarray(), varray());
bind_string_method(is_valid_identifier, sarray(), varray());
bind_string_method(is_valid_int, sarray(), varray());
bind_string_method(is_valid_float, sarray(), varray());
@@ -1849,6 +1851,7 @@ static void _register_variant_builtin_methods_math() {
bind_method(Rect2, intersection, sarray("b"), varray());
bind_method(Rect2, merge, sarray("b"), varray());
bind_method(Rect2, expand, sarray("to"), varray());
+ bind_method(Rect2, get_support, sarray("direction"), varray());
bind_method(Rect2, grow, sarray("amount"), varray());
bind_methodv(Rect2, grow_side, &Rect2::grow_side_bind, sarray("side", "amount"), varray());
bind_method(Rect2, grow_individual, sarray("left", "top", "right", "bottom"), varray());
@@ -2185,7 +2188,7 @@ static void _register_variant_builtin_methods_misc() {
bind_method(AABB, merge, sarray("with"), varray());
bind_method(AABB, expand, sarray("to_point"), varray());
bind_method(AABB, grow, sarray("by"), varray());
- bind_method(AABB, get_support, sarray("dir"), varray());
+ bind_method(AABB, get_support, sarray("direction"), varray());
bind_method(AABB, get_longest_axis, sarray(), varray());
bind_method(AABB, get_longest_axis_index, sarray(), varray());
bind_method(AABB, get_longest_axis_size, sarray(), varray());
@@ -2251,6 +2254,7 @@ static void _register_variant_builtin_methods_misc() {
bind_method(Dictionary, size, sarray(), varray());
bind_method(Dictionary, is_empty, sarray(), varray());
bind_method(Dictionary, clear, sarray(), varray());
+ bind_method(Dictionary, assign, sarray("dictionary"), varray());
bind_method(Dictionary, merge, sarray("dictionary", "overwrite"), varray(false));
bind_method(Dictionary, merged, sarray("dictionary", "overwrite"), varray(false));
bind_method(Dictionary, has, sarray("key"), varray());
@@ -2263,6 +2267,18 @@ static void _register_variant_builtin_methods_misc() {
bind_method(Dictionary, duplicate, sarray("deep"), varray(false));
bind_method(Dictionary, get, sarray("key", "default"), varray(Variant()));
bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant()));
+ bind_method(Dictionary, is_typed, sarray(), varray());
+ bind_method(Dictionary, is_typed_key, sarray(), varray());
+ bind_method(Dictionary, is_typed_value, sarray(), varray());
+ bind_method(Dictionary, is_same_typed, sarray("dictionary"), varray());
+ bind_method(Dictionary, is_same_typed_key, sarray("dictionary"), varray());
+ bind_method(Dictionary, is_same_typed_value, sarray("dictionary"), varray());
+ bind_method(Dictionary, get_typed_key_builtin, sarray(), varray());
+ bind_method(Dictionary, get_typed_value_builtin, sarray(), varray());
+ bind_method(Dictionary, get_typed_key_class_name, sarray(), varray());
+ bind_method(Dictionary, get_typed_value_class_name, sarray(), varray());
+ bind_method(Dictionary, get_typed_key_script, sarray(), varray());
+ bind_method(Dictionary, get_typed_value_script, sarray(), varray());
bind_method(Dictionary, make_read_only, sarray(), varray());
bind_method(Dictionary, is_read_only, sarray(), varray());
bind_method(Dictionary, recursive_equal, sarray("dictionary", "recursion_count"), varray());
diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp
index 1edae407c2..fb75a874e7 100644
--- a/core/variant/variant_construct.cpp
+++ b/core/variant/variant_construct.cpp
@@ -198,6 +198,7 @@ void Variant::_register_variant_constructors() {
add_constructor<VariantConstructNoArgs<Dictionary>>(sarray());
add_constructor<VariantConstructor<Dictionary, Dictionary>>(sarray("from"));
+ add_constructor<VariantConstructorTypedDictionary>(sarray("base", "key_type", "key_class_name", "key_script", "value_type", "value_class_name", "value_script"));
add_constructor<VariantConstructNoArgs<Array>>(sarray());
add_constructor<VariantConstructor<Array, Array>>(sarray("from"));
diff --git a/core/variant/variant_construct.h b/core/variant/variant_construct.h
index b824044b82..68210a9451 100644
--- a/core/variant/variant_construct.h
+++ b/core/variant/variant_construct.h
@@ -153,11 +153,14 @@ public:
class VariantConstructorObject {
public:
static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- VariantInternal::clear(&r_ret);
if (p_args[0]->get_type() == Variant::NIL) {
+ VariantInternal::clear(&r_ret);
+ VariantTypeChanger<Object *>::change(&r_ret);
VariantInternal::object_assign_null(&r_ret);
r_error.error = Callable::CallError::CALL_OK;
} else if (p_args[0]->get_type() == Variant::OBJECT) {
+ VariantInternal::clear(&r_ret);
+ VariantTypeChanger<Object *>::change(&r_ret);
VariantInternal::object_assign(&r_ret, p_args[0]);
r_error.error = Callable::CallError::CALL_OK;
} else {
@@ -169,6 +172,7 @@ public:
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
VariantInternal::clear(r_ret);
+ VariantTypeChanger<Object *>::change(r_ret);
VariantInternal::object_assign(r_ret, p_args[0]);
}
static void ptr_construct(void *base, const void **p_args) {
@@ -198,11 +202,13 @@ public:
}
VariantInternal::clear(&r_ret);
+ VariantTypeChanger<Object *>::change(&r_ret);
VariantInternal::object_assign_null(&r_ret);
}
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
VariantInternal::clear(r_ret);
+ VariantTypeChanger<Object *>::change(r_ret);
VariantInternal::object_assign_null(r_ret);
}
static void ptr_construct(void *base, const void **p_args) {
@@ -226,7 +232,7 @@ template <typename T>
class VariantConstructorFromString {
public:
static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- if (p_args[0]->get_type() != Variant::STRING) {
+ if (!p_args[0]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING;
@@ -234,7 +240,7 @@ public:
}
VariantTypeChanger<T>::change(&r_ret);
- const String &src_str = *VariantGetInternalPtr<String>::get_ptr(p_args[0]);
+ const String src_str = *p_args[0];
if (r_ret.get_type() == Variant::Type::INT) {
r_ret = src_str.to_int();
@@ -394,6 +400,112 @@ public:
}
};
+class VariantConstructorTypedDictionary {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ if (p_args[0]->get_type() != Variant::DICTIONARY) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ return;
+ }
+
+ if (p_args[1]->get_type() != Variant::INT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = Variant::INT;
+ return;
+ }
+
+ if (p_args[2]->get_type() != Variant::STRING_NAME) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 2;
+ r_error.expected = Variant::STRING_NAME;
+ return;
+ }
+
+ if (p_args[4]->get_type() != Variant::INT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 4;
+ r_error.expected = Variant::INT;
+ return;
+ }
+
+ if (p_args[5]->get_type() != Variant::STRING_NAME) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 5;
+ r_error.expected = Variant::STRING_NAME;
+ return;
+ }
+
+ const Dictionary &base_dict = *VariantGetInternalPtr<Dictionary>::get_ptr(p_args[0]);
+ const uint32_t key_type = p_args[1]->operator uint32_t();
+ const StringName &key_class_name = *VariantGetInternalPtr<StringName>::get_ptr(p_args[2]);
+ const uint32_t value_type = p_args[4]->operator uint32_t();
+ const StringName &value_class_name = *VariantGetInternalPtr<StringName>::get_ptr(p_args[5]);
+ r_ret = Dictionary(base_dict, key_type, key_class_name, *p_args[3], value_type, value_class_name, *p_args[6]);
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ const Dictionary &base_dict = *VariantGetInternalPtr<Dictionary>::get_ptr(p_args[0]);
+ const uint32_t key_type = p_args[1]->operator uint32_t();
+ const StringName &key_class_name = *VariantGetInternalPtr<StringName>::get_ptr(p_args[2]);
+ const uint32_t value_type = p_args[4]->operator uint32_t();
+ const StringName &value_class_name = *VariantGetInternalPtr<StringName>::get_ptr(p_args[5]);
+ *r_ret = Dictionary(base_dict, key_type, key_class_name, *p_args[3], value_type, value_class_name, *p_args[6]);
+ }
+
+ static void ptr_construct(void *base, const void **p_args) {
+ const Dictionary &base_dict = PtrToArg<Dictionary>::convert(p_args[0]);
+ const uint32_t key_type = PtrToArg<uint32_t>::convert(p_args[1]);
+ const StringName &key_class_name = PtrToArg<StringName>::convert(p_args[2]);
+ const Variant &key_script = PtrToArg<Variant>::convert(p_args[3]);
+ const uint32_t value_type = PtrToArg<uint32_t>::convert(p_args[4]);
+ const StringName &value_class_name = PtrToArg<StringName>::convert(p_args[5]);
+ const Variant &value_script = PtrToArg<Variant>::convert(p_args[6]);
+ Dictionary dst_arr = Dictionary(base_dict, key_type, key_class_name, key_script, value_type, value_class_name, value_script);
+
+ PtrConstruct<Dictionary>::construct(dst_arr, base);
+ }
+
+ static int get_argument_count() {
+ return 7;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ switch (p_arg) {
+ case 0: {
+ return Variant::DICTIONARY;
+ } break;
+ case 1: {
+ return Variant::INT;
+ } break;
+ case 2: {
+ return Variant::STRING_NAME;
+ } break;
+ case 3: {
+ return Variant::NIL;
+ } break;
+ case 4: {
+ return Variant::INT;
+ } break;
+ case 5: {
+ return Variant::STRING_NAME;
+ } break;
+ case 6: {
+ return Variant::NIL;
+ } break;
+ default: {
+ return Variant::NIL;
+ } break;
+ }
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::DICTIONARY;
+ }
+};
+
class VariantConstructorTypedArray {
public:
static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
@@ -411,7 +523,7 @@ public:
return;
}
- if (p_args[2]->get_type() != Variant::STRING_NAME) {
+ if (!p_args[2]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 2;
r_error.expected = Variant::STRING_NAME;
@@ -420,8 +532,7 @@ public:
const Array &base_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]);
const uint32_t type = p_args[1]->operator uint32_t();
- const StringName &class_name = *VariantGetInternalPtr<StringName>::get_ptr(p_args[2]);
- r_ret = Array(base_arr, type, class_name, *p_args[3]);
+ r_ret = Array(base_arr, type, *p_args[2], *p_args[3]);
}
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index c52ab6917b..58a45c0a1f 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -125,10 +125,6 @@ public:
}
}
- _FORCE_INLINE_ static bool initialize_ref(Object *object) {
- return Variant::initialize_ref(object);
- }
-
// Atomic types.
_FORCE_INLINE_ static bool *get_bool(Variant *v) { return &v->_data._bool; }
_FORCE_INLINE_ static const bool *get_bool(const Variant *v) { return &v->_data._bool; }
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index 0b94d79a97..ac39a4135f 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -548,14 +548,14 @@ public:
class OperatorEvaluatorEqualObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const ObjectID &a = VariantInternal::get_object_id(&p_left);
- const ObjectID &b = VariantInternal::get_object_id(&p_right);
+ const Object *a = p_left.get_validated_object();
+ const Object *b = p_right.get_validated_object();
*r_ret = a == b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const ObjectID &a = VariantInternal::get_object_id(left);
- const ObjectID &b = VariantInternal::get_object_id(right);
+ const Object *a = left->get_validated_object();
+ const Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -567,12 +567,12 @@ public:
class OperatorEvaluatorEqualObjectNil {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *a = p_left.operator Object *();
+ const Object *a = p_left.get_validated_object();
*r_ret = a == nullptr;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *a = left->operator Object *();
+ const Object *a = left->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -584,12 +584,12 @@ public:
class OperatorEvaluatorEqualNilObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *b = p_right.operator Object *();
+ const Object *b = p_right.get_validated_object();
*r_ret = nullptr == b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *b = right->operator Object *();
+ const Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -619,14 +619,14 @@ public:
class OperatorEvaluatorNotEqualObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const ObjectID &a = VariantInternal::get_object_id(&p_left);
- const ObjectID &b = VariantInternal::get_object_id(&p_right);
+ Object *a = p_left.get_validated_object();
+ Object *b = p_right.get_validated_object();
*r_ret = a != b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const ObjectID &a = VariantInternal::get_object_id(left);
- const ObjectID &b = VariantInternal::get_object_id(right);
+ Object *a = left->get_validated_object();
+ Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -638,12 +638,12 @@ public:
class OperatorEvaluatorNotEqualObjectNil {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *a = p_left.operator Object *();
+ Object *a = p_left.get_validated_object();
*r_ret = a != nullptr;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *a = left->operator Object *();
+ Object *a = left->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -655,12 +655,12 @@ public:
class OperatorEvaluatorNotEqualNilObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *b = p_right.operator Object *();
+ Object *b = p_right.get_validated_object();
*r_ret = nullptr != b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *b = right->operator Object *();
+ Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 9a0dd712ed..f5f96456d3 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -1140,6 +1140,146 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
}
+ } else if (id == "Dictionary") {
+ Error err = OK;
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_OPEN) {
+ r_err_str = "Expected '['";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_IDENTIFIER) {
+ r_err_str = "Expected type identifier for key";
+ return ERR_PARSE_ERROR;
+ }
+
+ static HashMap<StringName, Variant::Type> builtin_types;
+ if (builtin_types.is_empty()) {
+ for (int i = 1; i < Variant::VARIANT_MAX; i++) {
+ builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
+ }
+ }
+
+ Dictionary dict;
+ Variant::Type key_type = Variant::NIL;
+ StringName key_class_name;
+ Variant key_script;
+ bool got_comma_token = false;
+ if (builtin_types.has(token.value)) {
+ key_type = builtin_types.get(token.value);
+ } else if (token.value == "Resource" || token.value == "SubResource" || token.value == "ExtResource") {
+ Variant resource;
+ err = parse_value(token, resource, p_stream, line, r_err_str, p_res_parser);
+ if (err) {
+ if (token.value == "Resource" && err == ERR_PARSE_ERROR && r_err_str == "Expected '('" && token.type == TK_COMMA) {
+ err = OK;
+ r_err_str = String();
+ key_type = Variant::OBJECT;
+ key_class_name = token.value;
+ got_comma_token = true;
+ } else {
+ return err;
+ }
+ } else {
+ Ref<Script> script = resource;
+ if (script.is_valid() && script->is_valid()) {
+ key_type = Variant::OBJECT;
+ key_class_name = script->get_instance_base_type();
+ key_script = script;
+ }
+ }
+ } else if (ClassDB::class_exists(token.value)) {
+ key_type = Variant::OBJECT;
+ key_class_name = token.value;
+ }
+
+ if (!got_comma_token) {
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_COMMA) {
+ r_err_str = "Expected ',' after key type";
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_IDENTIFIER) {
+ r_err_str = "Expected type identifier for value";
+ return ERR_PARSE_ERROR;
+ }
+
+ Variant::Type value_type = Variant::NIL;
+ StringName value_class_name;
+ Variant value_script;
+ bool got_bracket_token = false;
+ if (builtin_types.has(token.value)) {
+ value_type = builtin_types.get(token.value);
+ } else if (token.value == "Resource" || token.value == "SubResource" || token.value == "ExtResource") {
+ Variant resource;
+ err = parse_value(token, resource, p_stream, line, r_err_str, p_res_parser);
+ if (err) {
+ if (token.value == "Resource" && err == ERR_PARSE_ERROR && r_err_str == "Expected '('" && token.type == TK_BRACKET_CLOSE) {
+ err = OK;
+ r_err_str = String();
+ value_type = Variant::OBJECT;
+ value_class_name = token.value;
+ got_comma_token = true;
+ } else {
+ return err;
+ }
+ } else {
+ Ref<Script> script = resource;
+ if (script.is_valid() && script->is_valid()) {
+ value_type = Variant::OBJECT;
+ value_class_name = script->get_instance_base_type();
+ value_script = script;
+ }
+ }
+ } else if (ClassDB::class_exists(token.value)) {
+ value_type = Variant::OBJECT;
+ value_class_name = token.value;
+ }
+
+ if (key_type != Variant::NIL || value_type != Variant::NIL) {
+ dict.set_typed(key_type, key_class_name, key_script, value_type, value_class_name, value_script);
+ }
+
+ if (!got_bracket_token) {
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_CLOSE) {
+ r_err_str = "Expected ']'";
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_OPEN) {
+ r_err_str = "Expected '('";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_CURLY_BRACKET_OPEN) {
+ r_err_str = "Expected '{'";
+ return ERR_PARSE_ERROR;
+ }
+
+ Dictionary values;
+ err = _parse_dictionary(values, p_stream, line, r_err_str, p_res_parser);
+ if (err) {
+ return err;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')'";
+ return ERR_PARSE_ERROR;
+ }
+
+ dict.assign(values);
+
+ value = dict;
} else if (id == "Array") {
Error err = OK;
@@ -2036,40 +2176,109 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
case Variant::DICTIONARY: {
Dictionary dict = p_variant;
+
+ if (dict.is_typed()) {
+ p_store_string_func(p_store_string_ud, "Dictionary[");
+
+ Variant::Type key_builtin_type = (Variant::Type)dict.get_typed_key_builtin();
+ StringName key_class_name = dict.get_typed_key_class_name();
+ Ref<Script> key_script = dict.get_typed_key_script();
+
+ if (key_script.is_valid()) {
+ String resource_text;
+ if (p_encode_res_func) {
+ resource_text = p_encode_res_func(p_encode_res_ud, key_script);
+ }
+ if (resource_text.is_empty() && key_script->get_path().is_resource_file()) {
+ resource_text = "Resource(\"" + key_script->get_path() + "\")";
+ }
+
+ if (!resource_text.is_empty()) {
+ p_store_string_func(p_store_string_ud, resource_text);
+ } else {
+ ERR_PRINT("Failed to encode a path to a custom script for a dictionary key type.");
+ p_store_string_func(p_store_string_ud, key_class_name);
+ }
+ } else if (key_class_name != StringName()) {
+ p_store_string_func(p_store_string_ud, key_class_name);
+ } else if (key_builtin_type == Variant::NIL) {
+ p_store_string_func(p_store_string_ud, "Variant");
+ } else {
+ p_store_string_func(p_store_string_ud, Variant::get_type_name(key_builtin_type));
+ }
+
+ p_store_string_func(p_store_string_ud, ", ");
+
+ Variant::Type value_builtin_type = (Variant::Type)dict.get_typed_value_builtin();
+ StringName value_class_name = dict.get_typed_value_class_name();
+ Ref<Script> value_script = dict.get_typed_value_script();
+
+ if (value_script.is_valid()) {
+ String resource_text;
+ if (p_encode_res_func) {
+ resource_text = p_encode_res_func(p_encode_res_ud, value_script);
+ }
+ if (resource_text.is_empty() && value_script->get_path().is_resource_file()) {
+ resource_text = "Resource(\"" + value_script->get_path() + "\")";
+ }
+
+ if (!resource_text.is_empty()) {
+ p_store_string_func(p_store_string_ud, resource_text);
+ } else {
+ ERR_PRINT("Failed to encode a path to a custom script for a dictionary value type.");
+ p_store_string_func(p_store_string_ud, value_class_name);
+ }
+ } else if (value_class_name != StringName()) {
+ p_store_string_func(p_store_string_ud, value_class_name);
+ } else if (value_builtin_type == Variant::NIL) {
+ p_store_string_func(p_store_string_ud, "Variant");
+ } else {
+ p_store_string_func(p_store_string_ud, Variant::get_type_name(value_builtin_type));
+ }
+
+ p_store_string_func(p_store_string_ud, "](");
+ }
+
if (unlikely(p_recursion_count > MAX_RECURSION)) {
ERR_PRINT("Max recursion reached");
p_store_string_func(p_store_string_ud, "{}");
} else {
- p_recursion_count++;
-
List<Variant> keys;
dict.get_key_list(&keys);
keys.sort();
- if (keys.is_empty()) { // Avoid unnecessary line break.
+ if (keys.is_empty()) {
+ // Avoid unnecessary line break.
p_store_string_func(p_store_string_ud, "{}");
- break;
- }
+ } else {
+ p_recursion_count++;
- p_store_string_func(p_store_string_ud, "{\n");
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat);
- p_store_string_func(p_store_string_ud, ": ");
- write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat);
- if (E->next()) {
- p_store_string_func(p_store_string_ud, ",\n");
- } else {
- p_store_string_func(p_store_string_ud, "\n");
+ p_store_string_func(p_store_string_ud, "{\n");
+
+ for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat);
+ p_store_string_func(p_store_string_ud, ": ");
+ write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat);
+ if (E->next()) {
+ p_store_string_func(p_store_string_ud, ",\n");
+ } else {
+ p_store_string_func(p_store_string_ud, "\n");
+ }
}
+
+ p_store_string_func(p_store_string_ud, "}");
}
+ }
- p_store_string_func(p_store_string_ud, "}");
+ if (dict.is_typed()) {
+ p_store_string_func(p_store_string_ud, ")");
}
} break;
case Variant::ARRAY: {
Array array = p_variant;
- if (array.get_typed_builtin() != Variant::NIL) {
+
+ if (array.is_typed()) {
p_store_string_func(p_store_string_ud, "Array[");
Variant::Type builtin_type = (Variant::Type)array.get_typed_builtin();
@@ -2107,6 +2316,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_recursion_count++;
p_store_string_func(p_store_string_ud, "[");
+
bool first = true;
for (const Variant &var : array) {
if (first) {
@@ -2120,7 +2330,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "]");
}
- if (array.get_typed_builtin() != Variant::NIL) {
+ if (array.is_typed()) {
p_store_string_func(p_store_string_ud, ")");
}
} break;
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 48176163a1..b60ff83cf1 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -703,6 +703,50 @@ struct VariantIndexedSetGet_Array {
static uint64_t get_indexed_size(const Variant *base) { return 0; }
};
+struct VariantIndexedSetGet_Dictionary {
+ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {
+ const Variant *ptr = VariantGetInternalPtr<Dictionary>::get_ptr(base)->getptr(index);
+ if (!ptr) {
+ *oob = true;
+ return;
+ }
+ *value = *ptr;
+ *oob = false;
+ }
+ static void ptr_get(const void *base, int64_t index, void *member) {
+ // Avoid ptrconvert for performance.
+ const Dictionary &v = *reinterpret_cast<const Dictionary *>(base);
+ const Variant *ptr = v.getptr(index);
+ NULL_TEST(ptr);
+ 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;
+ }
+ 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;
+ }
+ static void ptr_set(void *base, int64_t index, const void *member) {
+ Dictionary &v = *reinterpret_cast<Dictionary *>(base);
+ v[index] = PtrToArg<Variant>::convert(member);
+ }
+ static Variant::Type get_index_type() { return Variant::NIL; }
+ static uint32_t get_index_usage() { return PROPERTY_USAGE_DEFAULT; }
+ static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<Dictionary>::get_ptr(base)->size(); }
+};
+
struct VariantIndexedSetGet_String {
static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {
int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length();
@@ -789,51 +833,6 @@ struct VariantIndexedSetGet_String {
static uint64_t get_indexed_size(const Variant *base) { return VariantInternal::get_string(base)->length(); }
};
-#define INDEXED_SETGET_STRUCT_DICT(m_base_type) \
- struct VariantIndexedSetGet_##m_base_type { \
- static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \
- const Variant *ptr = VariantGetInternalPtr<m_base_type>::get_ptr(base)->getptr(index); \
- if (!ptr) { \
- *oob = true; \
- return; \
- } \
- *value = *ptr; \
- *oob = false; \
- } \
- static void ptr_get(const void *base, int64_t index, void *member) { \
- /* avoid ptrconvert for performance*/ \
- const m_base_type &v = *reinterpret_cast<const m_base_type *>(base); \
- const Variant *ptr = v.getptr(index); \
- NULL_TEST(ptr); \
- 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; \
- } \
- static void ptr_set(void *base, int64_t index, const void *member) { \
- m_base_type &v = *reinterpret_cast<m_base_type *>(base); \
- v[index] = PtrToArg<Variant>::convert(member); \
- } \
- static Variant::Type get_index_type() { return Variant::NIL; } \
- static uint32_t get_index_usage() { return PROPERTY_USAGE_DEFAULT; } \
- static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); } \
- };
-
INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2, double, real_t, 2)
INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2i, int64_t, int32_t, 2)
INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector3, double, real_t, 3)
@@ -858,8 +857,6 @@ INDEXED_SETGET_STRUCT_TYPED(PackedStringArray, String)
INDEXED_SETGET_STRUCT_TYPED(PackedColorArray, Color)
INDEXED_SETGET_STRUCT_TYPED(PackedVector4Array, Vector4)
-INDEXED_SETGET_STRUCT_DICT(Dictionary)
-
struct VariantIndexedSetterGetterInfo {
void (*setter)(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) = nullptr;
void (*getter)(const Variant *base, int64_t index, Variant *value, bool *oob) = nullptr;
@@ -1288,8 +1285,8 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
List<Variant> keys;
dic->get_key_list(&keys);
for (const Variant &E : keys) {
- if (E.get_type() == Variant::STRING) {
- p_list->push_back(PropertyInfo(Variant::STRING, E));
+ if (E.is_string()) {
+ p_list->push_back(PropertyInfo(dic->get_valid(E).get_type(), E));
}
}
} else if (type == OBJECT) {
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 7534a154a1..384fe6c4a6 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -452,12 +452,14 @@ Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, do
case Variant::QUATERNION:
case Variant::BASIS:
case Variant::COLOR:
+ case Variant::TRANSFORM2D:
+ case Variant::TRANSFORM3D:
break;
default:
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::NIL;
- return R"(Argument "from" must be "int", "float", "Vector2", "Vector3", "Vector4", "Quaternion", "Basis, or "Color".)";
+ return R"(Argument "from" must be "int", "float", "Vector2", "Vector3", "Vector4", "Color", "Quaternion", "Basis", "Transform2D", or "Transform3D".)";
}
if (from.get_type() != to.get_type()) {
@@ -490,6 +492,12 @@ Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, do
case Variant::BASIS: {
return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight);
} break;
+ case Variant::TRANSFORM2D: {
+ return VariantInternalAccessor<Transform2D>::get(&from).interpolate_with(VariantInternalAccessor<Transform2D>::get(&to), weight);
+ } break;
+ case Variant::TRANSFORM3D: {
+ return VariantInternalAccessor<Transform3D>::get(&from).interpolate_with(VariantInternalAccessor<Transform3D>::get(&to), weight);
+ } break;
case Variant::COLOR: {
return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight);
} break;