Mono: Add project export plugin

(cherry picked from commit 9fd606c549)
This commit is contained in:
Ignacio Etcheverry 2018-02-22 13:39:41 +01:00 committed by Hein-Pieter van Braam
parent d02c891a4b
commit 9e4d34aebb
12 changed files with 403 additions and 51 deletions

View File

@ -118,6 +118,8 @@ void CSharpLanguage::init() {
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&gdsharp_editor_init_callback); EditorNode::add_init_callback(&gdsharp_editor_init_callback);
GLOBAL_DEF("mono/export/include_scripts_content", true);
#endif #endif
} }

View File

@ -201,7 +201,7 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &
memdelete(da); memdelete(da);
if (err != OK) { if (err != OK) {
show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll"); show_build_error_dialog("Failed to copy " + assembly_file);
return false; return false;
} }
@ -297,7 +297,7 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
return true; return true;
} }
bool GodotSharpBuilds::build_project_blocking() { bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build return true; // No solution to build
@ -312,7 +312,7 @@ bool GodotSharpBuilds::build_project_blocking() {
pr.step("Building project solution"); pr.step("Building project solution");
MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), "Tools"); MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
if (!GodotSharpBuilds::get_singleton()->build(build_info)) { if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
GodotSharpBuilds::show_build_error_dialog("Failed to build project solution"); GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
return false; return false;
@ -323,6 +323,11 @@ bool GodotSharpBuilds::build_project_blocking() {
return true; return true;
} }
bool GodotSharpBuilds::editor_build_callback() {
return build_project_blocking("Tools");
}
GodotSharpBuilds *GodotSharpBuilds::singleton = NULL; GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) { void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
@ -376,7 +381,7 @@ GodotSharpBuilds::GodotSharpBuilds() {
singleton = this; singleton = this;
EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking); EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback);
// Build tool settings // Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton(); EditorSettings *ed_settings = EditorSettings::get_singleton();
@ -476,6 +481,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
if (ex) { if (ex) {
exited = true; exited = true;
GDMonoUtils::print_unhandled_exception(ex);
String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
build_tab->on_build_exec_failed(message); build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message); ERR_EXPLAIN(message);
@ -496,6 +502,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
if (ex) { if (ex) {
exited = true; exited = true;
GDMonoUtils::print_unhandled_exception(ex);
String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
build_tab->on_build_exec_failed(message); build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message); ERR_EXPLAIN(message);

View File

@ -91,7 +91,9 @@ public:
static bool make_api_sln(APIAssembly::Type p_api_type); static bool make_api_sln(APIAssembly::Type p_api_type);
static bool build_project_blocking(); static bool build_project_blocking(const String &p_config);
static bool editor_build_callback();
GodotSharpBuilds(); GodotSharpBuilds();
~GodotSharpBuilds(); ~GodotSharpBuilds();

View File

@ -41,6 +41,7 @@
#include "../utils/path_utils.h" #include "../utils/path_utils.h"
#include "bindings_generator.h" #include "bindings_generator.h"
#include "csharp_project.h" #include "csharp_project.h"
#include "godotsharp_export.h"
#include "net_solution.h" #include "net_solution.h"
#ifdef WINDOWS_ENABLED #ifdef WINDOWS_ENABLED
@ -316,6 +317,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
EditorSettings *ed_settings = EditorSettings::get_singleton(); EditorSettings *ed_settings = EditorSettings::get_singleton();
EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE); EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE);
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code")); ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code"));
// Export plugin
Ref<GodotSharpExport> godotsharp_export;
godotsharp_export.instance();
EditorExport::get_singleton()->add_export_plugin(godotsharp_export);
} }
GodotSharpEditor::~GodotSharpEditor() { GodotSharpEditor::~GodotSharpEditor() {

View File

@ -32,7 +32,6 @@
#define GODOTSHARP_EDITOR_H #define GODOTSHARP_EDITOR_H
#include "godotsharp_builds.h" #include "godotsharp_builds.h"
#include "monodevelop_instance.h" #include "monodevelop_instance.h"
class GodotSharpEditor : public Node { class GodotSharpEditor : public Node {

View File

@ -0,0 +1,163 @@
/*************************************************************************/
/* godotsharp_export.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godotsharp_export.h"
#include "../csharp_script.h"
#include "../godotsharp_defs.h"
#include "../godotsharp_dirs.h"
#include "godotsharp_builds.h"
void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
if (p_type != CSharpLanguage::get_singleton()->get_type())
return;
ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension());
// TODO what if the source file is not part of the game's C# project
if (!GLOBAL_GET("mono/export/include_scripts_content")) {
// We don't want to include the source code on exported games
add_file(p_path, Vector<uint8_t>(), false);
skip();
}
}
void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
// TODO right now there is no way to stop the export process with an error
String build_config = p_debug ? "Debug" : "Release";
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
// Add API assemblies
String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll");
ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path));
String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path));
// Add project assembly
String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
if (project_dll_name.empty()) {
project_dll_name = "UnnamedProject";
}
String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll");
String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll");
ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path));
// Add dependencies
MonoDomain *prev_domain = mono_domain_get();
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
ERR_FAIL_COND(!export_domain);
ERR_FAIL_COND(!mono_domain_set(export_domain, false));
Map<String, String> dependencies;
dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path());
GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true);
ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
ERR_FAIL_COND(!scripts_assembly);
Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies);
GDMono::get_singleton()->finalize_and_unload_domain(export_domain);
mono_domain_set(prev_domain, false);
ERR_FAIL_COND(depend_error != OK);
for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) {
String depend_src_path = E->value();
String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file());
ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path));
}
}
bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) {
FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ);
ERR_FAIL_COND_V(!f, false);
Vector<uint8_t> data;
data.resize(f->get_len());
f->get_buffer(data.ptrw(), data.size());
add_file(p_dst_path, data, false);
return true;
}
Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) {
MonoImage *image = p_assembly->get_image();
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
MonoAssemblyName *ref_aname = aname_prealloc;
mono_assembly_get_assemblyref(image, i, ref_aname);
String ref_name = mono_assembly_name_get_name(ref_aname);
if (ref_name == "mscorlib" || r_dependencies.find(ref_name))
continue;
GDMonoAssembly *ref_assembly = NULL;
if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) {
ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name);
ERR_FAIL_V(ERR_CANT_RESOLVE);
}
r_dependencies.insert(ref_name, ref_assembly->get_path());
Error err = _get_assembly_dependencies(ref_assembly, r_dependencies);
if (err != OK)
return err;
}
return OK;
}
GodotSharpExport::GodotSharpExport() {
// MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves.
// There isn't any api to allocate an empty one either, so we need to do it this way.
aname_prealloc = mono_assembly_name_new("whatever");
mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included)
}
GodotSharpExport::~GodotSharpExport() {
if (aname_prealloc)
mono_free(aname_prealloc);
}

View File

@ -0,0 +1,57 @@
/*************************************************************************/
/* godotsharp_export.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 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. */
/*************************************************************************/
#ifndef GODOTSHARP_EXPORT_H
#define GODOTSHARP_EXPORT_H
#include <mono/metadata/image.h>
#include "editor/editor_export.h"
#include "../mono_gd/gd_mono_header.h"
class GodotSharpExport : public EditorExportPlugin {
MonoAssemblyName *aname_prealloc;
bool _add_assembly(const String &p_src_path, const String &p_dst_path);
Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies);
protected:
virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features);
virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags);
public:
GodotSharpExport();
~GodotSharpExport();
};
#endif // GODOTSHARP_EXPORT_H

View File

@ -142,7 +142,7 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) {
void MonoBottomPanel::_build_project_pressed() { void MonoBottomPanel::_build_project_pressed() {
GodotSharpBuilds::get_singleton()->build_project_blocking(); GodotSharpBuilds::get_singleton()->build_project_blocking("Tools");
MonoReloadNode::get_singleton()->restart_reload_timer(); MonoReloadNode::get_singleton()->restart_reload_timer();
CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);

View File

@ -317,33 +317,44 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
return assemblies[domain_id].getptr(p_name); return assemblies[domain_id].getptr(p_name);
} }
bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) { bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
CRASH_COND(!r_assembly);
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
mono_assembly_name_free(aname);
mono_free(aname);
return result;
}
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
CRASH_COND(!r_assembly); CRASH_COND(!r_assembly);
if (OS::get_singleton()->is_stdout_verbose()) if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8()); OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...\n").utf8());
MonoImageOpenStatus status = MONO_IMAGE_OK; MonoImageOpenStatus status = MONO_IMAGE_OK;
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false);
mono_assembly_name_free(aname);
if (!assembly) if (!assembly)
return false; return false;
ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
uint32_t domain_id = mono_domain_get_id(mono_domain_get()); uint32_t domain_id = mono_domain_get_id(mono_domain_get());
GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
ERR_FAIL_COND_V(stored_assembly == NULL, false); ERR_FAIL_COND_V(stored_assembly == NULL, false);
ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false); ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
*r_assembly = *stored_assembly; *r_assembly = *stored_assembly;
if (OS::get_singleton()->is_stdout_verbose()) if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->print(String("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8()); OS::get_singleton()->print(String("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
return true; return true;
} }
@ -383,7 +394,7 @@ bool GDMono::_load_corlib_assembly() {
if (corlib_assembly) if (corlib_assembly)
return true; return true;
bool success = _load_assembly("mscorlib", &corlib_assembly); bool success = load_assembly("mscorlib", &corlib_assembly);
if (success) if (success)
GDMonoUtils::update_corlib_cache(); GDMonoUtils::update_corlib_cache();
@ -401,7 +412,7 @@ bool GDMono::_load_core_api_assembly() {
return false; return false;
#endif #endif
bool success = _load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); bool success = load_assembly(API_ASSEMBLY_NAME, &api_assembly);
if (success) { if (success) {
#ifndef MONO_GLUE_DISABLED #ifndef MONO_GLUE_DISABLED
@ -427,7 +438,7 @@ bool GDMono::_load_editor_api_assembly() {
return false; return false;
#endif #endif
bool success = _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
if (success) { if (success) {
#ifndef MONO_GLUE_DISABLED #ifndef MONO_GLUE_DISABLED
@ -450,7 +461,7 @@ bool GDMono::_load_editor_tools_assembly() {
_GDMONO_SCOPE_DOMAIN_(tools_domain) _GDMONO_SCOPE_DOMAIN_(tools_domain)
return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
} }
#endif #endif
@ -464,7 +475,7 @@ bool GDMono::_load_project_assembly() {
name = "UnnamedProject"; name = "UnnamedProject";
} }
bool success = _load_assembly(name, &project_assembly); bool success = load_assembly(name, &project_assembly);
if (success) { if (success) {
mono_assembly_set_main(project_assembly->get_assembly()); mono_assembly_set_main(project_assembly->get_assembly());
@ -706,6 +717,37 @@ Error GDMono::reload_scripts_domain() {
} }
#endif #endif
Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
CRASH_COND(p_domain == NULL);
String domain_name = mono_domain_get_friendly_name(p_domain);
if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print(String("Mono: Unloading domain `" + domain_name + "`...\n").utf8());
}
if (mono_domain_get() != root_domain)
mono_domain_set(root_domain, true);
mono_gc_collect(mono_gc_max_generation());
mono_domain_finalize(p_domain, 2000);
mono_gc_collect(mono_gc_max_generation());
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
MonoObject *ex = NULL;
mono_domain_try_unload(p_domain, &ex);
if (ex) {
ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:");
mono_print_unhandled_exception(ex);
return FAILED;
}
return OK;
}
GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
MonoImage *image = mono_class_get_image(p_raw_class); MonoImage *image = mono_class_get_image(p_raw_class);

View File

@ -140,8 +140,6 @@ class GDMono {
void _initialize_and_check_api_hashes(); void _initialize_and_check_api_hashes();
#endif #endif
bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly);
GDMonoLog *gdmono_log; GDMonoLog *gdmono_log;
#ifdef WINDOWS_ENABLED #ifdef WINDOWS_ENABLED
@ -196,6 +194,10 @@ public:
Error reload_scripts_domain(); Error reload_scripts_domain();
#endif #endif
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
Error finalize_and_unload_domain(MonoDomain *p_domain);
void initialize(); void initialize();
GDMono(); GDMono();

View File

@ -43,7 +43,23 @@
bool GDMonoAssembly::no_search = false; bool GDMonoAssembly::no_search = false;
Vector<String> GDMonoAssembly::search_dirs; Vector<String> GDMonoAssembly::search_dirs;
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) { 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) {
(void)user_data; // UNUSED (void)user_data; // UNUSED
@ -60,7 +76,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
no_search = true; // Avoid the recursion madness no_search = true; // Avoid the recursion madness
String path; String path;
MonoAssembly *res = NULL; GDMonoAssembly *res = NULL;
for (int i = 0; i < search_dirs.size(); i++) { for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i]; const String &search_dir = search_dirs[i];
@ -68,19 +84,19 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
if (has_extension) { if (has_extension) {
path = search_dir.plus_file(name); path = search_dir.plus_file(name);
if (FileAccess::exists(path)) { if (FileAccess::exists(path)) {
res = _load_assembly_from(name.get_basename(), path); res = _load_assembly_from(name.get_basename(), path, refonly);
break; break;
} }
} else { } else {
path = search_dir.plus_file(name + ".dll"); path = search_dir.plus_file(name + ".dll");
if (FileAccess::exists(path)) { if (FileAccess::exists(path)) {
res = _load_assembly_from(name, path); res = _load_assembly_from(name, path, refonly);
break; break;
} }
path = search_dir.plus_file(name + ".exe"); path = search_dir.plus_file(name + ".exe");
if (FileAccess::exists(path)) { if (FileAccess::exists(path)) {
res = _load_assembly_from(name, path); res = _load_assembly_from(name, path, refonly);
break; break;
} }
} }
@ -88,17 +104,15 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
no_search = false; no_search = false;
return res; return res ? res->get_assembly() : NULL;
} }
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) {
(void)user_data; // UNUSED (void)user_data; // UNUSED
if (search_dirs.empty()) { if (search_dirs.empty()) {
#ifdef TOOLS_DOMAIN
search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
#endif
search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
search_dirs.push_back(OS::get_singleton()->get_resource_dir()); search_dirs.push_back(OS::get_singleton()->get_resource_dir());
search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
@ -119,38 +133,78 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
} }
} }
String name = mono_assembly_name_get_name(aname);
bool has_extension = name.ends_with(".dll");
if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") {
GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
if (stored_assembly)
return (*stored_assembly)->get_assembly();
String path;
GDMonoAssembly *res = NULL;
for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i];
if (has_extension) {
path = search_dir.plus_file(name);
if (FileAccess::exists(path)) {
res = _load_assembly_from(name.get_basename(), path, refonly);
break;
}
} else {
path = search_dir.plus_file(name + ".dll");
if (FileAccess::exists(path)) {
res = _load_assembly_from(name, path, refonly);
break;
}
path = search_dir.plus_file(name + ".exe");
if (FileAccess::exists(path)) {
res = _load_assembly_from(name, path, refonly);
break;
}
}
}
return res ? res->get_assembly() : NULL;
}
return NULL; return NULL;
} }
MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) { GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path)); GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
MonoDomain *domain = mono_domain_get(); Error err = assembly->load(p_refonly);
Error err = assembly->load(domain);
if (err != OK) { if (err != OK) {
memdelete(assembly); memdelete(assembly);
ERR_FAIL_V(NULL); ERR_FAIL_V(NULL);
} }
MonoDomain *domain = mono_domain_get();
GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly); GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly);
return assembly->get_assembly(); return assembly;
} }
void GDMonoAssembly::initialize() { void GDMonoAssembly::initialize() {
// TODO refonly as well? mono_install_assembly_search_hook(&assembly_search_hook, NULL);
mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL); mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL);
mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL); mono_install_assembly_preload_hook(&assembly_preload_hook, NULL);
mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL);
} }
Error GDMonoAssembly::load(MonoDomain *p_domain) { Error GDMonoAssembly::load(bool p_refonly) {
ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE); ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
refonly = p_refonly;
uint64_t last_modified_time = FileAccess::get_modified_time(path); uint64_t last_modified_time = FileAccess::get_modified_time(path);
Vector<uint8_t> data = FileAccess::get_file_as_array(path); Vector<uint8_t> data = FileAccess::get_file_as_array(path);
@ -162,7 +216,7 @@ Error GDMonoAssembly::load(MonoDomain *p_domain) {
image = mono_image_open_from_data_with_name( image = mono_image_open_from_data_with_name(
(char *)&data[0], data.size(), (char *)&data[0], data.size(),
true, &status, false, true, &status, refonly,
image_filename.utf8().get_data()); image_filename.utf8().get_data());
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN); ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN);
@ -185,15 +239,10 @@ no_pdb:
#endif #endif
assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false); assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly);
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN); ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
if (p_domain && mono_image_get_entry_point(image)) {
// TODO should this be removed? do we want to call main? what other effects does this have?
mono_jit_exec(p_domain, assembly, 0, NULL);
}
loaded = true; loaded = true;
modified_time = last_modified_time; modified_time = last_modified_time;
@ -345,12 +394,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
return match; return match;
} }
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
if (loaded_asm)
return *loaded_asm;
no_search = true;
GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly);
no_search = false;
return res;
}
GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) { GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
loaded = false; loaded = false;
gdobject_class_cache_updated = false; gdobject_class_cache_updated = false;
name = p_name; name = p_name;
path = p_path; path = p_path;
refonly = false;
modified_time = 0; modified_time = 0;
assembly = NULL; assembly = NULL;
image = NULL; image = NULL;

View File

@ -71,6 +71,7 @@ class GDMonoAssembly {
MonoAssembly *assembly; MonoAssembly *assembly;
MonoImage *image; MonoImage *image;
bool refonly;
bool loaded; bool loaded;
String name; String name;
@ -90,19 +91,25 @@ class GDMonoAssembly {
static bool no_search; static bool no_search;
static Vector<String> search_dirs; static Vector<String> search_dirs;
static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data); static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
static MonoAssembly *_load_assembly_from(const String &p_name, const String &p_path); static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
friend class GDMono; friend class GDMono;
static void initialize(); static void initialize();
public: public:
Error load(MonoDomain *p_domain); Error load(bool p_refonly);
Error wrapper_for_image(MonoImage *p_image); Error wrapper_for_image(MonoImage *p_image);
void unload(); void unload();
_FORCE_INLINE_ bool is_refonly() const { return refonly; }
_FORCE_INLINE_ bool is_loaded() const { return loaded; } _FORCE_INLINE_ bool is_loaded() const { return loaded; }
_FORCE_INLINE_ MonoImage *get_image() const { return image; } _FORCE_INLINE_ MonoImage *get_image() const { return image; }
_FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; } _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
@ -115,6 +122,8 @@ public:
GDMonoClass *get_object_derived_class(const StringName &p_class); GDMonoClass *get_object_derived_class(const StringName &p_class);
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
GDMonoAssembly(const String &p_name, const String &p_path = String()); GDMonoAssembly(const String &p_name, const String &p_path = String());
~GDMonoAssembly(); ~GDMonoAssembly();
}; };