Преглед изворни кода

Add ability to bind typed arrays to script API

Note: Only replaced 2 instances to test, Node.get_children and TileMap.get_used_cells
Note: Will do a mass replace on later PRs of whathever I can find, but probably need
a tool to grep through doc.
Warning: Mono will break, needs to be fixed (and so do TypeScript and NativeScript, need to ask respective maintainers)
Juan Linietsky пре 5 година
родитељ
комит
5d4dc2d45c

+ 63 - 2
core/array.cpp

@@ -30,8 +30,10 @@
 
 #include "array.h"
 
+#include "container_type_validate.h"
 #include "core/hashfuncs.h"
 #include "core/object.h"
+#include "core/script_language.h"
 #include "core/variant.h"
 #include "core/vector.h"
 
@@ -39,6 +41,8 @@ class ArrayPrivate {
 public:
 	SafeRefCount refcount;
 	Vector<Variant> array;
+
+	ContainerTypeValidate typed;
 };
 
 void Array::_ref(const Array &p_from) const {
@@ -108,12 +112,34 @@ uint32_t Array::hash() const {
 	}
 	return h;
 }
-void Array::operator=(const Array &p_array) {
 
-	_ref(p_array);
+void 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, shuold 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 (p_array._p->typed.type == Variant::NIL) { //from untyped to typed, must try to check if they are all valid
+		for (int i = 0; i < p_array._p->array.size(); i++) {
+			if (!_p->typed.validate(p_array._p->array[i], "assign")) {
+				return;
+			}
+		}
+		_p->array = p_array._p->array; //then just copy, which is cheap anyway
+	} 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.");
+	}
+}
+
+void Array::operator=(const Array &p_array) {
+	_assign(p_array);
 }
 void Array::push_back(const Variant &p_value) {
 
+	ERR_FAIL_COND(!_p->typed.validate(p_value, "push_back"));
 	_p->array.push_back(p_value);
 }
 
@@ -124,11 +150,13 @@ Error Array::resize(int p_new_size) {
 
 void Array::insert(int p_pos, const Variant &p_value) {
 
+	ERR_FAIL_COND(!_p->typed.validate(p_value, "insert"));
 	_p->array.insert(p_pos, p_value);
 }
 
 void Array::erase(const Variant &p_value) {
 
+	ERR_FAIL_COND(!_p->typed.validate(p_value, "erase"));
 	_p->array.erase(p_value);
 }
 
@@ -144,6 +172,7 @@ Variant Array::back() const {
 
 int Array::find(const Variant &p_value, int p_from) const {
 
+	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "find"), -1);
 	return _p->array.find(p_value, p_from);
 }
 
@@ -151,6 +180,7 @@ int Array::rfind(const Variant &p_value, int p_from) const {
 
 	if (_p->array.size() == 0)
 		return -1;
+	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "rfind"), -1);
 
 	if (p_from < 0) {
 		// Relative offset from the end
@@ -173,11 +203,13 @@ int Array::rfind(const Variant &p_value, int p_from) const {
 
 int Array::find_last(const Variant &p_value) const {
 
+	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "find_last"), -1);
 	return rfind(p_value);
 }
 
 int Array::count(const Variant &p_value) const {
 
+	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "count"), 0);
 	if (_p->array.size() == 0)
 		return 0;
 
@@ -193,6 +225,8 @@ int Array::count(const Variant &p_value) const {
 }
 
 bool Array::has(const Variant &p_value) const {
+	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "use 'has'"), false);
+
 	return _p->array.find(p_value, 0) != -1;
 }
 
@@ -203,6 +237,8 @@ void Array::remove(int p_pos) {
 
 void Array::set(int p_idx, const Variant &p_value) {
 
+	ERR_FAIL_COND(!_p->typed.validate(p_value, "set"));
+
 	operator[](p_idx) = p_value;
 }
 
@@ -216,6 +252,7 @@ Array Array::duplicate(bool p_deep) const {
 	Array new_arr;
 	int element_count = size();
 	new_arr.resize(element_count);
+	new_arr._p->typed = _p->typed;
 	for (int i = 0; i < element_count; i++) {
 		new_arr[i] = p_deep ? get(i).duplicate(p_deep) : get(i);
 	}
@@ -369,11 +406,13 @@ _FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value
 
 int Array::bsearch(const Variant &p_value, bool p_before) {
 
+	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "binary search"), -1);
 	return bisect(_p->array, p_value, p_before, _ArrayVariantSort());
 }
 
 int Array::bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before) {
 
+	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "custom binary search"), -1);
 	ERR_FAIL_NULL_V(p_obj, 0);
 
 	_ArrayVariantSortCustom less;
