/**************************************************************************/ /* bindings_generator.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* 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. */ /**************************************************************************/ #ifndef BINDINGS_GENERATOR_H #define BINDINGS_GENERATOR_H #include "core/typedefs.h" // DEBUG_METHODS_ENABLED #if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED) #include "core/doc_data.h" #include "core/object/class_db.h" #include "core/string/string_builder.h" #include "core/string/ustring.h" #include "editor/doc_tools.h" #include "editor/editor_help.h" class BindingsGenerator { struct ConstantInterface { String name; String proxy_name; int64_t value = 0; const DocData::ConstantDoc *const_doc; ConstantInterface() {} ConstantInterface(const String &p_name, const String &p_proxy_name, int64_t p_value) { name = p_name; proxy_name = p_proxy_name; value = p_value; } }; struct EnumInterface { StringName cname; List constants; bool is_flags = false; _FORCE_INLINE_ bool operator==(const EnumInterface &p_ienum) const { return p_ienum.cname == cname; } EnumInterface() {} EnumInterface(const StringName &p_cname) { cname = p_cname; } }; struct PropertyInterface { StringName cname; String proxy_name; int index = 0; StringName setter; StringName getter; const DocData::PropertyDoc *prop_doc; }; struct TypeReference { StringName cname; bool is_enum = false; List generic_type_parameters; TypeReference() {} TypeReference(const StringName &p_cname) : cname(p_cname) {} }; struct ArgumentInterface { enum DefaultParamMode { CONSTANT, NULLABLE_VAL, NULLABLE_REF }; TypeReference type; String name; Variant def_param_value; DefaultParamMode def_param_mode = CONSTANT; /** * Determines the expression for the parameter default value. * Formatting elements: * %0 or %s: [cs_type] of the argument type */ String default_argument; ArgumentInterface() {} }; struct MethodInterface { String name; StringName cname; /** * Name of the C# method */ String proxy_name; /** * [TypeInterface::name] of the return type */ TypeReference return_type; /** * Determines if the method has a variable number of arguments (VarArg) */ bool is_vararg = false; /** * Determines if the method is static. */ bool is_static = false; /** * Virtual methods ("virtual" as defined by the Godot API) are methods that by default do nothing, * but can be overridden by the user to add custom functionality. * e.g.: _ready, _process, etc. */ bool is_virtual = false; /** * Determines if the call should fallback to Godot's object.Call(string, params) in C#. */ bool requires_object_call = false; /** * Determines if the method visibility is 'internal' (visible only to files in the same assembly). * Currently, we only use this for methods that are not meant to be exposed, * but are required by properties as getters or setters. * Methods that are not meant to be exposed are those that begin with underscore and are not virtual. */ bool is_internal = false; List arguments; const DocData::MethodDoc *method_doc = nullptr; bool is_deprecated = false; String deprecation_message; void add_argument(const ArgumentInterface &argument) { arguments.push_back(argument); } MethodInterface() {} }; struct SignalInterface { String name; StringName cname; /** * Name of the C# method */ String proxy_name; List arguments; const DocData::MethodDoc *method_doc = nullptr; bool is_deprecated = false; String deprecation_message; void add_argument(const ArgumentInterface &argument) { arguments.push_back(argument); } SignalInterface() {} }; struct TypeInterface { /** * Identifier name for this type. * Also used to format [c_out]. */ String name; StringName cname; int type_parameter_count = 0; /** * Identifier name of the base class. */ StringName base_name; /** * Name of the C# class */ String proxy_name; ClassDB::APIType api_type = ClassDB::API_NONE; bool is_enum = false; bool is_object_type = false; bool is_singleton = false; bool is_ref_counted = false; /** * Determines whether the native return value of this type must be disposed * by the generated internal call (think of `godot_string`, whose destructor * must be called). Some structs that are disposable may still disable this * flag if the ownership is transferred. */ bool c_type_is_disposable_struct = false; /** * Determines whether the native return value of this type must be zero initialized * before its address is passed to ptrcall. This is required for types whose destructor * is called before being assigned the return value by `PtrToArg::encode`, e.g.: * Array, Dictionary, String, StringName, Variant. * It's not necessary to set this to `true` if [c_type_is_disposable_struct] is already `true`. */ bool c_ret_needs_default_initialization = false; /** * Used only by Object-derived types. * Determines if this type is not abstract (incomplete). * e.g.: CanvasItem cannot be instantiated. */ bool is_instantiable = false; /** * Used only by Object-derived types. * Determines if the C# class owns the native handle and must free it somehow when disposed. * e.g.: RefCounted types must notify when the C# instance is disposed, for proper refcounting. */ bool memory_own = false; // !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name] // !! When renaming those fields, make sure to rename their references in the comments // --- C INTERFACE --- /** * One or more statements that transform the parameter before being passed as argument of a ptrcall. * If the statement adds a local that must be passed as the argument instead of the parameter, * the expression with the name of that local must be specified with [c_arg_in]. * Formatting elements: * %0: [c_type] of the parameter * %1: name of the parameter * %2-4: reserved * %5: indentation text */ String c_in; /** * One or more statements that transform the parameter before being passed as argument of a vararg call. * If the statement adds a local that must be passed as the argument instead of the parameter, * the name of that local must be specified with [c_arg_in]. * Formatting elements: * %0: [c_type] of the parameter * %1: name of the parameter * %2-4: reserved * %5: indentation text */ String c_in_vararg; /** * Determines the expression that will be passed as argument to ptrcall. * By default the value equals the name of the parameter, * this varies for types that require special manipulation via [c_in]. * Formatting elements: * %0 or %s: name of the parameter */ String c_arg_in = "%s"; /** * One or more statements that determine how a variable of this type is returned from a function. * It must contain the return statement(s). * Formatting elements: * %0: [c_type_out] of the return type * %1: name of the variable to be returned * %2: [name] of the return type * --------------------------------------- * If [ret_as_byref_arg] is true, the format is different. Instead of using a return statement, * the value must be assigned to a parameter. This type of this parameter is a pointer to [c_type_out]. * Formatting elements: * %0: [c_type_out] of the return type * %1: name of the variable to be returned * %2: [name] of the return type * %3-4: reserved * %5: indentation text */ String c_out; /** * The actual expected type, as seen (in most cases) in Variant copy constructors * Used for the type of the return variable and to format [c_in]. * The value must be the following depending of the type: * Object-derived types: Object* * Other types: [name] * -- Exceptions -- * VarArg (fictitious type to represent variable arguments): Array * float: double (because ptrcall only supports double) * int: int64_t (because ptrcall only supports int64_t and uint64_t) * RefCounted types override this for the type of the return variable: Ref */ String c_type; /** * Determines the type used for parameters in function signatures. */ String c_type_in; /** * Determines the return type used for function signatures. * Also used to construct a default value to return in case of errors, * and to format [c_out]. */ String c_type_out; // --- C# INTERFACE --- /** * An expression that overrides the way the parameter is passed to the internal call. * If empty, the parameter is passed as is. * Formatting elements: * %0: name of the parameter * %1: [c_type] of the parameter */ String cs_in_expr; bool cs_in_expr_is_unsafe = false; /** * One or more statements that transform the parameter before being passed to the internal call. * If the statement adds a local that must be passed as the argument instead of the parameter, * the expression with the name of that local must be specified with [cs_in_expr]. * Formatting elements: * %0: [c_type] of the parameter * %1: name of the parameter * %2-4: reserved * %5: indentation text */ String cs_in; /** * One or more statements that determine how a variable of this type is returned from a method. * It must contain the return statement(s). * Formatting elements: * %0: internal method name * %1: internal method call arguments without surrounding parenthesis * %2: [cs_type] of the return type * %3: [c_type_out] of the return type * %4: reserved * %5: indentation text */ String cs_out; /** * Type used for method signatures, both for parameters and the return type. * Same as [proxy_name] except for variable arguments (VarArg) and collections (which include the namespace). */ String cs_type; /** * Formatting elements: * %0: input expression of type `in godot_variant` * %1: [cs_type] of this type * %2: [name] of this type */ String cs_variant_to_managed; /** * Formatting elements: * %0: input expression * %1: [cs_type] of this type * %2: [name] of this type */ String cs_managed_to_variant; const DocData::ClassDoc *class_doc = nullptr; List constants; List enums; List properties; List methods; List signals_; HashSet ignored_members; bool has_virtual_methods = false; const MethodInterface *find_method_by_name(const StringName &p_cname) const { for (const MethodInterface &E : methods) { if (E.cname == p_cname) { return &E; } } return nullptr; } const MethodInterface *find_method_by_proxy_name(const String &p_proxy_name) const { for (const MethodInterface &E : methods) { if (E.proxy_name == p_proxy_name) { return &E; } } return nullptr; } const PropertyInterface *find_property_by_name(const StringName &p_cname) const { for (const PropertyInterface &E : properties) { if (E.cname == p_cname) { return &E; } } return nullptr; } const PropertyInterface *find_property_by_proxy_name(const String &p_proxy_name) const { for (const PropertyInterface &E : properties) { if (E.proxy_name == p_proxy_name) { return &E; } } return nullptr; } const SignalInterface *find_signal_by_name(const StringName &p_cname) const { for (const SignalInterface &E : signals_) { if (E.cname == p_cname) { return &E; } } return nullptr; } const SignalInterface *find_signal_by_proxy_name(const String &p_proxy_name) const { for (const SignalInterface &E : signals_) { if (E.proxy_name == p_proxy_name) { return &E; } } return nullptr; } bool is_intentionally_ignored(const String &p_name) const { return ignored_members.has(p_name); } private: static DocData::ClassDoc *_get_type_doc(TypeInterface &itype) { String doc_name = itype.name.begins_with("_") ? itype.name.substr(1) : itype.name; return &EditorHelp::get_doc_data()->class_list[doc_name]; } static void _init_value_type(TypeInterface &itype) { if (itype.proxy_name.is_empty()) { itype.proxy_name = itype.name; } itype.cs_type = itype.proxy_name; itype.c_type = itype.cs_type; itype.c_type_in = itype.cs_type + "*"; itype.c_type_out = itype.cs_type; itype.class_doc = _get_type_doc(itype); } static void _init_object_type(TypeInterface &itype, ClassDB::APIType p_api_type) { if (itype.proxy_name.is_empty()) { itype.proxy_name = itype.name; } if (itype.proxy_name.begins_with("_")) { itype.proxy_name = itype.proxy_name.substr(1); } itype.api_type = p_api_type; itype.is_object_type = true; itype.class_doc = _get_type_doc(itype); } public: static TypeInterface create_value_type(const String &p_name, const String &p_proxy_name) { TypeInterface itype; itype.name = p_name; itype.cname = p_name; itype.proxy_name = p_proxy_name; _init_value_type(itype); return itype; } static TypeInterface create_value_type(const StringName &p_cname, const String &p_proxy_name) { TypeInterface itype; itype.name = p_cname; itype.cname = p_cname; itype.proxy_name = p_proxy_name; _init_value_type(itype); return itype; } static TypeInterface create_value_type(const String &p_name) { TypeInterface itype; itype.name = p_name; itype.cname = p_name; _init_value_type(itype); return itype; } static TypeInterface create_value_type(const StringName &p_cname) { TypeInterface itype; itype.name = p_cname; itype.cname = p_cname; _init_value_type(itype); return itype; } static TypeInterface create_object_type(const StringName &p_cname, const String &p_proxy_name, ClassDB::APIType p_api_type) { TypeInterface itype; itype.name = p_cname; itype.cname = p_cname; itype.proxy_name = p_proxy_name; _init_object_type(itype, p_api_type); return itype; } static TypeInterface create_object_type(const StringName &p_cname, ClassDB::APIType p_api_type) { TypeInterface itype; itype.name = p_cname; itype.cname = p_cname; _init_object_type(itype, p_api_type); return itype; } static void postsetup_enum_type(TypeInterface &r_enum_itype); TypeInterface() { static String default_cs_variant_to_managed = "VariantUtils.ConvertTo<%1>(%0)"; static String default_cs_managed_to_variant = "VariantUtils.CreateFrom<%1>(%0)"; cs_variant_to_managed = default_cs_variant_to_managed; cs_managed_to_variant = default_cs_managed_to_variant; } }; struct InternalCall { String name; String unique_sig; // Unique signature to avoid duplicates in containers bool editor_only = false; bool is_vararg = false; bool is_static = false; TypeReference return_type; List argument_types; _FORCE_INLINE_ int get_arguments_count() const { return argument_types.size(); } InternalCall() {} InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_unique_sig = String()) { name = p_name; unique_sig = p_unique_sig; editor_only = api_type == ClassDB::API_EDITOR; } inline bool operator==(const InternalCall &p_a) const { return p_a.unique_sig == unique_sig; } }; bool log_print_enabled = true; bool initialized = false; HashMap obj_types; HashMap builtin_types; HashMap enum_types; List global_enums; List global_constants; List method_icalls; /// Stores the unique internal calls from [method_icalls] that are assigned to each method. HashMap method_icalls_map; HashMap> blacklisted_methods; void _initialize_blacklisted_methods(); struct NameCache { StringName type_void = StaticCString::create("void"); StringName type_Variant = StaticCString::create("Variant"); StringName type_VarArg = StaticCString::create("VarArg"); StringName type_Object = StaticCString::create("Object"); StringName type_RefCounted = StaticCString::create("RefCounted"); StringName type_RID = StaticCString::create("RID"); StringName type_Callable = StaticCString::create("Callable"); StringName type_Signal = StaticCString::create("Signal"); StringName type_String = StaticCString::create("String"); StringName type_StringName = StaticCString::create("StringName"); StringName type_NodePath = StaticCString::create("NodePath"); StringName type_Array_generic = StaticCString::create("Array_@generic"); StringName type_Dictionary_generic = StaticCString::create("Dictionary_@generic"); StringName type_at_GlobalScope = StaticCString::create("@GlobalScope"); StringName enum_Error = StaticCString::create("Error"); StringName type_sbyte = StaticCString::create("sbyte"); StringName type_short = StaticCString::create("short"); StringName type_int = StaticCString::create("int"); StringName type_byte = StaticCString::create("byte"); StringName type_ushort = StaticCString::create("ushort"); StringName type_uint = StaticCString::create("uint"); StringName type_long = StaticCString::create("long"); StringName type_ulong = StaticCString::create("ulong"); StringName type_bool = StaticCString::create("bool"); StringName type_float = StaticCString::create("float"); StringName type_double = StaticCString::create("double"); StringName type_Vector2 = StaticCString::create("Vector2"); StringName type_Rect2 = StaticCString::create("Rect2"); StringName type_Vector3 = StaticCString::create("Vector3"); StringName type_Vector3i = StaticCString::create("Vector3i"); StringName type_Vector4 = StaticCString::create("Vector4"); StringName type_Vector4i = StaticCString::create("Vector4i"); // Object not included as it must be checked for all derived classes static constexpr int nullable_types_count = 18; StringName nullable_types[nullable_types_count] = { type_String, type_StringName, type_NodePath, type_Array_generic, type_Dictionary_generic, StaticCString::create(_STR(Array)), StaticCString::create(_STR(Dictionary)), StaticCString::create(_STR(Callable)), StaticCString::create(_STR(Signal)), StaticCString::create(_STR(PackedByteArray)), StaticCString::create(_STR(PackedInt32Array)), StaticCString::create(_STR(PackedInt64Array)), StaticCString::create(_STR(PackedFloat32Array)), StaticCString::create(_STR(PackedFloat64Array)), StaticCString::create(_STR(PackedStringArray)), StaticCString::create(_STR(PackedVector2Array)), StaticCString::create(_STR(PackedVector3Array)), StaticCString::create(_STR(PackedColorArray)), }; bool is_nullable_type(const StringName &p_type) const { for (int i = 0; i < nullable_types_count; i++) { if (p_type == nullable_types[i]) { return true; } } return false; } NameCache() {} private: NameCache(const NameCache &); void operator=(const NameCache &); }; NameCache name_cache; const ConstantInterface *find_constant_by_name(const String &p_name, const List &p_constants) const { for (const ConstantInterface &E : p_constants) { if (E.name == p_name) { return &E; } } return nullptr; } inline String get_arg_unique_sig(const TypeInterface &p_type) { // For parameters, we treat reference and non-reference derived types the same. if (p_type.is_object_type) { return "Obj"; } else if (p_type.is_enum) { return "int"; } else if (p_type.cname == name_cache.type_Array_generic) { return "Array"; } else if (p_type.cname == name_cache.type_Dictionary_generic) { return "Dictionary"; } return p_type.name; } inline String get_ret_unique_sig(const TypeInterface *p_type) { // Reference derived return types are treated differently. if (p_type->is_ref_counted) { return "Ref"; } else if (p_type->is_object_type) { return "Obj"; } else if (p_type->is_enum) { return "int"; } else if (p_type->cname == name_cache.type_Array_generic) { return "Array"; } else if (p_type->cname == name_cache.type_Dictionary_generic) { return "Dictionary"; } return p_type->name; } String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); void _append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); void _append_xml_signal(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); void _append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); void _append_xml_constant(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); void _append_xml_constant_in_global_scope(StringBuilder &p_xml_output, const String &p_target_cname, const String &p_link_target); void _append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target); int _determine_enum_prefix(const EnumInterface &p_ienum); void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length); Error _populate_method_icalls_table(const TypeInterface &p_itype); const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); const String _get_generic_type_parameters(const TypeInterface &p_itype, const List &p_generic_type_parameters); StringName _get_type_name_from_meta(Variant::Type p_type, GodotTypeInfo::Metadata p_meta); StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta); StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta); bool _arg_default_value_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); bool _arg_default_value_is_assignable_to_type(const Variant &p_val, const TypeInterface &p_arg_type); bool _populate_object_type_interfaces(); void _populate_builtin_type_interfaces(); void _populate_global_constants(); Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file); Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output); Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output); Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output); Error _generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output); void _generate_array_extensions(StringBuilder &p_output); void _generate_global_constants(StringBuilder &p_output); Error _save_file(const String &p_path, const StringBuilder &p_content); void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void _initialize(); public: Error generate_cs_core_project(const String &p_proj_dir); Error generate_cs_editor_project(const String &p_proj_dir); Error generate_cs_api(const String &p_output_dir); _FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; } _FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; } _FORCE_INLINE_ bool is_initialized() { return initialized; } static void handle_cmdline_args(const List &p_cmdline_args); BindingsGenerator() { _initialize(); } }; #endif #endif // BINDINGS_GENERATOR_H