1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-06 12:20:30 +00:00

GDScript: Fix issues with typed arrays

This commit is contained in:
Dmitrii Maganov
2022-11-27 09:56:53 +02:00
parent e9de988020
commit 5909f9f075
50 changed files with 959 additions and 555 deletions

View File

@@ -64,7 +64,7 @@ void Array::_ref(const Array &p_from) const {
_unref();
_p = p_from._p;
_p = _fp;
}
void Array::_unref() const {
@@ -191,57 +191,6 @@ uint32_t Array::recursive_hash(int recursion_count) const {
return hash_fmix32(h);
}
bool Array::_assign(const Array &p_array) {
bool can_convert = p_array._p->typed.type == Variant::NIL;
can_convert |= _p->typed.type == Variant::STRING && p_array._p->typed.type == Variant::STRING_NAME;
can_convert |= _p->typed.type == Variant::STRING_NAME && p_array._p->typed.type == Variant::STRING;
if (_p->typed.type != Variant::OBJECT && _p->typed.type == p_array._p->typed.type) {
//same type or untyped, just reference, should be fine
_ref(p_array);
} else if (_p->typed.type == Variant::NIL) { //from typed to untyped, must copy, but this is cheap anyway
_p->array = p_array._p->array;
} else if (can_convert) { //from untyped to typed, must try to check if they are all valid
if (_p->typed.type == Variant::OBJECT) {
//for objects, it needs full validation, either can be converted or fail
for (int i = 0; i < p_array._p->array.size(); i++) {
const Variant &element = p_array._p->array[i];
if (element.get_type() != Variant::OBJECT || !_p->typed.validate_object(element, "assign")) {
return false;
}
}
_p->array = p_array._p->array; //then just copy, which is cheap anyway
} else {
//for non objects, we need to check if there is a valid conversion, which needs to happen one by one, so this is the worst case.
Vector<Variant> new_array;
new_array.resize(p_array._p->array.size());
for (int i = 0; i < p_array._p->array.size(); i++) {
Variant src_val = p_array._p->array[i];
if (src_val.get_type() == _p->typed.type) {
new_array.write[i] = src_val;
} else if (Variant::can_convert_strict(src_val.get_type(), _p->typed.type)) {
Variant *ptr = &src_val;
Callable::CallError ce;
Variant::construct(_p->typed.type, new_array.write[i], (const Variant **)&ptr, 1, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
}
} else {
ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
}
}
_p->array = new_array;
}
} else if (_p->typed.can_reference(p_array._p->typed)) { //same type or compatible
_ref(p_array);
} else {
ERR_FAIL_V_MSG(false, "Assignment of arrays of incompatible types.");
}
return true;
}
void Array::operator=(const Array &p_array) {
if (this == &p_array) {
return;
@@ -249,6 +198,68 @@ void Array::operator=(const Array &p_array) {
_ref(p_array);
}
void Array::assign(const Array &p_array) {
const ContainerTypeValidate &typed = _p->typed;
const ContainerTypeValidate &source_typed = p_array._p->typed;
if (typed == source_typed || typed.type == Variant::NIL || (source_typed.type == Variant::OBJECT && typed.can_reference(source_typed))) {
// from same to same or
// from anything to variants or
// from subclasses to base classes
_p->array = p_array._p->array;
return;
}
const Variant *source = p_array._p->array.ptr();
int size = p_array._p->array.size();
if ((source_typed.type == Variant::NIL && typed.type == Variant::OBJECT) || (source_typed.type == Variant::OBJECT && source_typed.can_reference(typed))) {
// from variants to objects or
// from base classes to subclasses
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)));
}
}
_p->array = p_array._p->array;
return;
}
Vector<Variant> array;
array.resize(size);
Variant *data = array.ptrw();
if (source_typed.type == Variant::NIL && typed.type != Variant::OBJECT) {
// from variants to primitives
for (int i = 0; i < size; i++) {
const Variant *value = source + i;
if (value->get_type() == typed.type) {
data[i] = *value;
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) + "'.");
}
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)));
}
} else if (Variant::can_convert_strict(source_typed.type, typed.type)) {
// from primitives to different convertable primitives
for (int i = 0; i < size; i++) {
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)));
}
} 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)));
}
_p->array = array;
}
void Array::push_back(const Variant &p_value) {
ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
Variant value = p_value;
@@ -269,7 +280,15 @@ void Array::append_array(const Array &p_array) {
Error Array::resize(int p_new_size) {
ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state.");
return _p->array.resize(p_new_size);
Variant::Type &variant_type = _p->typed.type;
int old_size = _p->array.size();
Error err = _p->array.resize_zeroed(p_new_size);
if (!err && variant_type != Variant::NIL && variant_type != Variant::OBJECT) {
for (int i = old_size; i < p_new_size; i++) {
VariantInternal::initialize(&_p->array.write[i], variant_type);
}
}
return err;
}
Error Array::insert(int p_pos, const Variant &p_value) {
@@ -403,24 +422,22 @@ Array Array::duplicate(bool p_deep) const {
Array Array::recursive_duplicate(bool p_deep, int recursion_count) const {
Array new_arr;
new_arr._p->typed = _p->typed;
if (recursion_count > MAX_RECURSION) {
ERR_PRINT("Max recursion reached");
return new_arr;
}
int element_count = size();
new_arr.resize(element_count);
new_arr._p->typed = _p->typed;
if (p_deep) {
recursion_count++;
int element_count = size();
new_arr.resize(element_count);
for (int i = 0; i < element_count; i++) {
new_arr[i] = get(i).recursive_duplicate(true, recursion_count);
}
} else {
for (int i = 0; i < element_count; i++) {
new_arr[i] = get(i);
}
new_arr._p->array = _p->array;
}
return new_arr;
@@ -737,11 +754,7 @@ Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_nam
_p = memnew(ArrayPrivate);
_p->refcount.init();
set_typed(p_type, p_class_name, p_script);
_assign(p_from);
}
bool Array::typed_assign(const Array &p_other) {
return _assign(p_other);
assign(p_from);
}
void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
@@ -763,6 +776,10 @@ bool Array::is_typed() const {
return _p->typed.type != Variant::NIL;
}
bool Array::is_same_typed(const Array &p_other) const {
return _p->typed == p_other._p->typed;
}
uint32_t Array::get_typed_builtin() const {
return _p->typed.type;
}