@@ -391,6 +430,7 @@ Array &Array::invert() {
 
 void Array::push_front(const Variant &p_value) {
 
+	ERR_FAIL_COND(!_p->typed.validate(p_value, "push_front"));
 	_p->array.insert(0, p_value);
 }
 
@@ -465,6 +505,27 @@ const void *Array::id() const {
 	return _p->array.ptr();
 }
 
+Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
+	_p = memnew(ArrayPrivate);
+	_p->refcount.init();
+	set_typed(p_type, p_class_name, p_script);
+	_assign(p_from);
+}
+
+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.");
+	ERR_FAIL_COND_MSG(_p->typed.type != Variant::NIL, "Type can only be set once.");
+	ERR_FAIL_COND_MSG(p_class_name != StringName() && p_type != Variant::OBJECT, "Class names can only be set for type OBJECT");
+	Ref<Script> script = p_script;
+	ERR_FAIL_COND_MSG(script.is_valid() && p_class_name == StringName(), "Script class can only be set together with base class name");
+
+	_p->typed.type = Variant::Type(p_type);
+	_p->typed.class_name = p_class_name;
+	_p->typed.script = script;
+	_p->typed.where = "TypedArray";
+}
+
 Array::Array(const Array &p_from) {
 
 	_p = nullptr;

+ 5 - 0
core/array.h

@@ -47,6 +47,10 @@ class Array {
 	int _clamp_index(int p_index) const;
 	static int _fix_slice_index(int p_index, int p_arr_len, int p_top_mod);
 
+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);
+
 public:
 	Variant &operator[](int p_idx);
 	const Variant &operator[](int p_idx) const;
@@ -101,6 +105,7 @@ public:
 
 	const void *id() const;
 
+	void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
 	Array(const Array &p_from);
 	Array();
 	~Array();

+ 98 - 0
core/container_type_validate.h

@@ -0,0 +1,98 @@
+#ifndef CONTAINER_TYPE_VALIDATE_H
+#define CONTAINER_TYPE_VALIDATE_H
+
+#include "core/script_language.h"
+#include "core/variant.h"
+
+struct ContainerTypeValidate {
+
+	Variant::Type type = Variant::NIL;
+	StringName class_name;
+	Ref<Script> script;
+	const char *where = "conatiner";
+
+	_FORCE_INLINE_ bool can_reference(const ContainerTypeValidate &p_type) const {
+		if (type == p_type.type) {
+			if (type != Variant::OBJECT) {
+				return true; //nothing else to check
+			}
+		} else {
+			return false;
+		}
+
+		//both are object
+
+		if ((class_name != StringName()) != (p_type.class_name != StringName())) {
+			return false; //both need to have class or none
+		}
+
+		if (class_name != p_type.class_name) {
+			if (!ClassDB::is_parent_class(p_type.class_name, class_name)) {
+				return false;
+			}
+		}
+
+		if (script.is_null() != p_type.script.is_null()) {
+			return false;
+		}
+
+		if (script != p_type.script) {
+			if (!p_type.script->inherits_script(script)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	_FORCE_INLINE_ bool validate(const Variant &p_variant, const char *p_operation = "use") {
+
+		if (type == Variant::NIL) {
+			return true;
+		}
+
+		ERR_FAIL_COND_V_MSG(type != p_variant.get_type(), false, "Attempted to " + String(p_operation) + " a variable of type '" + Variant::get_type_name(p_variant.get_type()) + "' into a " + where + " of type '" + Variant::get_type_name(type) + "'.");
+		if (type != p_variant.get_type()) {
+			return false;
+		}
+
+		if (type != Variant::OBJECT) {
+			return true;
+		}
+#ifdef DEBUG_ENABLED
+		ObjectID object_id = p_variant;
+		if (object_id == ObjectID()) {
+			return true; //fine its null;
+		}
+		Object *object = ObjectDB::get_instance(object_id);
+		ERR_FAIL_COND_V_MSG(object == nullptr, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + ".");
+#else
+		Object *object = p_variant;
+		if (object == nullptr) {
+			return true; //fine
+		}
+#endif
+		if (class_name == StringName()) {
+			return true; //all good, no class type requested
+		}
+
+		StringName obj_class = object->get_class_name();
+		if (obj_class != class_name) {
+			ERR_FAIL_COND_V_MSG(!ClassDB::is_parent_class(object->get_class_name(), class_name), false, "Attempted to " + String(p_operation) + " an object of type '" + object->get_class() + "' into a " + where + ", which does not inherit from '" + String(class_name) + "'.");
+		}
+
+		if (script.is_null()) {
+			return true; //all good
+		}
+
+		Ref<Script> other_script = object->get_script();
+
+		//check base script..
+		ERR_FAIL_COND_V_MSG(other_script.is_null(), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'.");
+		ERR_FAIL_COND_V_MSG(!other_script->inherits_script(script), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'.");
+
+		return true;
+	}
+};
+
+#endif // CONTAINER_TYPE_VALIDATE_H

+ 1 - 0
core/method_bind.h

@@ -39,6 +39,7 @@
 #include "core/method_ptrcall.h"
 #include "core/object.h"
 #include "core/type_info.h"
+#include "core/typedefs.h"
 #include "core/variant.h"
 
 #include <stdio.h>

+ 1 - 0
core/object.h

@@ -91,6 +91,7 @@ enum PropertyHint {
 	PROPERTY_HINT_NODE_PATH_VALID_TYPES,
 	PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
 	PROPERTY_HINT_INT_IS_OBJECTID,
+	PROPERTY_HINT_ARRAY_TYPE,
 	PROPERTY_HINT_MAX,
 	// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
 };

+ 2 - 0
core/script_language.h

@@ -135,6 +135,8 @@ public:
 
 	virtual Ref<Script> get_base_script() const = 0; //for script inheritance
 
+	virtual bool inherits_script(const Ref<Script> &p_script) const = 0;
+
 	virtual StringName get_instance_base_type() const = 0; // this may not work in all scripts, will return empty if so
 	virtual ScriptInstance *instance_create(Object *p_this) = 0;
 	virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) { return nullptr; }

+ 1 - 0
core/typed_array.cpp

@@ -0,0 +1 @@
+#include "typed_array.h"

+ 196 - 0
core/typed_array.h

@@ -0,0 +1,196 @@
+#ifndef TYPED_ARRAY_H
+#define TYPED_ARRAY_H
+
+#include "core/array.h"
+#include "core/method_ptrcall.h"
+#include "core/variant.h"
+
+template <class T>
+class TypedArray : public Array {
+public:
+	template <class U>
+	_FORCE_INLINE_ void operator=(const TypedArray<U> &p_array) {
+		static_assert(__is_base_of(T, U));
+		_assign(p_array);
+	}
+
+	_FORCE_INLINE_ void operator=(const Array &p_array) {
+		_assign(p_array);
+	}
+	_FORCE_INLINE_ TypedArray(const Array &p_array) :
+			Array(p_array, Variant::OBJECT, T::get_class_static(), Variant()) {
+	}
+	_FORCE_INLINE_ TypedArray() {
+		set_typed(Variant::OBJECT, T::get_class_static(), Variant());
+	}
+};
+
+//specialization for the rest of variant types
+
+#define MAKE_TYPED_ARRAY(m_type, m_variant_type)                          \
+	template <>                                                           \
+	class TypedArray<m_type> : public Array {                             \
+	public:                                                               \
+		_FORCE_INLINE_ void operator=(const Array &p_array) {             \
+			_assign(p_array);                                             \
+		}                                                                 \
+		_FORCE_INLINE_ TypedArray(const Array &p_array) :                 \
+				Array(p_array, m_variant_type, StringName(), Variant()) { \
+		}                                                                 \
+		_FORCE_INLINE_ TypedArray() {                                     \
+			set_typed(m_variant_type, StringName(), Variant());           \
+		}                                                                 \
+	};
+
+MAKE_TYPED_ARRAY(bool, Variant::BOOL)
+MAKE_TYPED_ARRAY(uint8_t, Variant::INT)
+MAKE_TYPED_ARRAY(int8_t, Variant::INT)
+MAKE_TYPED_ARRAY(uint16_t, Variant::INT)
+MAKE_TYPED_ARRAY(int16_t, Variant::INT)
+MAKE_TYPED_ARRAY(uint32_t, Variant::INT)
+MAKE_TYPED_ARRAY(int32_t, Variant::INT)
+MAKE_TYPED_ARRAY(uint64_t, Variant::INT)
+MAKE_TYPED_ARRAY(int64_t, Variant::INT)
+MAKE_TYPED_ARRAY(float, Variant::FLOAT)
+MAKE_TYPED_ARRAY(double, Variant::FLOAT)
+MAKE_TYPED_ARRAY(String, Variant::STRING)
+MAKE_TYPED_ARRAY(Vector2, Variant::VECTOR2)
+MAKE_TYPED_ARRAY(Vector2i, Variant::VECTOR2I)
+MAKE_TYPED_ARRAY(Rect2, Variant::RECT2)
+MAKE_TYPED_ARRAY(Rect2i, Variant::RECT2I)
+MAKE_TYPED_ARRAY(Vector3, Variant::VECTOR3)
+MAKE_TYPED_ARRAY(Vector3i, Variant::VECTOR3I)
+MAKE_TYPED_ARRAY(Transform2D, Variant::TRANSFORM2D)
+MAKE_TYPED_ARRAY(Plane, Variant::PLANE)
+MAKE_TYPED_ARRAY(Quat, Variant::QUAT)
+MAKE_TYPED_ARRAY(AABB, Variant::AABB)
+MAKE_TYPED_ARRAY(Basis, Variant::BASIS)
+MAKE_TYPED_ARRAY(Transform, Variant::TRANSFORM)
+MAKE_TYPED_ARRAY(Color, Variant::COLOR)
+MAKE_TYPED_ARRAY(StringName, Variant::STRING_NAME)
+MAKE_TYPED_ARRAY(NodePath, Variant::NODE_PATH)
+MAKE_TYPED_ARRAY(RID, Variant::_RID)
+MAKE_TYPED_ARRAY(Callable, Variant::CALLABLE)
+MAKE_TYPED_ARRAY(Signal, Variant::SIGNAL)
+MAKE_TYPED_ARRAY(Dictionary, Variant::DICTIONARY)
+MAKE_TYPED_ARRAY(Array, Variant::ARRAY)
+MAKE_TYPED_ARRAY(Vector<uint8_t>, Variant::PACKED_BYTE_ARRAY)
+MAKE_TYPED_ARRAY(Vector<int32_t>, Variant::PACKED_INT32_ARRAY)
+MAKE_TYPED_ARRAY(Vector<int64_t>, Variant::PACKED_INT64_ARRAY)
+MAKE_TYPED_ARRAY(Vector<float>, Variant::PACKED_FLOAT32_ARRAY)
+MAKE_TYPED_ARRAY(Vector<double>, Variant::PACKED_FLOAT64_ARRAY)
+MAKE_TYPED_ARRAY(Vector<String>, Variant::PACKED_STRING_ARRAY)
+MAKE_TYPED_ARRAY(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
+MAKE_TYPED_ARRAY(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
+MAKE_TYPED_ARRAY(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
+
+#ifdef PTRCALL_ENABLED
+
+template <class T>
+struct PtrToArg<TypedArray<T>> {
+
+	_FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) {
+
+		return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr));
+	}
+
+	_FORCE_INLINE_ static void encode(TypedArray<T> p_val, void *p_ptr) {
+
+		*(Array *)p_ptr = p_val;
+	}
+};
+
+template <class T>
+struct PtrToArg<const TypedArray<T> &> {
+
+	_FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) {
+
+		return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr));
+	}
+};
+
+#endif // PTRCALL_ENABLED
+
+#ifdef DEBUG_METHODS_ENABLED
+
+template <class T>
+struct GetTypeInfo<TypedArray<T>> {
+	static const Variant::Type VARIANT_TYPE = Variant::ARRAY;
+	static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+	static inline PropertyInfo get_class_info() {
+		return PropertyInfo(Variant::ARRAY, String(), PROPERTY_HINT_ARRAY_TYPE, T::get_class_static());
+	}
+};
+
+template <class T>
+struct GetTypeInfo<const TypedArray<T> &> {
+	static const Variant::Type VARIANT_TYPE = Variant::ARRAY;
+	static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+	static inline PropertyInfo get_class_info() {
+		return PropertyInfo(Variant::ARRAY, String(), PROPERTY_HINT_ARRAY_TYPE, T::get_class_static());
+	}
+};
+
+#define MAKE_TYPED_ARRAY_INFO(m_type, m_variant_type)                                                                        \
+	template <>                                                                                                              \
+	struct GetTypeInfo<TypedArray<m_type>> {                                                                                 \
+		static const Variant::Type VARIANT_TYPE = Variant::ARRAY;                                                            \
+		static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;                                        \
+		static inline PropertyInfo get_class_info() {                                                                        \
+			return PropertyInfo(Variant::ARRAY, String(), PROPERTY_HINT_ARRAY_TYPE, Variant::get_type_name(m_variant_type)); \
+		}                                                                                                                    \
+	};                                                                                                                       \
+	template <>                                                                                                              \
+	struct GetTypeInfo<const TypedArray<m_type> &> {                                                                         \
+		static const Variant::Type VARIANT_TYPE = Variant::ARRAY;                                                            \
+		static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;                                        \
+		static inline PropertyInfo get_class_info() {                                                                        \
+			return PropertyInfo(Variant::ARRAY, String(), PROPERTY_HINT_ARRAY_TYPE, Variant::get_type_name(m_variant_type)); \
+		}                                                                                                                    \
+	};
+
+MAKE_TYPED_ARRAY_INFO(bool, Variant::BOOL)
+MAKE_TYPED_ARRAY_INFO(uint8_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(int8_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(uint16_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(int16_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(uint32_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(int32_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(uint64_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(int64_t, Variant::INT)
+MAKE_TYPED_ARRAY_INFO(float, Variant::FLOAT)
+MAKE_TYPED_ARRAY_INFO(double, Variant::FLOAT)
+MAKE_TYPED_ARRAY_INFO(String, Variant::STRING)
+MAKE_TYPED_ARRAY_INFO(Vector2, Variant::VECTOR2)
+MAKE_TYPED_ARRAY_INFO(Vector2i, Variant::VECTOR2I)
+MAKE_TYPED_ARRAY_INFO(Rect2, Variant::RECT2)
+MAKE_TYPED_ARRAY_INFO(Rect2i, Variant::RECT2I)
+MAKE_TYPED_ARRAY_INFO(Vector3, Variant::VECTOR3)
+MAKE_TYPED_ARRAY_INFO(Vector3i, Variant::VECTOR3I)
+MAKE_TYPED_ARRAY_INFO(Transform2D, Variant::TRANSFORM2D)
+MAKE_TYPED_ARRAY_INFO(Plane, Variant::PLANE)
+MAKE_TYPED_ARRAY_INFO(Quat, Variant::QUAT)
+MAKE_TYPED_ARRAY_INFO(AABB, Variant::AABB)
+MAKE_TYPED_ARRAY_INFO(Basis, Variant::BASIS)
+MAKE_TYPED_ARRAY_INFO(Transform, Variant::TRANSFORM)
+MAKE_TYPED_ARRAY_INFO(Color, Variant::COLOR)
+MAKE_TYPED_ARRAY_INFO(StringName, Variant::STRING_NAME)
+MAKE_TYPED_ARRAY_INFO(NodePath, Variant::NODE_PATH)
+MAKE_TYPED_ARRAY_INFO(RID, Variant::_RID)
+MAKE_TYPED_ARRAY_INFO(Callable, Variant::CALLABLE)
+MAKE_TYPED_ARRAY_INFO(Signal, Variant::SIGNAL)
+MAKE_TYPED_ARRAY_INFO(Dictionary, Variant::DICTIONARY)
+MAKE_TYPED_ARRAY_INFO(Array, Variant::ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<uint8_t>, Variant::PACKED_BYTE_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<int32_t>, Variant::PACKED_INT32_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<int64_t>, Variant::PACKED_INT64_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<float>, Variant::PACKED_FLOAT32_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<double>, Variant::PACKED_FLOAT64_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<String>, Variant::PACKED_STRING_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
+MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
+
+#endif
+
+#endif // TYPED_ARRAY_H

+ 6 - 0
editor/doc_data.cpp

@@ -193,6 +193,8 @@ static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const Property
 		p_method.return_type = "int";
 	} else if (p_retinfo.class_name != StringName()) {
 		p_method.return_type = p_retinfo.class_name;
+	} else if (p_retinfo.type == Variant::ARRAY && p_retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+		p_method.return_type = p_retinfo.hint_string + "[]";
 	} else if (p_retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
 		p_method.return_type = p_retinfo.hint_string;
 	} else if (p_retinfo.type == Variant::NIL && p_retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@@ -215,6 +217,8 @@ static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const Pr
 		p_argument.type = "int";
 	} else if (p_arginfo.class_name != StringName()) {
 		p_argument.type = p_arginfo.class_name;
+	} else if (p_arginfo.type == Variant::ARRAY && p_arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+		p_argument.type = p_arginfo.hint_string + "[]";
 	} else if (p_arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
 		p_argument.type = p_arginfo.hint_string;
 	} else if (p_arginfo.type == Variant::NIL) {
@@ -354,6 +358,8 @@ void DocData::generate(bool p_basic_types) {
 						prop.type = "int";
 					} else if (retinfo.class_name != StringName()) {
 						prop.type = retinfo.class_name;
+					} else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+						prop.type = retinfo.hint_string + "[]";
 					} else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
 
 						prop.type = retinfo.hint_string;

+ 13 - 1
editor/editor_help.cpp

@@ -198,7 +198,12 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
 	const Color text_color = get_theme_color("default_color", "RichTextLabel");
 	const Color type_color = get_theme_color("accent_color", "Editor").linear_interpolate(text_color, 0.5);
 	class_desc->push_color(type_color);
+	bool add_array = false;
 	if (can_ref) {
+		if (t.ends_with("[]")) {
+			add_array = true;
+			t = t.replace("[]", "");
+		}
 		if (p_enum.empty()) {
 			class_desc->push_meta("#" + t); //class
 		} else {
@@ -206,8 +211,15 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
 		}
 	}
 	class_desc->add_text(t);
-	if (can_ref)
+	if (can_ref) {
 		class_desc->pop();
+		if (add_array) {
+			class_desc->add_text(" ");
+			class_desc->push_meta("#Array"); //class
+			class_desc->add_text("[]");
+			class_desc->pop();
+		}
+	}
 	class_desc->pop();
 }
 

+ 7 - 0
modules/gdnative/nativescript/nativescript.cpp

@@ -111,6 +111,13 @@ void NativeScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
 
 #endif
 
+bool NativeScript::inherits_script(const Ref<Script> &p_script) const {
+#ifndef _MSC_VER
+#warning inheritance needs to be implemented in NativeScript
+#endif
+	return false;
+}
+
 void NativeScript::set_class_name(String p_class_name) {
 	class_name = p_class_name;
 }

+ 2 - 0
modules/gdnative/nativescript/nativescript.h

@@ -133,6 +133,8 @@ protected:
 public:
 	inline NativeScriptDesc *get_script_desc() const;
 
+	bool inherits_script(const Ref<Script> &p_script) const;
+
 	void set_class_name(String p_class_name);
 	String get_class_name() const;
 

+ 7 - 0
modules/gdnative/pluginscript/pluginscript_script.cpp

@@ -140,6 +140,13 @@ bool PluginScript::can_instance() const {
 	return can;
 }
 
+bool PluginScript::inherits_script(const Ref<Script> &p_script) const {
+#ifndef _MSC_VER
+#warning inheritance needs to be implemented in PluginScript
+#endif
+	return false;
+}
+
 Ref<Script> PluginScript::get_base_script() const {
 	if (_ref_base_parent.is_valid()) {
 		return Ref<PluginScript>(_ref_base_parent);

+ 2 - 0
modules/gdnative/pluginscript/pluginscript_script.h

@@ -72,6 +72,8 @@ private:
 protected:
 	static void _bind_methods();
 
+	bool inherits_script(const Ref<Script> &p_script) const;
+
 	PluginScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, Callable::CallError &r_error);
 	Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
 

+ 18 - 0
modules/gdscript/gdscript.cpp

@@ -895,6 +895,24 @@ Ref<GDScript> GDScript::get_base() const {
 	return base;
 }
 
+bool GDScript::inherits_script(const Ref<Script> &p_script) const {
+	Ref<GDScript> gd = p_script;
+	if (gd.is_null()) {
+		return false;
+	}
+
+	const GDScript *s = this;
+
+	while (s) {
+		if (s == p_script.ptr()) {
+			return true;
+		}
+		s = s->_base;
+	}
+
+	return false;
+}
+
 bool GDScript::has_script_signal(const StringName &p_signal) const {
 	if (_signals.has(p_signal))
 		return true;

+ 2 - 0
modules/gdscript/gdscript.h

@@ -151,6 +151,8 @@ protected:
 public:
 	virtual bool is_valid() const { return valid; }
 
+	bool inherits_script(const Ref<Script> &p_script) const;
+
 	const Map<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; }
 	const Map<StringName, Variant> &get_constants() const { return constants; }
 	const Set<StringName> &get_members() const { return members; }

+ 12 - 0
modules/mono/csharp_script.cpp

@@ -3536,6 +3536,18 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
 	}
 }
 
+bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
+	Ref<CSharpScript> cs = p_script;
+	if (cs.is_null()) {
+		return false;
+	}
+
+#ifndef _MSC_VER
+#warning TODO: Implement CSharpScript::inherits_script and other relevant changes after GH-38063.
+#endif
+	return false;
+}
+
 Ref<Script> CSharpScript::get_base_script() const {
 
 	// TODO search in metadata file once we have it, not important any way?

+ 2 - 0
modules/mono/csharp_script.h

@@ -194,6 +194,8 @@ public:
 	virtual bool is_tool() const { return tool; }
 	virtual bool is_valid() const { return valid; }
 
+	bool inherits_script(const Ref<Script> &p_script) const;
+
 	virtual Ref<Script> get_base_script() const;
 	virtual ScriptLanguage *get_language() const;
 

+ 4 - 0
modules/visual_script/visual_script.cpp

@@ -1460,6 +1460,10 @@ VisualScript::VisualScript() {
 	is_tool_script = false;
 }
 
+bool VisualScript::inherits_script(const Ref<Script> &p_script) const {
+	return this == p_script.ptr(); //there is no inheritance in visual scripts, so this is enough
+}
+
 StringName VisualScript::get_default_func() const {
 	return StringName("f_312843592");
 }

+ 2 - 0
modules/visual_script/visual_script.h

@@ -272,6 +272,8 @@ protected:
 	static void _bind_methods();
 
 public:
+	bool inherits_script(const Ref<Script> &p_script) const;
+
 	// TODO: Remove it in future when breaking changes are acceptable
 	StringName get_default_func() const;
 	void add_function(const StringName &p_name);

+ 3 - 3
scene/2d/tile_map.cpp

@@ -1691,14 +1691,14 @@ bool TileMap::is_centered_textures_enabled() const {
 	return centered_textures;
 }
 
-Array TileMap::get_used_cells() const {
+TypedArray<Vector2i> TileMap::get_used_cells() const {
 
-	Array a;
+	TypedArray<Vector2i> a;
 	a.resize(tile_map.size());
 	int i = 0;
 	for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
 
-		Vector2 p(E->key().x, E->key().y);
+		Vector2i p(E->key().x, E->key().y);
 		a[i++] = p;
 	}
 

+ 1 - 1
scene/2d/tile_map.h

@@ -328,7 +328,7 @@ public:
 	void set_centered_textures(bool p_enable);
 	bool is_centered_textures_enabled() const;
 
-	Array get_used_cells() const;
+	TypedArray<Vector2i> get_used_cells() const;
 	Array get_used_cells_by_id(int p_id) const;
 	Rect2 get_used_rect(); // Not const because of cache
 

+ 2 - 2
scene/main/node.cpp

@@ -2693,9 +2693,9 @@ void Node::queue_delete() {
 	}
 }
 
-Array Node::_get_children() const {
+TypedArray<Node> Node::_get_children() const {
 
-	Array arr;
+	TypedArray<Node> arr;
 	int cc = get_child_count();
 	arr.resize(cc);
 	for (int i = 0; i < cc; i++)

+ 2 - 1
scene/main/node.h

@@ -37,6 +37,7 @@
 #include "core/object.h"
 #include "core/project_settings.h"
 #include "core/script_language.h"
+#include "core/typed_array.h"
 #include "scene/main/scene_tree.h"
 
 class Viewport;
@@ -182,7 +183,7 @@ private:
 	void _duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const;
 	Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) const;
 
-	Array _get_children() const;
+	TypedArray<Node> _get_children() const;
 	Array _get_groups() const;
 
 	Variant _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);