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

GDScript typed arrays #46830

Merged
merged 4 commits into from Mar 29, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 27 additions & 6 deletions core/variant/array.cpp
Expand Up @@ -139,7 +139,7 @@ uint32_t Array::hash() const {
return h;
}

void Array::_assign(const Array &p_array) {
bool Array::_assign(const Array &p_array) {
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);
Expand All @@ -150,7 +150,7 @@ void Array::_assign(const Array &p_array) {
//for objects, it needs full validation, either can be converted or fail
for (int i = 0; i < p_array._p->array.size(); i++) {
if (!_p->typed.validate(p_array._p->array[i], "assign")) {
return;
return false;
}
}
_p->array = p_array._p->array; //then just copy, which is cheap anyway
Expand All @@ -168,10 +168,10 @@ void Array::_assign(const Array &p_array) {
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_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
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_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
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) + "'.");
}
}

Expand All @@ -180,12 +180,13 @@ void Array::_assign(const Array &p_array) {
} else if (_p->typed.can_reference(p_array._p->typed)) { //same type or compatible
_ref(p_array);
} else {
ERR_FAIL_MSG("Assignment of arrays of incompatible types.");
ERR_FAIL_V_MSG(false, "Assignment of arrays of incompatible types.");
}
return true;
}

void Array::operator=(const Array &p_array) {
_assign(p_array);
_ref(p_array);
}

void Array::push_back(const Variant &p_value) {
Expand Down Expand Up @@ -528,6 +529,10 @@ Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_nam
_assign(p_from);
}

bool Array::typed_assign(const Array &p_other) {
return _assign(p_other);
}

void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty.");
ERR_FAIL_COND_MSG(_p->refcount.get() > 1, "Type can only be set when array has no more than one user.");
Expand All @@ -542,6 +547,22 @@ void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Var
_p->typed.where = "TypedArray";
}

bool Array::is_typed() const {
return _p->typed.type != Variant::NIL;
}

uint32_t Array::get_typed_builtin() const {
return _p->typed.type;
}

StringName Array::get_typed_class_name() const {
return _p->typed.class_name;
}

Variant Array::get_typed_script() const {
return _p->typed.script;
}

Array::Array(const Array &p_from) {
_p = nullptr;
_ref(p_from);
Expand Down
7 changes: 6 additions & 1 deletion core/variant/array.h
Expand Up @@ -48,7 +48,7 @@ class Array {

protected:
Array(const Array &p_base, uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
void _assign(const Array &p_array);
bool _assign(const Array &p_array);

public:
Variant &operator[](int p_idx);
Expand Down Expand Up @@ -111,7 +111,12 @@ class Array {

const void *id() const;

bool typed_assign(const Array &p_other);
void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
bool is_typed() const;
uint32_t get_typed_builtin() const;
StringName get_typed_class_name() const;
Variant get_typed_script() const;
Array(const Array &p_from);
Array();
~Array();
Expand Down
118 changes: 58 additions & 60 deletions core/variant/variant_setget.cpp
Expand Up @@ -875,65 +875,64 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
static uint64_t get_indexed_size(const Variant *base) { return m_max; } \
};

#define INDEXED_SETGET_STRUCT_VARIANT(m_base_type) \
struct VariantIndexedSetGet_##m_base_type { \
static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \
int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \
if (index < 0) { \
index += size; \
} \
if (index < 0 || index >= size) { \
*oob = true; \
return; \
} \
*value = (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index]; \
*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); \
if (index < 0) \
index += v.size(); \
OOB_TEST(index, v.size()); \
PtrToArg<Variant>::encode(v[index], member); \
} \
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \
int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \
if (index < 0) { \
index += size; \
} \
if (index < 0 || index >= size) { \
*oob = true; \
*valid = false; \
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) { \
int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); \
if (index < 0) { \
index += size; \
} \
if (index < 0 || index >= size) { \
*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) { \
/* avoid ptrconvert for performance*/ \
m_base_type &v = *reinterpret_cast<m_base_type *>(base); \
if (index < 0) \
index += v.size(); \
OOB_TEST(index, v.size()); \
v[index] = PtrToArg<Variant>::convert(member); \
} \
static Variant::Type get_index_type() { return Variant::NIL; } \
static uint64_t get_indexed_size(const Variant *base) { return 0; } \
};
struct VariantIndexedSetGet_Array {
static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {
int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
if (index < 0) {
index += size;
}
if (index < 0 || index >= size) {
*oob = true;
return;
}
*value = (*VariantGetInternalPtr<Array>::get_ptr(base))[index];
*oob = false;
}
static void ptr_get(const void *base, int64_t index, void *member) {
/* avoid ptrconvert for performance*/
const Array &v = *reinterpret_cast<const Array *>(base);
if (index < 0)
index += v.size();
OOB_TEST(index, v.size());
PtrToArg<Variant>::encode(v[index], member);
}
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
if (index < 0) {
index += size;
}
if (index < 0 || index >= size) {
*oob = true;
*valid = false;
return;
}
VariantGetInternalPtr<Array>::get_ptr(base)->set(index, *value);
*oob = false;
*valid = true;
}
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
if (index < 0) {
index += size;
}
if (index < 0 || index >= size) {
*oob = true;
return;
}
VariantGetInternalPtr<Array>::get_ptr(base)->set(index, *value);
*oob = false;
}
static void ptr_set(void *base, int64_t index, const void *member) {
/* avoid ptrconvert for performance*/
Array &v = *reinterpret_cast<Array *>(base);
if (index < 0)
index += v.size();
OOB_TEST(index, v.size());
v.set(index, PtrToArg<Variant>::convert(member));
}
static Variant::Type get_index_type() { return Variant::NIL; }
static uint64_t get_indexed_size(const Variant *base) { return 0; }
};

#define INDEXED_SETGET_STRUCT_DICT(m_base_type) \
struct VariantIndexedSetGet_##m_base_type { \
Expand Down Expand Up @@ -990,7 +989,6 @@ INDEXED_SETGET_STRUCT_TYPED(PackedVector3Array, Vector3)
INDEXED_SETGET_STRUCT_TYPED(PackedStringArray, String)
INDEXED_SETGET_STRUCT_TYPED(PackedColorArray, Color)

INDEXED_SETGET_STRUCT_VARIANT(Array)
INDEXED_SETGET_STRUCT_DICT(Dictionary)

struct VariantIndexedSetterGetterInfo {
Expand Down
34 changes: 21 additions & 13 deletions modules/gdscript/gdscript.cpp
Expand Up @@ -1310,21 +1310,29 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
return true; //function exists, call was successful
}
} else {
if (!member->data_type.is_type(p_value)) {
// Try conversion
Callable::CallError ce;
const Variant *value = &p_value;
Variant converted;
Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
if (ce.error == Callable::CallError::CALL_OK) {
members.write[member->index] = converted;
return true;
} else {
return false;
if (member->data_type.has_type) {
if (member->data_type.builtin_type == Variant::ARRAY && member->data_type.has_container_element_type()) {
// Typed array.
if (p_value.get_type() == Variant::ARRAY) {
return VariantInternal::get_array(&members.write[member->index])->typed_assign(p_value);
} else {
return false;
}
} else if (!member->data_type.is_type(p_value)) {
// Try conversion
Callable::CallError ce;
const Variant *value = &p_value;
Variant converted;
Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
if (ce.error == Callable::CallError::CALL_OK) {
members.write[member->index] = converted;
return true;
} else {
return false;
}
}
} else {
members.write[member->index] = p_value;
}
members.write[member->index] = p_value;
}
return true;
}
Expand Down