Browse Source

Mono: Add project export plugin

Ignacio Etcheverry 7 years ago
parent
commit
9fd606c549

+ 2 - 0
modules/mono/csharp_script.cpp

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

+ 11 - 4
modules/mono/editor/godotsharp_builds.cpp

@@ -200,7 +200,7 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &
 		memdelete(da);
 
 		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;
 		}
 	}
@@ -283,7 +283,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
 	return true;
 }
 
-bool GodotSharpBuilds::build_project_blocking() {
+bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
 
 	if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
 		return true; // No solution to build
@@ -298,7 +298,7 @@ bool GodotSharpBuilds::build_project_blocking() {
 
 	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)) {
 		GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
 		return false;
@@ -309,6 +309,11 @@ bool GodotSharpBuilds::build_project_blocking() {
 	return true;
 }
 
+bool GodotSharpBuilds::editor_build_callback() {
+
+	return build_project_blocking("Tools");
+}
+
 GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
 
 void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
@@ -362,7 +367,7 @@ GodotSharpBuilds::GodotSharpBuilds() {
 
 	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
 	EditorSettings *ed_settings = EditorSettings::get_singleton();
@@ -462,6 +467,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
 
 	if (ex) {
 		exited = true;
+		GDMonoUtils::print_unhandled_exception(ex);
 		String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
 		build_tab->on_build_exec_failed(message);
 		ERR_EXPLAIN(message);
@@ -482,6 +488,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
 
 	if (ex) {
 		exited = true;
+		GDMonoUtils::print_unhandled_exception(ex);
 		String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
 		build_tab->on_build_exec_failed(message);
 		ERR_EXPLAIN(message);

+ 3 - 1
modules/mono/editor/godotsharp_builds.h

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

+ 6 - 0
modules/mono/editor/godotsharp_editor.cpp

@@ -41,6 +41,7 @@
 #include "../utils/path_utils.h"
 #include "bindings_generator.h"
 #include "csharp_project.h"
+#include "godotsharp_export.h"
 #include "net_solution.h"
 
 #ifdef WINDOWS_ENABLED
@@ -316,6 +317,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
 	EditorSettings *ed_settings = EditorSettings::get_singleton();
 	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"));
+
+	// Export plugin
+	Ref<GodotSharpExport> godotsharp_export;
+	godotsharp_export.instance();
+	EditorExport::get_singleton()->add_export_plugin(godotsharp_export);
 }
 
 GodotSharpEditor::~GodotSharpEditor() {

+ 0 - 1
modules/mono/editor/godotsharp_editor.h

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

+ 163 - 0
modules/mono/editor/godotsharp_export.cpp

@@ -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);
+}

+ 57 - 0
modules/mono/editor/godotsharp_export.h

@@ -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

+ 1 - 1
modules/mono/editor/mono_bottom_panel.cpp

@@ -142,7 +142,7 @@ void MonoBottomPanel::_errors_toggled(bool p_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();
 	CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);

+ 55 - 13
modules/mono/mono_gd/gd_mono.cpp

@@ -271,33 +271,44 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &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);
 
 	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;
-	MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
-	MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false);
-	mono_assembly_name_free(aname);
+	MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
 
 	if (!assembly)
 		return false;
 
+	ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
+
 	uint32_t domain_id = mono_domain_get_id(mono_domain_get());
 
 	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)->get_assembly() != assembly, false);
+
 	*r_assembly = *stored_assembly;
 
 	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;
 }
