Skip to content

Commit

Permalink
GDScript: Fix issues with typed arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
vonagam committed Jan 31, 2023
1 parent e9de988 commit 5909f9f
Show file tree
Hide file tree
Showing 50 changed files with 943 additions and 539 deletions.
14 changes: 7 additions & 7 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ TypedArray<PackedVector2Array> Geometry2D::decompose_polygon_in_convex(const Vec
TypedArray<PackedVector2Array> Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::merge_polygons(p_polygon_a, p_polygon_b);

Array ret;
TypedArray<PackedVector2Array> ret;

for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
Expand All @@ -739,7 +739,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polygons(const Vector<Vector2> &
TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b);

Array ret;
TypedArray<PackedVector2Array> ret;

for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
Expand All @@ -750,7 +750,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vecto
TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b);

Array ret;
TypedArray<PackedVector2Array> ret;

for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
Expand All @@ -761,7 +761,7 @@ TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2
TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Vector<Vector<Point2>> polys = ::Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon);

Array ret;
TypedArray<PackedVector2Array> ret;

for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
Expand All @@ -772,7 +772,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vect
TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon);

Array ret;
TypedArray<PackedVector2Array> ret;

for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
Expand All @@ -783,7 +783,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const
TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
Vector<Vector<Point2>> polys = ::Geometry2D::offset_polygon(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type));

Array ret;
TypedArray<PackedVector2Array> ret;

for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
Expand All @@ -794,7 +794,7 @@ TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2>
TypedArray<PackedVector2Array> Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
Vector<Vector<Point2>> polys = ::Geometry2D::offset_polyline(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type), ::Geometry2D::PolyEndType(p_end_type));

Array ret;
TypedArray<PackedVector2Array> ret;

for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
Expand Down
12 changes: 10 additions & 2 deletions core/doc_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@

#include "doc_data.h"

String DocData::get_default_value_string(const Variant &p_value) {
if (p_value.get_type() == Variant::ARRAY) {
return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace("\n", " ");
} else {
return p_value.get_construct_string().replace("\n", " ");
}
}

void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) {
if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) {
p_method.return_type = p_retinfo.hint_string;
Expand Down Expand Up @@ -105,7 +113,7 @@ void DocData::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_propert
p_property.getter = p_memberinfo.getter;

if (p_memberinfo.has_default_value && p_memberinfo.default_value.get_type() != Variant::OBJECT) {
p_property.default_value = p_memberinfo.default_value.get_construct_string().replace("\n", "");
p_property.default_value = get_default_value_string(p_memberinfo.default_value);
}

p_property.overridden = false;
Expand Down Expand Up @@ -148,7 +156,7 @@ void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const Met
int default_arg_index = i - (p_methodinfo.arguments.size() - p_methodinfo.default_arguments.size());
if (default_arg_index >= 0) {
Variant default_arg = p_methodinfo.default_arguments[default_arg_index];
argument.default_value = default_arg.get_construct_string().replace("\n", "");
argument.default_value = get_default_value_string(default_arg);
}
p_method.arguments.push_back(argument);
}
Expand Down
2 changes: 2 additions & 0 deletions core/doc_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,8 @@ class DocData {
}
};

static String get_default_value_string(const Variant &p_value);

static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo);
static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo);
static void property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo);
Expand Down
7 changes: 7 additions & 0 deletions core/extension/gdextension_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,12 @@ static GDExtensionVariantPtr gdextension_array_operator_index_const(GDExtensionC
return (GDExtensionVariantPtr)&self->operator[](p_index);
}

void gdextension_array_ref(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from) {
Array *self = (Array *)p_self;
const Array *from = (const Array *)p_from;
self->_ref(*from);
}

void gdextension_array_set_typed(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script) {
Array *self = reinterpret_cast<Array *>(p_self);
const StringName *class_name = reinterpret_cast<const StringName *>(p_class_name);
Expand Down Expand Up @@ -1136,6 +1142,7 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) {

gde_interface.array_operator_index = gdextension_array_operator_index;
gde_interface.array_operator_index_const = gdextension_array_operator_index_const;
gde_interface.array_ref = gdextension_array_ref;
gde_interface.array_set_typed = gdextension_array_set_typed;

/* Dictionary functions */
Expand Down
1 change: 1 addition & 0 deletions core/extension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ typedef struct {

GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
void (*array_ref)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from); // p_self should be an Array ptr
void (*array_set_typed)(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr

/* Dictionary functions */
Expand Down
137 changes: 77 additions & 60 deletions core/variant/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ void Array::_ref(const Array &p_from) const {

_unref();

_p = p_from._p;
_p = _fp;
}

void Array::_unref() const {
Expand Down Expand Up @@ -191,62 +191,73 @@ 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;
void Array::operator=(const Array &p_array) {
if (this == &p_array) {
return;
}
_ref(p_array);
}

void Array::assign(const Array &p_array) {
const ContainerTypeValidate &typed = _p->typed;
const ContainerTypeValidate &source_typed = p_array._p->typed;

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
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;
} 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
return;
}

} 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) + "'.");
}
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;
}

_p->array = new_array;
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 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.");
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)));
}
return true;
}

void Array::operator=(const Array &p_array) {
if (this == &p_array) {
return;
}
_ref(p_array);
_p->array = array;
}

void Array::push_back(const Variant &p_value) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down
9 changes: 4 additions & 5 deletions core/variant/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,11 @@ class Callable;

class Array {
mutable ArrayPrivate *_p;
void _ref(const Array &p_from) const;
void _unref() const;

protected:
bool _assign(const Array &p_array);

public:
void _ref(const Array &p_from) const;

Variant &operator[](int p_idx);
const Variant &operator[](int p_idx) const;

Expand All @@ -68,6 +66,7 @@ class Array {
uint32_t recursive_hash(int recursion_count) const;
void operator=(const Array &p_array);

void assign(const Array &p_array);
void push_back(const Variant &p_value);
_FORCE_INLINE_ void append(const Variant &p_value) { push_back(p_value); } //for python compatibility
void append_array(const Array &p_array);
Expand Down Expand Up @@ -120,9 +119,9 @@ 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;
bool is_same_typed(const Array &p_other) const;
uint32_t get_typed_builtin() const;
StringName get_typed_class_name() const;
Variant get_typed_script() const;
Expand Down
11 changes: 9 additions & 2 deletions core/variant/container_type_validate.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,15 @@ struct ContainerTypeValidate {
return true;
}

_FORCE_INLINE_ bool operator==(const ContainerTypeValidate &p_type) const {
return type == p_type.type && class_name == p_type.class_name && script == p_type.script;
}
_FORCE_INLINE_ bool operator!=(const ContainerTypeValidate &p_type) const {
return type != p_type.type || class_name != p_type.class_name || script != p_type.script;
}

// Coerces String and StringName into each other when needed.
_FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") {
_FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") const {
if (type == Variant::NIL) {
return true;
}
Expand All @@ -102,7 +109,7 @@ struct ContainerTypeValidate {
return validate_object(inout_variant, p_operation);
}

_FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") {
_FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") const {
ERR_FAIL_COND_V(p_variant.get_type() != Variant::OBJECT, false);

#ifdef DEBUG_ENABLED
Expand Down
Loading

0 comments on commit 5909f9f

Please sign in to comment.