Parcourir la source

Merge pull request #43890 from vnen/gdscript-builtin-functions-refactor

GDScript: Refactor builtin functions
Rémi Verschelde il y a 4 ans
Parent
commit
abfc528439

+ 5 - 2
modules/gdscript/gdscript.cpp

@@ -2122,8 +2122,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
 		w++;
 	}
 
-	for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
-		p_words->push_back(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)));
+	List<StringName> functions;
+	GDScriptUtilityFunctions::get_function_list(&functions);
+
+	for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
+		p_words->push_back(String(E->get()));
 	}
 }
 

+ 2 - 2
modules/gdscript/gdscript.h

@@ -72,8 +72,8 @@ class GDScript : public Script {
 	friend class GDScriptFunction;
 	friend class GDScriptAnalyzer;
 	friend class GDScriptCompiler;
-	friend class GDScriptFunctions;
 	friend class GDScriptLanguage;
+	friend struct GDScriptUtilityFunctionsDefinitions;
 
 	Ref<GDScriptNativeClass> native;
 	Ref<GDScript> base;
@@ -270,8 +270,8 @@ public:
 class GDScriptInstance : public ScriptInstance {
 	friend class GDScript;
 	friend class GDScriptFunction;
-	friend class GDScriptFunctions;
 	friend class GDScriptCompiler;
+	friend struct GDScriptUtilityFunctionsDefinitions;
 
 	ObjectID owner_id;
 	Object *owner;

+ 85 - 10
modules/gdscript/gdscript_analyzer.cpp

@@ -37,6 +37,7 @@
 #include "core/os/file_access.h"
 #include "core/templates/hash_map.h"
 #include "gdscript.h"
+#include "gdscript_utility_functions.h"
 
 // TODO: Move this to a central location (maybe core?).
 static HashMap<StringName, StringName> underscore_map;
@@ -72,6 +73,39 @@ static StringName get_real_class_name(const StringName &p_source) {
 	return p_source;
 }
 
+static MethodInfo info_from_utility_func(const StringName &p_function) {
+	ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
+
+	MethodInfo info(p_function);
+
+	if (Variant::has_utility_function_return_value(p_function)) {
+		info.return_val.type = Variant::get_utility_function_return_type(p_function);
+		if (info.return_val.type == Variant::NIL) {
+			info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+		}
+	}
+
+	if (Variant::is_utility_function_vararg(p_function)) {
+		info.flags |= METHOD_FLAG_VARARG;
+	} else {
+		for (int i = 0; i < Variant::get_utility_function_argument_count(p_function); i++) {
+			PropertyInfo pi;
+#ifdef DEBUG_METHODS_ENABLED
+			pi.name = Variant::get_utility_function_argument_name(p_function, i);
+#else
+			pi.name = "arg" + itos(i + 1);
+#endif
+			pi.type = Variant::get_utility_function_argument_type(p_function, i);
+			if (pi.type == Variant::NIL) {
+				pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+			}
+			info.arguments.push_back(pi);
+		}
+	}
+
+	return info;
+}
+
 void GDScriptAnalyzer::cleanup() {
 	underscore_map.clear();
 }
@@ -1701,7 +1735,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
 		// Call to name directly.
 		StringName function_name = p_call->function_name;
 		Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
-		GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name);
 
 		if (builtin_type < Variant::VARIANT_MAX) {
 			// Is a builtin constructor.
@@ -1843,10 +1876,52 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
 			}
 			p_call->set_datatype(call_type);
 			return;
-		} else if (builtin_function < GDScriptFunctions::FUNC_MAX) {
-			MethodInfo function_info = GDScriptFunctions::get_info(builtin_function);
+		} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
+			MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
+
+			if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
+				// Can call on compilation.
+				Vector<const Variant *> args;
+				for (int i = 0; i < p_call->arguments.size(); i++) {
+					args.push_back(&(p_call->arguments[i]->reduced_value));
+				}
+
+				Variant value;
+				Callable::CallError err;
+				GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err);
+
+				switch (err.error) {
+					case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+						PropertyInfo wrong_arg = function_info.arguments[err.argument];
+						push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
+										   type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+								p_call->arguments[err.argument]);
+					} break;
+					case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+						push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
+						break;
+					case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+						push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+						break;
+					case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+						push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+						break;
+					case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+						break; // Can't happen in a builtin constructor.
+					case Callable::CallError::CALL_OK:
+						p_call->is_constant = true;
+						p_call->reduced_value = value;
+						break;
+				}
+			} else {
+				validate_call_arg(function_info, p_call);
+			}
+			p_call->set_datatype(type_from_property(function_info.return_val));
+			return;
+		} else if (Variant::has_utility_function(function_name)) {
+			MethodInfo function_info = info_from_utility_func(function_name);
 
-			if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) {
+			if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
 				// Can call on compilation.
 				Vector<const Variant *> args;
 				for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1855,23 +1930,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
 
 				Variant value;
 				Callable::CallError err;
-				GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err);
+				Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err);
 
 				switch (err.error) {
 					case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
 						PropertyInfo wrong_arg = function_info.arguments[err.argument];
-						push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1,
+						push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
 										   type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
 								p_call->arguments[err.argument]);
 					} break;
 					case Callable::CallError::CALL_ERROR_INVALID_METHOD:
-						push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call);
+						push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
 						break;
 					case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
-						push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+						push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
 						break;
 					case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
-						push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+						push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
 						break;
 					case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
 						break; // Can't happen in a builtin constructor.
@@ -2385,7 +2460,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
 
 	// Not found.
 	// Check if it's a builtin function.
-	if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) {
+	if (GDScriptUtilityFunctions::function_exists(name)) {
 		push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
 	} else {
 		push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);

+ 61 - 2
modules/gdscript/gdscript_byte_codegen.cpp

@@ -283,6 +283,30 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
 		function->_constructors_count = 0;
 	}
 
+	if (utilities_map.size()) {
+		function->utilities.resize(utilities_map.size());
+		function->_utilities_ptr = function->utilities.ptr();
+		function->_utilities_count = utilities_map.size();
+		for (const Map<Variant::ValidatedUtilityFunction, int>::Element *E = utilities_map.front(); E; E = E->next()) {
+			function->utilities.write[E->get()] = E->key();
+		}
+	} else {
+		function->_utilities_ptr = nullptr;
+		function->_utilities_count = 0;
+	}
+
+	if (gds_utilities_map.size()) {
+		function->gds_utilities.resize(gds_utilities_map.size());
+		function->_gds_utilities_ptr = function->gds_utilities.ptr();
+		function->_gds_utilities_count = gds_utilities_map.size();
+		for (const Map<GDScriptUtilityFunctions::FunctionPtr, int>::Element *E = gds_utilities_map.front(); E; E = E->next()) {
+			function->gds_utilities.write[E->get()] = E->key();
+		}
+	} else {
+		function->_gds_utilities_ptr = nullptr;
+		function->_gds_utilities_count = 0;
+	}
+
 	if (method_bind_map.size()) {
 		function->methods.resize(method_bind_map.size());
 		function->_methods_ptr = function->methods.ptrw();
@@ -704,8 +728,8 @@ void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const
 	append(p_function_name);
 }
 
-void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
-	append(GDScriptFunction::OPCODE_CALL_BUILT_IN, 1 + p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) {
+	append(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
@@ -714,6 +738,41 @@ void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDSc
 	append(p_function);
 }
 
+void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) {
+	bool is_validated = true;
+	if (Variant::is_utility_function_vararg(p_function)) {
+		is_validated = true; // Vararg works fine with any argument, since they can be any type.
+	} else if (p_arguments.size() == Variant::get_utility_function_argument_count(p_function)) {
+		bool all_types_exact = true;
+		for (int i = 0; i < p_arguments.size(); i++) {
+			if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_utility_function_argument_type(p_function, i))) {
+				all_types_exact = false;
+				break;
+			}
+		}
+
+		is_validated = all_types_exact;
+	}
+
+	if (is_validated) {
+		append(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
+		for (int i = 0; i < p_arguments.size(); i++) {
+			append(p_arguments[i]);
+		}
+		append(p_target);
+		append(p_arguments.size());
+		append(Variant::get_validated_utility_function(p_function));
+	} else {
+		append(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
+		for (int i = 0; i < p_arguments.size(); i++) {
+			append(p_arguments[i]);
+		}
+		append(p_target);
+		append(p_arguments.size());
+		append(p_function);
+	}
+}
+
 void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
 	bool is_validated = false;
 

+ 31 - 1
modules/gdscript/gdscript_byte_codegen.h

@@ -34,6 +34,7 @@
 #include "gdscript_codegen.h"
 
 #include "gdscript_function.h"
+#include "gdscript_utility_functions.h"
 
 class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 	bool ended = false;
@@ -76,6 +77,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 	Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
 	Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
 	Map<Variant::ValidatedConstructor, int> constructors_map;
+	Map<Variant::ValidatedUtilityFunction, int> utilities_map;
+	Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
 	Map<MethodBind *, int> method_bind_map;
 
 	// Lists since these can be nested.
@@ -241,6 +244,24 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 		return pos;
 	}
 
+	int get_utility_pos(const Variant::ValidatedUtilityFunction p_utility) {
+		if (utilities_map.has(p_utility)) {
+			return utilities_map[p_utility];
+		}
+		int pos = utilities_map.size();
+		utilities_map[p_utility] = pos;
+		return pos;
+	}
+
+	int get_gds_utility_pos(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+		if (gds_utilities_map.has(p_gds_utility)) {
+			return gds_utilities_map[p_gds_utility];
+		}
+		int pos = gds_utilities_map.size();
+		gds_utilities_map[p_gds_utility] = pos;
+		return pos;
+	}
+
 	int get_method_bind_pos(MethodBind *p_method) {
 		if (method_bind_map.has(p_method)) {
 			return method_bind_map[p_method];
@@ -346,6 +367,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 		opcodes.push_back(get_constructor_pos(p_constructor));
 	}
 
+	void append(const Variant::ValidatedUtilityFunction p_utility) {
+		opcodes.push_back(get_utility_pos(p_utility));
+	}
+
+	void append(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+		opcodes.push_back(get_gds_utility_pos(p_gds_utility));
+	}
+
 	void append(MethodBind *p_method) {
 		opcodes.push_back(get_method_bind_pos(p_method));
 	}
@@ -406,7 +435,8 @@ public:
 	virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
-	virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
+	virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
+	virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
 	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;

+ 3 - 2
modules/gdscript/gdscript_codegen.h

@@ -35,7 +35,7 @@
 #include "core/string/string_name.h"
 #include "core/variant/variant.h"
 #include "gdscript_function.h"
-#include "gdscript_functions.h"
+#include "gdscript_utility_functions.h"
 
 class GDScriptCodeGenerator {
 public:
@@ -127,7 +127,8 @@ public:
 	virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
-	virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
+	virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
+	virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;

+ 14 - 11
modules/gdscript/gdscript_compiler.cpp

@@ -33,6 +33,7 @@
 #include "gdscript.h"
 #include "gdscript_byte_codegen.h"
 #include "gdscript_cache.h"
+#include "gdscript_utility_functions.h"
 
 bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
 	if (codegen.function_node && codegen.function_node->is_static) {
@@ -456,15 +457,17 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 				arguments.push_back(arg);
 			}
 
-			if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
+			if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) {
 				// Construct a built-in type.
 				Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
 
 				gen->write_construct(result, vtype, arguments);
-			} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
-				// Built-in function.
-				GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
-				gen->write_call_builtin(result, func, arguments);
+			} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
+				// Variant utility function.
+				gen->write_call_utility(result, call->function_name, arguments);
+			} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
+				// GDScript utility function.
+				gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
 			} else {
 				// Regular function.
 				const GDScriptParser::ExpressionNode *callee = call->callee;
@@ -1135,7 +1138,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
 			// Evaluate expression type.
 			Vector<GDScriptCodeGenerator::Address> typeof_args;
 			typeof_args.push_back(expr_addr);
-			codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+			codegen.generator->write_call_utility(result_addr, "typeof", typeof_args);
 
 			// Check type equality.
 			codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
@@ -1199,7 +1202,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
 			GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
 			Vector<GDScriptCodeGenerator::Address> len_args;
 			len_args.push_back(p_value_addr);
-			codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args);
+			codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), len_args);
 
 			// Test length compatibility.
 			temp_type.builtin_type = Variant::BOOL;
