Browse Source

Add GDNative JSON generator for the builtin API

Which can be used by language bindings to generate code statically. This
is generated as a different file from the class API because it has
different requirements (the builtin types have constructors and don't
have signals), so bindings can better make use of each JSON file without
extra parsing.

This also cleans up a bit the old API generator, mainly initializing
structs and renaming "instanciable" to the more correct "instantiable".

The argument description in help text was updated to better reflect how
it should be used. The <path> argument is mandatory.
George Marques 4 years ago
parent
commit
8a8dbd76b1

+ 59 - 58
main/main.cpp

@@ -265,27 +265,27 @@ void Main::print_help(const char *p_binary) {
 	OS::get_singleton()->print("\n");
 	OS::get_singleton()->print("\n");
 
 
 	OS::get_singleton()->print("General options:\n");
 	OS::get_singleton()->print("General options:\n");
-	OS::get_singleton()->print("  -h, --help                       Display this help message.\n");
-	OS::get_singleton()->print("  --version                        Display the version string.\n");
-	OS::get_singleton()->print("  -v, --verbose                    Use verbose stdout mode.\n");
-	OS::get_singleton()->print("  --quiet                          Quiet mode, silences stdout messages. Errors are still displayed.\n");
+	OS::get_singleton()->print("  -h, --help                                   Display this help message.\n");
+	OS::get_singleton()->print("  --version                                    Display the version string.\n");
+	OS::get_singleton()->print("  -v, --verbose                                Use verbose stdout mode.\n");
+	OS::get_singleton()->print("  --quiet                                      Quiet mode, silences stdout messages. Errors are still displayed.\n");
 	OS::get_singleton()->print("\n");
 	OS::get_singleton()->print("\n");
 
 
 	OS::get_singleton()->print("Run options:\n");
 	OS::get_singleton()->print("Run options:\n");
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	OS::get_singleton()->print("  -e, --editor                     Start the editor instead of running the scene.\n");
-	OS::get_singleton()->print("  -p, --project-manager            Start the project manager, even if a project is auto-detected.\n");
+	OS::get_singleton()->print("  -e, --editor                                 Start the editor instead of running the scene.\n");
+	OS::get_singleton()->print("  -p, --project-manager                        Start the project manager, even if a project is auto-detected.\n");
 #endif
 #endif
-	OS::get_singleton()->print("  -q, --quit                       Quit after the first iteration.\n");
-	OS::get_singleton()->print("  -l, --language <locale>          Use a specific locale (<locale> being a two-letter code).\n");
-	OS::get_singleton()->print("  --path <directory>               Path to a project (<directory> must contain a 'project.godot' file).\n");
-	OS::get_singleton()->print("  -u, --upwards                    Scan folders upwards for project.godot file.\n");
-	OS::get_singleton()->print("  --main-pack <file>               Path to a pack (.pck) file to load.\n");
-	OS::get_singleton()->print("  --render-thread <mode>           Render thread mode ('unsafe', 'safe', 'separate').\n");
-	OS::get_singleton()->print("  --remote-fs <address>            Remote filesystem (<host/IP>[:<port>] address).\n");
-	OS::get_singleton()->print("  --remote-fs-password <password>  Password for remote filesystem.\n");
-
-	OS::get_singleton()->print("  --audio-driver <driver>          Audio driver [");
+	OS::get_singleton()->print("  -q, --quit                                   Quit after the first iteration.\n");
+	OS::get_singleton()->print("  -l, --language <locale>                      Use a specific locale (<locale> being a two-letter code).\n");
+	OS::get_singleton()->print("  --path <directory>                           Path to a project (<directory> must contain a 'project.godot' file).\n");
+	OS::get_singleton()->print("  -u, --upwards                                Scan folders upwards for project.godot file.\n");
+	OS::get_singleton()->print("  --main-pack <file>                           Path to a pack (.pck) file to load.\n");
+	OS::get_singleton()->print("  --render-thread <mode>                       Render thread mode ('unsafe', 'safe', 'separate').\n");
+	OS::get_singleton()->print("  --remote-fs <address>                        Remote filesystem (<host/IP>[:<port>] address).\n");
+	OS::get_singleton()->print("  --remote-fs-password <password>              Password for remote filesystem.\n");
+
+	OS::get_singleton()->print("  --audio-driver <driver>                      Audio driver [");
 	for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
 	for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
 		if (i > 0) {
 		if (i > 0) {
 			OS::get_singleton()->print(", ");
 			OS::get_singleton()->print(", ");
@@ -294,7 +294,7 @@ void Main::print_help(const char *p_binary) {
 	}
 	}
 	OS::get_singleton()->print("].\n");
 	OS::get_singleton()->print("].\n");
 
 
-	OS::get_singleton()->print("  --display-driver <driver>        Display driver (and rendering driver) [");
+	OS::get_singleton()->print("  --display-driver <driver>                    Display driver (and rendering driver) [");
 	for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
 	for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
 		if (i > 0) {
 		if (i > 0) {
 			OS::get_singleton()->print(", ");
 			OS::get_singleton()->print(", ");
@@ -311,26 +311,26 @@ void Main::print_help(const char *p_binary) {
 	}
 	}
 	OS::get_singleton()->print("].\n");
 	OS::get_singleton()->print("].\n");
 
 
-	OS::get_singleton()->print("  --rendering-driver <driver>      Rendering driver (depends on display driver).\n");
+	OS::get_singleton()->print("  --rendering-driver <driver>                  Rendering driver (depends on display driver).\n");
 
 
-	OS::get_singleton()->print("  --text-driver <driver>           Text driver (Fonts, BiDi, shaping)\n");
+	OS::get_singleton()->print("  --text-driver <driver>                       Text driver (Fonts, BiDi, shaping)\n");
 
 
 	OS::get_singleton()->print("\n");
 	OS::get_singleton()->print("\n");
 
 
 #ifndef SERVER_ENABLED
 #ifndef SERVER_ENABLED
 	OS::get_singleton()->print("Display options:\n");
 	OS::get_singleton()->print("Display options:\n");
-	OS::get_singleton()->print("  -f, --fullscreen                 Request fullscreen mode.\n");
-	OS::get_singleton()->print("  -m, --maximized                  Request a maximized window.\n");
-	OS::get_singleton()->print("  -w, --windowed                   Request windowed mode.\n");
-	OS::get_singleton()->print("  -t, --always-on-top              Request an always-on-top window.\n");
-	OS::get_singleton()->print("  --resolution <W>x<H>             Request window resolution.\n");
-	OS::get_singleton()->print("  --position <X>,<Y>               Request window position.\n");
-	OS::get_singleton()->print("  --low-dpi                        Force low-DPI mode (macOS and Windows only).\n");
-	OS::get_singleton()->print("  --no-window                      Disable window creation (Windows only). Useful together with --script.\n");
-	OS::get_singleton()->print("  --enable-vsync-via-compositor    When vsync is enabled, vsync via the OS' window compositor (Windows only).\n");
-	OS::get_singleton()->print("  --disable-vsync-via-compositor   Disable vsync via the OS' window compositor (Windows only).\n");
-	OS::get_singleton()->print("  --single-window                  Use a single window (no separate subwindows).\n");
-	OS::get_singleton()->print("  --tablet-driver                  Tablet input driver (");
+	OS::get_singleton()->print("  -f, --fullscreen                             Request fullscreen mode.\n");
+	OS::get_singleton()->print("  -m, --maximized                              Request a maximized window.\n");
+	OS::get_singleton()->print("  -w, --windowed                               Request windowed mode.\n");
+	OS::get_singleton()->print("  -t, --always-on-top                          Request an always-on-top window.\n");
+	OS::get_singleton()->print("  --resolution <W>x<H>                         Request window resolution.\n");
+	OS::get_singleton()->print("  --position <X>,<Y>                           Request window position.\n");
+	OS::get_singleton()->print("  --low-dpi                                    Force low-DPI mode (macOS and Windows only).\n");
+	OS::get_singleton()->print("  --no-window                                  Disable window creation (Windows only). Useful together with --script.\n");
+	OS::get_singleton()->print("  --enable-vsync-via-compositor                When vsync is enabled, vsync via the OS' window compositor (Windows only).\n");
+	OS::get_singleton()->print("  --disable-vsync-via-compositor               Disable vsync via the OS' window compositor (Windows only).\n");
+	OS::get_singleton()->print("  --single-window                              Use a single window (no separate subwindows).\n");
+	OS::get_singleton()->print("  --tablet-driver                              Tablet input driver (");
 	for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) {
 	for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) {
 		if (i != 0)
 		if (i != 0)
 			OS::get_singleton()->print(", ");
 			OS::get_singleton()->print(", ");
@@ -341,43 +341,44 @@ void Main::print_help(const char *p_binary) {
 #endif
 #endif
 
 
 	OS::get_singleton()->print("Debug options:\n");
 	OS::get_singleton()->print("Debug options:\n");
-	OS::get_singleton()->print("  -d, --debug                      Debug (local stdout debugger).\n");
-	OS::get_singleton()->print("  -b, --breakpoints                Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
-	OS::get_singleton()->print("  --profiling                      Enable profiling in the script debugger.\n");
-	OS::get_singleton()->print("  --vk-layers                      Enable Vulkan Validation layers for debugging.\n");
+	OS::get_singleton()->print("  -d, --debug                                  Debug (local stdout debugger).\n");
+	OS::get_singleton()->print("  -b, --breakpoints                            Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
+	OS::get_singleton()->print("  --profiling                                  Enable profiling in the script debugger.\n");
+	OS::get_singleton()->print("  --vk-layers                                  Enable Vulkan Validation layers for debugging.\n");
 #if DEBUG_ENABLED
 #if DEBUG_ENABLED
-	OS::get_singleton()->print("  --gpu-abort                      Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n");
+	OS::get_singleton()->print("  --gpu-abort                                  Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n");
 #endif
 #endif
-	OS::get_singleton()->print("  --remote-debug <uri>             Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
+	OS::get_singleton()->print("  --remote-debug <uri>                         Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
 #if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
 #if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
-	OS::get_singleton()->print("  --debug-collisions               Show collision shapes when running the scene.\n");
-	OS::get_singleton()->print("  --debug-navigation               Show navigation polygons when running the scene.\n");
+	OS::get_singleton()->print("  --debug-collisions                           Show collision shapes when running the scene.\n");
+	OS::get_singleton()->print("  --debug-navigation                           Show navigation polygons when running the scene.\n");
 #endif
 #endif
-	OS::get_singleton()->print("  --frame-delay <ms>               Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
-	OS::get_singleton()->print("  --time-scale <scale>             Force time scale (higher values are faster, 1.0 is normal speed).\n");
-	OS::get_singleton()->print("  --disable-render-loop            Disable render loop so rendering only occurs when called explicitly from script.\n");
-	OS::get_singleton()->print("  --disable-crash-handler          Disable crash handler when supported by the platform code.\n");
-	OS::get_singleton()->print("  --fixed-fps <fps>                Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
-	OS::get_singleton()->print("  --print-fps                      Print the frames per second to the stdout.\n");
-	OS::get_singleton()->print("  --profile-gpu                    Show a simple profile of the tasks that took more time during frame rendering.\n");
+	OS::get_singleton()->print("  --frame-delay <ms>                           Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
+	OS::get_singleton()->print("  --time-scale <scale>                         Force time scale (higher values are faster, 1.0 is normal speed).\n");
+	OS::get_singleton()->print("  --disable-render-loop                        Disable render loop so rendering only occurs when called explicitly from script.\n");
+	OS::get_singleton()->print("  --disable-crash-handler                      Disable crash handler when supported by the platform code.\n");
+	OS::get_singleton()->print("  --fixed-fps <fps>                            Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
+	OS::get_singleton()->print("  --print-fps                                  Print the frames per second to the stdout.\n");
+	OS::get_singleton()->print("  --profile-gpu                                Show a simple profile of the tasks that took more time during frame rendering.\n");
 	OS::get_singleton()->print("\n");
 	OS::get_singleton()->print("\n");
 
 
 	OS::get_singleton()->print("Standalone tools:\n");
 	OS::get_singleton()->print("Standalone tools:\n");
-	OS::get_singleton()->print("  -s, --script <script>            Run a script.\n");
-	OS::get_singleton()->print("  --check-only                     Only parse for errors and quit (use with --script).\n");
+	OS::get_singleton()->print("  -s, --script <script>                        Run a script.\n");
+	OS::get_singleton()->print("  --check-only                                 Only parse for errors and quit (use with --script).\n");
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	OS::get_singleton()->print("  --export <preset> <path>         Export the project using the given preset and matching release template. The preset name should match one defined in export_presets.cfg.\n");
-	OS::get_singleton()->print("                                   <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe'). The target directory should exist.\n");
-	OS::get_singleton()->print("  --export-debug <preset> <path>   Same as --export, but using the debug template.\n");
-	OS::get_singleton()->print("  --export-pack <preset> <path>    Same as --export, but only export the game pack for the given preset. The <path> extension determines whether it will be in PCK or ZIP format.\n");
-	OS::get_singleton()->print("  --doctool <path>                 Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n");
-	OS::get_singleton()->print("  --no-docbase                     Disallow dumping the base types (used with --doctool).\n");
-	OS::get_singleton()->print("  --build-solutions                Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
+	OS::get_singleton()->print("  --export <preset> <path>                     Export the project using the given preset and matching release template. The preset name should match one defined in export_presets.cfg.\n");
+	OS::get_singleton()->print("                                               <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe'). The target directory should exist.\n");
+	OS::get_singleton()->print("  --export-debug <preset> <path>               Same as --export, but using the debug template.\n");
+	OS::get_singleton()->print("  --export-pack <preset> <path>                Same as --export, but only export the game pack for the given preset. The <path> extension determines whether it will be in PCK or ZIP format.\n");
+	OS::get_singleton()->print("  --doctool <path>                             Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n");
+	OS::get_singleton()->print("  --no-docbase                                 Disallow dumping the base types (used with --doctool).\n");
+	OS::get_singleton()->print("  --build-solutions                            Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
 #ifdef DEBUG_METHODS_ENABLED
 #ifdef DEBUG_METHODS_ENABLED
-	OS::get_singleton()->print("  --gdnative-generate-json-api     Generate JSON dump of the Godot API for GDNative bindings.\n");
+	OS::get_singleton()->print("  --gdnative-generate-json-api <path>          Generate JSON dump of the Godot API for GDNative bindings and save it on the file specified in <path>.\n");
+	OS::get_singleton()->print("  --gdnative-generate-json-builtin-api <path>  Generate JSON dump of the Godot API of the builtin Variant types and utility functions for GDNative bindings and save it on the file specified in <path>.\n");
 #endif
 #endif
 #ifdef TESTS_ENABLED
 #ifdef TESTS_ENABLED
-	OS::get_singleton()->print("  --test [--help]                  Run unit tests. Use --test --help for more information.\n");
+	OS::get_singleton()->print("  --test [--help]                              Run unit tests. Use --test --help for more information.\n");
 #endif
 #endif
 	OS::get_singleton()->print("\n");
 	OS::get_singleton()->print("\n");
 #endif
 #endif
@@ -879,7 +880,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 			auto_build_solutions = true;
 			auto_build_solutions = true;
 			editor = true;
 			editor = true;
 #ifdef DEBUG_METHODS_ENABLED
 #ifdef DEBUG_METHODS_ENABLED
-		} else if (I->get() == "--gdnative-generate-json-api") {
+		} else if (I->get() == "--gdnative-generate-json-api" || I->get() == "--gdnative-generate-json-builtin-api") {
 			// Register as an editor instance to use low-end fallback if relevant.
 			// Register as an editor instance to use low-end fallback if relevant.
 			editor = true;
 			editor = true;
 
 

+ 394 - 18
modules/gdnative/nativescript/api_generator.cpp

@@ -36,6 +36,7 @@
 #include "core/core_constants.h"
 #include "core/core_constants.h"
 #include "core/object/class_db.h"
 #include "core/object/class_db.h"
 #include "core/os/file_access.h"
 #include "core/os/file_access.h"
+#include "core/string/string_builder.h"
 #include "core/templates/pair.h"
 #include "core/templates/pair.h"
 
 
 // helper stuff
 // helper stuff
@@ -65,14 +66,14 @@ struct MethodAPI {
 
 
 	Map<int, Variant> default_arguments;
 	Map<int, Variant> default_arguments;
 
 
-	int argument_count;
-	bool has_varargs;
-	bool is_editor;
-	bool is_noscript;
-	bool is_const;
-	bool is_reverse;
-	bool is_virtual;
-	bool is_from_script;
+	int argument_count = 0;
+	bool has_varargs = false;
+	bool is_editor = false;
+	bool is_noscript = false;
+	bool is_const = false;
+	bool is_reverse = false;
+	bool is_virtual = false;
+	bool is_from_script = false;
 };
 };
 
 
 struct PropertyAPI {
 struct PropertyAPI {
@@ -80,12 +81,14 @@ struct PropertyAPI {
 	String getter;
 	String getter;
 	String setter;
 	String setter;
 	String type;
 	String type;
-	int index;
+	int index = 0;
 };
 };
 
 
 struct ConstantAPI {
 struct ConstantAPI {
 	String constant_name;
 	String constant_name;
-	int constant_value;
+	int constant_value = 0;
+	Variant builtin_constant_value; // For builtin types;
+	String builtin_constant_type; // For builtin types;
 };
 };
 
 
 struct SignalAPI {
 struct SignalAPI {
@@ -100,23 +103,34 @@ struct EnumAPI {
 	List<Pair<int, String>> values;
 	List<Pair<int, String>> values;
 };
 };
 
 
+struct OperatorAPI { // For builtin types;
+	String name;
+	int oper = Variant::OP_MAX;
+	String other_type;
+	String return_type;
+};
+
 struct ClassAPI {
 struct ClassAPI {
 	String class_name;
 	String class_name;
 	String super_class_name;
 	String super_class_name;
 
 
-	ClassDB::APIType api_type;
+	ClassDB::APIType api_type = ClassDB::API_NONE;
 
 
-	bool is_singleton;
+	bool is_singleton = false;
 	String singleton_name;
 	String singleton_name;
-	bool is_instanciable;
+	bool is_instantiable = false;
 	// @Unclear
 	// @Unclear
-	bool is_reference;
+	bool is_reference = false;
+	bool has_indexing = false; // For builtin types.
+	bool is_keyed = false; // For builtin types.
 
 
 	List<MethodAPI> methods;
 	List<MethodAPI> methods;
+	List<MethodAPI> constructors; // For builtin types.
 	List<PropertyAPI> properties;
 	List<PropertyAPI> properties;
 	List<ConstantAPI> constants;
 	List<ConstantAPI> constants;
 	List<SignalAPI> signals_;
 	List<SignalAPI> signals_;
 	List<EnumAPI> enums;
 	List<EnumAPI> enums;
+	List<OperatorAPI> operators; // For builtin types.
 };
 };
 
 
 static String get_type_name(const PropertyInfo &info) {
 static String get_type_name(const PropertyInfo &info) {
@@ -180,7 +194,7 @@ List<ClassAPI> generate_c_api_classes() {
 		global_constants_api.api_type = ClassDB::API_CORE;
 		global_constants_api.api_type = ClassDB::API_CORE;
 		global_constants_api.is_singleton = true;
 		global_constants_api.is_singleton = true;
 		global_constants_api.singleton_name = "CoreConstants";
 		global_constants_api.singleton_name = "CoreConstants";
-		global_constants_api.is_instanciable = false;
+		global_constants_api.is_instantiable = false;
 		const int constants_count = CoreConstants::get_global_constant_count();
 		const int constants_count = CoreConstants::get_global_constant_count();
 		for (int i = 0; i < constants_count; ++i) {
 		for (int i = 0; i < constants_count; ++i) {
 			ConstantAPI constant_api;
 			ConstantAPI constant_api;
@@ -195,6 +209,10 @@ List<ClassAPI> generate_c_api_classes() {
 	for (List<StringName>::Element *e = classes.front(); e != nullptr; e = e->next()) {
 	for (List<StringName>::Element *e = classes.front(); e != nullptr; e = e->next()) {
 		StringName class_name = e->get();
 		StringName class_name = e->get();
 
 
+		if (!ClassDB::is_class_exposed(class_name)) {
+			continue;
+		}
+
 		ClassAPI class_api;
 		ClassAPI class_api;
 		class_api.api_type = ClassDB::get_api_type(e->get());
 		class_api.api_type = ClassDB::get_api_type(e->get());
 		class_api.class_name = class_name;
 		class_api.class_name = class_name;
@@ -209,7 +227,7 @@ List<ClassAPI> generate_c_api_classes() {
 				class_api.singleton_name = name;
 				class_api.singleton_name = name;
 			}
 			}
 		}
 		}
-		class_api.is_instanciable = !class_api.is_singleton && ClassDB::can_instance(class_name);
+		class_api.is_instantiable = !class_api.is_singleton && ClassDB::can_instance(class_name);
 
 
 		{
 		{
 			List<StringName> inheriters;
 			List<StringName> inheriters;
@@ -401,6 +419,191 @@ List<ClassAPI> generate_c_api_classes() {
 	return api;
 	return api;
 }
 }
 
 
+/*
+ * Reads the builtin Variant API to a list
+ */
+List<ClassAPI> generate_c_builtin_api_types() {
+	List<ClassAPI> api;
+
+	// Special class for the utility methods.
+	{
+		ClassAPI utility_api;
+		utility_api.class_name = "Utilities";
+		utility_api.is_instantiable = false;
+
+		List<StringName> utility_functions;
+		Variant::get_utility_function_list(&utility_functions);
+		for (const List<StringName>::Element *E = utility_functions.front(); E; E = E->next()) {
+			const StringName &function_name = E->get();
+
+			MethodAPI function_api;
+			function_api.method_name = function_name;
+			function_api.has_varargs = Variant::is_utility_function_vararg(function_name);
+			function_api.argument_count = function_api.has_varargs ? 0 : Variant::get_utility_function_argument_count(function_name);
+			function_api.is_const = Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH;
+
+			for (int i = 0; i < function_api.argument_count; i++) {
+				function_api.argument_names.push_back(Variant::get_utility_function_argument_name(function_name, i));
+				Variant::Type arg_type = Variant::get_utility_function_argument_type(function_name, i);
+				function_api.argument_types.push_back(arg_type == Variant::NIL ? "Variant" : Variant::get_type_name(arg_type));
+			}
+
+			if (Variant::has_utility_function_return_value(function_name)) {
+				Variant::Type ret_type = Variant::get_utility_function_return_type(function_name);
+				function_api.return_type = ret_type == Variant::NIL ? "Variant" : Variant::get_type_name(ret_type);
+			} else {
+				function_api.return_type = "void";
+			}
+
+			utility_api.methods.push_back(function_api);
+		}
+
+		api.push_back(utility_api);
+	}
+
+	for (int t = 0; t < Variant::VARIANT_MAX; t++) {
+		Variant::Type type = (Variant::Type)t;
+
+		ClassAPI class_api;
+		class_api.class_name = Variant::get_type_name(type);
+		class_api.is_instantiable = true;
+		class_api.has_indexing = Variant::has_indexing(type);
+		class_api.is_keyed = Variant::is_keyed(type);
+		// Types that are passed by reference.
+		switch (type) {
+			case Variant::OBJECT:
+			case Variant::DICTIONARY:
+			case Variant::ARRAY:
+			case Variant::PACKED_BYTE_ARRAY:
+			case Variant::PACKED_INT32_ARRAY:
+			case Variant::PACKED_INT64_ARRAY:
+			case Variant::PACKED_FLOAT32_ARRAY:
+			case Variant::PACKED_FLOAT64_ARRAY:
+			case Variant::PACKED_STRING_ARRAY:
+			case Variant::PACKED_VECTOR2_ARRAY:
+			case Variant::PACKED_VECTOR3_ARRAY:
+			case Variant::PACKED_COLOR_ARRAY:
+				class_api.is_reference = true;
+				break;
+			default:
+				class_api.is_reference = false;
+				break;
+		}
+
+		// Methods.
+
+		List<StringName> methods;
+		Variant::get_builtin_method_list(type, &methods);
+		for (const List<StringName>::Element *E = methods.front(); E; E = E->next()) {
+			const StringName &method_name = E->get();
+
+			MethodAPI method_api;
+
+			method_api.method_name = method_name;
+			method_api.argument_count = Variant::get_builtin_method_argument_count(type, method_name);
+			method_api.has_varargs = Variant::is_builtin_method_vararg(type, method_name);
+			method_api.is_const = Variant::is_builtin_method_const(type, method_name);
+
+			for (int i = 0; i < method_api.argument_count; i++) {
+				method_api.argument_names.push_back(Variant::get_builtin_method_argument_name(type, method_name, i));
+				Variant::Type arg_type = Variant::get_builtin_method_argument_type(type, method_name, i);
+				method_api.argument_types.push_back(arg_type == Variant::NIL ? "Variant" : Variant::get_type_name(arg_type));
+			}
+
+			Vector<Variant> default_arguments = Variant::get_builtin_method_default_arguments(type, method_name);
+
+			int default_start = method_api.argument_names.size() - default_arguments.size();
+
+			for (int i = 0; i < default_arguments.size(); i++) {
+				method_api.default_arguments[default_start + i] = default_arguments[i];
+			}
+
+			if (Variant::has_builtin_method_return_value(type, method_name)) {
+				Variant::Type ret_type = Variant::get_builtin_method_return_type(type, method_name);
+				method_api.return_type = ret_type == Variant::NIL ? "Variant" : Variant::get_type_name(ret_type);
+			} else {
+				method_api.return_type = "void";
+			}
+
+			class_api.methods.push_back(method_api);
+		}
+
+		// Constructors.
+
+		for (int c = 0; c < Variant::get_constructor_count(type); c++) {
+			MethodAPI constructor_api;
+
+			constructor_api.method_name = Variant::get_type_name(type);
+			constructor_api.argument_count = Variant::get_constructor_argument_count(type, c);
+			constructor_api.return_type = Variant::get_type_name(type);
+
+			for (int i = 0; i < constructor_api.argument_count; i++) {
+				constructor_api.argument_names.push_back(Variant::get_constructor_argument_name(type, c, i));
+				Variant::Type arg_type = Variant::get_constructor_argument_type(type, c, i);
+				constructor_api.argument_types.push_back(arg_type == Variant::NIL ? "Variant" : Variant::get_type_name(arg_type));
+			}
+
+			class_api.constructors.push_back(constructor_api);
+		}
+
+		// Constants.
+
+		List<StringName> constants;
+		Variant::get_constants_for_type(type, &constants);
+		for (const List<StringName>::Element *E = constants.front(); E; E = E->next()) {
+			const StringName &constant_name = E->get();
+			ConstantAPI constant_api;
+
+			constant_api.constant_name = constant_name;
+			constant_api.builtin_constant_value = Variant::get_constant_value(type, constant_name);
+			constant_api.builtin_constant_type = Variant::get_type_name(constant_api.builtin_constant_value.get_type());
+
+			class_api.constants.push_back(constant_api);
+		}
+
+		// Members.
+
+		List<StringName> members;
+		Variant::get_member_list(type, &members);
+		for (const List<StringName>::Element *E = members.front(); E; E = E->next()) {
+			const StringName &member_name = E->get();
+
+			PropertyAPI member_api;
+			member_api.name = member_name;
+			Variant::Type member_type = Variant::get_member_type(type, member_name);
+			member_api.type = member_type == Variant::NIL ? "Variant" : Variant::get_type_name(member_type);
+
+			class_api.properties.push_back(member_api);
+		}
+
+		// Operators.
+
+		for (int op = 0; op < Variant::OP_MAX; op++) {
+			Variant::Operator oper = (Variant::Operator)op;
+
+			for (int ot = 0; ot < Variant::VARIANT_MAX; ot++) {
+				Variant::Type other_type = (Variant::Type)ot;
+
+				if (!Variant::get_validated_operator_evaluator(oper, type, other_type)) {
+					continue;
+				}
+
+				OperatorAPI oper_api;
+				oper_api.name = Variant::get_operator_name(oper);
+				oper_api.oper = oper;
+				oper_api.other_type = Variant::get_type_name(other_type);
+				oper_api.return_type = Variant::get_type_name(Variant::get_operator_return_type(oper, type, other_type));
+
+				class_api.operators.push_back(oper_api);
+			}
+		}
+
+		api.push_back(class_api);
+	}
+
+	return api;
+}
+
 /*
 /*
  * Generates the JSON source from the API in p_api
  * Generates the JSON source from the API in p_api
  */
  */
@@ -421,9 +624,8 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
 		source.push_back(String("\t\t\"api_type\": \"") + (api.api_type == ClassDB::API_CORE ? "core" : (api.api_type == ClassDB::API_EDITOR ? "tools" : "none")) + "\",\n");
 		source.push_back(String("\t\t\"api_type\": \"") + (api.api_type == ClassDB::API_CORE ? "core" : (api.api_type == ClassDB::API_EDITOR ? "tools" : "none")) + "\",\n");
 		source.push_back(String("\t\t\"singleton\": ") + (api.is_singleton ? "true" : "false") + ",\n");
 		source.push_back(String("\t\t\"singleton\": ") + (api.is_singleton ? "true" : "false") + ",\n");
 		source.push_back("\t\t\"singleton_name\": \"" + api.singleton_name + "\",\n");
 		source.push_back("\t\t\"singleton_name\": \"" + api.singleton_name + "\",\n");
-		source.push_back(String("\t\t\"instanciable\": ") + (api.is_instanciable ? "true" : "false") + ",\n");
+		source.push_back(String("\t\t\"instantiable\": ") + (api.is_instantiable ? "true" : "false") + ",\n");
 		source.push_back(String("\t\t\"is_reference\": ") + (api.is_reference ? "true" : "false") + ",\n");
 		source.push_back(String("\t\t\"is_reference\": ") + (api.is_reference ? "true" : "false") + ",\n");
-		// @Unclear
 
 
 		source.push_back("\t\t\"constants\": {\n");
 		source.push_back("\t\t\"constants\": {\n");
 		for (List<ConstantAPI>::Element *e = api.constants.front(); e; e = e->next()) {
 		for (List<ConstantAPI>::Element *e = api.constants.front(); e; e = e->next()) {
@@ -508,6 +710,164 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
 	return source;
 	return source;
 }
 }
 
 
+static int indent_level = 0;
+
+static void append_indented(StringBuilder &p_source, const String &p_text) {
+	for (int i = 0; i < indent_level; i++) {
+		p_source.append("\t");
+	}
+	p_source.append(p_text);
+	p_source.append("\n");
+}
+
+static void append_indented(StringBuilder &p_source, const char *p_text) {
+	for (int i = 0; i < indent_level; i++) {
+		p_source.append("\t");
+	}
+	p_source.append(p_text);
+	p_source.append("\n");
+}
+
+static void write_builtin_method(StringBuilder &p_source, const MethodAPI &p_method) {
+	append_indented(p_source, vformat(R"("name": "%s",)", p_method.method_name));
+	append_indented(p_source, vformat(R"("return_type": "%s",)", p_method.return_type));
+	append_indented(p_source, vformat(R"("is_const": %s,)", p_method.is_const ? "true" : "false"));
+	append_indented(p_source, vformat(R"("has_varargs": %s,)", p_method.has_varargs ? "true" : "false"));
+
+	append_indented(p_source, R"("arguments": [)");
+	indent_level++;
+	for (int i = 0; i < p_method.argument_count; i++) {
+		append_indented(p_source, "{");
+		indent_level++;
+
+		append_indented(p_source, vformat(R"("name": "%s",)", p_method.argument_names[i]));
+		append_indented(p_source, vformat(R"("type": "%s",)", p_method.argument_types[i]));
+		append_indented(p_source, vformat(R"("has_default_value": %s,)", p_method.default_arguments.has(i) ? "true" : "false"));
+		append_indented(p_source, vformat(R"("default_value": "%s")", p_method.default_arguments.has(i) ? p_method.default_arguments[i].operator String() : ""));
+
+		indent_level--;
+		append_indented(p_source, i < p_method.argument_count - 1 ? "}," : "}");
+	}
+	indent_level--;
+	append_indented(p_source, "]");
+}
+
+static List<String> generate_c_builtin_api_json(const List<ClassAPI> &p_api) {
+	StringBuilder source;
+
+	source.append("[\n");
+
+	indent_level = 1;
+
+	for (const List<ClassAPI>::Element *C = p_api.front(); C; C = C->next()) {
+		const ClassAPI &class_api = C->get();
+		append_indented(source, "{");
+		indent_level++;
+
+		append_indented(source, vformat(R"("name": "%s",)", class_api.class_name));
+		append_indented(source, vformat(R"("is_instantiable": %s,)", class_api.is_instantiable ? "true" : "false"));
+		append_indented(source, vformat(R"("is_reference": %s,)", class_api.is_reference ? "true" : "false"));
+		append_indented(source, vformat(R"("has_indexing": %s,)", class_api.has_indexing ? "true" : "false"));
+		append_indented(source, vformat(R"("is_keyed": %s,)", class_api.is_keyed ? "true" : "false"));
+
+		// Constructors.
+		append_indented(source, R"("constructors": [)");
+		indent_level++;
+		for (const List<MethodAPI>::Element *E = class_api.constructors.front(); E; E = E->next()) {
+			const MethodAPI &constructor = E->get();
+			append_indented(source, "{");
+			indent_level++;
+
+			write_builtin_method(source, constructor);
+
+			indent_level--;
+			append_indented(source, E->next() ? "}," : "}");
+		}
+		indent_level--;
+		append_indented(source, "],");
+
+		// Constants.
+		append_indented(source, R"("constants": [)");
+		indent_level++;
+		for (const List<ConstantAPI>::Element *E = class_api.constants.front(); E; E = E->next()) {
+			const ConstantAPI &constant = E->get();
+			append_indented(source, "{");
+			indent_level++;
+
+			append_indented(source, vformat(R"("name": "%s",)", constant.constant_name));
+			append_indented(source, vformat(R"("type": "%s",)", constant.builtin_constant_type));
+			append_indented(source, vformat(R"("value": "%s")", constant.builtin_constant_value.operator String()));
+
+			indent_level--;
+			append_indented(source, E->next() ? "}," : "}");
+		}
+		indent_level--;
+		append_indented(source, "],");
+
+		// Methods.
+		append_indented(source, R"("methods": [)");
+		indent_level++;
+		for (const List<MethodAPI>::Element *E = class_api.methods.front(); E; E = E->next()) {
+			const MethodAPI &method = E->get();
+			append_indented(source, "{");
+			indent_level++;
+
+			write_builtin_method(source, method);
+
+			indent_level--;
+			append_indented(source, E->next() ? "}," : "}");
+		}
+		indent_level--;
+		append_indented(source, "],");
+
+		// Members.
+		append_indented(source, R"("members": [)");
+		indent_level++;
+		for (const List<PropertyAPI>::Element *E = class_api.properties.front(); E; E = E->next()) {
+			const PropertyAPI &member = E->get();
+			append_indented(source, "{");
+			indent_level++;
+
+			append_indented(source, vformat(R"("name": "%s",)", member.name));
+			append_indented(source, vformat(R"("type": "%s")", member.type));
+
+			indent_level--;
+			append_indented(source, E->next() ? "}," : "}");
+		}
+		indent_level--;
+		append_indented(source, "],");
+
+		// Operators.
+		append_indented(source, R"("operators": [)");
+		indent_level++;
+		for (const List<OperatorAPI>::Element *E = class_api.operators.front(); E; E = E->next()) {
+			const OperatorAPI &oper = E->get();
+			append_indented(source, "{");
+			indent_level++;
+
+			append_indented(source, vformat(R"("name": "%s",)", oper.name));
+			append_indented(source, vformat(R"("operator": %d,)", oper.oper));
+			append_indented(source, vformat(R"("other_type": "%s",)", oper.other_type));
+			append_indented(source, vformat(R"("return_type": "%s")", oper.return_type));
+
+			indent_level--;
+			append_indented(source, E->next() ? "}," : "}");
+		}
+		indent_level--;
+		append_indented(source, "]");
+
+		indent_level--;
+		append_indented(source, C->next() ? "}," : "}");
+	}
+
+	indent_level--;
+	source.append("]\n");
+
+	List<String> result;
+	result.push_back(source.as_string());
+	return result;
+}
+
 #endif
 #endif
 
 
 /*
 /*
@@ -526,3 +886,19 @@ Error generate_c_api(const String &p_path) {
 	return save_file(p_path, json_source);
 	return save_file(p_path, json_source);
 #endif
 #endif
 }
 }
+/*
+ * Saves the builtin Godot API to a JSON file located at
+ *  p_path
+ */
+Error generate_c_builtin_api(const String &p_path) {
+#ifndef TOOLS_ENABLED
+	return ERR_BUG;
+#else
+
+	List<ClassAPI> api = generate_c_builtin_api_types();
+
+	List<String> json_source = generate_c_builtin_api_json(api);
+
+	return save_file(p_path, json_source);
+#endif
+}

+ 1 - 0
modules/gdnative/nativescript/api_generator.h

@@ -35,5 +35,6 @@
 #include "core/typedefs.h"
 #include "core/typedefs.h"
 
 
 Error generate_c_api(const String &p_path);
 Error generate_c_api(const String &p_path);
+Error generate_c_builtin_api(const String &p_path);
 
 
 #endif // API_GENERATOR_H
 #endif // API_GENERATOR_H

+ 9 - 0
modules/gdnative/nativescript/nativescript.cpp

@@ -1257,6 +1257,15 @@ void NativeScriptLanguage::init() {
 		}
 		}
 		exit(0);
 		exit(0);
 	}
 	}
+
+	E = args.find("--gdnative-generate-json-builtin-api");
+
+	if (E && E->next()) {
+		if (generate_c_builtin_api(E->next()->get()) != OK) {
+			ERR_PRINT("Failed to generate C builtin API\n");
+		}
+		exit(0);
+	}
 #endif
 #endif
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED