godot/modules/mono/mono_gd/gd_mono_assembly.cpp

497 lines
17 KiB
C++
Raw Normal View History

/**************************************************************************/
/* gd_mono_assembly.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2017-10-02 21:24:00 +00:00
#include "gd_mono_assembly.h"
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/tokentype.h>
#include "core/io/file_access_pack.h"
#include "core/list.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/project_settings.h"
2017-10-02 21:24:00 +00:00
#include "../godotsharp_dirs.h"
2019-11-10 16:10:38 +00:00
#include "gd_mono_cache.h"
2017-10-02 21:24:00 +00:00
#include "gd_mono_class.h"
2017-10-07 02:21:55 +00:00
Vector<String> GDMonoAssembly::search_dirs;
2017-10-02 21:24:00 +00:00
void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
String framework_dir;
if (!p_custom_bcl_dir.empty()) {
framework_dir = p_custom_bcl_dir;
} else if (mono_assembly_getrootdir()) {
framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5");
}
if (!framework_dir.empty()) {
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
r_search_dirs.push_back(framework_dir);
r_search_dirs.push_back(framework_dir.plus_file("Facades"));
}
#if !defined(TOOLS_ENABLED)
String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir();
if (!data_game_assemblies_dir.empty()) {
r_search_dirs.push_back(data_game_assemblies_dir);
}
#endif
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
if (p_custom_config.length()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
} else {
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
}
if (p_custom_config.empty()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
} else {
String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug";
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
}
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir());
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
#ifdef TOOLS_ENABLED
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
// For GodotTools to find the api assemblies
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"));
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
#endif
}
// This is how these assembly loading hooks work:
//
// - The 'search' hook checks if the assembly has already been loaded, to avoid loading again.
// - The 'preload' hook does the actual loading and is only called if the
// 'search' hook didn't find the assembly in the list of loaded assemblies.
// - The 'load' hook is called after the assembly has been loaded. Its job is to add the
// assembly to the list of loaded assemblies so that the 'search' hook can look it up.
void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) {
String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
MonoImage *image = mono_assembly_get_image(assembly);
GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly));
#ifdef GD_MONO_HOT_RELOAD
String path = String::utf8(mono_image_get_filename(image));
if (FileAccess::exists(path))
gdassembly->modified_time = FileAccess::get_modified_time(path);
#endif
MonoDomain *domain = mono_domain_get();
GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
}
2018-02-22 12:39:41 +00:00
MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
return GDMonoAssembly::_search_hook(aname, user_data, false);
}
MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) {
return GDMonoAssembly::_search_hook(aname, user_data, true);
}
MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false);
}
MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
}
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) {
String name = String::utf8(mono_assembly_name_get_name(aname));
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
2017-10-02 21:24:00 +00:00
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
2017-10-07 02:21:55 +00:00
if (loaded_asm)
return loaded_asm->get_assembly();
2017-10-02 21:24:00 +00:00
return NULL;
2017-10-07 02:21:55 +00:00
}
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
String name = String::utf8(mono_assembly_name_get_name(aname));
return _load_assembly_search(name, aname, refonly, search_dirs);
2017-10-07 02:21:55 +00:00
}
2017-10-02 21:24:00 +00:00
MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
MonoAssembly *res = NULL;
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
String path;
bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
for (int i = 0; i < p_search_dirs.size(); i++) {
const String &search_dir = p_search_dirs[i];
if (has_extension) {
path = search_dir.plus_file(p_name);
if (FileAccess::exists(path)) {
res = _real_load_assembly_from(path, p_refonly, p_aname);
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
if (res != NULL)
return res;
}
} else {
path = search_dir.plus_file(p_name + ".dll");
if (FileAccess::exists(path)) {
res = _real_load_assembly_from(path, p_refonly, p_aname);
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
if (res != NULL)
return res;
}
path = search_dir.plus_file(p_name + ".exe");
if (FileAccess::exists(path)) {
res = _real_load_assembly_from(path, p_refonly, p_aname);
Mono: Editor and export template dependencies and fixes - Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
2018-10-03 17:01:57 +00:00
if (res != NULL)
return res;
}
}
}
return NULL;
}
String GDMonoAssembly::find_assembly(const String &p_name) {
String path;
bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i];
if (has_extension) {
path = search_dir.plus_file(p_name);
if (FileAccess::exists(path))
return path;
} else {
path = search_dir.plus_file(p_name + ".dll");
if (FileAccess::exists(path))
return path;
path = search_dir.plus_file(p_name + ".exe");
if (FileAccess::exists(path))
return path;
}
}
return String();
}
2017-10-02 21:24:00 +00:00
void GDMonoAssembly::initialize() {
fill_search_dirs(search_dirs);
2018-02-22 12:39:41 +00:00
mono_install_assembly_search_hook(&assembly_search_hook, NULL);
mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL);
mono_install_assembly_preload_hook(&assembly_preload_hook, NULL);
mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL);
mono_install_assembly_load_hook(&assembly_load_hook, NULL);
2017-10-02 21:24:00 +00:00
}
MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) {
Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
ERR_FAIL_COND_V_MSG(data.empty(), NULL, "Could read the assembly in the specified location");
2017-10-02 21:24:00 +00:00
String image_filename;
#ifdef ANDROID_ENABLED
if (p_path.begins_with("res://")) {
image_filename = p_path.substr(6, p_path.length());
} else {
image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
}
#else
// FIXME: globalize_path does not work on exported games
image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
#endif
2017-10-02 21:24:00 +00:00
MonoImageOpenStatus status = MONO_IMAGE_OK;
2017-10-02 21:24:00 +00:00
MonoImage *image = mono_image_open_from_data_with_name(
2017-10-02 21:24:00 +00:00
(char *)&data[0], data.size(),
true, &status, p_refonly,
image_filename.utf8());
2017-10-02 21:24:00 +00:00
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, NULL, "Failed to open assembly image from memory: '" + p_path + "'.");
if (p_aname != nullptr) {
// Check assembly version
const MonoTableInfo *table = mono_image_get_table_info(image, MONO_TABLE_ASSEMBLY);
ERR_FAIL_NULL_V(table, nullptr);
if (mono_table_info_get_rows(table)) {
uint32_t cols[MONO_ASSEMBLY_SIZE];
mono_metadata_decode_row(table, 0, cols, MONO_ASSEMBLY_SIZE);
// Not sure about .NET's policy. We will only ensure major and minor are equal, and ignore build and revision.
uint16_t major = cols[MONO_ASSEMBLY_MAJOR_VERSION];
uint16_t minor = cols[MONO_ASSEMBLY_MINOR_VERSION];
uint16_t required_minor;
uint16_t required_major = mono_assembly_name_get_version(p_aname, &required_minor, nullptr, nullptr);
if (required_major != 0) {
if (major != required_major && minor != required_minor) {
mono_image_close(image);
return nullptr;
}
}
}
}
2017-10-02 21:24:00 +00:00
#ifdef DEBUG_ENABLED
Vector<uint8_t> pdb_data;
String pdb_path(p_path + ".pdb");
2017-10-02 21:24:00 +00:00
if (!FileAccess::exists(pdb_path)) {
pdb_path = p_path.get_basename() + ".pdb"; // without .dll
2017-10-02 21:24:00 +00:00
if (!FileAccess::exists(pdb_path))
goto no_pdb;
}
pdb_data = FileAccess::get_file_as_array(pdb_path);
// mono_debug_close_image doesn't seem to be needed
2017-10-02 21:24:00 +00:00
mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
no_pdb:
#endif
bool need_manual_load_hook = mono_image_get_assembly(image) != nullptr; // Re-using an existing image with an assembly loaded
status = MONO_IMAGE_OK;
MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly);
2017-10-02 21:24:00 +00:00
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image");
2017-10-02 21:24:00 +00:00
if (need_manual_load_hook) {
// For some reason if an assembly survived domain reloading (maybe because it's referenced somewhere else),
// the mono internal search hook don't detect it, yet mono_image_open_from_data_with_name re-uses the image
// and assembly, and mono_assembly_load_from_full doesn't call the load hook. We need to call it manually.
String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
if (!loaded_asm)
assembly_load_hook(assembly, nullptr);
}
// Decrement refcount which was previously incremented by mono_image_open_from_data_with_name
mono_image_close(image);
return assembly;
2017-10-02 21:24:00 +00:00
}
void GDMonoAssembly::unload() {
ERR_FAIL_NULL(image); // Should not be called if already unloaded
2017-10-02 21:24:00 +00:00
for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) {
memdelete(E->value());
}
cached_classes.clear();
cached_raw.clear();
assembly = NULL;
image = NULL;
}
String GDMonoAssembly::get_path() const {
return String::utf8(mono_image_get_filename(image));
2017-10-02 21:24:00 +00:00
}
GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
ERR_FAIL_NULL_V(image, NULL);
2017-10-02 21:24:00 +00:00
ClassKey key(p_namespace, p_name);
GDMonoClass **match = cached_classes.getptr(key);
if (match)
return *match;
MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
if (!mono_class)
return NULL;
GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
cached_classes[key] = wrapped_class;
cached_raw[mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
ERR_FAIL_NULL_V(image, NULL);
2017-10-02 21:24:00 +00:00
Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
if (match)
return match->value();
2021-01-18 08:34:10 +00:00
StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
StringName class_name = String::utf8(mono_class_get_name(p_mono_class));
2017-10-02 21:24:00 +00:00
GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
cached_raw[p_mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) {
GDMonoClass *match = NULL;
if (gdobject_class_cache_updated) {
Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class);
if (result)
match = result->get();
} else {
List<GDMonoClass *> nested_classes;
int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
for (int i = 1; i < rows; i++) {
MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF);
if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class))
continue;
GDMonoClass *current = get_class(mono_class);
if (!current)
continue;
nested_classes.push_back(current);
if (!match && current->get_name() == p_class)
match = current;
while (!nested_classes.empty()) {
GDMonoClass *current_nested = nested_classes.front()->get();
nested_classes.pop_front();
2017-10-02 21:24:00 +00:00
void *iter = NULL;
while (true) {
MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_mono_ptr(), &iter);
2017-10-02 21:24:00 +00:00
if (!raw_nested)
break;
GDMonoClass *nested_class = get_class(raw_nested);
if (nested_class) {
gdobject_class_cache.insert(nested_class->get_name(), nested_class);
nested_classes.push_back(nested_class);
}
}
}
gdobject_class_cache.insert(current->get_name(), current);
}
gdobject_class_cache_updated = true;
}
return match;
}
GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll"))
return GDMono::get_singleton()->get_corlib_assembly();
// We need to manually call the search hook in this case, as it won't be called in the next step
MonoAssembly *assembly = mono_assembly_invoke_search_hook(p_aname);
if (!assembly) {
assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs);
if (!assembly) {
return nullptr;
}
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
ERR_FAIL_COND_V(loaded_asm->get_assembly() != assembly, nullptr);
return loaded_asm;
}
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
if (p_name == "mscorlib" || p_name == "mscorlib.dll")
return GDMono::get_singleton()->get_corlib_assembly();
// We need to manually call the search hook in this case, as it won't be called in the next step
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
MonoAssembly *assembly = mono_assembly_invoke_search_hook(aname);
mono_assembly_name_free(aname);
mono_free(aname);
2017-10-02 21:24:00 +00:00
if (!assembly) {
assembly = _real_load_assembly_from(p_path, p_refonly);
if (!assembly) {
return nullptr;
}
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
ERR_FAIL_NULL_V_MSG(loaded_asm, NULL, "Loaded assembly missing from table. Did we not receive the load hook?");
return loaded_asm;
}
GDMonoAssembly::GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly) :
name(p_name),
image(p_image),
assembly(p_assembly),
#ifdef GD_MONO_HOT_RELOAD
modified_time(0),
#endif
gdobject_class_cache_updated(false) {
2017-10-02 21:24:00 +00:00
}
GDMonoAssembly::~GDMonoAssembly() {
if (image)
2017-10-02 21:24:00 +00:00
unload();
}