@@ -1253,7 +1256,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
 				// Also get type of element.
 				Vector<GDScriptCodeGenerator::Address> typeof_args;
 				typeof_args.push_back(element_addr);
-				codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+				codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
 
 				// Try the pattern inside the element.
 				test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
@@ -1298,7 +1301,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
 			GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
 			Vector<GDScriptCodeGenerator::Address> func_args;
 			func_args.push_back(p_value_addr);
-			codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args);
+			codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), func_args);
 
 			// Test length compatibility.
 			temp_type.builtin_type = Variant::BOOL;
@@ -1367,7 +1370,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
 					// Also get type of value.
 					func_args.clear();
 					func_args.push_back(element_addr);
-					codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args);
+					codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
 
 					// Try the pattern inside the value.
 					test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
@@ -1500,7 +1503,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
 
 				Vector<GDScriptCodeGenerator::Address> typeof_args;
 				typeof_args.push_back(value);
-				gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args);
+				gen->write_call_utility(type, "typeof", typeof_args);
 
 				// Now we can actually start testing.
 				// For each branch.

+ 39 - 4
modules/gdscript/gdscript_disassembler.cpp

@@ -34,7 +34,6 @@
 
 #include "core/string/string_builder.h"
 #include "gdscript.h"
-#include "gdscript_functions.h"
 
 static String _get_variant_string(const Variant &p_variant) {
 	String txt;
@@ -607,13 +606,49 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 				incr = 5 + argc;
 			} break;
-			case OPCODE_CALL_BUILT_IN: {
-				text += "call-built-in ";
+			case OPCODE_CALL_UTILITY: {
+				text += "call-utility ";
 
 				int argc = _code_ptr[ip + 1 + instr_var_args];
 				text += DADDR(1 + argc) + " = ";
 
-				text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args]));
+				text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 4 + argc;
+			} break;
+			case OPCODE_CALL_UTILITY_VALIDATED: {
+				text += "call-utility ";
+
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				text += DADDR(1 + argc) + " = ";
+
+				text += "<unkown function>";
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 4 + argc;
+			} break;
+			case OPCODE_CALL_GDSCRIPT_UTILITY: {
+				text += "call-gscript-utility ";
+
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				text += DADDR(1 + argc) + " = ";
+
+				text += "<unknown function>";
 				text += "(";
 
 				for (int i = 0; i < argc; i++) {

+ 22 - 17
modules/gdscript/gdscript_editor.cpp

@@ -37,6 +37,7 @@
 #include "gdscript_compiler.h"
 #include "gdscript_parser.h"
 #include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
 
 #ifdef TOOLS_ENABLED
 #include "core/config/project_settings.h"
@@ -411,11 +412,14 @@ void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) con
 }
 
 void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
-	for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
-		p_functions->push_back(GDScriptFunctions::get_info(GDScriptFunctions::Function(i)));
+	List<StringName> functions;
+	GDScriptUtilityFunctions::get_function_list(&functions);
+
+	for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
+		p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E->get()));
 	}
 
-	//not really "functions", but..
+	// Not really "functions", but show in documentation.
 	{
 		MethodInfo mi;
 		mi.name = "preload";
@@ -1030,9 +1034,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
 		_find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1);
 	}
 
-	for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
-		MethodInfo function = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
-		ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION);
+	List<StringName> functions;
+	GDScriptUtilityFunctions::get_function_list(&functions);
+
+	for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
+		MethodInfo function = GDScriptUtilityFunctions::get_function_info(E->get());
+		ScriptCodeCompletionOption option(String(E->get()), ScriptCodeCompletionOption::KIND_FUNCTION);
 		if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
 			option.insert_text += "(";
 		} else {
@@ -1288,8 +1295,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
 					r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name);
 					found = true;
 					break;
-				} else if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
-					MethodInfo mi = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+				} else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+					MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name);
 					r_type = _type_from_property(mi.return_val);
 					found = true;
 					break;
@@ -2342,8 +2349,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
 
 	GDScriptCompletionIdentifier connect_base;
 
-	if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
-		MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+	if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+		MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
 		r_arghint = _make_arguments_hint(info, p_argidx);
 		return;
 	} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
@@ -2971,13 +2978,11 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
 		}
 	}
 
