diff options
Diffstat (limited to 'modules/gdscript')
28 files changed, 1180 insertions, 2214 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index eeb66ebfc0..4ed129b3ff 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -763,7 +763,7 @@  			<argument index="1" name="exp" type="float">  			</argument>  			<description> -				Returns the result of [code]x[/code] raised to the power of [code]y[/code]. +				Returns the result of [code]base[/code] raised to the power of [code]exp[/code].  				[codeblock]  				pow(2, 5) # Returns 32.0  				[/codeblock] diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index 49357f3d2e..e38647eaab 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -42,7 +42,7 @@ private:  		Color color;  		String start_key;  		String end_key; -		bool line_only; +		bool line_only = false;  	};  	Vector<ColorRegion> color_regions;  	Map<int, int> color_region_cache; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 4425b59d62..2220341b84 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -425,7 +425,7 @@ void GDScript::_update_doc() {  	_clear_doc();  	doc.script_path = "\"" + get_path().get_slice("://", 1) + "\""; -	if (!name.empty()) { +	if (!name.is_empty()) {  		doc.name = name;  	} else {  		doc.name = doc.script_path; @@ -793,10 +793,10 @@ Error GDScript::reload(bool p_keep_state) {  	{  		String source_path = path; -		if (source_path.empty()) { +		if (source_path.is_empty()) {  			source_path = get_path();  		} -		if (!source_path.empty()) { +		if (!source_path.is_empty()) {  			MutexLock lock(GDScriptCache::singleton->lock);  			if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) {  				GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this; @@ -812,7 +812,7 @@ Error GDScript::reload(bool p_keep_state) {  			GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);  		}  		// TODO: Show all error messages. -		_err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); +		_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);  		ERR_FAIL_V(ERR_PARSE_ERROR);  	} @@ -826,7 +826,7 @@ Error GDScript::reload(bool p_keep_state) {  		const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front();  		while (e != nullptr) { -			_err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); +			_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);  			e = e->next();  		}  		ERR_FAIL_V(ERR_PARSE_ERROR); @@ -846,7 +846,7 @@ Error GDScript::reload(bool p_keep_state) {  			if (EngineDebugger::is_active()) {  				GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());  			} -			_err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); +			_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);  			ERR_FAIL_V(ERR_COMPILATION_FAILED);  		} else {  			return err; @@ -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()));  	}  } @@ -2148,7 +2151,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b  	if (err == OK) {  		const GDScriptParser::ClassNode *c = parser.get_tree();  		if (r_icon_path) { -			if (c->icon_path.empty() || c->icon_path.is_abs_path()) { +			if (c->icon_path.is_empty() || c->icon_path.is_abs_path()) {  				*r_icon_path = c->icon_path;  			} else if (c->icon_path.is_rel_path()) {  				*r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); @@ -2160,7 +2163,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b  			GDScriptParser subparser;  			while (subclass) {  				if (subclass->extends_used) { -					if (!subclass->extends_path.empty()) { +					if (!subclass->extends_path.is_empty()) {  						if (subclass->extends.size() == 0) {  							get_global_class_name(subclass->extends_path, r_base_type);  							subclass = nullptr; @@ -2174,7 +2177,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b  							}  							String subsource = subfile->get_as_utf8_string(); -							if (subsource.empty()) { +							if (subsource.is_empty()) {  								break;  							}  							String subpath = subclass->extends_path; @@ -2371,7 +2374,7 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S  	ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'.");  	String source = file->get_as_utf8_string(); -	if (source.empty()) { +	if (source.is_empty()) {  		return;  	} diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 3eb260f95f..11c449c5f2 100644 --- a/modules/gdscript/gdscript.h +++ b/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; @@ -471,7 +471,7 @@ public:  	virtual bool has_named_classes() const;  	virtual bool supports_builtin_mode() const;  	virtual bool supports_documentation() const; -	virtual bool can_inherit_from_file() { return true; } +	virtual bool can_inherit_from_file() const { return true; }  	virtual int find_function(const String &p_function, const String &p_code) const;  	virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;  	virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1b76c7f967..a828cad1cf 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<StringName, StringName> underscore_map; @@ -59,7 +60,7 @@ static const char *underscore_classes[] = {  	nullptr,  };  static StringName get_real_class_name(const StringName &p_source) { -	if (underscore_map.empty()) { +	if (underscore_map.is_empty()) {  		const char **class_name = underscore_classes;  		while (*class_name != nullptr) {  			underscore_map[*class_name] = String("_") + *class_name; @@ -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();  } @@ -175,7 +209,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,  		int extends_index = 0; -		if (!p_class->extends_path.empty()) { +		if (!p_class->extends_path.is_empty()) {  			Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path);  			if (parser.is_null()) {  				push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class); @@ -190,7 +224,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,  			base = parser->get_parser()->head->get_datatype();  		} else { -			if (p_class->extends.empty()) { +			if (p_class->extends.is_empty()) {  				return ERR_PARSE_ERROR;  			}  			const StringName &name = p_class->extends[extends_index++]; @@ -342,7 +376,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type  	result.type_source = result.ANNOTATED_EXPLICIT;  	result.builtin_type = Variant::OBJECT; -	if (p_type->type_chain.empty()) { +	if (p_type->type_chain.is_empty()) {  		// void.  		result.kind = GDScriptParser::DataType::BUILTIN;  		result.builtin_type = Variant::NIL; @@ -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); @@ -3327,12 +3402,12 @@ Error GDScriptAnalyzer::resolve_inheritance() {  Error GDScriptAnalyzer::resolve_interface() {  	resolve_class_interface(parser->head); -	return parser->errors.empty() ? OK : ERR_PARSE_ERROR; +	return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;  }  Error GDScriptAnalyzer::resolve_body() {  	resolve_class_body(parser->head); -	return parser->errors.empty() ? OK : ERR_PARSE_ERROR; +	return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;  }  Error GDScriptAnalyzer::resolve_program() { @@ -3347,7 +3422,7 @@ Error GDScriptAnalyzer::resolve_program() {  		}  		depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED);  	} -	return parser->errors.empty() ? OK : ERR_PARSE_ERROR; +	return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;  }  Error GDScriptAnalyzer::analyze() { diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index d89b89c8b9..a5d96077d9 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/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(); @@ -404,7 +428,7 @@ void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {  	patch_jump(logic_op_jump_pos2.back()->get());  	logic_op_jump_pos1.pop_back();  	logic_op_jump_pos2.pop_back(); -	append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 0); +	append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);  	append(p_target);  } @@ -429,7 +453,7 @@ void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {  	// Jump away from the success condition.  	append(GDScriptFunction::OPCODE_JUMP, 0);  	append(opcodes.size() + 3); -	// Here it means one of operands is false. +	// Here it means one of operands is true.  	patch_jump(logic_op_jump_pos1.back()->get());  	patch_jump(logic_op_jump_pos2.back()->get());  	logic_op_jump_pos1.pop_back(); @@ -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; diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 5cbd12a0ba..21576b08a4 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/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; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 559f9b8406..e776007bd7 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/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; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index e3f058886f..24b24ad534 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/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) { @@ -106,7 +107,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D  			// Locate class by constructing the path to it and following that path  			GDScriptParser::ClassNode *class_type = p_datatype.class_type;  			if (class_type) { -				if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.empty() && class_type->fqcn.begins_with(main_script->name))) { +				if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) {  					// Local class.  					List<StringName> names;  					while (class_type->outer) { @@ -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. @@ -2228,7 +2231,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar  				}  				p_script->_signals[name] = parameters_names;  #ifdef TOOLS_ENABLED -				if (!signal->doc_description.empty()) { +				if (!signal->doc_description.is_empty()) {  					p_script->doc_signals[name] = signal->doc_description;  				}  #endif diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 92a44c57f8..5938cfd7b2 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/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; @@ -324,11 +323,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {  				incr += 4;  			} break;  			case OPCODE_ASSIGN_TYPED_NATIVE: { -				Variant class_name = _constants_ptr[_code_ptr[ip + 3]]; -				GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *()); -  				text += "assign typed native ("; -				text += nc->get_name().operator String(); +				text += DADDR(3);  				text += ") ";  				text += DADDR(1);  				text += " = "; @@ -607,13 +603,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 += _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 += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args])); +				text += "<unknown function>";  				text += "(";  				for (int i = 0; i < argc; i++) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index af9673a9b8..c4d5982622 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/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" @@ -122,10 +123,10 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl  	for (int i = 0; i < p_class->members.size(); i++) {  		if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) {  			const GDScriptParser::FunctionNode *function = p_class->members[i].function; -			r_funcs[function->start_line] = p_prefix.empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name); +			r_funcs[function->start_line] = p_prefix.is_empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name);  		} else if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {  			String new_prefix = p_class->members[i].m_class->identifier->name; -			get_function_names_recursively(p_class->members[i].m_class, p_prefix.empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs); +			get_function_names_recursively(p_class->members[i].m_class, p_prefix.is_empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs);  		}  	}  } @@ -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"; @@ -472,7 +476,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na  			s += p_args[i].get_slice(":", 0);  			if (th) {  				String type = p_args[i].get_slice(":", 1); -				if (!type.empty() && type != "var") { +				if (!type.is_empty() && type != "var") {  					s += ": " + type;  				}  			} @@ -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) { @@ -2504,7 +2511,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path  				break;  			} -			if (!type.enumeration.empty()) { +			if (!type.enumeration.is_empty()) {  				_find_enumeration_candidates(completion_context, type.enumeration, options);  				r_forced = options.size() > 0;  			} else { @@ -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) { diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 32372439c5..6d49022d3c 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -114,7 +114,7 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String  			ERR_CONTINUE(!sdmap.has(sd.identifier));  			sdmap[sd.identifier].pos.pop_back(); -			if (sdmap[sd.identifier].pos.empty()) { +			if (sdmap[sd.identifier].pos.is_empty()) {  				sdmap.erase(sd.identifier);  			}  		} diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 7bc20672d5..b669870b51 100644 --- a/modules/gdscript/gdscript_function.h +++ b/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; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp deleted file mode 100644 index 3a7c1a8676..0000000000 --- a/modules/gdscript/gdscript_functions.cpp +++ /dev/null @@ -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; -} diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 48fca16ab1..0c93702bfb 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -47,7 +47,7 @@  static HashMap<StringName, Variant::Type> builtin_types;  Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { -	if (builtin_types.empty()) { +	if (builtin_types.is_empty()) {  		builtin_types["bool"] = Variant::BOOL;  		builtin_types["int"] = Variant::INT;  		builtin_types["float"] = Variant::FLOAT; @@ -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); @@ -186,16 +177,16 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {  void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {  	ERR_FAIL_COND(p_source == nullptr);  	Vector<String> symbols; -	if (!p_symbol1.empty()) { +	if (!p_symbol1.is_empty()) {  		symbols.push_back(p_symbol1);  	} -	if (!p_symbol2.empty()) { +	if (!p_symbol2.is_empty()) {  		symbols.push_back(p_symbol2);  	} -	if (!p_symbol3.empty()) { +	if (!p_symbol3.is_empty()) {  		symbols.push_back(p_symbol3);  	} -	if (!p_symbol4.empty()) { +	if (!p_symbol4.is_empty()) {  		symbols.push_back(p_symbol4);  	}  	push_warning(p_source, p_code, symbols); @@ -293,7 +284,7 @@ void GDScriptParser::pop_completion_call() {  	if (!for_completion) {  		return;  	} -	ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to pop empty completion call stack"); +	ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to pop empty completion call stack");  	completion_call_stack.pop_back();  } @@ -301,7 +292,7 @@ void GDScriptParser::set_last_completion_call_arg(int p_argument) {  	if (!for_completion || passed_cursor) {  		return;  	} -	ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to set argument on empty completion call stack"); +	ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to set argument on empty completion call stack");  	completion_call_stack.back()->get().argument = p_argument;  } @@ -367,7 +358,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_  	}  #endif -	if (errors.empty()) { +	if (errors.is_empty()) {  		return OK;  	} else {  		return ERR_PARSE_ERROR; @@ -378,7 +369,7 @@ GDScriptTokenizer::Token GDScriptParser::advance() {  	if (current.type == GDScriptTokenizer::Token::TK_EOF) {  		ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream.");  	} -	if (for_completion && !completion_call_stack.empty()) { +	if (for_completion && !completion_call_stack.is_empty()) {  		if (completion_call.call == nullptr && tokenizer.is_past_cursor()) {  			completion_call = completion_call_stack.back()->get();  			passed_cursor = true; @@ -509,7 +500,7 @@ void GDScriptParser::parse_program() {  		// Order here doesn't matter, but there should be only one of each at most.  		switch (current.type) {  			case GDScriptTokenizer::Token::CLASS_NAME: -				if (!annotation_stack.empty()) { +				if (!annotation_stack.is_empty()) {  					push_error(R"("class_name" should be used before annotations.)");  				}  				advance(); @@ -520,7 +511,7 @@ void GDScriptParser::parse_program() {  				}  				break;  			case GDScriptTokenizer::Token::EXTENDS: -				if (!annotation_stack.empty()) { +				if (!annotation_stack.is_empty()) {  					push_error(R"("extends" should be used before annotations.)");  				}  				advance(); @@ -684,7 +675,7 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()  #endif // TOOLS_ENABLED  	// Consume annotations. -	while (!annotation_stack.empty()) { +	while (!annotation_stack.is_empty()) {  		AnnotationNode *last_annotation = annotation_stack.back()->get();  		if (last_annotation->applies_to(p_target)) {  			last_annotation->apply(this, member); @@ -1707,7 +1698,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {  		branch->patterns.push_back(pattern);  	} while (match(GDScriptTokenizer::Token::COMMA)); -	if (branch->patterns.empty()) { +	if (branch->patterns.is_empty()) {  		push_error(R"(No pattern found for "match" branch.)");  	} @@ -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); @@ -2760,7 +2751,7 @@ String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {  		}  		String line_join = (in_codeblock) ? "\n" : " "; -		doc = (doc.empty()) ? doc_line : doc + line_join + doc_line; +		doc = (doc.is_empty()) ? doc_line : doc + line_join + doc_line;  		line++;  	} @@ -2854,7 +2845,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &  			mode = TUTORIALS;  			in_codeblock = false; -		} else if (striped_line.empty()) { +		} else if (striped_line.is_empty()) {  			continue;  		} else {  			// Tutorial docs are single line, we need a @tag after it. @@ -3289,11 +3280,11 @@ String GDScriptParser::DataType::to_string() const {  				return script_type->get_class_name().operator String();  			}  			String name = script_type->get_name(); -			if (!name.empty()) { +			if (!name.is_empty()) {  				return name;  			}  			name = script_path; -			if (!name.empty()) { +			if (!name.is_empty()) {  				return name;  			}  			return native_type.operator String(); @@ -3338,7 +3329,7 @@ void GDScriptParser::TreePrinter::decrease_indent() {  }  void GDScriptParser::TreePrinter::push_line(const String &p_line) { -	if (!p_line.empty()) { +	if (!p_line.is_empty()) {  		push_text(p_line);  	}  	printed += "\n"; @@ -3547,7 +3538,7 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {  	if (p_class->extends_used) {  		bool first = true;  		push_text(" Extends "); -		if (!p_class->extends_path.empty()) { +		if (!p_class->extends_path.is_empty()) {  			push_text(vformat(R"("%s")", p_class->extends_path));  			first = false;  		} @@ -4009,7 +4000,7 @@ void GDScriptParser::TreePrinter::print_ternary_op(TernaryOpNode *p_ternary_op)  }  void GDScriptParser::TreePrinter::print_type(TypeNode *p_type) { -	if (p_type->type_chain.empty()) { +	if (p_type->type_chain.is_empty()) {  		push_text("Void");  	} else {  		for (int i = 0; i < p_type->type_chain.size(); i++) { @@ -4129,7 +4120,7 @@ void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) {  	if (p_parser.is_tool()) {  		push_line("@tool");  	} -	if (!p_parser.get_tree()->icon_path.empty()) { +	if (!p_parser.get_tree()->icon_path.is_empty()) {  		push_text(R"(@icon (")");  		push_text(p_parser.get_tree()->icon_path);  		push_line("\")"); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 44605bc20f..4cecdc6970 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/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; } diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index ac43105254..febb066223 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -221,7 +221,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) {  void GDScriptTokenizer::set_source_code(const String &p_source_code) {  	source = p_source_code; -	if (source.empty()) { +	if (source.is_empty()) {  		_source = U"";  	} else {  		_source = source.ptr(); @@ -287,7 +287,7 @@ void GDScriptTokenizer::push_paren(char32_t p_char) {  }  bool GDScriptTokenizer::pop_paren(char32_t p_expected) { -	if (paren_stack.empty()) { +	if (paren_stack.is_empty()) {  		return false;  	}  	char32_t actual = paren_stack.back()->get(); @@ -405,7 +405,7 @@ void GDScriptTokenizer::push_error(const Token &p_error) {  }  GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) { -	if (paren_stack.empty()) { +	if (paren_stack.is_empty()) {  		return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren));  	}  	Token error = make_error(vformat("Closing \"%c\" doesn't match the opening \"%c\".", p_paren, paren_stack.back()->get())); diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index f236c86f9f..70556d7166 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -230,7 +230,7 @@ private:  	_FORCE_INLINE_ bool _is_at_end() { return position >= length; }  	_FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; }  	int indent_level() const { return indent_stack.size(); } -	bool has_error() const { return !error_stack.empty(); } +	bool has_error() const { return !error_stack.is_empty(); }  	Token pop_error();  	char32_t _advance();  	void _skip_whitespace(); diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp new file mode 100644 index 0000000000..b1780446d0 --- /dev/null +++ b/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; +} diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_utility_functions.h index 005b49c5da..50867438d9 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/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 diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 7942ee8d97..dacd7df498 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/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,                   \ @@ -738,7 +739,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter];  				bool valid; -				setter(dst, index, value, valid); +				setter(dst, index, value, &valid);  #ifdef DEBUG_ENABLED  				if (!valid) { @@ -770,7 +771,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				int64_t int_index = *VariantInternal::get_int(index);  				bool oob; -				setter(dst, int_index, value, oob); +				setter(dst, int_index, value, &oob);  #ifdef DEBUG_ENABLED  				if (oob) { @@ -835,9 +836,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  #ifdef DEBUG_ENABLED  				// Allow better error message in cases where src and dst are the same stack position.  				Variant ret; -				getter(src, key, &ret, valid); +				getter(src, key, &ret, &valid);  #else -				getter(src, key, dst, valid); +				getter(src, key, dst, &valid);  #endif  #ifdef DEBUG_ENABLED  				if (!valid) { @@ -870,7 +871,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				int64_t int_index = *VariantInternal::get_int(index);  				bool oob; -				getter(src, int_index, dst, oob); +				getter(src, int_index, dst, &oob);  #ifdef DEBUG_ENABLED  				if (oob) { @@ -1292,7 +1293,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				GET_INSTRUCTION_ARG(dst, argc); -				constructor(*dst, (const Variant **)argptrs); +				constructor(dst, (const Variant **)argptrs);  				ip += 3;  			} @@ -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;  				} @@ -2223,7 +2282,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				VariantInternal::initialize(counter, Variant::INT);  				*VariantInternal::get_int(counter) = 0; -				if (!str->empty()) { +				if (!str->is_empty()) {  					GET_INSTRUCTION_ARG(iterator, 2);  					VariantInternal::initialize(iterator, Variant::STRING);  					*VariantInternal::get_string(iterator) = str->substr(0, 1); @@ -2249,7 +2308,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				const Variant *next = dict->next(nullptr);  				*counter = *next; -				if (!dict->empty()) { +				if (!dict->is_empty()) {  					GET_INSTRUCTION_ARG(iterator, 2);  					*iterator = *next; @@ -2275,7 +2334,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				VariantInternal::initialize(counter, Variant::INT);  				*VariantInternal::get_int(counter) = 0; -				if (!array->empty()) { +				if (!array->is_empty()) {  					GET_INSTRUCTION_ARG(iterator, 2);  					*iterator = array->get(0); @@ -2298,7 +2357,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  		Vector<m_elem_type> *array = VariantInternal::m_get_func(container);                                               \  		VariantInternal::initialize(counter, Variant::INT);                                                                \  		*VariantInternal::get_int(counter) = 0;                                                                            \ -		if (!array->empty()) {                                                                                             \ +		if (!array->is_empty()) {                                                                                          \  			GET_INSTRUCTION_ARG(iterator, 2);                                                                              \  			VariantInternal::initialize(iterator, Variant::m_var_ret_type);                                                \  			m_ret_type *it = VariantInternal::m_ret_get_func(iterator);                                                    \ @@ -2722,7 +2781,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  						GET_INSTRUCTION_ARG(message, 1);  						message_str = *message;  					} -					if (message_str.empty()) { +					if (message_str.is_empty()) {  						err_text = "Assertion failed.";  					} else {  						err_text = "Assertion failed: " + message_str; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 668dfd4835..d2d2c23249 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -147,7 +147,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p  	r_symbol.script_path = path;  	r_symbol.children.clear();  	r_symbol.name = p_class->identifier != nullptr ? String(p_class->identifier->name) : String(); -	if (r_symbol.name.empty()) { +	if (r_symbol.name.is_empty()) {  		r_symbol.name = path.get_file();  	}  	r_symbol.kind = lsp::SymbolKind::Class; @@ -215,9 +215,9 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p  				String value_text;  				if (default_value.get_type() == Variant::OBJECT) {  					RES res = default_value; -					if (res.is_valid() && !res->get_path().empty()) { +					if (res.is_valid() && !res->get_path().is_empty()) {  						value_text = "preload(\"" + res->get_path() + "\")"; -						if (symbol.documentation.empty()) { +						if (symbol.documentation.is_empty()) {  							if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {  								symbol.documentation = S->get()->class_symbol.documentation;  							} @@ -228,7 +228,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p  				} else {  					value_text = JSON::print(default_value);  				} -				if (!value_text.empty()) { +				if (!value_text.is_empty()) {  					symbol.detail += " = " + value_text;  				} @@ -453,7 +453,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c  			String line = lines[i];  			String first_part = line.substr(0, p_cursor.character);  			String last_part = line.substr(p_cursor.character + 1, lines[i].length()); -			if (!p_symbol.empty()) { +			if (!p_symbol.is_empty()) {  				String left_cursor_text;  				for (int c = p_cursor.character - 1; c >= 0; c--) {  					left_cursor_text = line.substr(c, p_cursor.character - c); @@ -589,7 +589,7 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int  }  const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name, const String &p_subclass) const { -	if (p_subclass.empty()) { +	if (p_subclass.is_empty()) {  		const lsp::DocumentSymbol *const *ptr = members.getptr(p_name);  		if (ptr) {  			return *ptr; @@ -611,7 +611,7 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const  }  const Array &ExtendGDScriptParser::get_member_completions() { -	if (member_completions.empty()) { +	if (member_completions.is_empty()) {  		const String *name = members.next(nullptr);  		while (name) {  			const lsp::DocumentSymbol *symbol = members.get(*name); @@ -723,8 +723,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode  			} break;  			case ClassNode::Member::ENUM: {  				Dictionary enum_dict; -				for (int j = 0; j < m.m_enum->values.size(); i++) { -					enum_dict[m.m_enum->values[i].identifier->name] = m.m_enum->values[i].value; +				for (int j = 0; j < m.m_enum->values.size(); j++) { +					enum_dict[m.m_enum->values[j].identifier->name] = m.m_enum->values[j].value;  				}  				Dictionary api; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 729be237ec..cf96722c82 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -96,7 +96,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() {  		// Response  		String output = GDScriptLanguageProtocol::get_singleton()->process_message(msg); -		if (!output.empty()) { +		if (!output.is_empty()) {  			res_queue.push_back(output.utf8());  		}  	} @@ -105,7 +105,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() {  Error GDScriptLanguageProtocol::LSPeer::send_data() {  	int sent = 0; -	if (!res_queue.empty()) { +	if (!res_queue.is_empty()) {  		CharString c_res = res_queue[0];  		if (res_sent < c_res.size()) {  			Error err = connection->put_partial_data((const uint8_t *)c_res.get_data() + res_sent, c_res.size() - res_sent - 1, sent); @@ -141,7 +141,7 @@ void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) {  String GDScriptLanguageProtocol::process_message(const String &p_text) {  	String ret = process_string(p_text); -	if (ret.empty()) { +	if (ret.is_empty()) {  		return ret;  	} else {  		return format_output(ret); diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index c6fe3169dc..abe849059f 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -147,7 +147,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {  	List<ScriptCodeCompletionOption> options;  	GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options); -	if (!options.empty()) { +	if (!options.is_empty()) {  		int i = 0;  		arr.resize(options.size()); @@ -257,7 +257,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {  	if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) {  		item.insertText = item.label + "("; -		if (symbol && symbol->children.empty()) { +		if (symbol && symbol->children.is_empty()) {  			item.insertText += ")";  		}  	} else if (item.kind == lsp::CompletionItemKind::Event) { @@ -341,7 +341,7 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {  	params.load(p_params);  	List<const lsp::DocumentSymbol *> symbols;  	Array arr = this->find_symbols(params, symbols); -	if (arr.empty() && !symbols.empty() && !symbols.front()->get()->native_class.empty()) { // Find a native symbol +	if (arr.is_empty() && !symbols.is_empty() && !symbols.front()->get()->native_class.is_empty()) { // Find a native symbol  		const lsp::DocumentSymbol *symbol = symbols.front()->get();  		if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {  			String id; @@ -425,7 +425,7 @@ Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams &  		GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(p_location, list);  		for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {  			if (const lsp::DocumentSymbol *s = E->get()) { -				if (!s->uri.empty()) { +				if (!s->uri.is_empty()) {  					lsp::Location location;  					location.uri = s->uri;  					location.range = s->range; diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 60668e7b31..bccb239f28 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -80,7 +80,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_  		if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) {  			const lsp::DocumentSymbol &class_symbol = E->value(); -			if (p_member.empty()) { +			if (p_member.is_empty()) {  				return &class_symbol;  			} else {  				for (int i = 0; i < class_symbol.children.size(); i++) { @@ -171,7 +171,7 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path)  Array GDScriptWorkspace::symbol(const Dictionary &p_params) {  	String query = p_params["query"];  	Array arr; -	if (!query.empty()) { +	if (!query.is_empty()) {  		for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {  			Vector<lsp::DocumentedSymbolInformation> script_symbols;  			E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols); @@ -199,7 +199,7 @@ Error GDScriptWorkspace::initialize() {  		class_symbol.native_class = class_name;  		class_symbol.kind = lsp::SymbolKind::Class;  		class_symbol.detail = String("<Native> class ") + class_name; -		if (!class_data.inherits.empty()) { +		if (!class_data.inherits.is_empty()) {  			class_symbol.detail += " extends " + class_data.inherits;  		}  		class_symbol.documentation = class_data.brief_description + "\n" + class_data.description; @@ -263,7 +263,7 @@ Error GDScriptWorkspace::initialize() {  				symbol_arg.kind = lsp::SymbolKind::Variable;  				symbol_arg.detail = arg.type; -				if (!arg_default_value_started && !arg.default_value.empty()) { +				if (!arg_default_value_started && !arg.default_value.is_empty()) {  					arg_default_value_started = true;  				}  				String arg_str = arg.name + ": " + arg.type; @@ -278,11 +278,11 @@ Error GDScriptWorkspace::initialize() {  				symbol.children.push_back(symbol_arg);  			}  			if (data.qualifiers.find("vararg") != -1) { -				params += params.empty() ? "..." : ", ..."; +				params += params.is_empty() ? "..." : ", ...";  			}  			String return_type = data.return_type; -			if (return_type.empty()) { +			if (return_type.is_empty()) {  				return_type = "void";  			}  			symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + return_type; @@ -448,13 +448,13 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu  		}  		lsp::Position pos = p_doc_pos.position; -		if (symbol_identifier.empty()) { +		if (symbol_identifier.is_empty()) {  			Vector2i offset;  			symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);  			pos.character += offset.y;  		} -		if (!symbol_identifier.empty()) { +		if (!symbol_identifier.is_empty()) {  			if (ScriptServer::is_global_class(symbol_identifier)) {  				String class_path = ScriptServer::get_global_class_path(symbol_identifier);  				symbol = get_script_symbol(class_path); @@ -474,7 +474,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu  					} else {  						String member = ret.class_member; -						if (member.empty() && symbol_identifier != ret.class_name) { +						if (member.is_empty() && symbol_identifier != ret.class_name) {  							member = symbol_identifier;  						}  						symbol = get_native_symbol(ret.class_name, member); @@ -529,7 +529,7 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP  const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) {  	if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) {  		const lsp::DocumentSymbol &symbol = E->get(); -		if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) { +		if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) {  			return &symbol;  		} diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 1029c53bbf..e844a402e2 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -687,7 +687,7 @@ struct Diagnostic {  		dict["severity"] = severity;  		dict["message"] = message;  		dict["source"] = source; -		if (!relatedInformation.empty()) { +		if (!relatedInformation.is_empty()) {  			Array arr;  			arr.resize(relatedInformation.size());  			for (int i = 0; i < relatedInformation.size(); i++) { @@ -1191,7 +1191,7 @@ struct DocumentSymbol {  	void symbol_tree_as_list(const String &p_uri, Vector<DocumentedSymbolInformation> &r_list, const String &p_container = "", bool p_join_name = false) const {  		DocumentedSymbolInformation si; -		if (p_join_name && !p_container.empty()) { +		if (p_join_name && !p_container.is_empty()) {  			si.name = p_container + ">" + name;  		} else {  			si.name = name; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 6c2af66c65..0c4996e9bb 100644 --- a/modules/gdscript/register_types.cpp +++ b/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 diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 643c2f10a2..29efa33e70 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -239,7 +239,7 @@ void init_autoloads() {  void test(TestType p_type) {  	List<String> cmdlargs = OS::get_singleton()->get_cmdline_args(); -	if (cmdlargs.empty()) { +	if (cmdlargs.is_empty()) {  		return;  	}  |