|
|
@@ -1,900 +1,1760 @@
|
|
|
#!/usr/bin/env python
|
|
|
-from __future__ import print_function
|
|
|
+
|
|
|
import json
|
|
|
-import os
|
|
|
-import errno
|
|
|
+import re
|
|
|
+import shutil
|
|
|
from pathlib import Path
|
|
|
|
|
|
-# Convenience function for using template get_node
|
|
|
-def correct_method_name(method_list):
|
|
|
- for method in method_list:
|
|
|
- if method["name"] == "get_node":
|
|
|
- method["name"] = "get_node_internal"
|
|
|
|
|
|
+def print_file_list(api_filepath, output_dir, headers=False, sources=False):
|
|
|
+ api = {}
|
|
|
+ end = ";"
|
|
|
+ with open(api_filepath) as api_file:
|
|
|
+ api = json.load(api_file)
|
|
|
|
|
|
-classes = []
|
|
|
+ include_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp"
|
|
|
+ source_gen_folder = Path(output_dir) / "gen" / "src"
|
|
|
|
|
|
+ for builtin_class in api["builtin_classes"]:
|
|
|
+ if is_pod_type(builtin_class["name"]):
|
|
|
+ continue
|
|
|
|
|
|
-def print_file_list(api_filepath, output_dir, headers=False, sources=False):
|
|
|
- global classes
|
|
|
- end = ';'
|
|
|
- with open(api_filepath) as api_file:
|
|
|
- classes = json.load(api_file)
|
|
|
- include_gen_folder = Path(output_dir) / 'include' / 'gen'
|
|
|
- source_gen_folder = Path(output_dir) / 'src' / 'gen'
|
|
|
- for _class in classes:
|
|
|
- header_filename = include_gen_folder / (strip_name(_class["name"]) + ".hpp")
|
|
|
- source_filename = source_gen_folder / (strip_name(_class["name"]) + ".cpp")
|
|
|
+ if is_included_type(builtin_class["name"]):
|
|
|
+ continue
|
|
|
+
|
|
|
+ header_filename = include_gen_folder / "variant" / (camel_to_snake(builtin_class["name"]) + ".hpp")
|
|
|
+ source_filename = source_gen_folder / "variant" / (camel_to_snake(builtin_class["name"]) + ".cpp")
|
|
|
+ if headers:
|
|
|
+ print(str(header_filename.as_posix()), end=end)
|
|
|
+ if sources:
|
|
|
+ print(str(source_filename.as_posix()), end=end)
|
|
|
+
|
|
|
+ for engine_class in api["classes"]:
|
|
|
+ # TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
|
|
|
+ if engine_class["name"] == "ClassDB":
|
|
|
+ continue
|
|
|
+ header_filename = include_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".hpp")
|
|
|
+ source_filename = source_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".cpp")
|
|
|
if headers:
|
|
|
print(str(header_filename.as_posix()), end=end)
|
|
|
if sources:
|
|
|
print(str(source_filename.as_posix()), end=end)
|
|
|
- icall_header_filename = include_gen_folder / '__icalls.hpp'
|
|
|
- register_types_filename = source_gen_folder / '__register_types.cpp'
|
|
|
- init_method_bindings_filename = source_gen_folder / '__init_method_bindings.cpp'
|
|
|
+
|
|
|
+ utility_functions_header_path = include_gen_folder / "variant" / "utility_functions.hpp"
|
|
|
+ utility_functions_source_path = source_gen_folder / "variant" / "utility_functions.cpp"
|
|
|
+ global_constants_header_path = include_gen_folder / "classes" / "global_constants.hpp"
|
|
|
if headers:
|
|
|
- print(str(icall_header_filename.as_posix()), end=end)
|
|
|
+ print(str(utility_functions_header_path.as_posix()), end=end)
|
|
|
+ print(str(global_constants_header_path.as_posix()), end=end)
|
|
|
if sources:
|
|
|
- print(str(register_types_filename.as_posix()), end=end)
|
|
|
- print(str(init_method_bindings_filename.as_posix()), end=end)
|
|
|
+ print(str(utility_functions_source_path.as_posix()), end=end)
|
|
|
|
|
|
|
|
|
def generate_bindings(api_filepath, use_template_get_node, output_dir="."):
|
|
|
- global classes
|
|
|
- with open(api_filepath) as api_file:
|
|
|
- classes = json.load(api_file)
|
|
|
+ api = None
|
|
|
|
|
|
- icalls = set()
|
|
|
- include_gen_folder = Path(output_dir) / 'include' / 'gen'
|
|
|
- source_gen_folder = Path(output_dir) / 'src' / 'gen'
|
|
|
+ target_dir = Path(output_dir) / "gen"
|
|
|
|
|
|
- try:
|
|
|
- include_gen_folder.mkdir(parents=True)
|
|
|
- except os.error as e:
|
|
|
- if e.errno == errno.EEXIST:
|
|
|
- print(str(source_gen_folder) + ": " + os.strerror(e.errno))
|
|
|
- else:
|
|
|
- exit(1)
|
|
|
+ with open(api_filepath) as api_file:
|
|
|
+ api = json.load(api_file)
|
|
|
|
|
|
- try:
|
|
|
- source_gen_folder.mkdir(parents=True)
|
|
|
- except os.error as e:
|
|
|
- if e.errno == errno.EEXIST:
|
|
|
- print(str(source_gen_folder) + ": " + os.strerror(e.errno))
|
|
|
- else:
|
|
|
- exit(1)
|
|
|
+ shutil.rmtree(target_dir, ignore_errors=True)
|
|
|
+ target_dir.mkdir(parents=True)
|
|
|
|
|
|
- for c in classes:
|
|
|
- # print(c['name'])
|
|
|
- used_classes = get_used_classes(c)
|
|
|
- if use_template_get_node and c["name"] == "Node":
|
|
|
- correct_method_name(c["methods"])
|
|
|
+ generate_global_constants(api, target_dir)
|
|
|
+ generate_builtin_bindings(api, target_dir, "float_64")
|
|
|
+ generate_engine_classes_bindings(api, target_dir, use_template_get_node)
|
|
|
+ generate_utility_functions(api, target_dir)
|
|
|
|
|
|
- header = generate_class_header(used_classes, c, use_template_get_node)
|
|
|
|
|
|
- impl = generate_class_implementation(icalls, used_classes, c, use_template_get_node)
|
|
|
+builtin_classes = []
|
|
|
|
|
|
- header_filename = include_gen_folder / (strip_name(c["name"]) + ".hpp")
|
|
|
- with header_filename.open("w+") as header_file:
|
|
|
- header_file.write(header)
|
|
|
+# Key is class name, value is boolean where True means the class is refcounted.
|
|
|
+engine_classes = {}
|
|
|
|
|
|
- source_filename = source_gen_folder / (strip_name(c["name"]) + ".cpp")
|
|
|
- with source_filename.open("w+") as source_file:
|
|
|
- source_file.write(impl)
|
|
|
+singletons = []
|
|
|
|
|
|
- icall_header_filename = include_gen_folder / '__icalls.hpp'
|
|
|
- with icall_header_filename.open("w+") as icall_header_file:
|
|
|
- icall_header_file.write(generate_icall_header(icalls))
|
|
|
|
|
|
- register_types_filename = source_gen_folder / '__register_types.cpp'
|
|
|
- with register_types_filename.open("w+") as register_types_file:
|
|
|
- register_types_file.write(generate_type_registry(classes))
|
|
|
+def generate_builtin_bindings(api, output_dir, build_config):
|
|
|
+ global builtin_classes
|
|
|
|
|
|
- init_method_bindings_filename = source_gen_folder / '__init_method_bindings.cpp'
|
|
|
- with init_method_bindings_filename.open("w+") as init_method_bindings_file:
|
|
|
- init_method_bindings_file.write(generate_init_method_bindings(classes))
|
|
|
+ include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "variant"
|
|
|
+ source_gen_folder = Path(output_dir) / "src" / "variant"
|
|
|
|
|
|
+ include_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
+ source_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
-def is_reference_type(t):
|
|
|
- for c in classes:
|
|
|
- if c['name'] != t:
|
|
|
+ # Store types beforehand.
|
|
|
+ for builtin_api in api["builtin_classes"]:
|
|
|
+ if is_pod_type(builtin_api["name"]):
|
|
|
continue
|
|
|
- if c['is_reference']:
|
|
|
- return True
|
|
|
- return False
|
|
|
-
|
|
|
-def make_gdnative_type(t, ref_allowed):
|
|
|
- if is_enum(t):
|
|
|
- return remove_enum_prefix(t) + " "
|
|
|
- elif is_class_type(t):
|
|
|
- if is_reference_type(t) and ref_allowed:
|
|
|
- return "Ref<" + strip_name(t) + "> "
|
|
|
- else:
|
|
|
- return strip_name(t) + " *"
|
|
|
- else:
|
|
|
- if t == "int":
|
|
|
- return "int64_t "
|
|
|
- if t == "float" or t == "real":
|
|
|
- return "real_t "
|
|
|
- return strip_name(t) + " "
|
|
|
-
|
|
|
-
|
|
|
-def generate_class_header(used_classes, c, use_template_get_node):
|
|
|
-
|
|
|
- source = []
|
|
|
- source.append("#ifndef GODOT_CPP_" + strip_name(c["name"]).upper() + "_HPP")
|
|
|
- source.append("#define GODOT_CPP_" + strip_name(c["name"]).upper() + "_HPP")
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- source.append("#include <gdnative_api_struct.gen.h>")
|
|
|
- source.append("#include <cstdint>")
|
|
|
- source.append("")
|
|
|
-
|
|
|
-
|
|
|
- source.append("#include <core/CoreTypes.hpp>")
|
|
|
-
|
|
|
- class_name = strip_name(c["name"])
|
|
|
-
|
|
|
- # Ref<T> is not included in object.h in Godot either,
|
|
|
- # so don't include it here because it's not needed
|
|
|
- if class_name != "Object" and class_name != "Reference":
|
|
|
- source.append("#include <core/Ref.hpp>")
|
|
|
- ref_allowed = True
|
|
|
- else:
|
|
|
- source.append("#include <core/TagDB.hpp>")
|
|
|
- ref_allowed = False
|
|
|
-
|
|
|
-
|
|
|
- included = []
|
|
|
-
|
|
|
- for used_class in used_classes:
|
|
|
- if is_enum(used_class) and is_nested_type(used_class):
|
|
|
- used_class_name = remove_enum_prefix(extract_nested_type(used_class))
|
|
|
- if used_class_name not in included:
|
|
|
- included.append(used_class_name)
|
|
|
- source.append("#include \"" + used_class_name + ".hpp\"")
|
|
|
- elif is_enum(used_class) and is_nested_type(used_class) and not is_nested_type(used_class, class_name):
|
|
|
- used_class_name = remove_enum_prefix(used_class)
|
|
|
- if used_class_name not in included:
|
|
|
- included.append(used_class_name)
|
|
|
- source.append("#include \"" + used_class_name + ".hpp\"")
|
|
|
+ builtin_classes.append(builtin_api["name"])
|
|
|
|
|
|
- source.append("")
|
|
|
+ builtin_sizes = {}
|
|
|
|
|
|
- if c["base_class"] != "":
|
|
|
- source.append("#include \"" + strip_name(c["base_class"]) + ".hpp\"")
|
|
|
+ for size_list in api["builtin_class_sizes"]:
|
|
|
+ if size_list["build_configuration"] == build_config:
|
|
|
+ for size in size_list["sizes"]:
|
|
|
+ builtin_sizes[size["name"]] = size["size"]
|
|
|
+ break
|
|
|
|
|
|
+ # Create a file for Variant size, since that class isn't generated.
|
|
|
+ variant_size_filename = include_gen_folder / "variant_size.hpp"
|
|
|
+ with variant_size_filename.open("+w") as variant_size_file:
|
|
|
+ variant_size_source = []
|
|
|
+ add_header("variant_size.hpp", variant_size_source)
|
|
|
|
|
|
- source.append("namespace godot {")
|
|
|
- source.append("")
|
|
|
+ header_guard = "GODOT_CPP_VARIANT_SIZE_HPP"
|
|
|
+ variant_size_source.append(f"#ifndef {header_guard}")
|
|
|
+ variant_size_source.append(f"#define {header_guard}")
|
|
|
+ variant_size_source.append(f'#define GODOT_CPP_VARIANT_SIZE {builtin_sizes["Variant"]}')
|
|
|
+ variant_size_source.append(f"#endif // ! {header_guard}")
|
|
|
|
|
|
+ variant_size_file.write("\n".join(variant_size_source))
|
|
|
|
|
|
- for used_type in used_classes:
|
|
|
- if is_enum(used_type) or is_nested_type(used_type, class_name):
|
|
|
+ for builtin_api in api["builtin_classes"]:
|
|
|
+ if is_pod_type(builtin_api["name"]):
|
|
|
+ continue
|
|
|
+ if is_included_type(builtin_api["name"]):
|
|
|
continue
|
|
|
- else:
|
|
|
- source.append("class " + strip_name(used_type) + ";")
|
|
|
-
|
|
|
-
|
|
|
- source.append("")
|
|
|
-
|
|
|
- vararg_templates = ""
|
|
|
-
|
|
|
- # generate the class definition here
|
|
|
- source.append("class " + class_name + (" : public _Wrapped" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"])) ) + " {")
|
|
|
-
|
|
|
- if c["base_class"] == "":
|
|
|
- source.append("public: enum { ___CLASS_IS_SCRIPT = 0, };")
|
|
|
- source.append("")
|
|
|
- source.append("private:")
|
|
|
-
|
|
|
- if c["singleton"]:
|
|
|
- source.append("\tstatic " + class_name + " *_singleton;")
|
|
|
- source.append("")
|
|
|
- source.append("\t" + class_name + "();")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- # Generate method table
|
|
|
- source.append("\tstruct ___method_bindings {")
|
|
|
-
|
|
|
- for method in c["methods"]:
|
|
|
- source.append("\t\tgodot_method_bind *mb_" + method["name"] + ";")
|
|
|
-
|
|
|
- source.append("\t};")
|
|
|
- source.append("\tstatic ___method_bindings ___mb;")
|
|
|
- source.append("\tstatic void *_detail_class_tag;")
|
|
|
- source.append("")
|
|
|
- source.append("public:")
|
|
|
- source.append("\tstatic void ___init_method_bindings();")
|
|
|
-
|
|
|
- # class id from core engine for casting
|
|
|
- source.append("\tinline static size_t ___get_id() { return (size_t)_detail_class_tag; }")
|
|
|
-
|
|
|
- source.append("")
|
|
|
-
|
|
|
-
|
|
|
- if c["singleton"]:
|
|
|
- source.append("\tstatic inline " + class_name + " *get_singleton()")
|
|
|
- source.append("\t{")
|
|
|
- source.append("\t\tif (!" + class_name + "::_singleton) {")
|
|
|
- source.append("\t\t\t" + class_name + "::_singleton = new " + class_name + ";")
|
|
|
- source.append("\t\t}")
|
|
|
- source.append("\t\treturn " + class_name + "::_singleton;")
|
|
|
- source.append("\t}")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- # godot::api->godot_global_get_singleton((char *) \"" + strip_name(c["name"]) + "\");"
|
|
|
-
|
|
|
- # class name:
|
|
|
- # Two versions needed needed because when the user implements a custom class,
|
|
|
- # we want to override `___get_class_name` while `___get_godot_class_name` can keep returning the base name
|
|
|
- source.append("\tstatic inline const char *___get_class_name() { return (const char *) \"" + strip_name(c["name"]) + "\"; }")
|
|
|
- source.append("\tstatic inline const char *___get_godot_class_name() { return (const char *) \"" + strip_name(c["name"]) + "\"; }")
|
|
|
-
|
|
|
- source.append("\tstatic inline Object *___get_from_variant(Variant a) { godot_object *o = (godot_object*) a; return (o) ? (Object *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, o) : nullptr; }")
|
|
|
-
|
|
|
- enum_values = []
|
|
|
-
|
|
|
- source.append("\n\t// enums")
|
|
|
- for enum in c["enums"]:
|
|
|
- source.append("\tenum " + strip_name(enum["name"]) + " {")
|
|
|
- for value in enum["values"]:
|
|
|
- source.append("\t\t" + remove_nested_type_prefix(value) + " = " + str(enum["values"][value]) + ",")
|
|
|
- enum_values.append(value)
|
|
|
- source.append("\t};")
|
|
|
-
|
|
|
- source.append("\n\t// constants")
|
|
|
-
|
|
|
- for name in c["constants"]:
|
|
|
- if name not in enum_values:
|
|
|
- source.append("\tconst static int " + name + " = " + str(c["constants"][name]) + ";")
|
|
|
-
|
|
|
-
|
|
|
- if c["instanciable"]:
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
- source.append("\tstatic " + class_name + " *_new();")
|
|
|
-
|
|
|
- source.append("\n\t// methods")
|
|
|
-
|
|
|
-
|
|
|
- if class_name == "Object":
|
|
|
- source.append("#ifndef GODOT_CPP_NO_OBJECT_CAST")
|
|
|
- source.append("\ttemplate<class T>")
|
|
|
- source.append("\tstatic T *cast_to(const Object *obj);")
|
|
|
- source.append("#endif")
|
|
|
- source.append("")
|
|
|
|
|
|
- for method in c["methods"]:
|
|
|
- method_signature = ""
|
|
|
+ size = builtin_sizes[builtin_api["name"]]
|
|
|
+
|
|
|
+ header_filename = include_gen_folder / (camel_to_snake(builtin_api["name"]) + ".hpp")
|
|
|
+ source_filename = source_gen_folder / (camel_to_snake(builtin_api["name"]) + ".cpp")
|
|
|
+
|
|
|
+ # Check used classes for header include
|
|
|
+ used_classes = set()
|
|
|
+ fully_used_classes = set()
|
|
|
+
|
|
|
+ class_name = builtin_api["name"]
|
|
|
+
|
|
|
+ if "constructors" in builtin_api:
|
|
|
+ for constructor in builtin_api["constructors"]:
|
|
|
+ if "arguments" in constructor:
|
|
|
+ for argument in constructor["arguments"]:
|
|
|
+ if is_included(argument["type"], class_name):
|
|
|
+ if "default_value" in argument and argument["type"] != "Variant":
|
|
|
+ fully_used_classes.add(argument["type"])
|
|
|
+ else:
|
|
|
+ used_classes.add(argument["type"])
|
|
|
+
|
|
|
+ if "methods" in builtin_api:
|
|
|
+ for method in builtin_api["methods"]:
|
|
|
+ if "arguments" in method:
|
|
|
+ for argument in method["arguments"]:
|
|
|
+ if is_included(argument["type"], class_name):
|
|
|
+ if "default_value" in argument and argument["type"] != "Variant":
|
|
|
+ fully_used_classes.add(argument["type"])
|
|
|
+ else:
|
|
|
+ used_classes.add(argument["type"])
|
|
|
+ if "return_type" in method:
|
|
|
+ if is_included(method["return_type"], class_name):
|
|
|
+ used_classes.add(method["return_type"])
|
|
|
+
|
|
|
+ if "members" in builtin_api:
|
|
|
+ for member in builtin_api["members"]:
|
|
|
+ if is_included(member["type"], class_name):
|
|
|
+ used_classes.add(member["type"])
|
|
|
+
|
|
|
+ if "indexing_return_type" in builtin_api:
|
|
|
+ if is_included(builtin_api["indexing_return_type"], class_name):
|
|
|
+ used_classes.add(builtin_api["indexing_return_type"])
|
|
|
+
|
|
|
+ if "operators" in builtin_api:
|
|
|
+ for operator in builtin_api["operators"]:
|
|
|
+ if "right_type" in operator:
|
|
|
+ # FIXME Temporary workaround for incorrect JSON
|
|
|
+ if operator["right_type"] == "Nil":
|
|
|
+ used_classes.add("Variant")
|
|
|
+ elif is_included(operator["right_type"], class_name):
|
|
|
+ used_classes.add(operator["right_type"])
|
|
|
+
|
|
|
+ for type_name in fully_used_classes:
|
|
|
+ if type_name in used_classes:
|
|
|
+ used_classes.remove(type_name)
|
|
|
|
|
|
- # TODO decide what to do about virtual methods
|
|
|
- # method_signature += "virtual " if method["is_virtual"] else ""
|
|
|
- method_signature += make_gdnative_type(method["return_type"], ref_allowed)
|
|
|
- method_name = escape_cpp(method["name"])
|
|
|
- method_signature += method_name + "("
|
|
|
+ with header_filename.open("w+") as header_file:
|
|
|
+ header_file.write(generate_builtin_class_header(builtin_api, size, used_classes, fully_used_classes))
|
|
|
|
|
|
+ with source_filename.open("w+") as source_file:
|
|
|
+ source_file.write(generate_builtin_class_source(builtin_api, size, used_classes, fully_used_classes))
|
|
|
|
|
|
- has_default_argument = False
|
|
|
- method_arguments = ""
|
|
|
+ # Create a header with all builtin types for convenience.
|
|
|
+ builtin_header_filename = include_gen_folder / "builtin_types.hpp"
|
|
|
+ with builtin_header_filename.open("w+") as builtin_header_file:
|
|
|
+ builtin_header = []
|
|
|
+ add_header("builtin_types.hpp", builtin_header)
|
|
|
|
|
|
- for i, argument in enumerate(method["arguments"]):
|
|
|
- method_signature += "const " + make_gdnative_type(argument["type"], ref_allowed)
|
|
|
- argument_name = escape_cpp(argument["name"])
|
|
|
- method_signature += argument_name
|
|
|
- method_arguments += argument_name
|
|
|
+ builtin_header.append("#ifndef GODOT_CPP_BUILTIN_TYPES_HPP")
|
|
|
+ builtin_header.append("#define GODOT_CPP_BUILTIN_TYPES_HPP")
|
|
|
|
|
|
+ builtin_header.append("")
|
|
|
|
|
|
- # default arguments
|
|
|
- def escape_default_arg(_type, default_value):
|
|
|
- if _type == "Color":
|
|
|
- return "Color(" + default_value + ")"
|
|
|
- if _type == "bool" or _type == "int":
|
|
|
- return default_value.lower()
|
|
|
- if _type == "Array":
|
|
|
- return "Array()"
|
|
|
- if _type in ["PoolVector2Array", "PoolStringArray", "PoolVector3Array", "PoolColorArray", "PoolIntArray", "PoolRealArray", "PoolByteArray"]:
|
|
|
- return _type + "()"
|
|
|
- if _type == "Vector2":
|
|
|
- return "Vector2" + default_value
|
|
|
- if _type == "Vector3":
|
|
|
- return "Vector3" + default_value
|
|
|
- if _type == "Transform":
|
|
|
- return "Transform()"
|
|
|
- if _type == "Transform2D":
|
|
|
- return "Transform2D()"
|
|
|
- if _type == "Rect2":
|
|
|
- return "Rect2" + default_value
|
|
|
- if _type == "Variant":
|
|
|
- return "Variant()" if default_value == "Null" else default_value
|
|
|
- if _type == "String" or _type == "NodePath":
|
|
|
- return "\"" + default_value + "\""
|
|
|
- if _type == "RID":
|
|
|
- return "RID()"
|
|
|
+ for builtin in builtin_classes:
|
|
|
+ builtin_header.append(f"#include <godot_cpp/variant/{camel_to_snake(builtin)}.hpp>")
|
|
|
|
|
|
- if default_value == "Null" or default_value == "[Object:null]":
|
|
|
- return "nullptr"
|
|
|
+ builtin_header.append("")
|
|
|
|
|
|
- return default_value
|
|
|
+ builtin_header.append("#endif // ! GODOT_CPP_BUILTIN_TYPES_HPP")
|
|
|
|
|
|
+ builtin_header_file.write("\n".join(builtin_header))
|
|
|
|
|
|
|
|
|
+def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_classes):
|
|
|
+ result = []
|
|
|
|
|
|
- if argument["has_default_value"] or has_default_argument:
|
|
|
- method_signature += " = " + escape_default_arg(argument["type"], argument["default_value"])
|
|
|
- has_default_argument = True
|
|
|
+ class_name = builtin_api["name"]
|
|
|
+ snake_class_name = camel_to_snake(class_name).upper()
|
|
|
|
|
|
+ header_guard = f"GODOT_CPP_{snake_class_name}_HPP"
|
|
|
|
|
|
+ add_header(f"{snake_class_name.lower()}.hpp", result)
|
|
|
|
|
|
- if i != len(method["arguments"]) - 1:
|
|
|
- method_signature += ", "
|
|
|
- method_arguments += ","
|
|
|
+ result.append(f"#ifndef {header_guard}")
|
|
|
+ result.append(f"#define {header_guard}")
|
|
|
|
|
|
- if method["has_varargs"]:
|
|
|
- if len(method["arguments"]) > 0:
|
|
|
- method_signature += ", "
|
|
|
- method_arguments += ", "
|
|
|
- vararg_templates += "\ttemplate <class... Args> " + method_signature + "Args... args){\n\t\treturn " + method_name + "(" + method_arguments + "Array::make(args...));\n\t}\n"""
|
|
|
- method_signature += "const Array& __var_args = Array()"
|
|
|
+ result.append("")
|
|
|
+ result.append("#include <godot_cpp/core/defs.hpp>")
|
|
|
+ result.append("")
|
|
|
|
|
|
- method_signature += ")" + (" const" if method["is_const"] else "")
|
|
|
+ # Special cases.
|
|
|
+ if class_name == "String":
|
|
|
+ result.append("#include <godot_cpp/variant/char_string.hpp>")
|
|
|
|
|
|
+ for include in fully_used_classes:
|
|
|
+ result.append(f"#include <godot_cpp/{get_include_path(include)}>")
|
|
|
|
|
|
- source.append("\t" + method_signature + ";")
|
|
|
+ if len(fully_used_classes) > 0:
|
|
|
+ result.append("")
|
|
|
|
|
|
- source.append(vararg_templates)
|
|
|
+ result.append(f"#include <godot/gdnative_interface.h>")
|
|
|
+ result.append("")
|
|
|
+ result.append("namespace godot {")
|
|
|
+ result.append("")
|
|
|
|
|
|
- if use_template_get_node and class_name == "Node":
|
|
|
- # Extra definition for template get_node that calls the renamed get_node_internal; has a default template parameter for backwards compatibility.
|
|
|
- source.append("\ttemplate <class T = Node>")
|
|
|
- source.append("\tT *get_node(const NodePath path) const {")
|
|
|
- source.append("\t\treturn Object::cast_to<T>(get_node_internal(path));")
|
|
|
- source.append("\t}")
|
|
|
+ for type_name in used_classes:
|
|
|
+ result.append(f"class {type_name};")
|
|
|
|
|
|
- source.append("};")
|
|
|
- source.append("")
|
|
|
+ if len(used_classes) > 0:
|
|
|
+ result.append("")
|
|
|
|
|
|
- # ...And a specialized version so we don't unnecessarily cast when using the default.
|
|
|
- source.append("template <>")
|
|
|
- source.append("inline Node *Node::get_node<Node>(const NodePath path) const {")
|
|
|
- source.append("\treturn get_node_internal(path);")
|
|
|
- source.append("}")
|
|
|
- source.append("")
|
|
|
- else:
|
|
|
- source.append("};")
|
|
|
- source.append("")
|
|
|
+ result.append(f"class {class_name} {{")
|
|
|
+ result.append(f"\tstatic constexpr size_t {snake_class_name}_SIZE = {size};")
|
|
|
+ result.append(f"\tuint8_t opaque[{snake_class_name}_SIZE] = {{}};")
|
|
|
+ result.append(
|
|
|
+ f"\t_FORCE_INLINE_ GDNativeTypePtr ptr() const {{ return const_cast<uint8_t (*)[{snake_class_name}_SIZE]>(&opaque); }}"
|
|
|
+ )
|
|
|
|
|
|
- source.append("}")
|
|
|
- source.append("")
|
|
|
+ result.append("")
|
|
|
+ result.append("\tfriend class Variant;")
|
|
|
|
|
|
- source.append("#endif")
|
|
|
+ result.append("")
|
|
|
+ result.append("\tstatic struct _MethodBindings {")
|
|
|
|
|
|
+ if "constructors" in builtin_api:
|
|
|
+ for constructor in builtin_api["constructors"]:
|
|
|
+ result.append(f'\t\tGDNativePtrConstructor constructor_{constructor["index"]};')
|
|
|
|
|
|
- return "\n".join(source)
|
|
|
+ if builtin_api["has_destructor"]:
|
|
|
+ result.append("\t\tGDNativePtrDestructor destructor;")
|
|
|
|
|
|
+ if "methods" in builtin_api:
|
|
|
+ for method in builtin_api["methods"]:
|
|
|
+ result.append(f'\t\tGDNativePtrBuiltInMethod method_{method["name"]};')
|
|
|
|
|
|
+ if "members" in builtin_api:
|
|
|
+ for member in builtin_api["members"]:
|
|
|
+ result.append(f'\t\tGDNativePtrSetter member_{member["name"]}_setter;')
|
|
|
+ result.append(f'\t\tGDNativePtrGetter member_{member["name"]}_getter;')
|
|
|
|
|
|
+ if "indexing_return_type" in builtin_api:
|
|
|
+ result.append(f"\t\tGDNativePtrIndexedSetter indexed_setter;")
|
|
|
+ result.append(f"\t\tGDNativePtrIndexedGetter indexed_getter;")
|
|
|
|
|
|
+ if "is_keyed" in builtin_api and builtin_api["is_keyed"]:
|
|
|
+ result.append(f"\t\tGDNativePtrKeyedSetter keyed_setter;")
|
|
|
+ result.append(f"\t\tGDNativePtrKeyedGetter keyed_getter;")
|
|
|
+ result.append(f"\t\tGDNativePtrKeyedChecker keyed_checker;")
|
|
|
|
|
|
-def generate_class_implementation(icalls, used_classes, c, use_template_get_node):
|
|
|
- class_name = strip_name(c["name"])
|
|
|
+ if "operators" in builtin_api:
|
|
|
+ for operator in builtin_api["operators"]:
|
|
|
+ if "right_type" in operator:
|
|
|
+ result.append(
|
|
|
+ f'\t\tGDNativePtrOperatorEvaluator operator_{get_operator_id_name(operator["name"])}_{operator["right_type"]};'
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ result.append(f'\t\tGDNativePtrOperatorEvaluator operator_{get_operator_id_name(operator["name"])};')
|
|
|
+
|
|
|
+ result.append("\t} _method_bindings;")
|
|
|
+
|
|
|
+ result.append("")
|
|
|
+ result.append("\tstatic void init_bindings();")
|
|
|
+
|
|
|
+ result.append("")
|
|
|
+ result.append("public:")
|
|
|
+
|
|
|
+ copy_constructor_index = -1
|
|
|
+
|
|
|
+ if "constructors" in builtin_api:
|
|
|
+ for constructor in builtin_api["constructors"]:
|
|
|
+ method_signature = f"\t{class_name}("
|
|
|
+ if "arguments" in constructor:
|
|
|
+ method_signature += make_function_parameters(
|
|
|
+ constructor["arguments"], include_default=True, for_builtin=True
|
|
|
+ )
|
|
|
+ if len(constructor["arguments"]) == 1 and constructor["arguments"][0]["type"] == class_name:
|
|
|
+ copy_constructor_index = constructor["index"]
|
|
|
+ method_signature += ");"
|
|
|
+
|
|
|
+ result.append(method_signature)
|
|
|
+
|
|
|
+ # Move constructor.
|
|
|
+ result.append(f"\t{class_name}({class_name} &&other);")
|
|
|
+
|
|
|
+ # Special cases.
|
|
|
+ if class_name == "String" or class_name == "StringName" or class_name == "NodePath":
|
|
|
+ result.append(f"\t{class_name}(const char *from);")
|
|
|
+ result.append(f"\t{class_name}(const wchar_t *from);")
|
|
|
+ result.append(f"\t{class_name}(const char16_t *from);")
|
|
|
+ result.append(f"\t{class_name}(const char32_t *from);")
|
|
|
+
|
|
|
+ if "constants" in builtin_api:
|
|
|
+ axis_constants_count = 0
|
|
|
+ for constant in builtin_api["constants"]:
|
|
|
+ # Special case: Vector3.Axis is the only enum in the bindings.
|
|
|
+ # It's technically not supported by Variant but works for direct access.
|
|
|
+ if class_name == "Vector3" and constant["name"].startswith("AXIS"):
|
|
|
+ if axis_constants_count == 0:
|
|
|
+ result.append("\tenum Axis {")
|
|
|
+ result.append(f'\t\t{constant["name"]} = {constant["value"]},')
|
|
|
+ axis_constants_count += 1
|
|
|
+ if axis_constants_count == 3:
|
|
|
+ result.append("\t};")
|
|
|
+ else:
|
|
|
+ result.append(f'\tstatic const {correct_type(constant["type"])} {constant["name"]};')
|
|
|
|
|
|
- ref_allowed = class_name != "Object" and class_name != "Reference"
|
|
|
+ if builtin_api["has_destructor"]:
|
|
|
+ result.append(f"\t~{class_name}();")
|
|
|
|
|
|
- source = []
|
|
|
- source.append("#include \"" + class_name + ".hpp\"")
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- source.append("#include <core/GodotGlobal.hpp>")
|
|
|
- source.append("#include <core/CoreTypes.hpp>")
|
|
|
- source.append("#include <core/Ref.hpp>")
|
|
|
+ method_list = []
|
|
|
|
|
|
- source.append("#include <core/Godot.hpp>")
|
|
|
- source.append("")
|
|
|
+ if "methods" in builtin_api:
|
|
|
+ for method in builtin_api["methods"]:
|
|
|
+ method_list.append(method["name"])
|
|
|
|
|
|
+ vararg = method["is_vararg"]
|
|
|
+ if vararg:
|
|
|
+ result.append("\ttemplate<class... Args>")
|
|
|
|
|
|
- source.append("#include \"__icalls.hpp\"")
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
+ method_signature = "\t"
|
|
|
+ if "is_static" in method and method["is_static"]:
|
|
|
+ method_signature += "static "
|
|
|
|
|
|
- for used_class in used_classes:
|
|
|
- if is_enum(used_class):
|
|
|
+ if "return_type" in method:
|
|
|
+ method_signature += f'{correct_type(method["return_type"])} '
|
|
|
+ else:
|
|
|
+ method_signature += "void "
|
|
|
+
|
|
|
+ method_signature += f'{method["name"]}('
|
|
|
+
|
|
|
+ method_arguments = []
|
|
|
+ if "arguments" in method:
|
|
|
+ method_arguments = method["arguments"]
|
|
|
+
|
|
|
+ method_signature += make_function_parameters(
|
|
|
+ method_arguments, include_default=True, for_builtin=True, is_vararg=vararg
|
|
|
+ )
|
|
|
+
|
|
|
+ method_signature += ")"
|
|
|
+ if method["is_const"]:
|
|
|
+ method_signature += " const"
|
|
|
+ method_signature += ";"
|
|
|
+
|
|
|
+ result.append(method_signature)
|
|
|
+
|
|
|
+ # Special cases.
|
|
|
+ if class_name == "String":
|
|
|
+ result.append("\tCharString utf8() const;")
|
|
|
+ result.append("\tCharString ascii() const;")
|
|
|
+ result.append("\tChar16String utf16() const;")
|
|
|
+ result.append("\tChar32String utf32() const;")
|
|
|
+ result.append("\tCharWideString wide_string() const;")
|
|
|
+
|
|
|
+ if "members" in builtin_api:
|
|
|
+ for member in builtin_api["members"]:
|
|
|
+ if f'get_{member["name"]}' not in method_list:
|
|
|
+ result.append(f'\t{correct_type(member["type"])} get_{member["name"]}() const;')
|
|
|
+ if f'set_{member["name"]}' not in method_list:
|
|
|
+ result.append(f'\tvoid set_{member["name"]}({type_for_parameter(member["type"])}value);')
|
|
|
+
|
|
|
+ if "operators" in builtin_api:
|
|
|
+ for operator in builtin_api["operators"]:
|
|
|
+ if operator["name"] not in ["in", "xor"]:
|
|
|
+ if "right_type" in operator:
|
|
|
+ result.append(
|
|
|
+ f'\t{correct_type(operator["return_type"])} operator{operator["name"]}({type_for_parameter(operator["right_type"])}other) const;'
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ result.append(
|
|
|
+ f'\t{correct_type(operator["return_type"])} operator{operator["name"].replace("unary", "")}() const;'
|
|
|
+ )
|
|
|
+
|
|
|
+ # Copy assignment.
|
|
|
+ if copy_constructor_index >= 0:
|
|
|
+ result.append(f"\t{class_name} &operator=(const {class_name} &other);")
|
|
|
+
|
|
|
+ # Move assignment.
|
|
|
+ result.append(f"\t{class_name} &operator=({class_name} &&other);")
|
|
|
+
|
|
|
+ # Special cases.
|
|
|
+ if class_name == "String":
|
|
|
+ result.append("String &operator=(const char *p_str);")
|
|
|
+ result.append("String &operator=(const wchar_t *p_str);")
|
|
|
+ result.append("String &operator=(const char16_t *p_str);")
|
|
|
+ result.append("String &operator=(const char32_t *p_str);")
|
|
|
+ result.append("bool operator==(const char *p_str) const;")
|
|
|
+ result.append("bool operator==(const wchar_t *p_str) const;")
|
|
|
+ result.append("bool operator==(const char16_t *p_str) const;")
|
|
|
+ result.append("bool operator==(const char32_t *p_str) const;")
|
|
|
+ result.append("bool operator!=(const char *p_str) const;")
|
|
|
+ result.append("bool operator!=(const wchar_t *p_str) const;")
|
|
|
+ result.append("bool operator!=(const char16_t *p_str) const;")
|
|
|
+ result.append("bool operator!=(const char32_t *p_str) const;")
|
|
|
+ result.append(f"\tconst char32_t &operator[](int p_index) const;")
|
|
|
+ result.append(f"\tchar32_t &operator[](int p_index);")
|
|
|
+
|
|
|
+ if is_packed_array(class_name):
|
|
|
+ return_type = correct_type(builtin_api["indexing_return_type"])
|
|
|
+ if class_name == "PackedByteArray":
|
|
|
+ return_type = "uint8_t"
|
|
|
+ elif class_name == "PackedInt32Array":
|
|
|
+ return_type = "int32_t"
|
|
|
+ elif class_name == "PackedFloat32Array":
|
|
|
+ return_type = "float"
|
|
|
+ result.append(f"\tconst " + return_type + f" &operator[](int p_index) const;")
|
|
|
+ result.append(f"\t" + return_type + f" &operator[](int p_index);")
|
|
|
+
|
|
|
+ result.append("};")
|
|
|
+
|
|
|
+ if class_name == "String":
|
|
|
+ result.append("")
|
|
|
+ result.append("bool operator==(const char *p_chr, const String &p_str);")
|
|
|
+ result.append("bool operator==(const wchar_t *p_chr, const String &p_str);")
|
|
|
+ result.append("bool operator==(const char16_t *p_chr, const String &p_str);")
|
|
|
+ result.append("bool operator==(const char32_t *p_chr, const String &p_str);")
|
|
|
+ result.append("bool operator!=(const char *p_chr, const String &p_str);")
|
|
|
+ result.append("bool operator!=(const wchar_t *p_chr, const String &p_str);")
|
|
|
+ result.append("bool operator!=(const char16_t *p_chr, const String &p_str);")
|
|
|
+ result.append("bool operator!=(const char32_t *p_chr, const String &p_str);")
|
|
|
+ result.append("String operator+(const char *p_chr, const String &p_str);")
|
|
|
+ result.append("String operator+(const wchar_t *p_chr, const String &p_str);")
|
|
|
+ result.append("String operator+(const char16_t *p_chr, const String &p_str);")
|
|
|
+ result.append("String operator+(const char32_t *p_chr, const String &p_str);")
|
|
|
+
|
|
|
+ result.append("")
|
|
|
+ result.append("} // namespace godot")
|
|
|
+
|
|
|
+ result.append(f"#endif // ! {header_guard}")
|
|
|
+
|
|
|
+ return "\n".join(result)
|
|
|
+
|
|
|
+
|
|
|
+def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_classes):
|
|
|
+ result = []
|
|
|
+
|
|
|
+ class_name = builtin_api["name"]
|
|
|
+ snake_class_name = camel_to_snake(class_name)
|
|
|
+ enum_type_name = f"GDNATIVE_VARIANT_TYPE_{snake_class_name.upper()}"
|
|
|
+
|
|
|
+ add_header(f"{snake_class_name}.cpp", result)
|
|
|
+
|
|
|
+ result.append("")
|
|
|
+ result.append(f"#include <godot_cpp/variant/{snake_class_name}.hpp>")
|
|
|
+ result.append("")
|
|
|
+ result.append("#include <godot_cpp/core/binder_common.hpp>")
|
|
|
+ result.append("")
|
|
|
+ result.append("#include <godot_cpp/godot.hpp>")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ # Only used since the "fully used" is included in header already.
|
|
|
+ for include in used_classes:
|
|
|
+ result.append(f"#include <godot_cpp/{get_include_path(include)}>")
|
|
|
+
|
|
|
+ if len(used_classes) > 0:
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ result.append("#include <godot_cpp/core/builtin_ptrcall.hpp>")
|
|
|
+ result.append("")
|
|
|
+ result.append("#include <utility>")
|
|
|
+ result.append("")
|
|
|
+ result.append("namespace godot {")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ result.append(f"{class_name}::_MethodBindings {class_name}::_method_bindings;")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ result.append(f"void {class_name}::init_bindings() {{")
|
|
|
+
|
|
|
+ if "constructors" in builtin_api:
|
|
|
+ for constructor in builtin_api["constructors"]:
|
|
|
+ result.append(
|
|
|
+ f'\t_method_bindings.constructor_{constructor["index"]} = internal::interface->variant_get_ptr_constructor({enum_type_name}, {constructor["index"]});'
|
|
|
+ )
|
|
|
+
|
|
|
+ if builtin_api["has_destructor"]:
|
|
|
+ result.append(
|
|
|
+ f"\t_method_bindings.destructor = internal::interface->variant_get_ptr_destructor({enum_type_name});"
|
|
|
+ )
|
|
|
+
|
|
|
+ if "methods" in builtin_api:
|
|
|
+ for method in builtin_api["methods"]:
|
|
|
+ # TODO: Add error check for hash mismatch.
|
|
|
+ result.append(
|
|
|
+ f'\t_method_bindings.method_{method["name"]} = internal::interface->variant_get_ptr_builtin_method({enum_type_name}, "{method["name"]}", {method["hash"]});'
|
|
|
+ )
|
|
|
+
|
|
|
+ if "members" in builtin_api:
|
|
|
+ for member in builtin_api["members"]:
|
|
|
+ result.append(
|
|
|
+ f'\t_method_bindings.member_{member["name"]}_setter = internal::interface->variant_get_ptr_setter({enum_type_name}, "{member["name"]}");'
|
|
|
+ )
|
|
|
+ result.append(
|
|
|
+ f'\t_method_bindings.member_{member["name"]}_getter = internal::interface->variant_get_ptr_getter({enum_type_name}, "{member["name"]}");'
|
|
|
+ )
|
|
|
+
|
|
|
+ if "indexing_return_type" in builtin_api:
|
|
|
+ result.append(
|
|
|
+ f"\t_method_bindings.indexed_setter = internal::interface->variant_get_ptr_indexed_setter({enum_type_name});"
|
|
|
+ )
|
|
|
+ result.append(
|
|
|
+ f"\t_method_bindings.indexed_getter = internal::interface->variant_get_ptr_indexed_getter({enum_type_name});"
|
|
|
+ )
|
|
|
+
|
|
|
+ if "is_keyed" in builtin_api and builtin_api["is_keyed"]:
|
|
|
+ result.append(
|
|
|
+ f"\t_method_bindings.keyed_setter = internal::interface->variant_get_ptr_keyed_setter({enum_type_name});"
|
|
|
+ )
|
|
|
+ result.append(
|
|
|
+ f"\t_method_bindings.keyed_getter = internal::interface->variant_get_ptr_keyed_getter({enum_type_name});"
|
|
|
+ )
|
|
|
+ result.append(
|
|
|
+ f"\t_method_bindings.keyed_checker = internal::interface->variant_get_ptr_keyed_checker({enum_type_name});"
|
|
|
+ )
|
|
|
+
|
|
|
+ if "operators" in builtin_api:
|
|
|
+ for operator in builtin_api["operators"]:
|
|
|
+ if "right_type" in operator:
|
|
|
+ result.append(
|
|
|
+ f'\t_method_bindings.operator_{get_operator_id_name(operator["name"])}_{operator["right_type"]} = internal::interface->variant_get_ptr_operator_evaluator(GDNATIVE_VARIANT_OP_{get_operator_id_name(operator["name"]).upper()}, {enum_type_name}, GDNATIVE_VARIANT_TYPE_{camel_to_snake(operator["right_type"]).upper()});'
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ result.append(
|
|
|
+ f'\t_method_bindings.operator_{get_operator_id_name(operator["name"])} = internal::interface->variant_get_ptr_operator_evaluator(GDNATIVE_VARIANT_OP_{get_operator_id_name(operator["name"]).upper()}, {enum_type_name}, GDNATIVE_VARIANT_TYPE_NIL);'
|
|
|
+ )
|
|
|
+
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ copy_constructor_index = -1
|
|
|
+
|
|
|
+ if "constructors" in builtin_api:
|
|
|
+ for constructor in builtin_api["constructors"]:
|
|
|
+ method_signature = f"{class_name}::{class_name}("
|
|
|
+ if "arguments" in constructor:
|
|
|
+ method_signature += make_function_parameters(
|
|
|
+ constructor["arguments"], include_default=False, for_builtin=True
|
|
|
+ )
|
|
|
+ method_signature += ") {"
|
|
|
+
|
|
|
+ result.append(method_signature)
|
|
|
+
|
|
|
+ method_call = (
|
|
|
+ f'\tinternal::_call_builtin_constructor(_method_bindings.constructor_{constructor["index"]}, &opaque'
|
|
|
+ )
|
|
|
+ if "arguments" in constructor:
|
|
|
+ if len(constructor["arguments"]) == 1 and constructor["arguments"][0]["type"] == class_name:
|
|
|
+ copy_constructor_index = constructor["index"]
|
|
|
+ method_call += ", "
|
|
|
+ arguments = []
|
|
|
+ for argument in constructor["arguments"]:
|
|
|
+ (encode, arg_name) = get_encoded_arg(
|
|
|
+ argument["name"],
|
|
|
+ argument["type"],
|
|
|
+ argument["meta"] if "meta" in argument else None,
|
|
|
+ )
|
|
|
+ result += encode
|
|
|
+ arguments.append(arg_name)
|
|
|
+ method_call += ", ".join(arguments)
|
|
|
+ method_call += ");"
|
|
|
+
|
|
|
+ result.append(method_call)
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ # Move constructor.
|
|
|
+ result.append(f"{class_name}::{class_name}({class_name} &&other) {{")
|
|
|
+ result.append("\tstd::swap(opaque, other.opaque);")
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if builtin_api["has_destructor"]:
|
|
|
+ result.append(f"{class_name}::~{class_name}() {{")
|
|
|
+ result.append("\t_method_bindings.destructor(&opaque);")
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ method_list = []
|
|
|
+
|
|
|
+ if "methods" in builtin_api:
|
|
|
+ for method in builtin_api["methods"]:
|
|
|
+ method_list.append(method["name"])
|
|
|
+
|
|
|
+ if "is_vararg" in method and method["is_vararg"]:
|
|
|
+ # Done in the header because of the template.
|
|
|
+ continue
|
|
|
+
|
|
|
+ method_signature = make_signature(class_name, method, for_builtin=True)
|
|
|
+ result.append(method_signature + "{")
|
|
|
+
|
|
|
+ method_call = "\t"
|
|
|
+ if "return_type" in method:
|
|
|
+ method_call += f'return internal::_call_builtin_method_ptr_ret<{correct_type(method["return_type"])}>('
|
|
|
+ else:
|
|
|
+ method_call += f"internal::_call_builtin_method_ptr_no_ret("
|
|
|
+ method_call += f'_method_bindings.method_{method["name"]}, '
|
|
|
+ if "is_static" in method and method["is_static"]:
|
|
|
+ method_call += "nullptr"
|
|
|
+ else:
|
|
|
+ method_call += "(GDNativeTypePtr)&opaque"
|
|
|
+
|
|
|
+ if "arguments" in method:
|
|
|
+ arguments = []
|
|
|
+ method_call += ", "
|
|
|
+ for argument in method["arguments"]:
|
|
|
+ (encode, arg_name) = get_encoded_arg(
|
|
|
+ argument["name"],
|
|
|
+ argument["type"],
|
|
|
+ argument["meta"] if "meta" in argument else None,
|
|
|
+ )
|
|
|
+ result += encode
|
|
|
+ arguments.append(arg_name)
|
|
|
+ method_call += ", ".join(arguments)
|
|
|
+ method_call += ");"
|
|
|
+
|
|
|
+ result.append(method_call)
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if "members" in builtin_api:
|
|
|
+ for member in builtin_api["members"]:
|
|
|
+ if f'get_{member["name"]}' not in method_list:
|
|
|
+ result.append(f'{correct_type(member["type"])} {class_name}::get_{member["name"]}() const {{')
|
|
|
+ result.append(
|
|
|
+ f'\treturn internal::_call_builtin_ptr_getter<{correct_type(member["type"])}>(_method_bindings.member_{member["name"]}_getter, (const GDNativeTypePtr)&opaque);'
|
|
|
+ )
|
|
|
+ result.append("}")
|
|
|
+
|
|
|
+ if f'set_{member["name"]}' not in method_list:
|
|
|
+ result.append(f'void {class_name}::set_{member["name"]}({type_for_parameter(member["type"])}value) {{')
|
|
|
+ (encode, arg_name) = get_encoded_arg("value", member["type"], None)
|
|
|
+ result += encode
|
|
|
+ result.append(
|
|
|
+ f'\t_method_bindings.member_{member["name"]}_setter((const GDNativeTypePtr)&opaque, (const GDNativeTypePtr){arg_name});'
|
|
|
+ )
|
|
|
+
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if "operators" in builtin_api:
|
|
|
+ for operator in builtin_api["operators"]:
|
|
|
+ if operator["name"] not in ["in", "xor"]:
|
|
|
+ if "right_type" in operator:
|
|
|
+ result.append(
|
|
|
+ f'{correct_type(operator["return_type"])} {class_name}::operator{operator["name"]}({type_for_parameter(operator["right_type"])}other) const {{'
|
|
|
+ )
|
|
|
+ (encode, arg_name) = get_encoded_arg("other", operator["right_type"], None)
|
|
|
+ result += encode
|
|
|
+ result.append(
|
|
|
+ f'\treturn internal::_call_builtin_operator_ptr<{correct_type(operator["return_type"])}>(_method_bindings.operator_{get_operator_id_name(operator["name"])}_{operator["right_type"]}, (const GDNativeTypePtr)&opaque, (const GDNativeTypePtr){arg_name});'
|
|
|
+ )
|
|
|
+ result.append("}")
|
|
|
+ else:
|
|
|
+ result.append(
|
|
|
+ f'{correct_type(operator["return_type"])} {class_name}::operator{operator["name"].replace("unary", "")}() const {{'
|
|
|
+ )
|
|
|
+ result.append(
|
|
|
+ f'\treturn internal::_call_builtin_operator_ptr<{correct_type(operator["return_type"])}>(_method_bindings.operator_{get_operator_id_name(operator["name"])}, (const GDNativeTypePtr)&opaque, (const GDNativeTypePtr)nullptr);'
|
|
|
+ )
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ # Copy assignment.
|
|
|
+ if copy_constructor_index >= 0:
|
|
|
+ result.append(f"{class_name} &{class_name}::operator=(const {class_name} &other) {{")
|
|
|
+ if builtin_api["has_destructor"]:
|
|
|
+ result.append("\t_method_bindings.destructor(&opaque);")
|
|
|
+ (encode, arg_name) = get_encoded_arg(
|
|
|
+ "other",
|
|
|
+ class_name,
|
|
|
+ None,
|
|
|
+ )
|
|
|
+ result += encode
|
|
|
+ result.append(
|
|
|
+ f"\tinternal::_call_builtin_constructor(_method_bindings.constructor_{copy_constructor_index}, &opaque, {arg_name});"
|
|
|
+ )
|
|
|
+ result.append("\treturn *this;")
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ # Move assignment.
|
|
|
+ result.append(f"{class_name} &{class_name}::operator=({class_name} &&other) {{")
|
|
|
+ result.append("\tstd::swap(opaque, other.opaque);")
|
|
|
+ result.append("\treturn *this;")
|
|
|
+ result.append("}")
|
|
|
+
|
|
|
+ result.append("")
|
|
|
+ result.append("} //namespace godot")
|
|
|
+
|
|
|
+ return "\n".join(result)
|
|
|
+
|
|
|
+
|
|
|
+def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
|
|
|
+ global engine_classes
|
|
|
+ global singletons
|
|
|
+
|
|
|
+ include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "classes"
|
|
|
+ source_gen_folder = Path(output_dir) / "src" / "classes"
|
|
|
+
|
|
|
+ include_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
+ source_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
+
|
|
|
+ # First create map of classes and singletons.
|
|
|
+ for class_api in api["classes"]:
|
|
|
+ # TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
|
|
|
+ if class_api["name"] == "ClassDB":
|
|
|
continue
|
|
|
+ engine_classes[class_api["name"]] = class_api["is_refcounted"]
|
|
|
+ for native_struct in api["native_structures"]:
|
|
|
+ engine_classes[native_struct["name"]] = False
|
|
|
+ for singleton in api["singletons"]:
|
|
|
+ singletons.append(singleton["name"])
|
|
|
+
|
|
|
+ for class_api in api["classes"]:
|
|
|
+ # TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
|
|
|
+ if class_api["name"] == "ClassDB":
|
|
|
+ continue
|
|
|
+ # Check used classes for header include.
|
|
|
+ used_classes = set()
|
|
|
+ fully_used_classes = set()
|
|
|
+
|
|
|
+ class_name = class_api["name"]
|
|
|
+
|
|
|
+ header_filename = include_gen_folder / (camel_to_snake(class_api["name"]) + ".hpp")
|
|
|
+ source_filename = source_gen_folder / (camel_to_snake(class_api["name"]) + ".cpp")
|
|
|
+
|
|
|
+ if "methods" in class_api:
|
|
|
+ for method in class_api["methods"]:
|
|
|
+ if "arguments" in method:
|
|
|
+ for argument in method["arguments"]:
|
|
|
+ type_name = argument["type"]
|
|
|
+ if type_name.endswith("*"):
|
|
|
+ type_name = type_name[:-1]
|
|
|
+ if is_included(type_name, class_name):
|
|
|
+ if is_enum(type_name):
|
|
|
+ fully_used_classes.add(get_enum_class(type_name))
|
|
|
+ elif "default_value" in argument:
|
|
|
+ fully_used_classes.add(type_name)
|
|
|
+ else:
|
|
|
+ used_classes.add(type_name)
|
|
|
+ if is_refcounted(type_name):
|
|
|
+ fully_used_classes.add("Ref")
|
|
|
+ if "return_value" in method:
|
|
|
+ type_name = method["return_value"]["type"]
|
|
|
+ if type_name.endswith("*"):
|
|
|
+ type_name = type_name[:-1]
|
|
|
+ if is_included(type_name, class_name):
|
|
|
+ if is_enum(type_name):
|
|
|
+ fully_used_classes.add(get_enum_class(type_name))
|
|
|
+ elif is_variant(type_name):
|
|
|
+ fully_used_classes.add(type_name)
|
|
|
+ else:
|
|
|
+ used_classes.add(type_name)
|
|
|
+ if is_refcounted(type_name):
|
|
|
+ fully_used_classes.add("Ref")
|
|
|
+
|
|
|
+ if "members" in class_api:
|
|
|
+ for member in class_api["members"]:
|
|
|
+ if is_included(member["type"], class_name):
|
|
|
+ if is_enum(member["type"]):
|
|
|
+ fully_used_classes.add(get_enum_class(member["type"]))
|
|
|
+ else:
|
|
|
+ used_classes.add(member["type"])
|
|
|
+ if is_refcounted(member["type"]):
|
|
|
+ fully_used_classes.add("Ref")
|
|
|
+
|
|
|
+ if "inherits" in class_api:
|
|
|
+ if is_included(class_api["inherits"], class_name):
|
|
|
+ fully_used_classes.add(class_api["inherits"])
|
|
|
+ if is_refcounted(class_api["name"]):
|
|
|
+ fully_used_classes.add("Ref")
|
|
|
else:
|
|
|
- source.append("#include \"" + strip_name(used_class) + ".hpp\"")
|
|
|
-
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- source.append("namespace godot {")
|
|
|
-
|
|
|
-
|
|
|
- core_object_name = "this"
|
|
|
-
|
|
|
-
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- if c["singleton"]:
|
|
|
- source.append("" + class_name + " *" + class_name + "::_singleton = NULL;")
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- # FIXME Test if inlining has a huge impact on binary size
|
|
|
- source.append(class_name + "::" + class_name + "() {")
|
|
|
- source.append("\t_owner = godot::api->godot_global_get_singleton((char *) \"" + strip_name(c["name"]) + "\");")
|
|
|
- source.append("}")
|
|
|
-
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- # Method table initialization
|
|
|
- source.append(class_name + "::___method_bindings " + class_name + "::___mb = {};")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- source.append("void *" + class_name + "::_detail_class_tag = nullptr;")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- source.append("void " + class_name + "::___init_method_bindings() {")
|
|
|
+ fully_used_classes.add("Wrapped")
|
|
|
|
|
|
- for method in c["methods"]:
|
|
|
- source.append("\t___mb.mb_" + method["name"] + " = godot::api->godot_method_bind_get_method(\"" + c["name"] + "\", \"" + ("get_node" if use_template_get_node and method["name"] == "get_node_internal" else method["name"]) + "\");")
|
|
|
+ for type_name in fully_used_classes:
|
|
|
+ if type_name in used_classes:
|
|
|
+ used_classes.remove(type_name)
|
|
|
|
|
|
- source.append("\tgodot_string_name class_name;")
|
|
|
- source.append("\tgodot::api->godot_string_name_new_data(&class_name, \"" + c["name"] + "\");")
|
|
|
- source.append("\t_detail_class_tag = godot::core_1_2_api->godot_get_class_tag(&class_name);")
|
|
|
- source.append("\tgodot::api->godot_string_name_destroy(&class_name);")
|
|
|
-
|
|
|
- source.append("}")
|
|
|
- source.append("")
|
|
|
+ with header_filename.open("w+") as header_file:
|
|
|
+ header_file.write(
|
|
|
+ generate_engine_class_header(class_api, used_classes, fully_used_classes, use_template_get_node)
|
|
|
+ )
|
|
|
|
|
|
- if c["instanciable"]:
|
|
|
- source.append(class_name + " *" + strip_name(c["name"]) + "::_new()")
|
|
|
- source.append("{")
|
|
|
- source.append("\treturn (" + class_name + " *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, godot::api->godot_get_class_constructor((char *)\"" + c["name"] + "\")());")
|
|
|
- source.append("}")
|
|
|
+ with source_filename.open("w+") as source_file:
|
|
|
+ source_file.write(
|
|
|
+ generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node)
|
|
|
+ )
|
|
|
|
|
|
- for method in c["methods"]:
|
|
|
- method_signature = ""
|
|
|
+ for native_struct in api["native_structures"]:
|
|
|
+ struct_name = native_struct["name"]
|
|
|
+ snake_struct_name = camel_to_snake(struct_name)
|
|
|
|
|
|
- method_signature += make_gdnative_type(method["return_type"], ref_allowed)
|
|
|
- method_signature += strip_name(c["name"]) + "::" + escape_cpp(method["name"]) + "("
|
|
|
+ header_filename = include_gen_folder / (snake_struct_name + ".hpp")
|
|
|
|
|
|
- for i, argument in enumerate(method["arguments"]):
|
|
|
- method_signature += "const " + make_gdnative_type(argument["type"], ref_allowed)
|
|
|
- method_signature += escape_cpp(argument["name"])
|
|
|
+ result = []
|
|
|
+ add_header(f"{snake_struct_name}.hpp", result)
|
|
|
|
|
|
- if i != len(method["arguments"]) - 1:
|
|
|
- method_signature += ", "
|
|
|
+ header_guard = f"GODOT_CPP_{snake_struct_name.upper()}_HPP"
|
|
|
+ result.append(f"#ifndef {header_guard}")
|
|
|
+ result.append(f"#define {header_guard}")
|
|
|
|
|
|
- if method["has_varargs"]:
|
|
|
- if len(method["arguments"]) > 0:
|
|
|
- method_signature += ", "
|
|
|
- method_signature += "const Array& __var_args"
|
|
|
+ result.append("")
|
|
|
+ result.append("namespace godot {")
|
|
|
+ result.append("")
|
|
|
|
|
|
- method_signature += ")" + (" const" if method["is_const"] else "")
|
|
|
+ result.append(f"struct {struct_name} {{")
|
|
|
+ for field in native_struct["format"].split(","):
|
|
|
+ result.append(f"\t{field};")
|
|
|
+ result.append("};")
|
|
|
|
|
|
- source.append(method_signature + " {")
|
|
|
+ result.append("")
|
|
|
+ result.append("} // namespace godot")
|
|
|
+ result.append("")
|
|
|
+ result.append(f"#endif // ! {header_guard}")
|
|
|
|
|
|
+ with header_filename.open("w+") as header_file:
|
|
|
+ header_file.write("\n".join(result))
|
|
|
|
|
|
- if method["name"] == "free":
|
|
|
- # dirty hack because Object::free is marked virtual but doesn't actually exist...
|
|
|
- source.append("\tgodot::api->godot_object_destroy(_owner);")
|
|
|
- source.append("}")
|
|
|
- source.append("")
|
|
|
- continue
|
|
|
|
|
|
- return_statement = ""
|
|
|
- return_type_is_ref = is_reference_type(method["return_type"]) and ref_allowed
|
|
|
+def generate_engine_class_header(class_api, used_classes, fully_used_classes, use_template_get_node):
|
|
|
+ global singletons
|
|
|
+ result = []
|
|
|
|
|
|
- if method["return_type"] != "void":
|
|
|
- if is_class_type(method["return_type"]):
|
|
|
- if is_enum(method["return_type"]):
|
|
|
- return_statement += "return (" + remove_enum_prefix(method["return_type"]) + ") "
|
|
|
- elif return_type_is_ref:
|
|
|
- return_statement += "return Ref<" + strip_name(method["return_type"]) + ">::__internal_constructor("
|
|
|
- else:
|
|
|
- return_statement += "return " + ("(" + strip_name(method["return_type"]) + " *) " if is_class_type(method["return_type"]) else "")
|
|
|
+ class_name = class_api["name"]
|
|
|
+ snake_class_name = camel_to_snake(class_name).upper()
|
|
|
+ is_singleton = class_name in singletons
|
|
|
+
|
|
|
+ add_header(f"{snake_class_name.lower()}.hpp", result)
|
|
|
+
|
|
|
+ header_guard = f"GODOT_CPP_{snake_class_name}_HPP"
|
|
|
+
|
|
|
+ result.append(f"#ifndef {header_guard}")
|
|
|
+ result.append(f"#define {header_guard}")
|
|
|
+
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ for included in fully_used_classes:
|
|
|
+ result.append(f"#include <godot_cpp/{get_include_path(included)}>")
|
|
|
+
|
|
|
+ if len(fully_used_classes) > 0:
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if class_name != "Object":
|
|
|
+ result.append("#include <godot_cpp/core/class_db.hpp>")
|
|
|
+ result.append("")
|
|
|
+ result.append("#include <type_traits>")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ result.append("namespace godot {")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ for type_name in used_classes:
|
|
|
+ result.append(f"class {type_name};")
|
|
|
+
|
|
|
+ if len(used_classes) > 0:
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ inherits = class_api["inherits"] if "inherits" in class_api else "Wrapped"
|
|
|
+ result.append(f"class {class_name} : public {inherits} {{")
|
|
|
+
|
|
|
+ result.append(f"\tGDNATIVE_CLASS({class_name}, {inherits})")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ result.append("public:")
|
|
|
+
|
|
|
+ # Constructor override, since parent Wrapped has protected constructor.
|
|
|
+ result.append(f"\t{class_name}() = default;")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if "enums" in class_api:
|
|
|
+ for enum_api in class_api["enums"]:
|
|
|
+ result.append(f'\tenum {enum_api["name"]} {{')
|
|
|
+ for value in enum_api["values"]:
|
|
|
+ result.append(f'\t\t{value["name"]} = {value["value"]},')
|
|
|
+ result.append("\t};")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if is_singleton:
|
|
|
+ result.append(f"\tstatic {class_name} *get_singleton();")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if "methods" in class_api:
|
|
|
+ for method in class_api["methods"]:
|
|
|
+ if method["is_virtual"]:
|
|
|
+ # Will be done later.
|
|
|
+ continue
|
|
|
+
|
|
|
+ vararg = "is_vararg" in method and method["is_vararg"]
|
|
|
+
|
|
|
+ method_signature = "\t"
|
|
|
+ if vararg:
|
|
|
+ method_signature += "private: "
|
|
|
+ method_signature += make_signature(
|
|
|
+ class_name, method, for_header=True, use_template_get_node=use_template_get_node
|
|
|
+ )
|
|
|
+ result.append(method_signature + ";")
|
|
|
+
|
|
|
+ if vararg:
|
|
|
+ # Add templated version.
|
|
|
+ result += make_varargs_template(method)
|
|
|
+
|
|
|
+ # Virtuals now.
|
|
|
+ for method in class_api["methods"]:
|
|
|
+ if not method["is_virtual"]:
|
|
|
+ continue
|
|
|
+
|
|
|
+ method_signature = "\t"
|
|
|
+ method_signature += make_signature(
|
|
|
+ class_name, method, for_header=True, use_template_get_node=use_template_get_node
|
|
|
+ )
|
|
|
+ result.append(method_signature + ";")
|
|
|
+
|
|
|
+ result.append("protected:")
|
|
|
+ result.append("\ttemplate <class T>")
|
|
|
+ result.append("\tstatic void register_virtuals() {")
|
|
|
+ if class_name != "Object":
|
|
|
+ result.append(f"\t\t{inherits}::register_virtuals<T>();")
|
|
|
+ if "methods" in class_api:
|
|
|
+ for method in class_api["methods"]:
|
|
|
+ if not method["is_virtual"]:
|
|
|
+ continue
|
|
|
+ method_name = escape_identifier(method["name"])
|
|
|
+ result.append(
|
|
|
+ f"\t\tif constexpr (!std::is_same_v<decltype(&{class_name}::{method_name}),decltype(&T::{method_name})>) {{"
|
|
|
+ )
|
|
|
+ result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name});")
|
|
|
+ result.append("\t\t}")
|
|
|
+
|
|
|
+ result.append("\t}")
|
|
|
+ result.append("")
|
|
|
+ result.append("public:")
|
|
|
+
|
|
|
+ # Special cases.
|
|
|
+ if class_name == "Object":
|
|
|
+ result.append("")
|
|
|
+ result.append("\ttemplate<class T>")
|
|
|
+ result.append("\tstatic T *cast_to(Object *p_object);")
|
|
|
+
|
|
|
+ result.append("\tvirtual ~Object() {};")
|
|
|
+ elif use_template_get_node and class_name == "Node":
|
|
|
+ result.append("\ttemplate<class T>")
|
|
|
+ result.append(
|
|
|
+ "\tT *get_node(const NodePath &p_path) const { return Object::cast_to<T>(get_node_internal(p_path)); }"
|
|
|
+ )
|
|
|
+
|
|
|
+ result.append("")
|
|
|
+ result.append("};")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ result.append("} // namespace godot")
|
|
|
+
|
|
|
+ result.append(f"#endif // ! {header_guard}")
|
|
|
+
|
|
|
+ return "\n".join(result)
|
|
|
+
|
|
|
+
|
|
|
+def generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node):
|
|
|
+ global singletons
|
|
|
+ result = []
|
|
|
+
|
|
|
+ class_name = class_api["name"]
|
|
|
+ snake_class_name = camel_to_snake(class_name)
|
|
|
+ inherits = class_api["inherits"] if "inherits" in class_api else "Wrapped"
|
|
|
+ is_singleton = class_name in singletons
|
|
|
+
|
|
|
+ add_header(f"{snake_class_name}.cpp", result)
|
|
|
+
|
|
|
+ result.append(f"#include <godot_cpp/classes/{snake_class_name}.hpp>")
|
|
|
+ result.append("")
|
|
|
+ result.append(f"#include <godot_cpp/core/engine_ptrcall.hpp>")
|
|
|
+ result.append(f"#include <godot_cpp/core/error_macros.hpp>")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ for included in used_classes:
|
|
|
+ result.append(f"#include <godot_cpp/{get_include_path(included)}>")
|
|
|
+
|
|
|
+ if len(used_classes) > 0:
|
|
|
+ result.append(f"")
|
|
|
+
|
|
|
+ result.append("namespace godot {")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if is_singleton:
|
|
|
+ result.append(f"{class_name} *{class_name}::get_singleton() {{")
|
|
|
+ result.append(
|
|
|
+ f'\tstatic GDNativeObjectPtr singleton_obj = internal::interface->global_get_singleton("{class_name}");'
|
|
|
+ )
|
|
|
+ result.append("#ifdef DEBUG_ENABLED")
|
|
|
+ result.append("\tERR_FAIL_COND_V(singleton_obj == nullptr, nullptr);")
|
|
|
+ result.append("#endif // DEBUG_ENABLED")
|
|
|
+ result.append(
|
|
|
+ f"\tstatic {class_name} *singleton = reinterpret_cast<{class_name} *>(internal::interface->object_get_instance_binding(singleton_obj, internal::token, &{class_name}::___binding_callbacks));"
|
|
|
+ )
|
|
|
+ result.append("\treturn singleton;")
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ if "methods" in class_api:
|
|
|
+ for method in class_api["methods"]:
|
|
|
+ if method["is_virtual"]:
|
|
|
+ # Will be done later
|
|
|
+ continue
|
|
|
+
|
|
|
+ vararg = "is_vararg" in method and method["is_vararg"]
|
|
|
+
|
|
|
+ # Method signature.
|
|
|
+ method_signature = make_signature(class_name, method, use_template_get_node=use_template_get_node)
|
|
|
+ result.append(method_signature + " {")
|
|
|
+
|
|
|
+ # Method body.
|
|
|
+ result.append(
|
|
|
+ f'\tstatic GDNativeMethodBindPtr ___method_bind = internal::interface->classdb_get_method_bind("{class_name}", "{method["name"]}", {method["hash"]});'
|
|
|
+ )
|
|
|
+ method_call = "\t"
|
|
|
+ has_return = "return_value" in method and method["return_value"]["type"] != "void"
|
|
|
+
|
|
|
+ if has_return:
|
|
|
+ result.append(
|
|
|
+ f'\tCHECK_METHOD_BIND_RET(___method_bind, {get_default_value_for_type(method["return_value"]["type"])});'
|
|
|
+ )
|
|
|
else:
|
|
|
- return_statement += "return "
|
|
|
-
|
|
|
- def get_icall_type_name(name):
|
|
|
- if is_enum(name):
|
|
|
- return "int"
|
|
|
- if is_class_type(name):
|
|
|
- return "Object"
|
|
|
- return name
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- if method["has_varargs"]:
|
|
|
-
|
|
|
- if len(method["arguments"]) != 0:
|
|
|
- source.append("\tVariant __given_args[" + str(len(method["arguments"])) + "];")
|
|
|
-
|
|
|
- for i, argument in enumerate(method["arguments"]):
|
|
|
- source.append("\tgodot::api->godot_variant_new_nil((godot_variant *) &__given_args[" + str(i) + "]);")
|
|
|
-
|
|
|
- source.append("")
|
|
|
-
|
|
|
-
|
|
|
- for i, argument in enumerate(method["arguments"]):
|
|
|
- source.append("\t__given_args[" + str(i) + "] = " + escape_cpp(argument["name"]) + ";")
|
|
|
-
|
|
|
- source.append("")
|
|
|
-
|
|
|
- size = ""
|
|
|
- if method["has_varargs"]:
|
|
|
- size = "(__var_args.size() + " + str(len(method["arguments"])) + ")"
|
|
|
+ result.append(f"\tCHECK_METHOD_BIND(___method_bind);")
|
|
|
+
|
|
|
+ is_ref = False
|
|
|
+ if not vararg:
|
|
|
+ if has_return:
|
|
|
+ return_type = method["return_value"]["type"]
|
|
|
+ meta_type = method["return_value"]["meta"] if "meta" in method["return_value"] else None
|
|
|
+ if is_pod_type(return_type) or is_variant(return_type) or is_enum(return_type):
|
|
|
+ method_call += f"return internal::_call_native_mb_ret<{correct_type(return_type, meta_type)}>(___method_bind, _owner"
|
|
|
+ elif is_refcounted(return_type):
|
|
|
+ method_call += f"return Ref<{return_type}>::___internal_constructor(internal::_call_native_mb_ret_obj<{return_type}>(___method_bind, _owner"
|
|
|
+ is_ref = True
|
|
|
+ else:
|
|
|
+ method_call += f"return internal::_call_native_mb_ret_obj<{return_type}>(___method_bind, _owner"
|
|
|
+ else:
|
|
|
+ method_call += f"internal::_call_native_mb_no_ret(___method_bind, _owner"
|
|
|
+
|
|
|
+ if "arguments" in method:
|
|
|
+ method_call += ", "
|
|
|
+ arguments = []
|
|
|
+ for argument in method["arguments"]:
|
|
|
+ (encode, arg_name) = get_encoded_arg(
|
|
|
+ argument["name"],
|
|
|
+ argument["type"],
|
|
|
+ argument["meta"] if "meta" in argument else None,
|
|
|
+ )
|
|
|
+ result += encode
|
|
|
+ arguments.append(arg_name)
|
|
|
+ method_call += ", ".join(arguments)
|
|
|
+ else: # vararg.
|
|
|
+ result.append("\tGDNativeCallError error;")
|
|
|
+ result.append("\tVariant ret;")
|
|
|
+ method_call += "internal::interface->object_method_bind_call(___method_bind, _owner, (const GDNativeVariantPtr *)args, arg_count, &ret, &error"
|
|
|
+
|
|
|
+ if is_ref:
|
|
|
+ method_call += ")" # Close Ref<> constructor.
|
|
|
+ method_call += ");"
|
|
|
+ result.append(method_call)
|
|
|
+
|
|
|
+ if vararg and ("return_value" in method and method["return_value"]["type"] != "void"):
|
|
|
+ result.append("\treturn ret;")
|
|
|
+
|
|
|
+ result.append("}")
|
|
|
+ result.append("")
|
|
|
+
|
|
|
+ # Virtuals now.
|
|
|
+ for method in class_api["methods"]:
|
|
|
+ if not method["is_virtual"]:
|
|
|
+ continue
|
|
|
+
|
|
|
+ method_signature = make_signature(class_name, method, use_template_get_node=use_template_get_node)
|
|
|
+ method_signature += " {"
|
|
|
+ if "return_value" in method and correct_type(method["return_value"]["type"]) != "void":
|
|
|
+ result.append(method_signature)
|
|
|
+ result.append(f'\treturn {get_default_value_for_type(method["return_value"]["type"])};')
|
|
|
+ result.append("}")
|
|
|
else:
|
|
|
- size = "(" + str(len(method["arguments"])) + ")"
|
|
|
-
|
|
|
- source.append("\tgodot_variant **__args = (godot_variant **) alloca(sizeof(godot_variant *) * " + size + ");")
|
|
|
-
|
|
|
- source.append("")
|
|
|
-
|
|
|
- for i, argument in enumerate(method["arguments"]):
|
|
|
- source.append("\t__args[" + str(i) + "] = (godot_variant *) &__given_args[" + str(i) + "];")
|
|
|
+ method_signature += "}"
|
|
|
+ result.append(method_signature)
|
|
|
+ result.append("")
|
|
|
|
|
|
- source.append("")
|
|
|
+ result.append("")
|
|
|
+ result.append("} // namespace godot ")
|
|
|
|
|
|
- if method["has_varargs"]:
|
|
|
- source.append("\tfor (int i = 0; i < __var_args.size(); i++) {")
|
|
|
- source.append("\t\t__args[i + " + str(len(method["arguments"])) + "] = (godot_variant *) &((Array &) __var_args)[i];")
|
|
|
- source.append("\t}")
|
|
|
+ return "\n".join(result)
|
|
|
|
|
|
- source.append("")
|
|
|
|
|
|
- source.append("\tVariant __result;")
|
|
|
- source.append("\t*(godot_variant *) &__result = godot::api->godot_method_bind_call(___mb.mb_" + method["name"] + ", ((const Object *) " + core_object_name + ")->_owner, (const godot_variant **) __args, " + size + ", nullptr);")
|
|
|
+def generate_global_constants(api, output_dir):
|
|
|
+ include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "classes"
|
|
|
+ source_gen_folder = Path(output_dir) / "src" / "classes"
|
|
|
|
|
|
- source.append("")
|
|
|
+ include_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
+ source_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
- if is_class_type(method["return_type"]):
|
|
|
- source.append("\tObject *obj = Object::___get_from_variant(__result);")
|
|
|
- source.append("\tif (obj->has_method(\"reference\"))")
|
|
|
- source.append("\t\tobj->callv(\"reference\", Array());")
|
|
|
+ # Generate header
|
|
|
|
|
|
- source.append("")
|
|
|
+ header = []
|
|
|
+ add_header("global_constants.hpp", header)
|
|
|
|
|
|
+ header_filename = include_gen_folder / "global_constants.hpp"
|
|
|
|
|
|
- for i, argument in enumerate(method["arguments"]):
|
|
|
- source.append("\tgodot::api->godot_variant_destroy((godot_variant *) &__given_args[" + str(i) + "]);")
|
|
|
+ header_guard = "GODOT_CPP_GLOBAL_CONSTANTS_HPP"
|
|
|
+ header.append(f"#ifndef {header_guard}")
|
|
|
+ header.append(f"#define {header_guard}")
|
|
|
+ header.append("")
|
|
|
+ header.append("namespace godot {")
|
|
|
+ header.append("")
|
|
|
|
|
|
- source.append("")
|
|
|
+ for constant in api["global_constants"]:
|
|
|
+ header.append(f'\tconst int {escape_identifier(constant["name"])} = {constant["value"]};')
|
|
|
|
|
|
- if method["return_type"] != "void":
|
|
|
- cast = ""
|
|
|
- if is_class_type(method["return_type"]):
|
|
|
- if return_type_is_ref:
|
|
|
- cast += "Ref<" + strip_name(method["return_type"]) + ">::__internal_constructor(__result);"
|
|
|
- else:
|
|
|
- cast += "(" + strip_name(method["return_type"]) + " *) " + strip_name(method["return_type"] + "::___get_from_variant(") + "__result);"
|
|
|
- else:
|
|
|
- cast += "__result;"
|
|
|
- source.append("\treturn " + cast)
|
|
|
-
|
|
|
-
|
|
|
- else:
|
|
|
-
|
|
|
- args = []
|
|
|
- for arg in method["arguments"]:
|
|
|
- args.append(get_icall_type_name(arg["type"]))
|
|
|
-
|
|
|
- icall_ret_type = get_icall_type_name(method["return_type"])
|
|
|
+ header.append("")
|
|
|
|
|
|
- icall_sig = tuple((icall_ret_type, tuple(args)))
|
|
|
-
|
|
|
- icalls.add(icall_sig)
|
|
|
+ for enum_def in api["global_enums"]:
|
|
|
+ if enum_def["name"].startswith("Variant."):
|
|
|
+ continue
|
|
|
|
|
|
- icall_name = get_icall_name(icall_sig)
|
|
|
+ header.append(f'\tenum {enum_def["name"]} {{')
|
|
|
+ for value in enum_def["values"]:
|
|
|
+ header.append(f'\t\t{value["name"]} = {value["value"]},')
|
|
|
+ header.append("\t};")
|
|
|
+ header.append("")
|
|
|
|
|
|
- return_statement += icall_name + "(___mb.mb_" + method["name"] + ", (const Object *) " + core_object_name
|
|
|
+ header.append("} // namespace godot")
|
|
|
|
|
|
- for arg in method["arguments"]:
|
|
|
- arg_is_ref = is_reference_type(arg["type"]) and ref_allowed
|
|
|
- return_statement += ", " + escape_cpp(arg["name"]) + (".ptr()" if arg_is_ref else "")
|
|
|
+ header.append("")
|
|
|
+ header.append(f"#endif // ! {header_guard}")
|
|
|
|
|
|
- return_statement += ")"
|
|
|
+ with header_filename.open("w+") as header_file:
|
|
|
+ header_file.write("\n".join(header))
|
|
|
|
|
|
- if return_type_is_ref:
|
|
|
- return_statement += ")"
|
|
|
|
|
|
- source.append("\t" + return_statement + ";")
|
|
|
+def generate_utility_functions(api, output_dir):
|
|
|
+ include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "variant"
|
|
|
+ source_gen_folder = Path(output_dir) / "src" / "variant"
|
|
|
|
|
|
- source.append("}")
|
|
|
- source.append("")
|
|
|
+ include_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
+ source_gen_folder.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
+ # Generate header.
|
|
|
|
|
|
+ header = []
|
|
|
+ add_header("utility_functions.hpp", header)
|
|
|
|
|
|
- source.append("}")
|
|
|
+ header_filename = include_gen_folder / "utility_functions.hpp"
|
|
|
|
|
|
+ header_guard = "GODOT_CPP_UTILITY_FUNCTIONS_HPP"
|
|
|
+ header.append(f"#ifndef {header_guard}")
|
|
|
+ header.append(f"#define {header_guard}")
|
|
|
+ header.append("")
|
|
|
+ header.append("#include <godot_cpp/variant/builtin_types.hpp>")
|
|
|
+ header.append("#include <godot_cpp/variant/variant.hpp>")
|
|
|
+ header.append("")
|
|
|
+ header.append("#include <array>")
|
|
|
+ header.append("")
|
|
|
+ header.append("namespace godot {")
|
|
|
+ header.append("")
|
|
|
+ header.append("class UtilityFunctions {")
|
|
|
+ header.append("public:")
|
|
|
|
|
|
- return "\n".join(source)
|
|
|
+ for function in api["utility_functions"]:
|
|
|
+ vararg = "is_vararg" in function and function["is_vararg"]
|
|
|
|
|
|
+ function_signature = "\t"
|
|
|
+ function_signature += make_signature("UtilityFunctions", function, for_header=True, static=True)
|
|
|
+ header.append(function_signature + ";")
|
|
|
|
|
|
+ if vararg:
|
|
|
+ # Add templated version.
|
|
|
+ header += make_varargs_template(function, static=True)
|
|
|
|
|
|
+ header.append("};")
|
|
|
+ header.append("")
|
|
|
+ header.append("} // namespace godot")
|
|
|
+ header.append("")
|
|
|
+ header.append(f"#endif // ! {header_guard}")
|
|
|
|
|
|
+ with header_filename.open("w+") as header_file:
|
|
|
+ header_file.write("\n".join(header))
|
|
|
|
|
|
-def generate_icall_header(icalls):
|
|
|
+ # Generate source.
|
|
|
|
|
|
source = []
|
|
|
- source.append("#ifndef GODOT_CPP__ICALLS_HPP")
|
|
|
- source.append("#define GODOT_CPP__ICALLS_HPP")
|
|
|
-
|
|
|
- source.append("")
|
|
|
+ add_header("utility_functions.cpp", source)
|
|
|
+ source_filename = source_gen_folder / "utility_functions.cpp"
|
|
|
|
|
|
- source.append("#include <gdnative_api_struct.gen.h>")
|
|
|
- source.append("#include <stdint.h>")
|
|
|
- source.append("")
|
|
|
-
|
|
|
- source.append("#include <core/GodotGlobal.hpp>")
|
|
|
- source.append("#include <core/CoreTypes.hpp>")
|
|
|
- source.append("#include \"Object.hpp\"")
|
|
|
+ source.append("#include <godot_cpp/variant/utility_functions.hpp>")
|
|
|
source.append("")
|
|
|
+ source.append("#include <godot_cpp/core/error_macros.hpp>")
|
|
|
+ source.append("#include <godot_cpp/core/engine_ptrcall.hpp>")
|
|
|
source.append("")
|
|
|
-
|
|
|
source.append("namespace godot {")
|
|
|
source.append("")
|
|
|
|
|
|
- for icall in icalls:
|
|
|
- ret_type = icall[0]
|
|
|
- args = icall[1]
|
|
|
-
|
|
|
- method_signature = "static inline "
|
|
|
+ for function in api["utility_functions"]:
|
|
|
+ vararg = "is_vararg" in function and function["is_vararg"]
|
|
|
|
|
|
- method_signature += get_icall_return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, const Object *inst"
|
|
|
+ function_signature = make_signature("UtilityFunctions", function)
|
|
|
+ source.append(function_signature + " {")
|
|
|
|
|
|
- for i, arg in enumerate(args):
|
|
|
- method_signature += ", const "
|
|
|
+ # Function body.
|
|
|
|
|
|
- if is_core_type(arg):
|
|
|
- method_signature += arg + "&"
|
|
|
- elif arg == "int":
|
|
|
- method_signature += "int64_t "
|
|
|
- elif arg == "float":
|
|
|
- method_signature += "double "
|
|
|
- elif is_primitive(arg):
|
|
|
- method_signature += arg + " "
|
|
|
+ source.append(
|
|
|
+ f'\tstatic GDNativePtrUtilityFunction ___function = internal::interface->variant_get_ptr_utility_function("{function["name"]}", {function["hash"]});'
|
|
|
+ )
|
|
|
+ has_return = "return_type" in function and function["return_type"] != "void"
|
|
|
+ if has_return:
|
|
|
+ source.append(
|
|
|
+ f'\tCHECK_METHOD_BIND_RET(___function, {get_default_value_for_type(function["return_type"])});'
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ source.append(f"\tCHECK_METHOD_BIND(___function);")
|
|
|
+
|
|
|
+ function_call = "\t"
|
|
|
+ if not vararg:
|
|
|
+ if has_return:
|
|
|
+ function_call += "return "
|
|
|
+ if function["return_type"] == "Object":
|
|
|
+ function_call += "internal::_call_utility_ret_obj(___function"
|
|
|
+ else:
|
|
|
+ function_call += f'internal::_call_utility_ret<{correct_type(function["return_type"])}>(___function'
|
|
|
else:
|
|
|
- method_signature += "Object *"
|
|
|
+ function_call += "internal::_call_utility_no_ret(___function"
|
|
|
+
|
|
|
+ if "arguments" in function:
|
|
|
+ function_call += ", "
|
|
|
+ arguments = []
|
|
|
+ for argument in function["arguments"]:
|
|
|
+ (encode, arg_name) = get_encoded_arg(
|
|
|
+ argument["name"],
|
|
|
+ argument["type"],
|
|
|
+ argument["meta"] if "meta" in argument else None,
|
|
|
+ )
|
|
|
+ source += encode
|
|
|
+ arguments.append(arg_name)
|
|
|
+ function_call += ", ".join(arguments)
|
|
|
+ else:
|
|
|
+ source.append("\tVariant ret;")
|
|
|
+ function_call += "___function(&ret, (const GDNativeVariantPtr *)args, arg_count"
|
|
|
|
|
|
- method_signature += "arg" + str(i)
|
|
|
+ function_call += ");"
|
|
|
+ source.append(function_call)
|
|
|
|
|
|
- method_signature += ")"
|
|
|
+ if vararg and has_return:
|
|
|
+ source.append("\treturn ret;")
|
|
|
|
|
|
- source.append(method_signature + " {")
|
|
|
+ source.append("}")
|
|
|
+ source.append("")
|
|
|
|
|
|
- if ret_type != "void":
|
|
|
- source.append("\t" + ("godot_object *" if is_class_type(ret_type) else get_icall_return_type(ret_type)) + "ret;")
|
|
|
- if is_class_type(ret_type):
|
|
|
- source.append("\tret = nullptr;")
|
|
|
+ source.append("} // namespace godot")
|
|
|
|
|
|
+ with source_filename.open("w+") as source_file:
|
|
|
+ source_file.write("\n".join(source))
|
|
|
|
|
|
- source.append("\tconst void *args[" + ("1" if len(args) == 0 else "") + "] = {")
|
|
|
|
|
|
- for i, arg in enumerate(args):
|
|
|
+# Helper functions.
|
|
|
|
|
|
- wrapped_argument = "\t\t"
|
|
|
- if is_primitive(arg) or is_core_type(arg):
|
|
|
- wrapped_argument += "(void *) &arg" + str(i)
|
|
|
- else:
|
|
|
- wrapped_argument += "(void *) (arg" + str(i) + ") ? arg" + str(i) + "->_owner : nullptr"
|
|
|
|
|
|
- wrapped_argument += ","
|
|
|
- source.append(wrapped_argument)
|
|
|
+def camel_to_snake(name):
|
|
|
+ name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
|
|
|
+ name = re.sub("([a-z0-9])([A-Z])", r"\1_\2", name)
|
|
|
+ return name.replace("2_D", "2D").replace("3_D", "3D").lower()
|
|
|
|
|
|
- source.append("\t};")
|
|
|
- source.append("")
|
|
|
|
|
|
- source.append("\tgodot::api->godot_method_bind_ptrcall(mb, inst->_owner, args, " + ("nullptr" if ret_type == "void" else "&ret") + ");")
|
|
|
+def make_function_parameters(parameters, include_default=False, for_builtin=False, is_vararg=False):
|
|
|
+ signature = []
|
|
|
|
|
|
- if ret_type != "void":
|
|
|
- if is_class_type(ret_type):
|
|
|
- source.append("\tif (ret) {")
|
|
|
- source.append("\t\treturn (Object *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, ret);")
|
|
|
- source.append("\t}")
|
|
|
- source.append("")
|
|
|
- source.append("\treturn (Object *) ret;")
|
|
|
- else:
|
|
|
- source.append("\treturn ret;")
|
|
|
+ for index, par in enumerate(parameters):
|
|
|
+ parameter = type_for_parameter(par["type"], par["meta"] if "meta" in par else None)
|
|
|
+ parameter_name = escape_identifier(par["name"])
|
|
|
+ if len(parameter_name) == 0:
|
|
|
+ parameter_name = "arg_" + str(index + 1)
|
|
|
+ parameter += parameter_name
|
|
|
|
|
|
- source.append("}")
|
|
|
- source.append("")
|
|
|
+ if include_default and "default_value" in par and (not for_builtin or par["type"] != "Variant"):
|
|
|
+ parameter += " = "
|
|
|
+ if is_enum(par["type"]):
|
|
|
+ parameter_type = correct_type(par["type"])
|
|
|
+ if parameter_type == "void":
|
|
|
+ parameter_type = "Variant"
|
|
|
+ parameter += f"({parameter_type})"
|
|
|
+ parameter += correct_default_value(par["default_value"], par["type"])
|
|
|
+ signature.append(parameter)
|
|
|
|
|
|
- source.append("}")
|
|
|
- source.append("")
|
|
|
+ if is_vararg:
|
|
|
+ signature.append("const Args&... args")
|
|
|
|
|
|
- source.append("#endif")
|
|
|
+ return ", ".join(signature)
|
|
|
|
|
|
- return "\n".join(source)
|
|
|
|
|
|
+def type_for_parameter(type_name, meta=None):
|
|
|
+ if type_name == "void":
|
|
|
+ return "Variant "
|
|
|
+ elif is_pod_type(type_name) and type_name != "Nil" or is_enum(type_name):
|
|
|
+ return f"{correct_type(type_name, meta)} "
|
|
|
+ elif is_variant(type_name) or is_refcounted(type_name):
|
|
|
+ return f"const {correct_type(type_name)} &"
|
|
|
+ else:
|
|
|
+ return f"{correct_type(type_name)}"
|
|
|
|
|
|
-def generate_type_registry(classes):
|
|
|
- source = []
|
|
|
|
|
|
- source.append("#include \"TagDB.hpp\"")
|
|
|
- source.append("#include <typeinfo>")
|
|
|
- source.append("\n")
|
|
|
+def get_include_path(type_name):
|
|
|
+ base_dir = ""
|
|
|
+ if type_name == "Object":
|
|
|
+ base_dir = "core"
|
|
|
+ elif is_variant(type_name):
|
|
|
+ base_dir = "variant"
|
|
|
+ else:
|
|
|
+ base_dir = "classes"
|
|
|
|
|
|
- for c in classes:
|
|
|
- source.append("#include <" + strip_name(c["name"]) + ".hpp>")
|
|
|
+ return f"{base_dir}/{camel_to_snake(type_name)}.hpp"
|
|
|
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
|
|
|
- source.append("namespace godot {")
|
|
|
+def get_encoded_arg(arg_name, type_name, type_meta):
|
|
|
+ result = []
|
|
|
|
|
|
- source.append("void ___register_types()")
|
|
|
- source.append("{")
|
|
|
+ name = escape_identifier(arg_name)
|
|
|
+ arg_type = correct_type(type_name)
|
|
|
+ if is_pod_type(arg_type):
|
|
|
+ result.append(f"\t{get_gdnative_type(arg_type)} {name}_encoded;")
|
|
|
+ result.append(f"\tPtrToArg<{correct_type(type_name)}>::encode({name}, &{name}_encoded);")
|
|
|
+ name = f"&{name}_encoded"
|
|
|
+ elif is_engine_class(type_name):
|
|
|
+ name = f"{name}->_owner"
|
|
|
+ else:
|
|
|
+ name = f"&{name}"
|
|
|
|
|
|
- for c in classes:
|
|
|
- class_name = strip_name(c["name"])
|
|
|
- base_class_name = strip_name(c["base_class"])
|
|
|
+ return (result, name)
|
|
|
|
|
|
- class_type_hash = "typeid(" + class_name + ").hash_code()"
|
|
|
|
|
|
- base_class_type_hash = "typeid(" + base_class_name + ").hash_code()"
|
|
|
+def make_signature(
|
|
|
+ class_name, function_data, for_header=False, use_template_get_node=True, for_builtin=False, static=False
|
|
|
+):
|
|
|
+ function_signature = ""
|
|
|
|
|
|
- if base_class_name == "":
|
|
|
- base_class_type_hash = "0"
|
|
|
+ is_vararg = "is_vararg" in function_data and function_data["is_vararg"]
|
|
|
|
|
|
- source.append("\tgodot::_TagDB::register_global_type(\"" + c["name"] + "\", " + class_type_hash + ", " + base_class_type_hash + ");")
|
|
|
+ if for_header:
|
|
|
+ if "is_virtual" in function_data and function_data["is_virtual"]:
|
|
|
+ function_signature += "virtual "
|
|
|
|
|
|
- source.append("}")
|
|
|
+ if is_vararg:
|
|
|
+ function_signature += "private: "
|
|
|
|
|
|
- source.append("")
|
|
|
- source.append("}")
|
|
|
+ if static:
|
|
|
+ function_signature += "static "
|
|
|
|
|
|
+ return_type = "void"
|
|
|
+ return_meta = None
|
|
|
+ if "return_type" in function_data:
|
|
|
+ return_type = correct_type(function_data["return_type"])
|
|
|
+ elif "return_value" in function_data:
|
|
|
+ return_type = function_data["return_value"]["type"]
|
|
|
+ return_meta = function_data["return_value"]["meta"] if "meta" in function_data["return_value"] else None
|
|
|
|
|
|
- return "\n".join(source)
|
|
|
+ function_signature += correct_type(
|
|
|
+ return_type,
|
|
|
+ return_meta,
|
|
|
+ )
|
|
|
|
|
|
+ if not function_signature.endswith("*"):
|
|
|
+ function_signature += " "
|
|
|
|
|
|
-def generate_init_method_bindings(classes):
|
|
|
- source = []
|
|
|
+ if not for_header:
|
|
|
+ function_signature += f"{class_name}::"
|
|
|
|
|
|
- for c in classes:
|
|
|
- source.append("#include <" + strip_name(c["name"]) + ".hpp>")
|
|
|
+ function_signature += escape_identifier(function_data["name"])
|
|
|
|
|
|
- source.append("")
|
|
|
- source.append("")
|
|
|
+ if is_vararg or (
|
|
|
+ not for_builtin and use_template_get_node and class_name == "Node" and function_data["name"] == "get_node"
|
|
|
+ ):
|
|
|
+ function_signature += "_internal"
|
|
|
|
|
|
- source.append("namespace godot {")
|
|
|
+ function_signature += "("
|
|
|
|
|
|
- source.append("void ___init_method_bindings()")
|
|
|
- source.append("{")
|
|
|
+ arguments = function_data["arguments"] if "arguments" in function_data else []
|
|
|
|
|
|
- for c in classes:
|
|
|
- source.append("\t" + strip_name(c["name"]) + "::___init_method_bindings();")
|
|
|
+ if not is_vararg:
|
|
|
+ function_signature += make_function_parameters(arguments, for_header, for_builtin, is_vararg)
|
|
|
+ else:
|
|
|
+ function_signature += "const Variant **args, GDNativeInt arg_count"
|
|
|
|
|
|
- source.append("}")
|
|
|
+ function_signature += ")"
|
|
|
|
|
|
- source.append("")
|
|
|
- source.append("}")
|
|
|
+ if "is_const" in function_data and function_data["is_const"]:
|
|
|
+ function_signature += " const"
|
|
|
|
|
|
- return "\n".join(source)
|
|
|
+ return function_signature
|
|
|
|
|
|
|
|
|
-def get_icall_return_type(t):
|
|
|
- if is_class_type(t):
|
|
|
- return "Object *"
|
|
|
- if t == "int":
|
|
|
- return "int64_t "
|
|
|
- if t == "float" or t == "real":
|
|
|
- return "double "
|
|
|
- return t + " "
|
|
|
+def make_varargs_template(function_data, static=False):
|
|
|
+ result = []
|
|
|
|
|
|
+ function_signature = "\tpublic: template<class... Args> "
|
|
|
|
|
|
-def get_icall_name(sig):
|
|
|
- ret_type = sig[0]
|
|
|
- args = sig[1]
|
|
|
+ if static:
|
|
|
+ function_signature += "static "
|
|
|
|
|
|
- name = "___godot_icall_"
|
|
|
- name += strip_name(ret_type)
|
|
|
- for arg in args:
|
|
|
- name += "_" + strip_name(arg)
|
|
|
+ return_type = "void"
|
|
|
+ return_meta = None
|
|
|
+ if "return_type" in function_data:
|
|
|
+ return_type = correct_type(function_data["return_type"])
|
|
|
+ elif "return_value" in function_data:
|
|
|
+ return_type = function_data["return_value"]["type"]
|
|
|
+ return_meta = function_data["return_value"]["meta"] if "meta" in function_data["return_value"] else None
|
|
|
|
|
|
- return name
|
|
|
+ function_signature += correct_type(
|
|
|
+ return_type,
|
|
|
+ return_meta,
|
|
|
+ )
|
|
|
|
|
|
+ if not function_signature.endswith("*"):
|
|
|
+ function_signature += " "
|
|
|
|
|
|
+ function_signature += f'{escape_identifier(function_data["name"])}'
|
|
|
|
|
|
+ method_arguments = []
|
|
|
+ if "arguments" in function_data:
|
|
|
+ method_arguments = function_data["arguments"]
|
|
|
|
|
|
+ function_signature += "("
|
|
|
|
|
|
-def get_used_classes(c):
|
|
|
- classes = []
|
|
|
- for method in c["methods"]:
|
|
|
- if is_class_type(method["return_type"]) and not (method["return_type"] in classes):
|
|
|
- classes.append(method["return_type"])
|
|
|
+ is_vararg = "is_vararg" in function_data and function_data["is_vararg"]
|
|
|
|
|
|
- for arg in method["arguments"]:
|
|
|
- if is_class_type(arg["type"]) and not (arg["type"] in classes):
|
|
|
- classes.append(arg["type"])
|
|
|
- return classes
|
|
|
+ function_signature += make_function_parameters(method_arguments, include_default=True, is_vararg=is_vararg)
|
|
|
|
|
|
+ function_signature += ")"
|
|
|
|
|
|
+ if "is_const" in function_data and function_data["is_const"]:
|
|
|
+ function_signature += " const"
|
|
|
|
|
|
+ function_signature += " {"
|
|
|
+ result.append(function_signature)
|
|
|
+
|
|
|
+ args_array = f"\t\tstd::array<Variant, {len(method_arguments)} + sizeof...(Args)> variant_args {{ "
|
|
|
+ for argument in method_arguments:
|
|
|
+ if argument["type"] == "Variant":
|
|
|
+ args_array += argument["name"]
|
|
|
+ else:
|
|
|
+ args_array += f'Variant({argument["name"]})'
|
|
|
+ args_array += ", "
|
|
|
+
|
|
|
+ args_array += "Variant(args)... };"
|
|
|
+ result.append(args_array)
|
|
|
+ result.append(f"\t\tstd::array<const Variant *, {len(method_arguments)} + sizeof...(Args)> call_args;")
|
|
|
+ result.append("\t\tfor(size_t i = 0; i < variant_args.size(); i++) {")
|
|
|
+ result.append("\t\t\tcall_args[i] = &variant_args[i];")
|
|
|
+ result.append("\t\t}")
|
|
|
+
|
|
|
+ call_line = "\t\t"
|
|
|
+
|
|
|
+ if return_type != "void":
|
|
|
+ call_line += "return "
|
|
|
+
|
|
|
+ call_line += f'{escape_identifier(function_data["name"])}_internal(call_args.data(), variant_args.size());'
|
|
|
+ result.append(call_line)
|
|
|
+ result.append("\t}")
|
|
|
+
|
|
|
+ return result
|
|
|
+
|
|
|
+
|
|
|
+# Engine idiosyncrasies.
|
|
|
+
|
|
|
+
|
|
|
+def is_pod_type(type_name):
|
|
|
+ """
|
|
|
+ Those are types for which no class should be generated.
|
|
|
+ """
|
|
|
+ return type_name in [
|
|
|
+ "Nil",
|
|
|
+ "void",
|
|
|
+ "int",
|
|
|
+ "float",
|
|
|
+ "bool",
|
|
|
+ "double",
|
|
|
+ "int32_t",
|
|
|
+ "int64_t",
|
|
|
+ "uint32_t",
|
|
|
+ "uint64_t",
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+def is_included_type(type_name):
|
|
|
+ """
|
|
|
+ Those are types for which we already have a class file implemented.
|
|
|
+ """
|
|
|
+ return type_name in [
|
|
|
+ "AABB",
|
|
|
+ "Basis",
|
|
|
+ "Color",
|
|
|
+ "Plane",
|
|
|
+ "Quaternion",
|
|
|
+ "Rect2",
|
|
|
+ "Rect2i",
|
|
|
+ "Transform2D",
|
|
|
+ "Transform3D",
|
|
|
+ "Vector2",
|
|
|
+ "Vector2i",
|
|
|
+ "Vector3",
|
|
|
+ "Vector3i",
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+def is_packed_array(type_name):
|
|
|
+ """
|
|
|
+ Those are types for which we add our extra packed array functions.
|
|
|
+ """
|
|
|
+ return type_name in [
|
|
|
+ "PackedByteArray",
|
|
|
+ "PackedColorArray",
|
|
|
+ "PackedFloat32Array",
|
|
|
+ "PackedFloat64Array",
|
|
|
+ "PackedInt32Array",
|
|
|
+ "PackedInt64Array",
|
|
|
+ "PackedStringArray",
|
|
|
+ "PackedVector2Array",
|
|
|
+ "PackedVector3Array",
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+def is_enum(type_name):
|
|
|
+ return type_name.startswith("enum::")
|
|
|
+
|
|
|
+
|
|
|
+def get_enum_class(enum_name: str):
|
|
|
+ if "." in enum_name:
|
|
|
+ return enum_name.replace("enum::", "").split(".")[0]
|
|
|
+ else:
|
|
|
+ return "GlobalConstants"
|
|
|
|
|
|
|
|
|
-def strip_name(name):
|
|
|
- if len(name) == 0:
|
|
|
- return name
|
|
|
- if name[0] == '_':
|
|
|
- return name[1:]
|
|
|
- return name
|
|
|
+def get_enum_name(enum_name: str):
|
|
|
+ return enum_name.replace("enum::", "").split(".")[-1]
|
|
|
|
|
|
-def extract_nested_type(nested_type):
|
|
|
- return strip_name(nested_type[:nested_type.find("::")])
|
|
|
|
|
|
-def remove_nested_type_prefix(name):
|
|
|
- return name if name.find("::") == -1 else strip_name(name[name.find("::") + 2:])
|
|
|
+def is_variant(type_name):
|
|
|
+ return type_name == "Variant" or type_name in builtin_classes or type_name == "Nil"
|
|
|
|
|
|
-def remove_enum_prefix(name):
|
|
|
- return strip_name(name[name.find("enum.") + 5:])
|
|
|
|
|
|
-def is_nested_type(name, type = ""):
|
|
|
- return name.find(type + "::") != -1
|
|
|
+def is_engine_class(type_name):
|
|
|
+ global engine_classes
|
|
|
+ return type_name == "Object" or type_name in engine_classes
|
|
|
|
|
|
-def is_enum(name):
|
|
|
- return name.find("enum.") == 0
|
|
|
|
|
|
-def is_class_type(name):
|
|
|
- return not is_core_type(name) and not is_primitive(name)
|
|
|
+def is_refcounted(type_name):
|
|
|
+ return type_name in engine_classes and engine_classes[type_name]
|
|
|
|
|
|
-def is_core_type(name):
|
|
|
- core_types = ["Array",
|
|
|
- "Basis",
|
|
|
- "Color",
|
|
|
- "Dictionary",
|
|
|
- "Error",
|
|
|
- "NodePath",
|
|
|
- "Plane",
|
|
|
- "PoolByteArray",
|
|
|
- "PoolIntArray",
|
|
|
- "PoolRealArray",
|
|
|
- "PoolStringArray",
|
|
|
- "PoolVector2Array",
|
|
|
- "PoolVector3Array",
|
|
|
- "PoolColorArray",
|
|
|
- "PoolIntArray",
|
|
|
- "PoolRealArray",
|
|
|
- "Quat",
|
|
|
- "Rect2",
|
|
|
- "AABB",
|
|
|
- "RID",
|
|
|
- "String",
|
|
|
- "Transform",
|
|
|
- "Transform2D",
|
|
|
- "Variant",
|
|
|
- "Vector2",
|
|
|
- "Vector3"]
|
|
|
- return name in core_types
|
|
|
|
|
|
+def is_included(type_name, current_type):
|
|
|
+ """
|
|
|
+ Check if a builtin type should be included.
|
|
|
+ This removes Variant and POD types from inclusion, and the current type.
|
|
|
+ """
|
|
|
+ to_include = get_enum_class(type_name) if is_enum(type_name) else type_name
|
|
|
+ if to_include == current_type or is_pod_type(to_include):
|
|
|
+ return False
|
|
|
+ if to_include == "GlobalConstants" or to_include == "UtilityFunctions":
|
|
|
+ return True
|
|
|
+ return is_engine_class(to_include) or is_variant(to_include)
|
|
|
|
|
|
|
|
|
+def correct_default_value(value, type_name):
|
|
|
+ value_map = {
|
|
|
+ "null": "nullptr",
|
|
|
+ '""': "String()",
|
|
|
+ '&""': "StringName()",
|
|
|
+ "[]": "Array()",
|
|
|
+ "{}": "Dictionary()",
|
|
|
+ "Transform2D(1, 0, 0, 1, 0, 0)": "Transform2D()", # Default transform.
|
|
|
+ "Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)": "Transform3D()", # Default transform.
|
|
|
+ }
|
|
|
+ if value in value_map:
|
|
|
+ return value_map[value]
|
|
|
+ if value == "":
|
|
|
+ return f"{type_name}()"
|
|
|
+ return value
|
|
|
+
|
|
|
+
|
|
|
+def correct_type(type_name, meta=None):
|
|
|
+ type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
|
|
|
+ if meta != None:
|
|
|
+ if "int" in meta:
|
|
|
+ return f"{meta}_t"
|
|
|
+ elif meta in type_conversion:
|
|
|
+ return type_conversion[type_name]
|
|
|
+ else:
|
|
|
+ return meta
|
|
|
+ if type_name in type_conversion:
|
|
|
+ return type_conversion[type_name]
|
|
|
+ if is_enum(type_name):
|
|
|
+ base_class = get_enum_class(type_name)
|
|
|
+ if base_class == "GlobalConstants":
|
|
|
+ return f"{get_enum_name(type_name)}"
|
|
|
+ return f"{base_class}::{get_enum_name(type_name)}"
|
|
|
+ if is_refcounted(type_name):
|
|
|
+ return f"Ref<{type_name}>"
|
|
|
+ if type_name == "Object" or is_engine_class(type_name):
|
|
|
+ return f"{type_name} *"
|
|
|
+ if type_name.endswith("*"):
|
|
|
+ return f"{type_name[:-1]} *"
|
|
|
+ return type_name
|
|
|
+
|
|
|
+
|
|
|
+def get_gdnative_type(type_name):
|
|
|
+ type_conversion_map = {
|
|
|
+ "bool": "uint32_t",
|
|
|
+ "int": "int64_t",
|
|
|
+ "float": "double",
|
|
|
+ }
|
|
|
|
|
|
-def is_primitive(name):
|
|
|
- core_types = ["int", "bool", "real", "float", "void"]
|
|
|
- return name in core_types
|
|
|
-
|
|
|
-def escape_cpp(name):
|
|
|
- escapes = {
|
|
|
- "class": "_class",
|
|
|
- "enum": "_enum",
|
|
|
- "char": "_char",
|
|
|
- "short": "_short",
|
|
|
- "bool": "_bool",
|
|
|
- "int": "_int",
|
|
|
- "default": "_default",
|
|
|
- "case": "_case",
|
|
|
- "switch": "_switch",
|
|
|
- "export": "_export",
|
|
|
+ if type_name in type_conversion_map:
|
|
|
+ return type_conversion_map[type_name]
|
|
|
+ return type_name
|
|
|
+
|
|
|
+
|
|
|
+def escape_identifier(id):
|
|
|
+ cpp_keywords_map = {
|
|
|
+ "class": "_class",
|
|
|
+ "char": "_char",
|
|
|
+ "short": "_short",
|
|
|
+ "bool": "_bool",
|
|
|
+ "int": "_int",
|
|
|
+ "default": "_default",
|
|
|
+ "case": "_case",
|
|
|
+ "switch": "_switch",
|
|
|
+ "export": "_export",
|
|
|
"template": "_template",
|
|
|
- "new": "new_",
|
|
|
+ "new": "new_",
|
|
|
"operator": "_operator",
|
|
|
- "typename": "_typename"
|
|
|
+ "typeof": "type_of",
|
|
|
+ "typename": "type_name",
|
|
|
}
|
|
|
- if name in escapes:
|
|
|
- return escapes[name]
|
|
|
- return name
|
|
|
+ if id in cpp_keywords_map:
|
|
|
+ return cpp_keywords_map[id]
|
|
|
+ return id
|
|
|
+
|
|
|
+
|
|
|
+def get_operator_id_name(op):
|
|
|
+ op_id_map = {
|
|
|
+ "==": "equal",
|
|
|
+ "!=": "not_equal",
|
|
|
+ "<": "less",
|
|
|
+ "<=": "less_equal",
|
|
|
+ ">": "greater",
|
|
|
+ ">=": "greater_equal",
|
|
|
+ "+": "add",
|
|
|
+ "-": "subtract",
|
|
|
+ "*": "multiply",
|
|
|
+ "/": "divide",
|
|
|
+ "unary-": "negate",
|
|
|
+ "unary+": "positive",
|
|
|
+ "%": "module",
|
|
|
+ "<<": "shift_left",
|
|
|
+ ">>": "shift_right",
|
|
|
+ "&": "bit_and",
|
|
|
+ "|": "bit_or",
|
|
|
+ "^": "bit_xor",
|
|
|
+ "~": "bit_negate",
|
|
|
+ "and": "and",
|
|
|
+ "or": "or",
|
|
|
+ "xor": "xor",
|
|
|
+ "not": "not",
|
|
|
+ "and": "and",
|
|
|
+ "in": "in",
|
|
|
+ }
|
|
|
+ return op_id_map[op]
|
|
|
+
|
|
|
+
|
|
|
+def get_default_value_for_type(type_name):
|
|
|
+ if type_name == "int":
|
|
|
+ return "0"
|
|
|
+ if type_name == "float":
|
|
|
+ return "0.0"
|
|
|
+ if type_name == "bool":
|
|
|
+ return "false"
|
|
|
+ if is_enum(type_name):
|
|
|
+ return f"{correct_type(type_name)}(0)"
|
|
|
+ if is_variant(type_name):
|
|
|
+ return f"{type_name}()"
|
|
|
+ if is_refcounted(type_name):
|
|
|
+ return f"Ref<{type_name}>()"
|
|
|
+ return "nullptr"
|
|
|
+
|
|
|
+
|
|
|
+header = """\
|
|
|
+/*************************************************************************/
|
|
|
+/* $filename */
|
|
|
+/*************************************************************************/
|
|
|
+/* This file is part of: */
|
|
|
+/* GODOT ENGINE */
|
|
|
+/* https://godotengine.org */
|
|
|
+/*************************************************************************/
|
|
|
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
|
+/* Copyright (c) 2014-2021 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. */
|
|
|
+/*************************************************************************/
|
|
|
+"""
|
|
|
+
|
|
|
+
|
|
|
+def add_header(filename, lines):
|
|
|
+ desired_length = len(header.split("\n")[0])
|
|
|
+ pad_spaces = desired_length - 6 - len(filename)
|
|
|
+
|
|
|
+ for num, line in enumerate(header.split("\n")):
|
|
|
+ if num == 1:
|
|
|
+ new_line = f"/* {filename}{' ' * pad_spaces}*/"
|
|
|
+ lines.append(new_line)
|
|
|
+ else:
|
|
|
+ lines.append(line)
|
|
|
+
|
|
|
+ lines.append("// THIS FILE IS GENERATED. EDITS WILL BE LOST.")
|
|
|
+ lines.append("")
|