-	for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
-		if (GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)) == p_symbol) {
-			r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
-			r_result.class_name = "@GDScript";
-			r_result.class_member = p_symbol;
-			return OK;
-		}
+	if (GDScriptUtilityFunctions::function_exists(p_symbol)) {
+		r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+		r_result.class_name = "@GDScript";
+		r_result.class_member = p_symbol;
+		return OK;
 	}
 
 	if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {

+ 10 - 1
modules/gdscript/gdscript_function.h

@@ -38,6 +38,7 @@
 #include "core/templates/pair.h"
 #include "core/templates/self_list.h"
 #include "core/variant/variant.h"
+#include "gdscript_utility_functions.h"
 
 class GDScriptInstance;
 class GDScript;
@@ -190,7 +191,9 @@ public:
 		OPCODE_CALL,
 		OPCODE_CALL_RETURN,
 		OPCODE_CALL_ASYNC,
-		OPCODE_CALL_BUILT_IN,
+		OPCODE_CALL_UTILITY,
+		OPCODE_CALL_UTILITY_VALIDATED,
+		OPCODE_CALL_GDSCRIPT_UTILITY,
 		OPCODE_CALL_BUILTIN_TYPE_VALIDATED,
 		OPCODE_CALL_SELF_BASE,
 		OPCODE_CALL_METHOD_BIND,
@@ -344,6 +347,10 @@ private:
 	const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
 	int _constructors_count = 0;
 	const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
+	int _utilities_count = 0;
+	const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr;
+	int _gds_utilities_count = 0;
+	const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
 	int _methods_count = 0;
 	MethodBind **_methods_ptr = nullptr;
 	const int *_code_ptr = nullptr;
@@ -372,6 +379,8 @@ private:
 	Vector<Variant::ValidatedIndexedGetter> indexed_getters;
 	Vector<Variant::ValidatedBuiltInMethod> builtin_methods;
 	Vector<Variant::ValidatedConstructor> constructors;
+	Vector<Variant::ValidatedUtilityFunction> utilities;
+	Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
 	Vector<MethodBind *> methods;
 	Vector<int> code;
 	Vector<GDScriptDataType> argument_types;

+ 0 - 1942
modules/gdscript/gdscript_functions.cpp

@@ -1,1942 +0,0 @@
-/*************************************************************************/
-/*  gdscript_functions.cpp                                               */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "gdscript_functions.h"
-
-#include "core/io/json.h"
-#include "core/io/marshalls.h"
-#include "core/math/math_funcs.h"
-#include "core/object/class_db.h"
-#include "core/object/reference.h"
-#include "core/os/os.h"
-#include "core/variant/variant_parser.h"
-#include "gdscript.h"
-
-const char *GDScriptFunctions::get_func_name(Function p_func) {
-	ERR_FAIL_INDEX_V(p_func, FUNC_MAX, "");
-
-	static const char *_names[FUNC_MAX] = {
-		"sin",
-		"cos",
-		"tan",
-		"sinh",
-		"cosh",
-		"tanh",
-		"asin",
-		"acos",
-		"atan",
-		"atan2",
-		"sqrt",
-		"fmod",
-		"fposmod",
-		"posmod",
-		"floor",
-		"ceil",
-		"round",
-		"abs",
-		"sign",
-		"pow",
-		"log",
-		"exp",
-		"is_nan",
-		"is_inf",
-		"is_equal_approx",
-		"is_zero_approx",
-		"ease",
-		"step_decimals",
-		"stepify",
-		"lerp",
-		"lerp_angle",
-		"inverse_lerp",
-		"range_lerp",
-		"smoothstep",
-		"move_toward",
-		"dectime",
-		"randomize",
-		"randi",
-		"randf",
-		"randf_range",
-		"randi_range",
-		"seed",
-		"rand_seed",
-		"deg2rad",
-		"rad2deg",
-		"linear2db",
-		"db2linear",
-		"polar2cartesian",
-		"cartesian2polar",
-		"wrapi",
-		"wrapf",
-		"max",
-		"min",
-		"clamp",
-		"nearest_po2",
-		"weakref",
-		"convert",
-		"typeof",
-		"type_exists",
-		"char",
-		"ord",
-		"str",
-		"print",
-		"printt",
-		"prints",
-		"printerr",
-		"printraw",
-		"print_debug",
-		"push_error",
-		"push_warning",
-		"var2str",
-		"str2var",
-		"var2bytes",
-		"bytes2var",
-		"range",
-		"load",
-		"inst2dict",
-		"dict2inst",
-		"validate_json",
-		"parse_json",
-		"to_json",
-		"hash",
-		"Color8",
-		"ColorN",
-		"print_stack",
-		"get_stack",
-		"instance_from_id",
-		"len",
-		"is_instance_valid",
-	};
-
-	return _names[p_func];
-}
-
-void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error) {
-	r_error.error = Callable::CallError::CALL_OK;
-#ifdef DEBUG_ENABLED
-
-#define VALIDATE_ARG_COUNT(m_count)                                         \
-	if (p_arg_count < m_count) {                                            \
-		r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;  \
-		r_error.argument = m_count;                                         \
-		r_error.expected = m_count;                                         \
-		r_ret = Variant();                                                  \
-		return;                                                             \
-	}                                                                       \
-	if (p_arg_count > m_count) {                                            \
-		r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
-		r_error.argument = m_count;                                         \
-		r_error.expected = m_count;                                         \
-		r_ret = Variant();                                                  \
-		return;                                                             \
-	}
-
-#define VALIDATE_ARG_NUM(m_arg)                                           \
-	if (!p_args[m_arg]->is_num()) {                                       \
-		r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
-		r_error.argument = m_arg;                                         \
-		r_error.expected = Variant::FLOAT;                                \
-		r_ret = Variant();                                                \
-		return;                                                           \
-	}
-
-#else
-
-#define VALIDATE_ARG_COUNT(m_count)
-#define VALIDATE_ARG_NUM(m_arg)
-#endif
-
-	//using a switch, so the compiler generates a jumptable
-
-	switch (p_func) {
-		case MATH_SIN: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::sin((double)*p_args[0]);
-		} break;
-		case MATH_COS: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::cos((double)*p_args[0]);
-		} break;
-		case MATH_TAN: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::tan((double)*p_args[0]);
-		} break;
-		case MATH_SINH: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::sinh((double)*p_args[0]);
-		} break;
-		case MATH_COSH: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::cosh((double)*p_args[0]);
-		} break;
-		case MATH_TANH: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::tanh((double)*p_args[0]);
-		} break;
-		case MATH_ASIN: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::asin((double)*p_args[0]);
-		} break;
-		case MATH_ACOS: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::acos((double)*p_args[0]);
-		} break;
-		case MATH_ATAN: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::atan((double)*p_args[0]);
-		} break;
-		case MATH_ATAN2: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::atan2((double)*p_args[0], (double)*p_args[1]);
-		} break;
-		case MATH_SQRT: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::sqrt((double)*p_args[0]);
-		} break;
-		case MATH_FMOD: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::fmod((double)*p_args[0], (double)*p_args[1]);
-		} break;
-		case MATH_FPOSMOD: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::fposmod((double)*p_args[0], (double)*p_args[1]);
-		} break;
-		case MATH_POSMOD: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::posmod((int)*p_args[0], (int)*p_args[1]);
-		} break;
-		case MATH_FLOOR: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::floor((double)*p_args[0]);
-		} break;
-		case MATH_CEIL: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::ceil((double)*p_args[0]);
-		} break;
-		case MATH_ROUND: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::round((double)*p_args[0]);
-		} break;
-		case MATH_ABS: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() == Variant::INT) {
-				int64_t i = *p_args[0];
-				r_ret = ABS(i);
-			} else if (p_args[0]->get_type() == Variant::FLOAT) {
-				double r = *p_args[0];
-				r_ret = Math::abs(r);
-			} else {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::FLOAT;
-				r_ret = Variant();
-			}
-		} break;
-		case MATH_SIGN: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() == Variant::INT) {
-				int64_t i = *p_args[0];
-				r_ret = i < 0 ? -1 : (i > 0 ? +1 : 0);
-			} else if (p_args[0]->get_type() == Variant::FLOAT) {
-				double r = *p_args[0];
-				r_ret = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0);
-			} else {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::FLOAT;
-				r_ret = Variant();
-			}
-		} break;
-		case MATH_POW: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::pow((double)*p_args[0], (double)*p_args[1]);
-		} break;
-		case MATH_LOG: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::log((double)*p_args[0]);
-		} break;
-		case MATH_EXP: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::exp((double)*p_args[0]);
-		} break;
-		case MATH_ISNAN: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::is_nan((double)*p_args[0]);
-		} break;
-		case MATH_ISINF: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::is_inf((double)*p_args[0]);
-		} break;
-		case MATH_ISEQUALAPPROX: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]);
-		} break;
-		case MATH_ISZEROAPPROX: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::is_zero_approx((real_t)*p_args[0]);
-		} break;
-		case MATH_EASE: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::ease((double)*p_args[0], (double)*p_args[1]);
-		} break;
-		case MATH_STEP_DECIMALS: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::step_decimals((double)*p_args[0]);
-		} break;
-		case MATH_STEPIFY: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::stepify((double)*p_args[0], (double)*p_args[1]);
-		} break;
-		case MATH_LERP: {
-			VALIDATE_ARG_COUNT(3);
-			VALIDATE_ARG_NUM(2);
-			const double t = (double)*p_args[2];
-			switch (p_args[0]->get_type() == p_args[1]->get_type() ? p_args[0]->get_type() : Variant::FLOAT) {
-				case Variant::VECTOR2: {
-					r_ret = ((Vector2)*p_args[0]).lerp((Vector2)*p_args[1], t);
-				} break;
-				case Variant::VECTOR3: {
-					r_ret = (p_args[0]->operator Vector3()).lerp(p_args[1]->operator Vector3(), t);
-				} break;
-				case Variant::COLOR: {
-					r_ret = ((Color)*p_args[0]).lerp((Color)*p_args[1], t);
-				} break;
-				default: {
-					VALIDATE_ARG_NUM(0);
-					VALIDATE_ARG_NUM(1);
-					r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], t);
-				} break;
-			}
-		} break;
-		case MATH_LERP_ANGLE: {
-			VALIDATE_ARG_COUNT(3);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			VALIDATE_ARG_NUM(2);
-			r_ret = Math::lerp_angle((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
-		} break;
-		case MATH_INVERSE_LERP: {
-			VALIDATE_ARG_COUNT(3);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			VALIDATE_ARG_NUM(2);
-			r_ret = Math::inverse_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
-		} break;
-		case MATH_RANGE_LERP: {
-			VALIDATE_ARG_COUNT(5);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			VALIDATE_ARG_NUM(2);
-			VALIDATE_ARG_NUM(3);
-			VALIDATE_ARG_NUM(4);
-			r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]);
-		} break;
-		case MATH_SMOOTHSTEP: {
-			VALIDATE_ARG_COUNT(3);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			VALIDATE_ARG_NUM(2);
-			r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
-		} break;
-		case MATH_MOVE_TOWARD: {
-			VALIDATE_ARG_COUNT(3);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			VALIDATE_ARG_NUM(2);
-			r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
-		} break;
-		case MATH_DECTIME: {
-			VALIDATE_ARG_COUNT(3);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			VALIDATE_ARG_NUM(2);
-			r_ret = Math::dectime((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
-		} break;
-		case MATH_RANDOMIZE: {
-			VALIDATE_ARG_COUNT(0);
-			Math::randomize();
-			r_ret = Variant();
-		} break;
-		case MATH_RANDI: {
-			VALIDATE_ARG_COUNT(0);
-			r_ret = Math::rand();
-		} break;
-		case MATH_RANDF: {
-			VALIDATE_ARG_COUNT(0);
-			r_ret = Math::randf();
-		} break;
-		case MATH_RANDF_RANGE: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::random((double)*p_args[0], (double)*p_args[1]);
-		} break;
-		case MATH_RANDI_RANGE: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			r_ret = Math::random((int)*p_args[0], (int)*p_args[1]);
-		} break;
-		case MATH_SEED: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			uint64_t seed = *p_args[0];
-			Math::seed(seed);
-			r_ret = Variant();
-		} break;
-		case MATH_RANDSEED: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			uint64_t seed = *p_args[0];
-			int ret = Math::rand_from_seed(&seed);
-			Array reta;
-			reta.push_back(ret);
-			reta.push_back(seed);
-			r_ret = reta;
-
-		} break;
-		case MATH_DEG2RAD: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::deg2rad((double)*p_args[0]);
-		} break;
-		case MATH_RAD2DEG: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::rad2deg((double)*p_args[0]);
-		} break;
-		case MATH_LINEAR2DB: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::linear2db((double)*p_args[0]);
-		} break;
-		case MATH_DB2LINEAR: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			r_ret = Math::db2linear((double)*p_args[0]);
-		} break;
-		case MATH_POLAR2CARTESIAN: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			double r = *p_args[0];
-			double th = *p_args[1];
-			r_ret = Vector2(r * Math::cos(th), r * Math::sin(th));
-		} break;
-		case MATH_CARTESIAN2POLAR: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			double x = *p_args[0];
-			double y = *p_args[1];
-			r_ret = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x));
-		} break;
-		case MATH_WRAP: {
-			VALIDATE_ARG_COUNT(3);
-			r_ret = Math::wrapi((int64_t)*p_args[0], (int64_t)*p_args[1], (int64_t)*p_args[2]);
-		} break;
-		case MATH_WRAPF: {
-			VALIDATE_ARG_COUNT(3);
-			r_ret = Math::wrapf((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
-		} break;
-		case LOGIC_MAX: {
-			VALIDATE_ARG_COUNT(2);
-			if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
-				int64_t a = *p_args[0];
-				int64_t b = *p_args[1];
-				r_ret = MAX(a, b);
-			} else {
-				VALIDATE_ARG_NUM(0);
-				VALIDATE_ARG_NUM(1);
-
-				double a = *p_args[0];
-				double b = *p_args[1];
-
-				r_ret = MAX(a, b);
-			}
-
-		} break;
-		case LOGIC_MIN: {
-			VALIDATE_ARG_COUNT(2);
-			if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
-				int64_t a = *p_args[0];
-				int64_t b = *p_args[1];
-				r_ret = MIN(a, b);
-			} else {
-				VALIDATE_ARG_NUM(0);
-				VALIDATE_ARG_NUM(1);
-
-				double a = *p_args[0];
-				double b = *p_args[1];
-
-				r_ret = MIN(a, b);
-			}
-		} break;
-		case LOGIC_CLAMP: {
-			VALIDATE_ARG_COUNT(3);
-			if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT && p_args[2]->get_type() == Variant::INT) {
-				int64_t a = *p_args[0];
-				int64_t b = *p_args[1];
-				int64_t c = *p_args[2];
-				r_ret = CLAMP(a, b, c);
-			} else {
-				VALIDATE_ARG_NUM(0);
-				VALIDATE_ARG_NUM(1);
-				VALIDATE_ARG_NUM(2);
-
-				double a = *p_args[0];
-				double b = *p_args[1];
-				double c = *p_args[2];
-
-				r_ret = CLAMP(a, b, c);
-			}
-		} break;
-		case LOGIC_NEAREST_PO2: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			int64_t num = *p_args[0];
-			r_ret = next_power_of_2(num);
-		} break;
-		case OBJ_WEAKREF: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() == Variant::OBJECT) {
-				if (p_args[0]->is_ref()) {
-					Ref<WeakRef> wref = memnew(WeakRef);
-					REF r = *p_args[0];
-					if (r.is_valid()) {
-						wref->set_ref(r);
-					}
-					r_ret = wref;
-				} else {
-					Ref<WeakRef> wref = memnew(WeakRef);
-					Object *obj = *p_args[0];
-					if (obj) {
-						wref->set_obj(obj);
-					}
-					r_ret = wref;
-				}
-			} else if (p_args[0]->get_type() == Variant::NIL) {
-				Ref<WeakRef> wref = memnew(WeakRef);
-				r_ret = wref;
-			} else {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::OBJECT;
-				r_ret = Variant();
-				return;
-			}
-		} break;
-		case TYPE_CONVERT: {
-			VALIDATE_ARG_COUNT(2);
-			VALIDATE_ARG_NUM(1);
-			int type = *p_args[1];
-			if (type < 0 || type >= Variant::VARIANT_MAX) {
-				r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::INT;
-				return;
-
-			} else {
-				Variant::construct(Variant::Type(type), r_ret, p_args, 1, r_error);
-			}
-		} break;
-		case TYPE_OF: {
-			VALIDATE_ARG_COUNT(1);
-			r_ret = p_args[0]->get_type();
-
-		} break;
-		case TYPE_EXISTS: {
-			VALIDATE_ARG_COUNT(1);
-			r_ret = ClassDB::class_exists(*p_args[0]);
-
-		} break;
-		case TEXT_CHAR: {
-			VALIDATE_ARG_COUNT(1);
-			VALIDATE_ARG_NUM(0);
-			char32_t result[2] = { *p_args[0], 0 };
-			r_ret = String(result);
-		} break;
-		case TEXT_ORD: {
-			VALIDATE_ARG_COUNT(1);
-
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = Variant();
-				return;
-			}
-
-			String str = p_args[0]->operator String();
-
-			if (str.length() != 1) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = RTR("Expected a string of length 1 (a character).");
-				return;
-			}
-
-			r_ret = str.get(0);
-
-		} break;
-		case TEXT_STR: {
-			if (p_arg_count < 1) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-				r_error.argument = 1;
-				r_ret = Variant();
-
-				return;
-			}
-			String str;
-			for (int i = 0; i < p_arg_count; i++) {
-				String os = p_args[i]->operator String();
-
-				if (i == 0) {
-					str = os;
-				} else {
-					str += os;
-				}
-			}
-
-			r_ret = str;
-
-		} break;
-		case TEXT_PRINT: {
-			String str;
-			for (int i = 0; i < p_arg_count; i++) {
-				str += p_args[i]->operator String();
-			}
-
-			print_line(str);
-			r_ret = Variant();
-
-		} break;
-		case TEXT_PRINT_TABBED: {
-			String str;
-			for (int i = 0; i < p_arg_count; i++) {
-				if (i) {
-					str += "\t";
-				}
-				str += p_args[i]->operator String();
-			}
-
-			print_line(str);
-			r_ret = Variant();
-
-		} break;
-		case TEXT_PRINT_SPACED: {
-			String str;
-			for (int i = 0; i < p_arg_count; i++) {
-				if (i) {
-					str += " ";
-				}
-				str += p_args[i]->operator String();
-			}
-
-			print_line(str);
-			r_ret = Variant();
-
-		} break;
-
-		case TEXT_PRINTERR: {
-			String str;
-			for (int i = 0; i < p_arg_count; i++) {
-				str += p_args[i]->operator String();
-			}
-
-			print_error(str);
-			r_ret = Variant();
-
-		} break;
-		case TEXT_PRINTRAW: {
-			String str;
-			for (int i = 0; i < p_arg_count; i++) {
-				str += p_args[i]->operator String();
-			}
-
-			OS::get_singleton()->print("%s", str.utf8().get_data());
-			r_ret = Variant();
-
-		} break;
-		case TEXT_PRINT_DEBUG: {
-			String str;
-			for (int i = 0; i < p_arg_count; i++) {
-				str += p_args[i]->operator String();
-			}
-
-			ScriptLanguage *script = GDScriptLanguage::get_singleton();
-			if (script->debug_get_stack_level_count() > 0) {
-				str += "\n   At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
-			}
-
-			print_line(str);
-			r_ret = Variant();
-		} break;
-		case PUSH_ERROR: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = Variant();
-				break;
-			}
-
-			String message = *p_args[0];
-			ERR_PRINT(message);
-			r_ret = Variant();
-		} break;
-		case PUSH_WARNING: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = Variant();
-				break;
-			}
-
-			String message = *p_args[0];
-			WARN_PRINT(message);
-			r_ret = Variant();
-		} break;
-		case VAR_TO_STR: {
-			VALIDATE_ARG_COUNT(1);
-			String vars;
-			VariantWriter::write_to_string(*p_args[0], vars);
-			r_ret = vars;
-		} break;
-		case STR_TO_VAR: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = Variant();
-				return;
-			}
-			r_ret = *p_args[0];
-
-			VariantParser::StreamString ss;
-			ss.s = *p_args[0];
-
-			String errs;
-			int line;
-			(void)VariantParser::parse(&ss, r_ret, errs, line);
-		} break;
-		case VAR_TO_BYTES: {
-			bool full_objects = false;
-			if (p_arg_count < 1) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-				r_error.argument = 1;
-				r_ret = Variant();
-				return;
-			} else if (p_arg_count > 2) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
-				r_error.argument = 2;
-				r_ret = Variant();
-			} else if (p_arg_count == 2) {
-				if (p_args[1]->get_type() != Variant::BOOL) {
-					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-					r_error.argument = 1;
-					r_error.expected = Variant::BOOL;
-					r_ret = Variant();
-					return;
-				}
-				full_objects = *p_args[1];
-			}
-
-			PackedByteArray barr;
-			int len;
-			Error err = encode_variant(*p_args[0], nullptr, len, full_objects);
-			if (err) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::NIL;
-				r_ret = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
-				return;
-			}
-
-			barr.resize(len);
-			{
-				uint8_t *w = barr.ptrw();
-				encode_variant(*p_args[0], w, len, full_objects);
-			}
-			r_ret = barr;
-		} break;
-		case BYTES_TO_VAR: {
-			bool allow_objects = false;
-			if (p_arg_count < 1) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-				r_error.argument = 1;
-				r_ret = Variant();
-				return;
-			} else if (p_arg_count > 2) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
-				r_error.argument = 2;
-				r_ret = Variant();
-			} else if (p_arg_count == 2) {
-				if (p_args[1]->get_type() != Variant::BOOL) {
-					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-					r_error.argument = 1;
-					r_error.expected = Variant::BOOL;
-					r_ret = Variant();
-					return;
-				}
-				allow_objects = *p_args[1];
-			}
-
-			if (p_args[0]->get_type() != Variant::PACKED_BYTE_ARRAY) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 1;
-				r_error.expected = Variant::PACKED_BYTE_ARRAY;
-				r_ret = Variant();
-				return;
-			}
-
-			PackedByteArray varr = *p_args[0];
-			Variant ret;
-			{
-				const uint8_t *r = varr.ptr();
-				Error err = decode_variant(ret, r, varr.size(), nullptr, allow_objects);
-				if (err != OK) {
-					r_ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
-					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-					r_error.argument = 0;
-					r_error.expected = Variant::PACKED_BYTE_ARRAY;
-					return;
-				}
-			}
-
-			r_ret = ret;
-
-		} break;
-		case GEN_RANGE: {
-			switch (p_arg_count) {
-				case 0: {
-					r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-					r_error.argument = 1;
-					r_error.expected = 1;
-					r_ret = Variant();
-
-				} break;
-				case 1: {
-					VALIDATE_ARG_NUM(0);
-					int count = *p_args[0];
-					Array arr;
-					if (count <= 0) {
-						r_ret = arr;
-						return;
-					}
-					Error err = arr.resize(count);
-					if (err != OK) {
-						r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
-						r_ret = Variant();
-						return;
-					}
-
-					for (int i = 0; i < count; i++) {
-						arr[i] = i;
-					}
-
-					r_ret = arr;
-				} break;
-				case 2: {
-					VALIDATE_ARG_NUM(0);
-					VALIDATE_ARG_NUM(1);
-
-					int from = *p_args[0];
-					int to = *p_args[1];
-
-					Array arr;
-					if (from >= to) {
-						r_ret = arr;
-						return;
-					}
-					Error err = arr.resize(to - from);
-					if (err != OK) {
-						r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
-						r_ret = Variant();
-						return;
-					}
-					for (int i = from; i < to; i++) {
-						arr[i - from] = i;
-					}
-					r_ret = arr;
-				} break;
-				case 3: {
-					VALIDATE_ARG_NUM(0);
-					VALIDATE_ARG_NUM(1);
-					VALIDATE_ARG_NUM(2);
-
-					int from = *p_args[0];
-					int to = *p_args[1];
-					int incr = *p_args[2];
-					if (incr == 0) {
-						r_ret = RTR("Step argument is zero!");
-						r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
-						return;
-					}
-
-					Array arr;
-					if (from >= to && incr > 0) {
-						r_ret = arr;
-						return;
-					}
-					if (from <= to && incr < 0) {
-						r_ret = arr;
-						return;
-					}
-
-					//calculate how many
-					int count = 0;
-					if (incr > 0) {
-						count = ((to - from - 1) / incr) + 1;
-					} else {
-						count = ((from - to - 1) / -incr) + 1;
-					}
-
-					Error err = arr.resize(count);
-
-					if (err != OK) {
-						r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
-						r_ret = Variant();
-						return;
-					}
-
-					if (incr > 0) {
-						int idx = 0;
-						for (int i = from; i < to; i += incr) {
-							arr[idx++] = i;
-						}
-					} else {
-						int idx = 0;
-						for (int i = from; i > to; i += incr) {
-							arr[idx++] = i;
-						}
-					}
-
-					r_ret = arr;
-				} break;
-				default: {
-					r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
-					r_error.argument = 3;
-					r_error.expected = 3;
-					r_ret = Variant();
-
-				} break;
-			}
-
-		} break;
-		case RESOURCE_LOAD: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = Variant();
-			} else {
-				r_ret = ResourceLoader::load(*p_args[0]);
-			}
-
-		} break;
-		case INST2DICT: {
-			VALIDATE_ARG_COUNT(1);
-
-			if (p_args[0]->get_type() == Variant::NIL) {
-				r_ret = Variant();
-			} else if (p_args[0]->get_type() != Variant::OBJECT) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_ret = Variant();
-			} else {
-				Object *obj = *p_args[0];
-				if (!obj) {
-					r_ret = Variant();
-
-				} else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
-					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-					r_error.argument = 0;
-					r_error.expected = Variant::DICTIONARY;
-					r_ret = RTR("Not a script with an instance");
-					return;
-				} else {
-					GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
-					Ref<GDScript> base = ins->get_script();
-					if (base.is_null()) {
-						r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-						r_error.argument = 0;
-						r_error.expected = Variant::DICTIONARY;
-						r_ret = RTR("Not based on a script");
-						return;
-					}
-
-					GDScript *p = base.ptr();
-					Vector<StringName> sname;
-
-					while (p->_owner) {
-						sname.push_back(p->name);
-						p = p->_owner;
-					}
-					sname.invert();
-
-					if (!p->path.is_resource_file()) {
-						r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-						r_error.argument = 0;
-						r_error.expected = Variant::DICTIONARY;
-						r_ret = Variant();
-
-						r_ret = RTR("Not based on a resource file");
-
-						return;
-					}
-
-					NodePath cp(sname, Vector<StringName>(), false);
-
-					Dictionary d;
-					d["@subpath"] = cp;
-					d["@path"] = p->get_path();
-
-					for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
-						if (!d.has(E->key())) {
-							d[E->key()] = ins->members[E->get().index];
-						}
-					}
-					r_ret = d;
-				}
-			}
-
-		} break;
-		case DICT2INST: {
-			VALIDATE_ARG_COUNT(1);
-
-			if (p_args[0]->get_type() != Variant::DICTIONARY) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::DICTIONARY;
-				r_ret = Variant();
-
-				return;
-			}
-
-			Dictionary d = *p_args[0];
-
-			if (!d.has("@path")) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::OBJECT;
-				r_ret = RTR("Invalid instance dictionary format (missing @path)");
-
-				return;
-			}
-
-			Ref<Script> scr = ResourceLoader::load(d["@path"]);
-			if (!scr.is_valid()) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::OBJECT;
-				r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
-				return;
-			}
-
-			Ref<GDScript> gdscr = scr;
-
-			if (!gdscr.is_valid()) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::OBJECT;
-				r_ret = Variant();
-				r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
-				return;
-			}
-
-			NodePath sub;
-			if (d.has("@subpath")) {
-				sub = d["@subpath"];
-			}
-
-			for (int i = 0; i < sub.get_name_count(); i++) {
-				gdscr = gdscr->subclasses[sub.get_name(i)];
-				if (!gdscr.is_valid()) {
-					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-					r_error.argument = 0;
-					r_error.expected = Variant::OBJECT;
-					r_ret = Variant();
-					r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
-					return;
-				}
-			}
-			r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
-
-			if (r_error.error != Callable::CallError::CALL_OK) {
-				r_ret = Variant();
-				return;
-			}
-
-			GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(r_ret)->get_script_instance());
-			Ref<GDScript> gd_ref = ins->get_script();
-
-			for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
-				if (d.has(E->key())) {
-					ins->members.write[E->get().index] = d[E->key()];
-				}
-			}
-
-		} break;
-		case VALIDATE_JSON: {
-			VALIDATE_ARG_COUNT(1);
-
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = Variant();
-				return;
-			}
-
-			String errs;
-			int errl;
-
-			Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
-			if (err != OK) {
-				r_ret = itos(errl) + ":" + errs;
-			} else {
-				r_ret = "";
-			}
-
-		} break;
-		case PARSE_JSON: {
-			VALIDATE_ARG_COUNT(1);
-
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::STRING;
-				r_ret = Variant();
-				return;
-			}
-
-			String errs;
-			int errl;
-
-			Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
-			if (err != OK) {
-				r_ret = Variant();
-				ERR_PRINT(vformat("Error parsing JSON at line %s: %s", errl, errs));
-			}
-
-		} break;
-		case TO_JSON: {
-			VALIDATE_ARG_COUNT(1);
-
-			r_ret = JSON::print(*p_args[0]);
-		} break;
-		case HASH: {
-			VALIDATE_ARG_COUNT(1);
-			r_ret = p_args[0]->hash();
-
-		} break;
-		case COLOR8: {
-			if (p_arg_count < 3) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-				r_error.argument = 3;
-				r_ret = Variant();
-
-				return;
-			}
-			if (p_arg_count > 4) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
-				r_error.argument = 4;
-				r_ret = Variant();
-
-				return;
-			}
-
-			VALIDATE_ARG_NUM(0);
-			VALIDATE_ARG_NUM(1);
-			VALIDATE_ARG_NUM(2);
-
-			Color color((float)*p_args[0] / 255.0f, (float)*p_args[1] / 255.0f, (float)*p_args[2] / 255.0f);
-
-			if (p_arg_count == 4) {
-				VALIDATE_ARG_NUM(3);
-				color.a = (float)*p_args[3] / 255.0f;
-			}
-
-			r_ret = color;
-
-		} break;
-		case COLORN: {
-			if (p_arg_count < 1) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-				r_error.argument = 1;
-				r_ret = Variant();
-				return;
-			}
-
-			if (p_arg_count > 2) {
-				r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
-				r_error.argument = 2;
-				r_ret = Variant();
-				return;
-			}
-
-			if (p_args[0]->get_type() != Variant::STRING) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_ret = Variant();
-			} else {
-				Color color = Color::named(*p_args[0]);
-				if (p_arg_count == 2) {
-					VALIDATE_ARG_NUM(1);
-					color.a = *p_args[1];
-				}
-				r_ret = color;
-			}
-
-		} break;
-
-		case PRINT_STACK: {
-			VALIDATE_ARG_COUNT(0);
-
-			ScriptLanguage *script = GDScriptLanguage::get_singleton();
-			for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
-				print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
-			};
-		} break;
-
-		case GET_STACK: {
-			VALIDATE_ARG_COUNT(0);
-
-			ScriptLanguage *script = GDScriptLanguage::get_singleton();
-			Array ret;
-			for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
-				Dictionary frame;
-				frame["source"] = script->debug_get_stack_level_source(i);
-				frame["function"] = script->debug_get_stack_level_function(i);
-				frame["line"] = script->debug_get_stack_level_line(i);
-				ret.push_back(frame);
-			};
-			r_ret = ret;
-		} break;
-
-		case INSTANCE_FROM_ID: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() != Variant::INT && p_args[0]->get_type() != Variant::FLOAT) {
-				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-				r_error.argument = 0;
-				r_error.expected = Variant::INT;
-				r_ret = Variant();
-				break;
-			}
-
-			ObjectID id = *p_args[0];
-			r_ret = ObjectDB::get_instance(id);
-
-		} break;
-		case LEN: {
-			VALIDATE_ARG_COUNT(1);
-			switch (p_args[0]->get_type()) {
-				case Variant::STRING: {
-					String d = *p_args[0];
-					r_ret = d.length();
-				} break;
-				case Variant::DICTIONARY: {
-					Dictionary d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::ARRAY: {
-					Array d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_BYTE_ARRAY: {
-					Vector<uint8_t> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_INT32_ARRAY: {
-					Vector<int32_t> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_INT64_ARRAY: {
-					Vector<int64_t> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_FLOAT32_ARRAY: {
-					Vector<float> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_FLOAT64_ARRAY: {
-					Vector<double> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_STRING_ARRAY: {
-					Vector<String> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_VECTOR2_ARRAY: {
-					Vector<Vector2> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_VECTOR3_ARRAY: {
-					Vector<Vector3> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				case Variant::PACKED_COLOR_ARRAY: {
-					Vector<Color> d = *p_args[0];
-					r_ret = d.size();
-				} break;
-				default: {
-					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-					r_error.argument = 0;
-					r_error.expected = Variant::OBJECT;
-					r_ret = Variant();
-					r_ret = RTR("Object can't provide a length.");
-				}
-			}
-
-		} break;
-		case IS_INSTANCE_VALID: {
-			VALIDATE_ARG_COUNT(1);
-			if (p_args[0]->get_type() != Variant::OBJECT) {
-				r_ret = false;
-			} else {
-				Object *obj = p_args[0]->get_validated_object();
-				r_ret = obj != nullptr;
-			}
-
-		} break;
-		case FUNC_MAX: {
-			ERR_FAIL();
-		} break;
-	}
-}
-
-bool GDScriptFunctions::is_deterministic(Function p_func) {
-	//man i couldn't have chosen a worse function name,
-	//way too controversial..
-
-	switch (p_func) {
-		case MATH_SIN:
-		case MATH_COS:
-		case MATH_TAN:
-		case MATH_SINH:
-		case MATH_COSH:
-		case MATH_TANH:
-		case MATH_ASIN:
-		case MATH_ACOS:
-		case MATH_ATAN:
-		case MATH_ATAN2:
-		case MATH_SQRT:
-		case MATH_FMOD:
-		case MATH_FPOSMOD:
-		case MATH_POSMOD:
-		case MATH_FLOOR:
-		case MATH_CEIL:
-		case MATH_ROUND:
-		case MATH_ABS:
-		case MATH_SIGN:
-		case MATH_POW:
-		case MATH_LOG:
-		case MATH_EXP:
-		case MATH_ISNAN:
-		case MATH_ISINF:
-		case MATH_EASE:
-		case MATH_STEP_DECIMALS:
-		case MATH_STEPIFY:
-		case MATH_LERP:
-		case MATH_INVERSE_LERP:
-		case MATH_RANGE_LERP:
-		case MATH_SMOOTHSTEP:
-		case MATH_MOVE_TOWARD:
-		case MATH_DECTIME:
-		case MATH_DEG2RAD:
-		case MATH_RAD2DEG:
-		case MATH_LINEAR2DB:
-		case MATH_DB2LINEAR:
-		case MATH_POLAR2CARTESIAN:
-		case MATH_CARTESIAN2POLAR:
-		case MATH_WRAP:
-		case MATH_WRAPF:
-		case LOGIC_MAX:
-		case LOGIC_MIN:
-		case LOGIC_CLAMP:
-		case LOGIC_NEAREST_PO2:
-		case TYPE_CONVERT:
-		case TYPE_OF:
-		case TYPE_EXISTS:
-		case TEXT_CHAR:
-		case TEXT_ORD:
-		case TEXT_STR:
-		case COLOR8:
-		case LEN:
-			// enable for debug only, otherwise not desirable - case GEN_RANGE:
-			return true;
-		default:
-			return false;
-	}
-
-	return false;
-}
-
-MethodInfo GDScriptFunctions::get_info(Function p_func) {
-#ifdef DEBUG_ENABLED
-	//using a switch, so the compiler generates a jumptable
-
-	switch (p_func) {
-		case MATH_SIN: {
-			MethodInfo mi("sin", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-
-		} break;
-		case MATH_COS: {
-			MethodInfo mi("cos", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_TAN: {
-			MethodInfo mi("tan", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_SINH: {
-			MethodInfo mi("sinh", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_COSH: {
-			MethodInfo mi("cosh", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_TANH: {
-			MethodInfo mi("tanh", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_ASIN: {
-			MethodInfo mi("asin", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_ACOS: {
-			MethodInfo mi("acos", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_ATAN: {
-			MethodInfo mi("atan", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_ATAN2: {
-			MethodInfo mi("atan2", PropertyInfo(Variant::FLOAT, "y"), PropertyInfo(Variant::FLOAT, "x"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_SQRT: {
-			MethodInfo mi("sqrt", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_FMOD: {
-			MethodInfo mi("fmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_FPOSMOD: {
-			MethodInfo mi("fposmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_POSMOD: {
-			MethodInfo mi("posmod", PropertyInfo(Variant::INT, "a"), PropertyInfo(Variant::INT, "b"));
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case MATH_FLOOR: {
-			MethodInfo mi("floor", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_CEIL: {
-			MethodInfo mi("ceil", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_ROUND: {
-			MethodInfo mi("round", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_ABS: {
-			MethodInfo mi("abs", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_SIGN: {
-			MethodInfo mi("sign", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_POW: {
-			MethodInfo mi("pow", PropertyInfo(Variant::FLOAT, "base"), PropertyInfo(Variant::FLOAT, "exp"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_LOG: {
-			MethodInfo mi("log", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_EXP: {
-			MethodInfo mi("exp", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_ISNAN: {
-			MethodInfo mi("is_nan", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::BOOL;
-			return mi;
-		} break;
-		case MATH_ISINF: {
-			MethodInfo mi("is_inf", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::BOOL;
-			return mi;
-		} break;
-		case MATH_ISEQUALAPPROX: {
-			MethodInfo mi("is_equal_approx", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
-			mi.return_val.type = Variant::BOOL;
-			return mi;
-		} break;
-		case MATH_ISZEROAPPROX: {
-			MethodInfo mi("is_zero_approx", PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::BOOL;
-			return mi;
-		} break;
-		case MATH_EASE: {
-			MethodInfo mi("ease", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "curve"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_STEP_DECIMALS: {
-			MethodInfo mi("step_decimals", PropertyInfo(Variant::FLOAT, "step"));
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case MATH_STEPIFY: {
-			MethodInfo mi("stepify", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "step"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_LERP: {
-			MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::FLOAT, "weight"));
-			mi.return_val.type = Variant::NIL;
-			mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
-			return mi;
-		} break;
-		case MATH_LERP_ANGLE: {
-			MethodInfo mi("lerp_angle", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_INVERSE_LERP: {
-			MethodInfo mi("inverse_lerp", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_RANGE_LERP: {
-			MethodInfo mi("range_lerp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "istart"), PropertyInfo(Variant::FLOAT, "istop"), PropertyInfo(Variant::FLOAT, "ostart"), PropertyInfo(Variant::FLOAT, "ostop"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_SMOOTHSTEP: {
-			MethodInfo mi("smoothstep", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "s"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_MOVE_TOWARD: {
-			MethodInfo mi("move_toward", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "delta"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_DECTIME: {
-			MethodInfo mi("dectime", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "amount"), PropertyInfo(Variant::FLOAT, "step"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_RANDOMIZE: {
-			MethodInfo mi("randomize");
-			mi.return_val.type = Variant::NIL;
-			return mi;
-		} break;
-		case MATH_RANDI: {
-			MethodInfo mi("randi");
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case MATH_RANDF: {
-			MethodInfo mi("randf");
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_RANDF_RANGE: {
-			MethodInfo mi("randf_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_RANDI_RANGE: {
-			MethodInfo mi("randi_range", PropertyInfo(Variant::INT, "from"), PropertyInfo(Variant::INT, "to"));
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case MATH_SEED: {
-			MethodInfo mi("seed", PropertyInfo(Variant::INT, "seed"));
-			mi.return_val.type = Variant::NIL;
-			return mi;
-		} break;
-		case MATH_RANDSEED: {
-			MethodInfo mi("rand_seed", PropertyInfo(Variant::INT, "seed"));
-			mi.return_val.type = Variant::ARRAY;
-			return mi;
-		} break;
-		case MATH_DEG2RAD: {
-			MethodInfo mi("deg2rad", PropertyInfo(Variant::FLOAT, "deg"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_RAD2DEG: {
-			MethodInfo mi("rad2deg", PropertyInfo(Variant::FLOAT, "rad"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_LINEAR2DB: {
-			MethodInfo mi("linear2db", PropertyInfo(Variant::FLOAT, "nrg"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_DB2LINEAR: {
-			MethodInfo mi("db2linear", PropertyInfo(Variant::FLOAT, "db"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case MATH_POLAR2CARTESIAN: {
-			MethodInfo mi("polar2cartesian", PropertyInfo(Variant::FLOAT, "r"), PropertyInfo(Variant::FLOAT, "th"));
-			mi.return_val.type = Variant::VECTOR2;
-			return mi;
-		} break;
-		case MATH_CARTESIAN2POLAR: {
-			MethodInfo mi("cartesian2polar", PropertyInfo(Variant::FLOAT, "x"), PropertyInfo(Variant::FLOAT, "y"));
-			mi.return_val.type = Variant::VECTOR2;
-			return mi;
-		} break;
-		case MATH_WRAP: {
-			MethodInfo mi("wrapi", PropertyInfo(Variant::INT, "value"), PropertyInfo(Variant::INT, "min"), PropertyInfo(Variant::INT, "max"));
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case MATH_WRAPF: {
-			MethodInfo mi("wrapf", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case LOGIC_MAX: {
-			MethodInfo mi("max", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-
-		} break;
-		case LOGIC_MIN: {
-			MethodInfo mi("min", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case LOGIC_CLAMP: {
-			MethodInfo mi("clamp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
-			mi.return_val.type = Variant::FLOAT;
-			return mi;
-		} break;
-		case LOGIC_NEAREST_PO2: {
-			MethodInfo mi("nearest_po2", PropertyInfo(Variant::INT, "value"));
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case OBJ_WEAKREF: {
-			MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
-			mi.return_val.type = Variant::OBJECT;
-			mi.return_val.class_name = "WeakRef";
-
-			return mi;
-
-		} break;
-		case TYPE_CONVERT: {
-			MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
-			mi.return_val.type = Variant::NIL;
-			mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
-			return mi;
-		} break;
-		case TYPE_OF: {
-			MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
-			mi.return_val.type = Variant::INT;
-			return mi;
-
-		} break;
-		case TYPE_EXISTS: {
-			MethodInfo mi("type_exists", PropertyInfo(Variant::STRING, "type"));
-			mi.return_val.type = Variant::BOOL;
-			return mi;
-
-		} break;
-		case TEXT_CHAR: {
-			MethodInfo mi("char", PropertyInfo(Variant::INT, "code"));
-			mi.return_val.type = Variant::STRING;
-			return mi;
-
-		} break;
-		case TEXT_ORD: {
-			MethodInfo mi("ord", PropertyInfo(Variant::STRING, "char"));
-			mi.return_val.type = Variant::INT;
-			return mi;
-
-		} break;
-		case TEXT_STR: {
-			MethodInfo mi("str");
-			mi.return_val.type = Variant::STRING;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-
-		} break;
-		case TEXT_PRINT: {
-			MethodInfo mi("print");
-			mi.return_val.type = Variant::NIL;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-
-		} break;
-		case TEXT_PRINT_TABBED: {
-			MethodInfo mi("printt");
-			mi.return_val.type = Variant::NIL;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-
-		} break;
-		case TEXT_PRINT_SPACED: {
-			MethodInfo mi("prints");
-			mi.return_val.type = Variant::NIL;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-
-		} break;
-		case TEXT_PRINTERR: {
-			MethodInfo mi("printerr");
-			mi.return_val.type = Variant::NIL;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-
-		} break;
-		case TEXT_PRINTRAW: {
-			MethodInfo mi("printraw");
-			mi.return_val.type = Variant::NIL;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-
-		} break;
-		case TEXT_PRINT_DEBUG: {
-			MethodInfo mi("print_debug");
-			mi.return_val.type = Variant::NIL;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-
-		} break;
-		case PUSH_ERROR: {
-			MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message"));
-			mi.return_val.type = Variant::NIL;
-			return mi;
-
-		} break;
-		case PUSH_WARNING: {
-			MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message"));
-			mi.return_val.type = Variant::NIL;
-			return mi;
-
-		} break;
-		case VAR_TO_STR: {
-			MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
-			mi.return_val.type = Variant::STRING;
-			return mi;
-		} break;
-		case STR_TO_VAR: {
-			MethodInfo mi(Variant::NIL, "str2var", PropertyInfo(Variant::STRING, "string"));
-			mi.return_val.type = Variant::NIL;
-			mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
-			return mi;
-		} break;
-		case VAR_TO_BYTES: {
-			MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects"));
-			mi.default_arguments.push_back(false);
-			mi.return_val.type = Variant::PACKED_BYTE_ARRAY;
-			return mi;
-		} break;
-		case BYTES_TO_VAR: {
-			MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects"));
-			mi.default_arguments.push_back(false);
-			mi.return_val.type = Variant::NIL;
-			mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
-			return mi;
-		} break;
-		case GEN_RANGE: {
-			MethodInfo mi("range");
-			mi.return_val.type = Variant::ARRAY;
-			mi.flags |= METHOD_FLAG_VARARG;
-			return mi;
-		} break;
-		case RESOURCE_LOAD: {
-			MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
-			mi.return_val.type = Variant::OBJECT;
-			mi.return_val.class_name = "Resource";
-			return mi;
-		} break;
-		case INST2DICT: {
-			MethodInfo mi("inst2dict", PropertyInfo(Variant::OBJECT, "inst"));
-			mi.return_val.type = Variant::DICTIONARY;
-			return mi;
-		} break;
-		case DICT2INST: {
-			MethodInfo mi("dict2inst", PropertyInfo(Variant::DICTIONARY, "dict"));
-			mi.return_val.type = Variant::OBJECT;
-			return mi;
-		} break;
-		case VALIDATE_JSON: {
-			MethodInfo mi("validate_json", PropertyInfo(Variant::STRING, "json"));
-			mi.return_val.type = Variant::STRING;
-			return mi;
-		} break;
-		case PARSE_JSON: {
-			MethodInfo mi(Variant::NIL, "parse_json", PropertyInfo(Variant::STRING, "json"));
-			mi.return_val.type = Variant::NIL;
-			mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
-			return mi;
-		} break;
-		case TO_JSON: {
-			MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
-			mi.return_val.type = Variant::STRING;
-			return mi;
-		} break;
-		case HASH: {
-			MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case COLOR8: {
-			MethodInfo mi("Color8", PropertyInfo(Variant::INT, "r8"), PropertyInfo(Variant::INT, "g8"), PropertyInfo(Variant::INT, "b8"), PropertyInfo(Variant::INT, "a8"));
-			mi.default_arguments.push_back(255);
-			mi.return_val.type = Variant::COLOR;
-			return mi;
-		} break;
-		case COLORN: {
-			MethodInfo mi("ColorN", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "alpha"));
-			mi.default_arguments.push_back(1.0f);
-			mi.return_val.type = Variant::COLOR;
-			return mi;
-		} break;
-
-		case PRINT_STACK: {
-			MethodInfo mi("print_stack");
-			mi.return_val.type = Variant::NIL;
-			return mi;
-		} break;
-		case GET_STACK: {
-			MethodInfo mi("get_stack");
-			mi.return_val.type = Variant::ARRAY;
-			return mi;
-		} break;
-
-		case INSTANCE_FROM_ID: {
-			MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id"));
-			mi.return_val.type = Variant::OBJECT;
-			return mi;
-		} break;
-		case LEN: {
-			MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
-			mi.return_val.type = Variant::INT;
-			return mi;
-		} break;
-		case IS_INSTANCE_VALID: {
-			MethodInfo mi("is_instance_valid", PropertyInfo(Variant::OBJECT, "instance"));
-			mi.return_val.type = Variant::BOOL;
-			return mi;
-		} break;
-		default: {
-			ERR_FAIL_V(MethodInfo());
-		} break;
-	}
-#endif
-	MethodInfo mi;
-	mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
-	return mi;
-}

+ 1 - 10
modules/gdscript/gdscript_parser.cpp

@@ -98,15 +98,6 @@ void GDScriptParser::cleanup() {
 	builtin_types.clear();
 }
 
-GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) {
-	for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
-		if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) {
-			return GDScriptFunctions::Function(i);
-		}
-	}
-	return GDScriptFunctions::FUNC_MAX;
-}
-
 void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
 	List<StringName> keys;
 	valid_annotations.get_key_list(&keys);
@@ -2553,7 +2544,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
 
 	// Arguments.
 	CompletionType ct = COMPLETION_CALL_ARGUMENTS;
-	if (get_builtin_function(call->function_name) == GDScriptFunctions::RESOURCE_LOAD) {
+	if (call->function_name == "load") {
 		ct = COMPLETION_RESOURCE_PATH;
 	}
 	push_completion_call(call);

+ 0 - 2
modules/gdscript/gdscript_parser.h

@@ -43,7 +43,6 @@
 #include "core/templates/vector.h"
 #include "core/variant/variant.h"
 #include "gdscript_cache.h"
-#include "gdscript_functions.h"
 #include "gdscript_tokenizer.h"
 
 #ifdef DEBUG_ENABLED
@@ -1314,7 +1313,6 @@ public:
 	ClassNode *get_tree() const { return head; }
 	bool is_tool() const { return _is_tool; }
 	static Variant::Type get_builtin_type(const StringName &p_type);
-	static GDScriptFunctions::Function get_builtin_function(const StringName &p_name);
 
 	CompletionContext get_completion_context() const { return completion_context; }
 	CompletionCall get_completion_call() const { return completion_call; }

+ 718 - 0
modules/gdscript/gdscript_utility_functions.cpp

@@ -0,0 +1,718 @@
+/*************************************************************************/
+/*  gdscript_utility_functions.cpp                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "gdscript_utility_functions.h"
+
+#include "core/io/resource_loader.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/object/object.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/vector.h"
+#include "gdscript.h"
+
+#ifdef DEBUG_ENABLED
+
+#define VALIDATE_ARG_COUNT(m_count)                                         \
+	if (p_arg_count < m_count) {                                            \
+		r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;  \
+		r_error.argument = m_count;                                         \
+		r_error.expected = m_count;                                         \
+		*r_ret = Variant();                                                 \
+		return;                                                             \
+	}                                                                       \
+	if (p_arg_count > m_count) {                                            \
+		r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
+		r_error.argument = m_count;                                         \
+		r_error.expected = m_count;                                         \
+		*r_ret = Variant();                                                 \
+		return;                                                             \
+	}
+
+#define VALIDATE_ARG_INT(m_arg)                                           \
+	if (p_args[m_arg]->get_type() != Variant::INT) {                      \
+		r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+		r_error.argument = m_arg;                                         \
+		r_error.expected = Variant::INT;                                  \
+		*r_ret = Variant();                                               \
+		return;                                                           \
+	}
+
+#define VALIDATE_ARG_NUM(m_arg)                                           \
+	if (!p_args[m_arg]->is_num()) {                                       \
+		r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+		r_error.argument = m_arg;                                         \
+		r_error.expected = Variant::FLOAT;                                \
+		*r_ret = Variant();                                               \
+		return;                                                           \
+	}
+
+#else
+
+#define VALIDATE_ARG_COUNT(m_count)
+#define VALIDATE_ARG_INT(m_arg)
+#define VALIDATE_ARG_NUM(m_arg)
+
+#endif
+
+struct GDScriptUtilityFunctionsDefinitions {
+	static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(2);
+		VALIDATE_ARG_INT(1);
+		int type = *p_args[1];
+		if (type < 0 || type >= Variant::VARIANT_MAX) {
+			*r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = 0;
+			r_error.expected = Variant::INT;
+			return;
+
+		} else {
+			Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
+		}
+	}
+
+	static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(1);
+		*r_ret = ClassDB::class_exists(*p_args[0]);
+	}
+
+	static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(1);
+		VALIDATE_ARG_INT(0);
+		char32_t result[2] = { *p_args[0], 0 };
+		*r_ret = String(result);
+	}
+
+	static inline void str(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		if (p_arg_count < 1) {
+			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+			r_error.argument = 1;
+			*r_ret = Variant();
+			return;
+		}
+
+		String str;
+		for (int i = 0; i < p_arg_count; i++) {
+			String os = p_args[i]->operator String();
+
+			if (i == 0) {
+				str = os;
+			} else {
+				str += os;
+			}
+		}
+		*r_ret = str;
+	}
+
+	static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		switch (p_arg_count) {
+			case 0: {
+				r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+				r_error.argument = 1;
+				r_error.expected = 1;
+				*r_ret = Variant();
+			} break;
+			case 1: {
+				VALIDATE_ARG_NUM(0);
+				int count = *p_args[0];
+				Array arr;
+				if (count <= 0) {
+					*r_ret = arr;
+					return;
+				}
+				Error err = arr.resize(count);
+				if (err != OK) {
+					r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+					*r_ret = Variant();
+					return;
+				}
+
+				for (int i = 0; i < count; i++) {
+					arr[i] = i;
+				}
+
+				*r_ret = arr;
+			} break;
+			case 2: {
+				VALIDATE_ARG_NUM(0);
+				VALIDATE_ARG_NUM(1);
+
+				int from = *p_args[0];
+				int to = *p_args[1];
+
+				Array arr;
+				if (from >= to) {
+					*r_ret = arr;
+					return;
+				}
+				Error err = arr.resize(to - from);
+				if (err != OK) {
+					r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+					*r_ret = Variant();
+					return;
+				}
+				for (int i = from; i < to; i++) {
+					arr[i - from] = i;
+				}
+				*r_ret = arr;
+			} break;
+			case 3: {
+				VALIDATE_ARG_NUM(0);
+				VALIDATE_ARG_NUM(1);
+				VALIDATE_ARG_NUM(2);
+
+				int from = *p_args[0];
+				int to = *p_args[1];
+				int incr = *p_args[2];
+				if (incr == 0) {
+					*r_ret = RTR("Step argument is zero!");
+					r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+					return;
+				}
+
+				Array arr;
+				if (from >= to && incr > 0) {
+					*r_ret = arr;
+					return;
+				}
+				if (from <= to && incr < 0) {
+					*r_ret = arr;
+					return;
+				}
+
+				// Calculate how many.
+				int count = 0;
+				if (incr > 0) {
+					count = ((to - from - 1) / incr) + 1;
+				} else {
+					count = ((from - to - 1) / -incr) + 1;
+				}
+
+				Error err = arr.resize(count);
+
+				if (err != OK) {
+					r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+					*r_ret = Variant();
+					return;
+				}
+
+				if (incr > 0) {
+					int idx = 0;
+					for (int i = from; i < to; i += incr) {
+						arr[idx++] = i;
+					}
+				} else {
+					int idx = 0;
+					for (int i = from; i > to; i += incr) {
+						arr[idx++] = i;
+					}
+				}
+
+				*r_ret = arr;
+			} break;
+			default: {
+				r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+				r_error.argument = 3;
+				r_error.expected = 3;
+				*r_ret = Variant();
+
+			} break;
+		}
+	}
+
+	static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(1);
+		if (p_args[0]->get_type() != Variant::STRING) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = 0;
+			r_error.expected = Variant::STRING;
+			*r_ret = Variant();
+		} else {
+			*r_ret = ResourceLoader::load(*p_args[0]);
+		}
+	}
+
+	static inline void inst2dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(1);
+
+		if (p_args[0]->get_type() == Variant::NIL) {
+			*r_ret = Variant();
+		} else if (p_args[0]->get_type() != Variant::OBJECT) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = 0;
+			*r_ret = Variant();
+		} else {
+			Object *obj = *p_args[0];
+			if (!obj) {
+				*r_ret = Variant();
+
+			} else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
+				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+				r_error.argument = 0;
+				r_error.expected = Variant::DICTIONARY;
+				*r_ret = RTR("Not a script with an instance");
+				return;
+			} else {
+				GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
+				Ref<GDScript> base = ins->get_script();
+				if (base.is_null()) {
+					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+					r_error.argument = 0;
+					r_error.expected = Variant::DICTIONARY;
+					*r_ret = RTR("Not based on a script");
+					return;
+				}
+
+				GDScript *p = base.ptr();
+				Vector<StringName> sname;
+
+				while (p->_owner) {
+					sname.push_back(p->name);
+					p = p->_owner;
+				}
+				sname.invert();
+
+				if (!p->path.is_resource_file()) {
+					r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+					r_error.argument = 0;
+					r_error.expected = Variant::DICTIONARY;
+					*r_ret = Variant();
+
+					*r_ret = RTR("Not based on a resource file");
+
+					return;
+				}
+
+				NodePath cp(sname, Vector<StringName>(), false);
+
+				Dictionary d;
+				d["@subpath"] = cp;
+				d["@path"] = p->get_path();
+
+				for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
+					if (!d.has(E->key())) {
+						d[E->key()] = ins->members[E->get().index];
+					}
+				}
+				*r_ret = d;
+			}
+		}
+	}
+
+	static inline void dict2inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(1);
+
+		if (p_args[0]->get_type() != Variant::DICTIONARY) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = 0;
+			r_error.expected = Variant::DICTIONARY;
+			*r_ret = Variant();
+
+			return;
+		}
+
+		Dictionary d = *p_args[0];
+
+		if (!d.has("@path")) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = 0;
+			r_error.expected = Variant::OBJECT;
+			*r_ret = RTR("Invalid instance dictionary format (missing @path)");
+
+			return;
+		}
+
+		Ref<Script> scr = ResourceLoader::load(d["@path"]);
+		if (!scr.is_valid()) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = 0;
+			r_error.expected = Variant::OBJECT;
+			*r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
+			return;
+		}
+
+		Ref<GDScript> gdscr = scr;
+
+		if (!gdscr.is_valid()) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = 0;
+			r_error.expected = Variant::OBJECT;
+			*r_ret = Variant();
+			*r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
+			return;
+		}
+
+		NodePath sub;
+		if (d.has("@subpath")) {
+			sub = d["@subpath"];
+		}
+
+		for (int i = 0; i < sub.get_name_count(); i++) {
+			gdscr = gdscr->subclasses[sub.get_name(i)];
+			if (!gdscr.is_valid()) {
+				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+				r_error.argument = 0;
+				r_error.expected = Variant::OBJECT;
+				*r_ret = Variant();
+				*r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
+				return;
+			}
+		}
+		*r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
+
+		if (r_error.error != Callable::CallError::CALL_OK) {
+			*r_ret = Variant();
+			return;
+		}
+
+		GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
+		Ref<GDScript> gd_ref = ins->get_script();
+
+		for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
+			if (d.has(E->key())) {
+				ins->members.write[E->get().index] = d[E->key()];
+			}
+		}
+	}
+
+	static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		if (p_arg_count < 3) {
+			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+			r_error.argument = 3;
+			*r_ret = Variant();
+			return;
+		}
+		if (p_arg_count > 4) {
+			r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+			r_error.argument = 4;
+			*r_ret = Variant();
+			return;
+		}
+
+		VALIDATE_ARG_INT(0);
+		VALIDATE_ARG_INT(1);
+		VALIDATE_ARG_INT(2);
+
+		Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f);
+
+		if (p_arg_count == 4) {
+			VALIDATE_ARG_INT(3);
+			color.a = (int64_t)*p_args[3] / 255.0f;
+		}
+
+		*r_ret = color;
+	}
+
+	static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		String str;
+		for (int i = 0; i < p_arg_count; i++) {
+			str += p_args[i]->operator String();
+		}
+
+		ScriptLanguage *script = GDScriptLanguage::get_singleton();
+		if (script->debug_get_stack_level_count() > 0) {
+			str += "\n   At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
+		}
+
+		print_line(str);
+		*r_ret = Variant();
+	}
+
+	static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(0);
+
+		ScriptLanguage *script = GDScriptLanguage::get_singleton();
+		for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+			print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
+		};
+	}
+
+	static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(0);
+
+		ScriptLanguage *script = GDScriptLanguage::get_singleton();
+		Array ret;
+		for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+			Dictionary frame;
+			frame["source"] = script->debug_get_stack_level_source(i);
+			frame["function"] = script->debug_get_stack_level_function(i);
+			frame["line"] = script->debug_get_stack_level_line(i);
+			ret.push_back(frame);
+		};
+		*r_ret = ret;
+	}
+
+	static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+		VALIDATE_ARG_COUNT(1);
+		switch (p_args[0]->get_type()) {
+			case Variant::STRING: {
+				String d = *p_args[0];
+				*r_ret = d.length();
+			} break;
+			case Variant::DICTIONARY: {
+				Dictionary d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::ARRAY: {
+				Array d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_BYTE_ARRAY: {
+				Vector<uint8_t> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_INT32_ARRAY: {
+				Vector<int32_t> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_INT64_ARRAY: {
+				Vector<int64_t> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_FLOAT32_ARRAY: {
+				Vector<float> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_FLOAT64_ARRAY: {
+				Vector<double> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_STRING_ARRAY: {
+				Vector<String> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_VECTOR2_ARRAY: {
+				Vector<Vector2> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_VECTOR3_ARRAY: {
+				Vector<Vector3> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			case Variant::PACKED_COLOR_ARRAY: {
+				Vector<Color> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
+			default: {
+				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+				r_error.argument = 0;
+				r_error.expected = Variant::NIL;
+				*r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
+			}
+		}
+	}
+};
+
+struct GDScriptUtilityFunctionInfo {
+	GDScriptUtilityFunctions::FunctionPtr function;
+	MethodInfo info;
+	bool is_constant = false;
+};
+
+static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table;
+static List<StringName> utility_function_name_table;
+
+static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
+	StringName sname(p_name);
+
+	ERR_FAIL_COND(utility_function_table.has(sname));
+
+	GDScriptUtilityFunctionInfo function;
+	function.function = p_function;
+	function.info = p_method_info;
+	function.is_constant = p_is_const;
+
+	utility_function_table.insert(sname, function);
+	utility_function_name_table.push_back(sname);
+}
+
+#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...)                                    \
+	{                                                                                            \
+		String name(#m_func);                                                                    \
+		if (name.begins_with("_")) {                                                             \
+			name = name.substr(1, name.length() - 1);                                            \
+		}                                                                                        \
+		MethodInfo info = MethodInfo(name, __VA_ARGS__);                                         \
+		info.return_val.type = m_return_type;                                                    \
+		_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+	}
+
+#define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type)                                 \
+	{                                                                                            \
+		String name(#m_func);                                                                    \
+		if (name.begins_with("_")) {                                                             \
+			name = name.substr(1, name.length() - 1);                                            \
+		}                                                                                        \
+		MethodInfo info = MethodInfo(name);                                                      \
+		info.return_val.type = m_return_type;                                                    \
+		_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+	}
+
+#define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type)                                  \
+	{                                                                                            \
+		String name(#m_func);                                                                    \
+		if (name.begins_with("_")) {                                                             \
+			name = name.substr(1, name.length() - 1);                                            \
+		}                                                                                        \
+		MethodInfo info = MethodInfo(name);                                                      \
+		info.return_val.type = m_return_type;                                                    \
+		info.flags |= METHOD_FLAG_VARARG;                                                        \
+		_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+	}
+
+#define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...)                                           \
+	{                                                                                            \
+		String name(#m_func);                                                                    \
+		if (name.begins_with("_")) {                                                             \
+			name = name.substr(1, name.length() - 1);                                            \
+		}                                                                                        \
+		MethodInfo info = MethodInfo(name, __VA_ARGS__);                                         \
+		info.return_val.type = Variant::NIL;                                                     \
+		info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;                                  \
+		_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+	}
+
+#define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...)                              \
+	{                                                                                            \
+		String name(#m_func);                                                                    \
+		if (name.begins_with("_")) {                                                             \
+			name = name.substr(1, name.length() - 1);                                            \
+		}                                                                                        \
+		MethodInfo info = MethodInfo(name, __VA_ARGS__);                                         \
+		info.return_val.type = Variant::OBJECT;                                                  \
+		info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE;                                      \
+		info.return_val.class_name = m_return_type;                                              \
+		_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+	}
+
+#define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...)                     \
+	{                                                                                            \
+		String name(#m_func);                                                                    \
+		if (name.begins_with("_")) {                                                             \
+			name = name.substr(1, name.length() - 1);                                            \
+		}                                                                                        \
+		MethodInfo info = MethodInfo(name, __VA_ARGS__);                                         \
+		info.return_val.type = m_return_type;                                                    \
+		info.default_arguments.push_back(m_default);                                             \
+		_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+	}
+
+#define ARG(m_name, m_type) \
+	PropertyInfo(m_type, m_name)
+
+#define VARARG(m_name) \
+	PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)
+
+void GDScriptUtilityFunctions::register_functions() {
+	REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT));
+	REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME));
+	REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT));
+	REGISTER_VARARG_FUNC(str, true, Variant::STRING);
+	REGISTER_VARARG_FUNC(range, false, Variant::ARRAY);
+	REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
+	REGISTER_FUNC(inst2dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
+	REGISTER_FUNC(dict2inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
+	REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT));
+	REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL);
+	REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL);
+	REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY);
+	REGISTER_FUNC(len, true, Variant::INT, VARARG("var"));
+}
+
+void GDScriptUtilityFunctions::unregister_functions() {
+	utility_function_name_table.clear();
+	utility_function_table.clear();
+}
+
+GDScriptUtilityFunctions::FunctionPtr GDScriptUtilityFunctions::get_function(const StringName &p_function) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, nullptr);
+	return info->function;
+}
+
+bool GDScriptUtilityFunctions::has_function_return_value(const StringName &p_function) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, false);
+	return info->info.return_val.type != Variant::NIL || bool(info->info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_return_type(const StringName &p_function) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, Variant::NIL);
+	return info->info.return_val.type;
+}
+
+StringName GDScriptUtilityFunctions::get_function_return_class(const StringName &p_function) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, StringName());
+	return info->info.return_val.class_name;
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringName &p_function, int p_arg) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, Variant::NIL);
+	ERR_FAIL_COND_V(p_arg >= info->info.arguments.size(), Variant::NIL);
+	return info->info.arguments[p_arg].type;
+}
+
+int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, 0);
+	return info->info.arguments.size();
+}
+
+bool GDScriptUtilityFunctions::is_function_vararg(const StringName &p_function) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, false);
+	return (bool)(info->info.flags & METHOD_FLAG_VARARG);
+}
+
+bool GDScriptUtilityFunctions::is_function_constant(const StringName &p_function) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, false);
+	return info->is_constant;
+}
+
+bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) {
+	return utility_function_table.has(p_function);
+}
+
+void GDScriptUtilityFunctions::get_function_list(List<StringName> *r_functions) {
+	for (const List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) {
+		r_functions->push_back(E->get());
+	}
+}
+
+MethodInfo GDScriptUtilityFunctions::get_function_info(const StringName &p_function) {
+	GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+	ERR_FAIL_COND_V(!info, MethodInfo());
+	return info->info;
+}

+ 22 - 101
modules/gdscript/gdscript_functions.h → modules/gdscript/gdscript_utility_functions.h

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  gdscript_functions.h                                                 */
+/*  gdscript_utility_functions.h                                         */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
@@ -28,110 +28,31 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifndef GDSCRIPT_FUNCTIONS_H
-#define GDSCRIPT_FUNCTIONS_H
+#ifndef GDSCRIPT_UTILITY_FUNCTIONS_H
+#define GDSCRIPT_UTILITY_FUNCTIONS_H
 
+#include "core/string/string_name.h"
 #include "core/variant/variant.h"
 
-class GDScriptFunctions {
+class GDScriptUtilityFunctions {
 public:
-	enum Function {
-		MATH_SIN,
-		MATH_COS,
-		MATH_TAN,
-		MATH_SINH,
-		MATH_COSH,
-		MATH_TANH,
-		MATH_ASIN,
-		MATH_ACOS,
-		MATH_ATAN,
-		MATH_ATAN2,
-		MATH_SQRT,
-		MATH_FMOD,
-		MATH_FPOSMOD,
-		MATH_POSMOD,
-		MATH_FLOOR,
-		MATH_CEIL,
-		MATH_ROUND,
-		MATH_ABS,
-		MATH_SIGN,
-		MATH_POW,
-		MATH_LOG,
-		MATH_EXP,
-		MATH_ISNAN,
-		MATH_ISINF,
-		MATH_ISEQUALAPPROX,
-		MATH_ISZEROAPPROX,
-		MATH_EASE,
-		MATH_STEP_DECIMALS,
-		MATH_STEPIFY,
-		MATH_LERP,
-		MATH_LERP_ANGLE,
-		MATH_INVERSE_LERP,
-		MATH_RANGE_LERP,
-		MATH_SMOOTHSTEP,
-		MATH_MOVE_TOWARD,
-		MATH_DECTIME,
-		MATH_RANDOMIZE,
-		MATH_RANDI,
-		MATH_RANDF,
-		MATH_RANDF_RANGE,
-		MATH_RANDI_RANGE,
-		MATH_SEED,
-		MATH_RANDSEED,
-		MATH_DEG2RAD,
-		MATH_RAD2DEG,
-		MATH_LINEAR2DB,
-		MATH_DB2LINEAR,
-		MATH_POLAR2CARTESIAN,
-		MATH_CARTESIAN2POLAR,
-		MATH_WRAP,
-		MATH_WRAPF,
-		LOGIC_MAX,
-		LOGIC_MIN,
-		LOGIC_CLAMP,
-		LOGIC_NEAREST_PO2,
-		OBJ_WEAKREF,
-		TYPE_CONVERT,
-		TYPE_OF,
-		TYPE_EXISTS,
-		TEXT_CHAR,
-		TEXT_ORD,
-		TEXT_STR,
-		TEXT_PRINT,
-		TEXT_PRINT_TABBED,
-		TEXT_PRINT_SPACED,
-		TEXT_PRINTERR,
-		TEXT_PRINTRAW,
-		TEXT_PRINT_DEBUG,
-		PUSH_ERROR,
-		PUSH_WARNING,
-		VAR_TO_STR,
-		STR_TO_VAR,
-		VAR_TO_BYTES,
-		BYTES_TO_VAR,
-		GEN_RANGE,
-		RESOURCE_LOAD,
-		INST2DICT,
-		DICT2INST,
-		VALIDATE_JSON,
-		PARSE_JSON,
-		TO_JSON,
-		HASH,
-		COLOR8,
-		COLORN,
-		PRINT_STACK,
-		GET_STACK,
-		INSTANCE_FROM_ID,
-		LEN,
-		IS_INSTANCE_VALID,
-		FUNC_MAX
-	};
+	typedef void (*FunctionPtr)(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
 
-	static const char *get_func_name(Function p_func);
-	static void call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error);
-	static bool is_deterministic(Function p_func);
-	static MethodInfo get_info(Function p_func);
+	static FunctionPtr get_function(const StringName &p_function);
+	static bool has_function_return_value(const StringName &p_function);
+	static Variant::Type get_function_return_type(const StringName &p_function);
+	static StringName get_function_return_class(const StringName &p_function);
+	static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg);
+	static int get_function_argument_count(const StringName &p_function, int p_arg);
+	static bool is_function_vararg(const StringName &p_function);
+	static bool is_function_constant(const StringName &p_function);
+
+	static bool function_exists(const StringName &p_function);
+	static void get_function_list(List<StringName> *r_functions);
+	static MethodInfo get_function_info(const StringName &p_function);
+
+	static void register_functions();
+	static void unregister_functions();
 };
 
-#endif // GDSCRIPT_FUNCTIONS_H
+#endif // GDSCRIPT_UTILITY_FUNCTIONS_H

+ 68 - 9
modules/gdscript/gdscript_vm.cpp

@@ -33,7 +33,6 @@
 #include "core/core_string_names.h"
 #include "core/os/os.h"
 #include "gdscript.h"
-#include "gdscript_functions.h"
 
 Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
 	int address = p_address & ADDR_MASK;
@@ -220,7 +219,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 		&&OPCODE_CALL,                               \
 		&&OPCODE_CALL_RETURN,                        \
 		&&OPCODE_CALL_ASYNC,                         \
-		&&OPCODE_CALL_BUILT_IN,                      \
+		&&OPCODE_CALL_UTILITY,                       \
+		&&OPCODE_CALL_UTILITY_VALIDATED,             \
+		&&OPCODE_CALL_GDSCRIPT_UTILITY,              \
 		&&OPCODE_CALL_BUILTIN_TYPE_VALIDATED,        \
 		&&OPCODE_CALL_SELF_BASE,                     \
 		&&OPCODE_CALL_METHOD_BIND,                   \
@@ -1749,7 +1750,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			DISPATCH_OPCODE;
 
-			OPCODE(OPCODE_CALL_BUILT_IN) {
+			OPCODE(OPCODE_CALL_UTILITY) {
 				CHECK_SPACE(3 + instr_arg_count);
 
 				ip += instr_arg_count;
@@ -1757,22 +1758,80 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				int argc = _code_ptr[ip + 1];
 				GD_ERR_BREAK(argc < 0);
 
-				GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 2]);
+				GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _global_names_count);
+				StringName function = _global_names_ptr[_code_ptr[ip + 2]];
+
+				Variant **argptrs = instruction_args;
+
+				GET_INSTRUCTION_ARG(dst, argc);
+
+				Callable::CallError err;
+				Variant::call_utility_function(function, dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+				if (err.error != Callable::CallError::CALL_OK) {
+					String methodstr = function;
+					if (dst->get_type() == Variant::STRING) {
+						// Call provided error string.
+						err_text = "Error calling utility function '" + methodstr + "': " + String(*dst);
+					} else {
+						err_text = _get_call_error(err, "utility function '" + methodstr + "'", (const Variant **)argptrs);
+					}
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CALL_UTILITY_VALIDATED) {
+				CHECK_SPACE(3 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+
+				GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _utilities_count);
+				Variant::ValidatedUtilityFunction function = _utilities_ptr[_code_ptr[ip + 2]];
+
+				Variant **argptrs = instruction_args;
+
+				GET_INSTRUCTION_ARG(dst, argc);
+
+				function(dst, (const Variant **)argptrs, argc);
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CALL_GDSCRIPT_UTILITY) {
+				CHECK_SPACE(3 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+
+				GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _gds_utilities_count);
+				GDScriptUtilityFunctions::FunctionPtr function = _gds_utilities_ptr[_code_ptr[ip + 2]];
+
 				Variant **argptrs = instruction_args;
 
 				GET_INSTRUCTION_ARG(dst, argc);
 
 				Callable::CallError err;
-				GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
+				function(dst, (const Variant **)argptrs, argc, err);
 
 #ifdef DEBUG_ENABLED
 				if (err.error != Callable::CallError::CALL_OK) {
-					String methodstr = GDScriptFunctions::get_func_name(func);
+					// TODO: Add this information in debug.
+					String methodstr = "<unkown function>";
 					if (dst->get_type() == Variant::STRING) {
-						//call provided error string
-						err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
+						// Call provided error string.
+						err_text = "Error calling GDScript utility function '" + methodstr + "': " + String(*dst);
 					} else {
-						err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
+						err_text = _get_call_error(err, "GDScript utility function '" + methodstr + "'", (const Variant **)argptrs);
 					}
 					OPCODE_BREAK;
 				}

+ 4 - 0
modules/gdscript/register_types.cpp

@@ -38,6 +38,7 @@
 #include "gdscript_analyzer.h"
 #include "gdscript_cache.h"
 #include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
 
 #ifdef TESTS_ENABLED
 #include "tests/test_gdscript.h"
@@ -130,6 +131,8 @@ void register_gdscript_types() {
 	gdscript_translation_parser_plugin.instance();
 	EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
 #endif // TOOLS_ENABLED
+
+	GDScriptUtilityFunctions::register_functions();
 }
 
 void unregister_gdscript_types() {
@@ -156,6 +159,7 @@ void unregister_gdscript_types() {
 
 	GDScriptParser::cleanup();
 	GDScriptAnalyzer::cleanup();
+	GDScriptUtilityFunctions::unregister_functions();
 }
 
 #ifdef TESTS_ENABLED