Forráskód Böngészése

GDScript: Fix some lambda bugs

Danil Alexeev 1 éve
szülő
commit
b1eb737719

+ 0 - 10
.editorconfig

@@ -21,13 +21,3 @@ indent_size = 4
 [*.{yml,yaml}]
 indent_style = space
 indent_size = 2
-
-# GDScript unit test files
-[*.gd]
-indent_style = tab
-indent_size = 4
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-[*.out]
-insert_final_newline = true

+ 8 - 0
modules/gdscript/.editorconfig

@@ -0,0 +1,8 @@
+[*.gd]
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.out]
+insert_final_newline = true

+ 32 - 5
modules/gdscript/gdscript_compiler.cpp

@@ -84,7 +84,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
 	}
 }
 
-GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) {
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype) {
 	if (!p_datatype.is_set() || !p_datatype.is_hard_type() || p_datatype.is_coroutine) {
 		return GDScriptDataType();
 	}
@@ -101,11 +101,25 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 			result.builtin_type = p_datatype.builtin_type;
 		} break;
 		case GDScriptParser::DataType::NATIVE: {
+			if (p_handle_metatype && p_datatype.is_meta_type) {
+				result.kind = GDScriptDataType::NATIVE;
+				result.builtin_type = Variant::OBJECT;
+				result.native_type = GDScriptNativeClass::get_class_static();
+				break;
+			}
+
 			result.kind = GDScriptDataType::NATIVE;
 			result.native_type = p_datatype.native_type;
 			result.builtin_type = p_datatype.builtin_type;
 		} break;
 		case GDScriptParser::DataType::SCRIPT: {
+			if (p_handle_metatype && p_datatype.is_meta_type) {
+				result.kind = GDScriptDataType::NATIVE;
+				result.builtin_type = Variant::OBJECT;
+				result.native_type = p_datatype.script_type.is_valid() ? p_datatype.script_type->get_class() : Script::get_class_static();
+				break;
+			}
+
 			result.kind = GDScriptDataType::SCRIPT;
 			result.builtin_type = p_datatype.builtin_type;
 			result.script_type_ref = p_datatype.script_type;
@@ -113,6 +127,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 			result.native_type = p_datatype.native_type;
 		} break;
 		case GDScriptParser::DataType::CLASS: {
+			if (p_handle_metatype && p_datatype.is_meta_type) {
+				result.kind = GDScriptDataType::NATIVE;
+				result.builtin_type = Variant::OBJECT;
+				result.native_type = GDScript::get_class_static();
+				break;
+			}
+
 			result.kind = GDScriptDataType::GDSCRIPT;
 			result.builtin_type = p_datatype.builtin_type;
 			result.native_type = p_datatype.native_type;
@@ -148,6 +169,12 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 			}
 		} break;
 		case GDScriptParser::DataType::ENUM:
+			if (p_handle_metatype && p_datatype.is_meta_type) {
+				result.kind = GDScriptDataType::BUILTIN;
+				result.builtin_type = Variant::DICTIONARY;
+				break;
+			}
+
 			result.kind = GDScriptDataType::BUILTIN;
 			result.builtin_type = p_datatype.builtin_type;
 			break;
@@ -159,7 +186,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 	}
 
 	if (p_datatype.has_container_element_type()) {
-		result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner));
+		result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner, false));
 	}
 
 	return result;
@@ -533,7 +560,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 		} break;
 		case GDScriptParser::Node::CAST: {
 			const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
-			GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script);
+			GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script, false);
 
 			GDScriptCodeGenerator::Address result;
 			if (cast_type.has_type) {
@@ -911,7 +938,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 			GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(type_test->get_datatype(), codegen.script));
 
 			GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, type_test->operand);
-			GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script);
+			GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script, false);
 			if (r_error) {
 				return GDScriptCodeGenerator::Address();
 			}
@@ -2587,7 +2614,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
 		}
 	}
 
-	GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script);
+	GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script, false);
 
 	int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];
 	p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];

+ 1 - 1
modules/gdscript/gdscript_compiler.h

@@ -124,7 +124,7 @@ class GDScriptCompiler {
 	Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
 	Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
 
-	GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner);
+	GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype = true);
 
 	GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
 	GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());

+ 72 - 2
modules/gdscript/gdscript_lambda_callable.cpp

@@ -79,13 +79,48 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V
 		args.resize(p_argcount + captures_amount);
 		for (int i = 0; i < captures_amount; i++) {
 			args.write[i] = &captures[i];
+			if (captures[i].get_type() == Variant::OBJECT) {
+				bool was_freed = false;
+				captures[i].get_validated_object_with_check(was_freed);
+				if (was_freed) {
+					ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
+					static Variant nil;
+					args.write[i] = &nil;
+				}
+			}
 		}
 		for (int i = 0; i < p_argcount; i++) {
 			args.write[i + captures_amount] = p_arguments[i];
 		}
 
 		r_return_value = function->call(nullptr, args.ptrw(), args.size(), r_call_error);