@@ -307,7 +318,7 @@ bool GDMono::_load_corlib_assembly() {
 	if (corlib_assembly)
 		return true;
 
-	bool success = _load_assembly("mscorlib", &corlib_assembly);
+	bool success = load_assembly("mscorlib", &corlib_assembly);
 
 	if (success)
 		GDMonoUtils::update_corlib_cache();
@@ -320,7 +331,7 @@ bool GDMono::_load_core_api_assembly() {
 	if (api_assembly)
 		return true;
 
-	bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly);
+	bool success = load_assembly(API_ASSEMBLY_NAME, &api_assembly);
 
 	if (success)
 		GDMonoUtils::update_godot_api_cache();
@@ -334,7 +345,7 @@ bool GDMono::_load_editor_api_assembly() {
 	if (editor_api_assembly)
 		return true;
 
-	return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
+	return load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
 }
 #endif
 
@@ -346,7 +357,7 @@ bool GDMono::_load_editor_tools_assembly() {
 
 	_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
 
@@ -360,7 +371,7 @@ bool GDMono::_load_project_assembly() {
 		name = "UnnamedProject";
 	}
 
-	bool success = _load_assembly(name, &project_assembly);
+	bool success = load_assembly(name, &project_assembly);
 
 	if (success)
 		mono_assembly_set_main(project_assembly->get_assembly());
@@ -511,6 +522,37 @@ Error GDMono::reload_scripts_domain() {
 }
 #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) {
 
 	MonoImage *image = mono_class_get_image(p_raw_class);

+ 4 - 2
modules/mono/mono_gd/gd_mono.h

@@ -94,8 +94,6 @@ class GDMono {
 	void _initialize_and_check_api_hashes();
 #endif
 
-	bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly);
-
 	GDMonoLog *gdmono_log;
 
 #ifdef WINDOWS_ENABLED
@@ -145,6 +143,10 @@ public:
 	Error reload_scripts_domain();
 #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();
 
 	GDMono();

+ 62 - 30
modules/mono/mono_gd/gd_mono_assembly.cpp

@@ -43,7 +43,23 @@
 bool GDMonoAssembly::no_search = false;
 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
 
@@ -60,7 +76,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
 	no_search = true; // Avoid the recursion madness
 
 	String path;
-	MonoAssembly *res = NULL;
+	GDMonoAssembly *res = NULL;
 
 	for (int i = 0; i < search_dirs.size(); i++) {
 		const String &search_dir = search_dirs[i];
@@ -68,19 +84,19 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
 		if (has_extension) {
 			path = search_dir.plus_file(name);
 			if (FileAccess::exists(path)) {
-				res = _load_assembly_from(name.get_basename(), 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);
+				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);
+				res = _load_assembly_from(name, path, refonly);
 				break;
 			}
 		}
@@ -88,17 +104,15 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
 
 	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
 
 	if (search_dirs.empty()) {
-#ifdef TOOLS_DOMAIN
 		search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
-#endif
 		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_executable_path().get_base_dir());
@@ -121,10 +135,11 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 
 	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();
+		if (stored_assembly)
+			return (*stored_assembly)->get_assembly();
 
 		String path;
-		MonoAssembly *res = NULL;
+		GDMonoAssembly *res = NULL;
 
 		for (int i = 0; i < search_dirs.size(); i++) {
 			const String &search_dir = search_dirs[i];
@@ -132,53 +147,61 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 			if (has_extension) {
 				path = search_dir.plus_file(name);
 				if (FileAccess::exists(path)) {
-					res = _load_assembly_from(name.get_basename(), 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);
+					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;
 				}
 			}
 		}
 
-		if (res) return res;
+		return res ? res->get_assembly() : 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));
 
-	MonoDomain *domain = mono_domain_get();
-
-	Error err = assembly->load(domain);
+	Error err = assembly->load(p_refonly);
 
 	if (err != OK) {
 		memdelete(assembly);
 		ERR_FAIL_V(NULL);
 	}
 
+	MonoDomain *domain = mono_domain_get();
 	GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly);
 
-	return assembly->get_assembly();
+	return assembly;
 }
 
 void GDMonoAssembly::initialize() {
 
-	// TODO refonly as well?
-	mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL);
-	mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL);
+	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);
 }
 
-Error GDMonoAssembly::load(MonoDomain *p_domain) {
+Error GDMonoAssembly::load(bool p_refonly) {
 
 	ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
 
+	refonly = p_refonly;
+
 	uint64_t last_modified_time = FileAccess::get_modified_time(path);
 
 	Vector<uint8_t> data = FileAccess::get_file_as_array(path);
@@ -190,7 +213,7 @@ Error GDMonoAssembly::load(MonoDomain *p_domain) {
 
 	image = mono_image_open_from_data_with_name(
 			(char *)&data[0], data.size(),
-			true, &status, false,
+			true, &status, refonly,
 			image_filename.utf8().get_data());
 
 	ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN);
@@ -213,15 +236,10 @@ no_pdb:
 
 #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);
 
-	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;
 	modified_time = last_modified_time;
 
@@ -373,12 +391,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
 	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) {
 
 	loaded = false;
 	gdobject_class_cache_updated = false;
 	name = p_name;
 	path = p_path;
+	refonly = false;
 	modified_time = 0;
 	assembly = NULL;
 	image = NULL;

+ 13 - 4
modules/mono/mono_gd/gd_mono_assembly.h

@@ -71,6 +71,7 @@ class GDMonoAssembly {
 	MonoAssembly *assembly;
 	MonoImage *image;
 
+	bool refonly;
 	bool loaded;
 
 	String name;
@@ -90,19 +91,25 @@ class GDMonoAssembly {
 	static bool no_search;
 	static Vector<String> search_dirs;
 
-	static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data);
-	static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
+	static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, 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;
 	static void initialize();
 
 public:
-	Error load(MonoDomain *p_domain);
+	Error load(bool p_refonly);
 	Error wrapper_for_image(MonoImage *p_image);
 	void unload();
 
+	_FORCE_INLINE_ bool is_refonly() const { return refonly; }
 	_FORCE_INLINE_ bool is_loaded() const { return loaded; }
 	_FORCE_INLINE_ MonoImage *get_image() const { return image; }
 	_FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
@@ -115,6 +122,8 @@ public:
 
 	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();
 };