From c7b6a7adcc3552fd6fd2e9061ab790798dd9830a Mon Sep 17 00:00:00 2001 From: George Marques Date: Thu, 26 Nov 2020 11:56:32 -0300 Subject: GDScript: Refactor builtin functions They are now called "utility functions" to avoid confusion with methods of builtin types, and be consistent with the naming in Variant. Core utility functions are now available in GDScript. The ones missing in core are added specifically to GDScript as helpers for convenience. Some functions were remove when there are better ways to do, reducing redundancy and cleaning up the global scope. --- modules/gdscript/gdscript_analyzer.cpp | 95 ++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 10 deletions(-) (limited to 'modules/gdscript/gdscript_analyzer.cpp') diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 851994eff3..a3492cb6cc 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/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 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(); } @@ -1694,7 +1728,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. @@ -1816,10 +1849,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 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 args; for (int i = 0; i < p_call->arguments.size(); i++) { @@ -1828,23 +1903,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. @@ -2350,7 +2425,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); -- cgit v1.2.3