-		r_call_error.argument -= captures_amount;
+		switch (r_call_error.error) {
+			case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+				r_call_error.argument -= captures_amount;
+#ifdef DEBUG_ENABLED
+				if (r_call_error.argument < 0) {
+					ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
+					r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+					r_call_error.argument = 0;
+					r_call_error.expected = 0;
+				}
+#endif
+				break;
+			case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+			case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+				r_call_error.expected -= captures_amount;
+#ifdef DEBUG_ENABLED
+				if (r_call_error.expected < 0) {
+					ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
+					r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+					r_call_error.argument = 0;
+					r_call_error.expected = 0;
+				}
+#endif
+				break;
+			default:
+				break;
+		}
 	} else {
 		r_return_value = function->call(nullptr, p_arguments, p_argcount, r_call_error);
 	}
@@ -148,13 +183,48 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
 		args.resize(p_argcount + captures_amount);
 		for (int i = 0; i < captures_amount; i++) {
 			args.write[i] = &captures[i];
+			if (captures[i].get_type() == Variant::OBJECT) {
+				bool was_freed = false;
+				captures[i].get_validated_object_with_check(was_freed);
+				if (was_freed) {
+					ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
+					static Variant nil;
+					args.write[i] = &nil;
+				}
+			}
 		}
 		for (int i = 0; i < p_argcount; i++) {
 			args.write[i + captures_amount] = p_arguments[i];
 		}
 
 		r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error);
-		r_call_error.argument -= captures_amount;
+		switch (r_call_error.error) {
+			case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+				r_call_error.argument -= captures_amount;
+#ifdef DEBUG_ENABLED
+				if (r_call_error.argument < 0) {
+					ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
+					r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+					r_call_error.argument = 0;
+					r_call_error.expected = 0;
+				}
+#endif
+				break;
+			case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+			case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+				r_call_error.expected -= captures_amount;
+#ifdef DEBUG_ENABLED
+				if (r_call_error.expected < 0) {
+					ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
+					r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+					r_call_error.argument = 0;
+					r_call_error.expected = 0;
+				}
+#endif
+				break;
+			default:
+				break;
+		}
 	} else {
 		r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error);
 	}

+ 5 - 4
modules/gdscript/gdscript_vm.cpp

@@ -116,6 +116,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 
 	if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
 		int errorarg = p_err.argument;
+		ERR_FAIL_COND_V_MSG(errorarg < 0 || argptrs[errorarg] == nullptr, "GDScript bug (please report): Invalid CallError argument index or null pointer.", "Invalid CallError argument index or null pointer.");
 		// Handle the Object to Object case separately as we don't have further class details.
 #ifdef DEBUG_ENABLED
 		if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
@@ -128,9 +129,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 			err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
 		}
 	} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
-		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
 	} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
-		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
 	} else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
 		err_text = "Invalid call. Nonexistent " + p_where + ".";
 	} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
@@ -511,13 +512,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 		if (p_argcount != _argument_count) {
 			if (p_argcount > _argument_count) {
 				r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
-				r_err.argument = _argument_count;
+				r_err.expected = _argument_count;
 
 				call_depth--;
 				return _get_default_variant_for_data_type(return_type);
 			} else if (p_argcount < _argument_count - _default_arg_count) {
 				r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-				r_err.argument = _argument_count - _default_arg_count;
+				r_err.expected = _argument_count - _default_arg_count;
 				call_depth--;
 				return _get_default_variant_for_data_type(return_type);
 			} else {

+ 6 - 59
modules/gdscript/tests/scripts/runtime/features/member_info.gd

@@ -5,6 +5,8 @@ class MyClass:
 
 enum MyEnum {}
 
+const Utils = preload("../../utils.notest.gd")
+
 static var test_static_var_untyped
 static var test_static_var_weak_null = null
 static var test_static_var_weak_int = 1
@@ -58,68 +60,13 @@ func test():
 	var script: Script = get_script()
 	for property in script.get_property_list():
 		if str(property.name).begins_with("test_"):
-			if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
-				print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
-			print("static var ", property.name, ": ", get_type(property))
+			print(Utils.get_property_signature(property, true))
 	for property in get_property_list():
 		if str(property.name).begins_with("test_"):
-			if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
-				print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
-			print("var ", property.name, ": ", get_type(property))
+			print(Utils.get_property_signature(property))
 	for method in get_method_list():
 		if str(method.name).begins_with("test_"):
-			print(get_signature(method))
+			print(Utils.get_method_signature(method))
 	for method in get_signal_list():
 		if str(method.name).begins_with("test_"):
-			print(get_signature(method, true))
-
-func get_type(property: Dictionary, is_return: bool = false) -> String:
-	match property.type:
-		TYPE_NIL:
-			if property.usage & PROPERTY_USAGE_NIL_IS_VARIANT:
-				return "Variant"
-			return "void" if is_return else "null"
-		TYPE_BOOL:
-			return "bool"
-		TYPE_INT:
-			if property.usage & PROPERTY_USAGE_CLASS_IS_ENUM:
-				return property.class_name
-			return "int"
-		TYPE_STRING:
-			return "String"
-		TYPE_DICTIONARY:
-			return "Dictionary"
-		TYPE_ARRAY:
-			if property.hint == PROPERTY_HINT_ARRAY_TYPE:
-				return "Array[%s]" % property.hint_string
-			return "Array"
-		TYPE_OBJECT:
-			if not str(property.class_name).is_empty():
-				return property.class_name
-			return "Object"
-	return "<error>"
-
-func get_signature(method: Dictionary, is_signal: bool = false) -> String:
-	var result: String = ""
-	if method.flags & METHOD_FLAG_STATIC:
-		result += "static "
-	result += ("signal " if is_signal else "func ") + method.name + "("
-
-	var args: Array[Dictionary] = method.args
-	var default_args: Array = method.default_args
-	var mandatory_argc: int = args.size() - default_args.size()
-	for i in args.size():
-		if i > 0:
-			result += ", "
-		var arg: Dictionary = args[i]
-		result += arg.name + ": " + get_type(arg)
-		if i >= mandatory_argc:
-			result += " = " + var_to_str(default_args[i - mandatory_argc])
-
-	result += ")"
-	if is_signal:
-		if get_type(method.return, true) != "void":
-			print("Error: Signal return type must be `void`.")
-	else:
-		result += " -> " + get_type(method.return, true)
-	return result
+			print(Utils.get_method_signature(method, true))

+ 3 - 3
modules/gdscript/tests/scripts/runtime/features/member_info.out

@@ -6,13 +6,13 @@ static var test_static_var_hard_int: int
 var test_var_untyped: Variant
 var test_var_weak_null: Variant
 var test_var_weak_int: Variant
-var test_var_weak_int_exported: int
+@export var test_var_weak_int_exported: int
 var test_var_weak_variant_type: Variant
-var test_var_weak_variant_type_exported: Variant.Type
+@export var test_var_weak_variant_type_exported: Variant.Type
 var test_var_hard_variant: Variant
 var test_var_hard_int: int
 var test_var_hard_variant_type: Variant.Type
-var test_var_hard_variant_type_exported: Variant.Type
+@export var test_var_hard_variant_type_exported: Variant.Type
 var test_var_hard_node_process_mode: Node.ProcessMode
 var test_var_hard_my_enum: TestMemberInfo.MyEnum
 var test_var_hard_array: Array

+ 36 - 0
modules/gdscript/tests/scripts/runtime/features/metatypes.gd

@@ -0,0 +1,36 @@
+class MyClass:
+	const TEST = 10
+
+enum MyEnum {A, B, C}
+
+const Utils = preload("../../utils.notest.gd")
+const Other = preload("./metatypes.notest.gd")
+
+var test_native := JSON
+var test_script := Other
+var test_class := MyClass
+var test_enum := MyEnum
+
+func check_gdscript_native_class(value: Variant) -> void:
+	print(var_to_str(value).get_slice(",", 0).trim_prefix("Object("))
+
+func check_gdscript(value: GDScript) -> void:
+	print(value.get_class())
+
+func check_enum(value: Dictionary) -> void:
+	print(value)
+
+func test():
+	for property in get_property_list():
+		if str(property.name).begins_with("test_"):
+			print(Utils.get_property_signature(property))
+
+	check_gdscript_native_class(test_native)
+	check_gdscript(test_script)
+	check_gdscript(test_class)
+	check_enum(test_enum)
+
+	print(test_native.stringify([]))
+	print(test_script.TEST)
+	print(test_class.TEST)
+	print(test_enum.keys())

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/metatypes.notest.gd

@@ -0,0 +1 @@
+const TEST = 100

+ 13 - 0
modules/gdscript/tests/scripts/runtime/features/metatypes.out

@@ -0,0 +1,13 @@
+GDTEST_OK
+var test_native: GDScriptNativeClass
+var test_script: GDScript
+var test_class: GDScript
+var test_enum: Dictionary
+GDScriptNativeClass
+GDScript
+GDScript
+{ "A": 0, "B": 1, "C": 2 }
+[]
+100
+10
+["A", "B", "C"]

+ 137 - 0
modules/gdscript/tests/scripts/utils.notest.gd

@@ -0,0 +1,137 @@
+static func get_type(property: Dictionary, is_return: bool = false) -> String:
+	match property.type:
+		TYPE_NIL:
+			if property.usage & PROPERTY_USAGE_NIL_IS_VARIANT:
+				return "Variant"
+			return "void" if is_return else "null"
+		TYPE_INT:
+			if property.usage & PROPERTY_USAGE_CLASS_IS_ENUM:
+				if property.class_name == &"":
+					return "<unknown enum>"
+				return property.class_name
+		TYPE_ARRAY:
+			if property.hint == PROPERTY_HINT_ARRAY_TYPE:
+				if str(property.hint_string).is_empty():
+					return "Array[<unknown type>]"
+				return "Array[%s]" % property.hint_string
+		TYPE_OBJECT:
+			if not str(property.class_name).is_empty():
+				return property.class_name
+	return variant_get_type_name(property.type)
+
+static func get_property_signature(property: Dictionary, is_static: bool = false) -> String:
+	var result: String = ""
+	if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
+		printerr("Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
+	if property.usage & PROPERTY_USAGE_DEFAULT:
+		result += "@export "
+	if is_static:
+		result += "static "
+	result += "var " + property.name + ": " + get_type(property)
+	return result
+
+static func get_method_signature(method: Dictionary, is_signal: bool = false) -> String:
+	var result: String = ""
+	if method.flags & METHOD_FLAG_STATIC:
+		result += "static "
+	result += ("signal " if is_signal else "func ") + method.name + "("
+
+	var args: Array[Dictionary] = method.args
+	var default_args: Array = method.default_args
+	var mandatory_argc: int = args.size() - default_args.size()
+	for i in args.size():
+		if i > 0:
+			result += ", "
+		var arg: Dictionary = args[i]
+		result += arg.name + ": " + get_type(arg)
+		if i >= mandatory_argc:
+			result += " = " + var_to_str(default_args[i - mandatory_argc])
+
+	result += ")"
+	if is_signal:
+		if get_type(method.return, true) != "void":
+			printerr("Signal return type must be `void`.")
+	else:
+		result += " -> " + get_type(method.return, true)
+	return result
+
+static func variant_get_type_name(type: Variant.Type) -> String:
+	match type:
+		TYPE_NIL:
+			return "Nil" # `Nil` in core, `null` in GDScript.
+		TYPE_BOOL:
+			return "bool"
+		TYPE_INT:
+			return "int"
+		TYPE_FLOAT:
+			return "float"
+		TYPE_STRING:
+			return "String"
+		TYPE_VECTOR2:
+			return "Vector2"
+		TYPE_VECTOR2I:
+			return "Vector2i"
+		TYPE_RECT2:
+			return "Rect2"
+		TYPE_RECT2I:
+			return "Rect2i"
+		TYPE_VECTOR3:
+			return "Vector3"
+		TYPE_VECTOR3I:
+			return "Vector3i"
+		TYPE_TRANSFORM2D:
+			return "Transform2D"
+		TYPE_VECTOR4:
+			return "Vector4"
+		TYPE_VECTOR4I:
+			return "Vector4i"
+		TYPE_PLANE:
+			return "Plane"
+		TYPE_QUATERNION:
+			return "Quaternion"
+		TYPE_AABB:
+			return "AABB"
+		TYPE_BASIS:
+			return "Basis"
+		TYPE_TRANSFORM3D:
+			return "Transform3D"
+		TYPE_PROJECTION:
+			return "Projection"
+		TYPE_COLOR:
+			return "Color"
+		TYPE_STRING_NAME:
+			return "StringName"
+		TYPE_NODE_PATH:
+			return "NodePath"
+		TYPE_RID:
+			return "RID"
+		TYPE_OBJECT:
+			return "Object"
+		TYPE_CALLABLE:
+			return "Callable"
+		TYPE_SIGNAL:
+			return "Signal"
+		TYPE_DICTIONARY:
+			return "Dictionary"
+		TYPE_ARRAY:
+			return "Array"
+		TYPE_PACKED_BYTE_ARRAY:
+			return "PackedByteArray"
+		TYPE_PACKED_INT32_ARRAY:
+			return "PackedInt32Array"
+		TYPE_PACKED_INT64_ARRAY:
+			return "PackedInt64Array"
+		TYPE_PACKED_FLOAT32_ARRAY:
+			return "PackedFloat32Array"
+		TYPE_PACKED_FLOAT64_ARRAY:
+			return "PackedFloat64Array"
+		TYPE_PACKED_STRING_ARRAY:
+			return "PackedStringArray"
+		TYPE_PACKED_VECTOR2_ARRAY:
+			return "PackedVector2Array"
+		TYPE_PACKED_VECTOR3_ARRAY:
+			return "PackedVector3Array"
+		TYPE_PACKED_COLOR_ARRAY:
+			return "PackedColorArray"
+	push_error("Argument `type` is invalid. Use `TYPE_*` constants.")
+	return "<invalid type>"