C#/Mono: Check assembly version when loading
Not sure if we should check revision too, but this is good enough for what we want. This will be needed to load the correct Microsoft.Build when we switch to the nuget version.
This commit is contained in:
parent
61306eb830
commit
af4acb5b11
|
@ -1664,6 +1664,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p_imethod.is_internal) {
|
if (!p_imethod.is_internal) {
|
||||||
|
// TODO: This alone adds ~0.2 MB of bloat to the core API assembly. It would be
|
||||||
|
// better to generate a table in the C++ glue instead. That way the strings wouldn't
|
||||||
|
// add that much extra bloat as they're already used in engine code. Also, it would
|
||||||
|
// probably be much faster than looking up the attributes when fetching methods.
|
||||||
p_output.append(MEMBER_BEGIN "[GodotMethod(\"");
|
p_output.append(MEMBER_BEGIN "[GodotMethod(\"");
|
||||||
p_output.append(p_imethod.name);
|
p_output.append(p_imethod.name);
|
||||||
p_output.append("\")]");
|
p_output.append("\")]");
|
||||||
|
@ -2139,7 +2143,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
|
||||||
if (return_type->ret_as_byref_arg) {
|
if (return_type->ret_as_byref_arg) {
|
||||||
p_output.append("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = ");
|
p_output.append("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = ");
|
||||||
p_output.append(fail_ret);
|
p_output.append(fail_ret);
|
||||||
p_output.append("; ERR_FAIL_MSG(\"Parameter ' arg_ret ' is null.\"); }\n");
|
p_output.append("; ERR_FAIL_MSG(\"Parameter ' " CS_PARAM_INSTANCE " ' is null.\"); }\n");
|
||||||
} else {
|
} else {
|
||||||
p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", ");
|
p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", ");
|
||||||
p_output.append(fail_ret);
|
p_output.append(fail_ret);
|
||||||
|
@ -2390,6 +2394,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
|
||||||
if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY)
|
if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (property.name.find("/") >= 0) {
|
||||||
|
// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
PropertyInterface iprop;
|
PropertyInterface iprop;
|
||||||
iprop.cname = property.name;
|
iprop.cname = property.name;
|
||||||
iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname);
|
iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname);
|
||||||
|
@ -2402,7 +2411,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
|
||||||
|
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid);
|
iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid);
|
||||||
ERR_FAIL_COND_V(!valid, false);
|
ERR_FAIL_COND_V_MSG(!valid, false, "Invalid property: '" + itype.name + "." + String(iprop.cname) + "'.");
|
||||||
|
|
||||||
iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname));
|
iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname));
|
||||||
|
|
||||||
|
@ -2414,8 +2423,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
|
||||||
iprop.proxy_name += "_";
|
iprop.proxy_name += "_";
|
||||||
}
|
}
|
||||||
|
|
||||||
iprop.proxy_name = iprop.proxy_name.replace("/", "__"); // Some members have a slash...
|
|
||||||
|
|
||||||
iprop.prop_doc = nullptr;
|
iprop.prop_doc = nullptr;
|
||||||
|
|
||||||
for (int i = 0; i < itype.class_doc->properties.size(); i++) {
|
for (int i = 0; i < itype.class_doc->properties.size(); i++) {
|
||||||
|
|
|
@ -32,68 +32,70 @@
|
||||||
|
|
||||||
#include <mono/metadata/image.h>
|
#include <mono/metadata/image.h>
|
||||||
|
|
||||||
|
#include "core/io/file_access_pack.h"
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
|
#include "core/project_settings.h"
|
||||||
|
|
||||||
#include "../mono_gd/gd_mono.h"
|
#include "../mono_gd/gd_mono.h"
|
||||||
#include "../mono_gd/gd_mono_assembly.h"
|
#include "../mono_gd/gd_mono_assembly.h"
|
||||||
#include "../mono_gd/gd_mono_cache.h"
|
#include "../mono_gd/gd_mono_cache.h"
|
||||||
|
#include "../utils/macros.h"
|
||||||
|
|
||||||
namespace GodotSharpExport {
|
namespace GodotSharpExport {
|
||||||
|
|
||||||
String get_assemblyref_name(MonoImage *p_image, int index) {
|
struct AssemblyRefInfo {
|
||||||
|
String name;
|
||||||
|
uint16_t major;
|
||||||
|
uint16_t minor;
|
||||||
|
uint16_t build;
|
||||||
|
uint16_t revision;
|
||||||
|
};
|
||||||
|
|
||||||
|
AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
|
||||||
const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
|
const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
|
||||||
|
|
||||||
uint32_t cols[MONO_ASSEMBLYREF_SIZE];
|
uint32_t cols[MONO_ASSEMBLYREF_SIZE];
|
||||||
|
|
||||||
mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
|
mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
|
||||||
|
|
||||||
return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));
|
return {
|
||||||
|
String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])),
|
||||||
|
(uint16_t)cols[MONO_ASSEMBLYREF_MAJOR_VERSION],
|
||||||
|
(uint16_t)cols[MONO_ASSEMBLYREF_MINOR_VERSION],
|
||||||
|
(uint16_t)cols[MONO_ASSEMBLYREF_BUILD_NUMBER],
|
||||||
|
(uint16_t)cols[MONO_ASSEMBLYREF_REV_NUMBER]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
|
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
|
||||||
MonoImage *image = p_assembly->get_image();
|
MonoImage *image = p_assembly->get_image();
|
||||||
|
|
||||||
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
|
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
|
||||||
String ref_name = get_assemblyref_name(image, i);
|
AssemblyRefInfo ref_info = get_assemblyref_name(image, i);
|
||||||
|
|
||||||
|
const String &ref_name = ref_info.name;
|
||||||
|
|
||||||
if (r_assembly_dependencies.has(ref_name))
|
if (r_assembly_dependencies.has(ref_name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GDMonoAssembly *ref_assembly = nullptr;
|
GDMonoAssembly *ref_assembly = NULL;
|
||||||
String path;
|
|
||||||
bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe");
|
|
||||||
|
|
||||||
for (int j = 0; j < p_search_dirs.size(); j++) {
|
{
|
||||||
const String &search_dir = p_search_dirs[j];
|
MonoAssemblyName *ref_aname = mono_assembly_name_new("A"); // We can't allocate an empty MonoAssemblyName, hence "A"
|
||||||
|
CRASH_COND(ref_aname == nullptr);
|
||||||
|
SCOPE_EXIT {
|
||||||
|
mono_assembly_name_free(ref_aname);
|
||||||
|
mono_free(ref_aname);
|
||||||
|
};
|
||||||
|
|
||||||
if (has_extension) {
|
mono_assembly_get_assemblyref(image, i, ref_aname);
|
||||||
path = search_dir.plus_file(ref_name);
|
|
||||||
if (FileAccess::exists(path)) {
|
if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
|
||||||
GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), path, &ref_assembly, true);
|
ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
|
||||||
if (ref_assembly != nullptr)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
path = search_dir.plus_file(ref_name + ".dll");
|
|
||||||
if (FileAccess::exists(path)) {
|
|
||||||
GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
|
|
||||||
if (ref_assembly != nullptr)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path = search_dir.plus_file(ref_name + ".exe");
|
r_assembly_dependencies[ref_name] = ref_assembly->get_path();
|
||||||
if (FileAccess::exists(path)) {
|
|
||||||
GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
|
|
||||||
if (ref_assembly != nullptr)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
|
|
||||||
|
|
||||||
// Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir.
|
|
||||||
r_assembly_dependencies[ref_name] = path;
|
|
||||||
|
|
||||||
Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies);
|
Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies);
|
||||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
|
||||||
|
@ -113,6 +115,11 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
|
||||||
Vector<String> search_dirs;
|
Vector<String> search_dirs;
|
||||||
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
|
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
|
||||||
|
|
||||||
|
if (p_custom_bcl_dir.length()) {
|
||||||
|
// Only one mscorlib can be loaded. We need this workaround to make sure we get it from the right BCL directory.
|
||||||
|
r_assembly_dependencies["mscorlib"] = p_custom_bcl_dir.plus_file("mscorlib.dll").simplify_path();
|
||||||
|
}
|
||||||
|
|
||||||
for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
|
for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
|
||||||
String assembly_name = *key;
|
String assembly_name = *key;
|
||||||
String assembly_path = p_initial_assemblies[*key];
|
String assembly_path = p_initial_assemblies[*key];
|
||||||
|
|
|
@ -82,6 +82,7 @@ CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectID ManagedCallable::get_object() const {
|
ObjectID ManagedCallable::get_object() const {
|
||||||
|
// TODO: If the delegate target extends Godot.Object, use that instead!
|
||||||
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
|
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -515,8 +515,8 @@ void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
|
||||||
|
|
||||||
GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
|
GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
|
||||||
|
|
||||||
if (p_name == "mscorlib")
|
if (p_name == "mscorlib" && corlib_assembly)
|
||||||
return get_corlib_assembly();
|
return corlib_assembly;
|
||||||
|
|
||||||
MonoDomain *domain = mono_domain_get();
|
MonoDomain *domain = mono_domain_get();
|
||||||
uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
|
uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
|
||||||
|
@ -526,7 +526,9 @@ GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
|
||||||
|
|
||||||
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
|
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
CRASH_COND(!r_assembly);
|
CRASH_COND(!r_assembly);
|
||||||
|
#endif
|
||||||
|
|
||||||
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
|
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
|
||||||
bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
|
bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
|
||||||
|
@ -538,26 +540,27 @@ bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bo
|
||||||
|
|
||||||
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
|
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
CRASH_COND(!r_assembly);
|
CRASH_COND(!r_assembly);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return load_assembly(p_name, p_aname, r_assembly, p_refonly, GDMonoAssembly::get_default_search_dirs());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs) {
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
CRASH_COND(!r_assembly);
|
||||||
|
#endif
|
||||||
|
|
||||||
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
|
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
|
||||||
|
|
||||||
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, p_refonly, p_search_dirs);
|
||||||
MonoAssembly *assembly = mono_assembly_load_full(p_aname, nullptr, &status, p_refonly);
|
|
||||||
|
|
||||||
if (!assembly)
|
if (!assembly)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
|
*r_assembly = assembly;
|
||||||
|
|
||||||
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
|
|
||||||
|
|
||||||
GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
|
|
||||||
|
|
||||||
ERR_FAIL_COND_V(stored_assembly == nullptr, false);
|
|
||||||
ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
|
|
||||||
|
|
||||||
*r_assembly = *stored_assembly;
|
|
||||||
|
|
||||||
print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
|
print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
|
||||||
|
|
||||||
|
|
|
@ -241,6 +241,7 @@ public:
|
||||||
|
|
||||||
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
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);
|
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||||
|
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs);
|
||||||
bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||||
|
|
||||||
Error finalize_and_unload_domain(MonoDomain *p_domain);
|
Error finalize_and_unload_domain(MonoDomain *p_domain);
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <mono/metadata/mono-debug.h>
|
#include <mono/metadata/mono-debug.h>
|
||||||
#include <mono/metadata/tokentype.h>
|
#include <mono/metadata/tokentype.h>
|
||||||
|
|
||||||
|
#include "core/io/file_access_pack.h"
|
||||||
#include "core/list.h"
|
#include "core/list.h"
|
||||||
#include "core/os/file_access.h"
|
#include "core/os/file_access.h"
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
|
@ -99,7 +100,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
|
||||||
// - The 'load' hook is called after the assembly has been loaded. Its job is to add the
|
// - 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.
|
// 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) {
|
void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] void *user_data) {
|
||||||
|
|
||||||
String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
|
String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
|
||||||
|
|
||||||
|
@ -133,9 +134,7 @@ MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *an
|
||||||
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
|
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) {
|
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unused]] void *user_data, bool refonly) {
|
||||||
|
|
||||||
(void)user_data; // UNUSED
|
|
||||||
|
|
||||||
String name = String::utf8(mono_assembly_name_get_name(aname));
|
String name = String::utf8(mono_assembly_name_get_name(aname));
|
||||||
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
|
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
|
||||||
|
@ -147,15 +146,13 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
|
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, [[maybe_unused]] void *user_data, bool refonly) {
|
||||||
|
|
||||||
(void)user_data; // UNUSED
|
|
||||||
|
|
||||||
String name = String::utf8(mono_assembly_name_get_name(aname));
|
String name = String::utf8(mono_assembly_name_get_name(aname));
|
||||||
return _load_assembly_search(name, search_dirs, refonly);
|
return _load_assembly_search(name, aname, refonly, search_dirs);
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) {
|
MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
|
||||||
|
|
||||||
MonoAssembly *res = nullptr;
|
MonoAssembly *res = nullptr;
|
||||||
String path;
|
String path;
|
||||||
|
@ -168,21 +165,21 @@ MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const
|
||||||
if (has_extension) {
|
if (has_extension) {
|
||||||
path = search_dir.plus_file(p_name);
|
path = search_dir.plus_file(p_name);
|
||||||
if (FileAccess::exists(path)) {
|
if (FileAccess::exists(path)) {
|
||||||
res = _real_load_assembly_from(path, p_refonly);
|
res = _real_load_assembly_from(path, p_refonly, p_aname);
|
||||||
if (res != nullptr)
|
if (res != nullptr)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
path = search_dir.plus_file(p_name + ".dll");
|
path = search_dir.plus_file(p_name + ".dll");
|
||||||
if (FileAccess::exists(path)) {
|
if (FileAccess::exists(path)) {
|
||||||
res = _real_load_assembly_from(path, p_refonly);
|
res = _real_load_assembly_from(path, p_refonly, p_aname);
|
||||||
if (res != nullptr)
|
if (res != nullptr)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = search_dir.plus_file(p_name + ".exe");
|
path = search_dir.plus_file(p_name + ".exe");
|
||||||
if (FileAccess::exists(path)) {
|
if (FileAccess::exists(path)) {
|
||||||
res = _real_load_assembly_from(path, p_refonly);
|
res = _real_load_assembly_from(path, p_refonly, p_aname);
|
||||||
if (res != nullptr)
|
if (res != nullptr)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -230,7 +227,7 @@ void GDMonoAssembly::initialize() {
|
||||||
mono_install_assembly_load_hook(&assembly_load_hook, nullptr);
|
mono_install_assembly_load_hook(&assembly_load_hook, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly) {
|
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);
|
Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
|
||||||
ERR_FAIL_COND_V_MSG(data.empty(), nullptr, "Could read the assembly in the specified location");
|
ERR_FAIL_COND_V_MSG(data.empty(), nullptr, "Could read the assembly in the specified location");
|
||||||
|
@ -255,7 +252,33 @@ MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, boo
|
||||||
true, &status, p_refonly,
|
true, &status, p_refonly,
|
||||||
image_filename.utf8());
|
image_filename.utf8());
|
||||||
|
|
||||||
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from the loaded data");
|
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
Vector<uint8_t> pdb_data;
|
Vector<uint8_t> pdb_data;
|
||||||
|
@ -425,6 +448,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
|
||||||
return match;
|
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);
|
||||||
|
ERR_FAIL_NULL_V(assembly, 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) {
|
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
|
||||||
|
|
||||||
if (p_name == "mscorlib" || p_name == "mscorlib.dll")
|
if (p_name == "mscorlib" || p_name == "mscorlib.dll")
|
||||||
|
|
|
@ -93,8 +93,8 @@ class GDMonoAssembly {
|
||||||
static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
|
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 MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
|
||||||
|
|
||||||
static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly);
|
static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname = nullptr);
|
||||||
static MonoAssembly *_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly);
|
static MonoAssembly *_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
|
||||||
|
|
||||||
friend class GDMono;
|
friend class GDMono;
|
||||||
static void initialize();
|
static void initialize();
|
||||||
|
@ -120,7 +120,9 @@ public:
|
||||||
static String find_assembly(const String &p_name);
|
static String find_assembly(const String &p_name);
|
||||||
|
|
||||||
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
|
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
|
||||||
|
static const Vector<String> &get_default_search_dirs() { return search_dirs; }
|
||||||
|
|
||||||
|
static GDMonoAssembly *load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
|
||||||
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
|
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
|
||||||
|
|
||||||
GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly);
|
GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly);
|
||||||
|
|
|
@ -175,7 +175,7 @@ void GDMonoLog::initialize() {
|
||||||
log_level_id = get_log_level_id(log_level.get_data());
|
log_level_id = get_log_level_id(log_level.get_data());
|
||||||
|
|
||||||
if (log_file) {
|
if (log_file) {
|
||||||
OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data());
|
OS::get_singleton()->print("Mono: Log file is: '%s'\n", log_file_path.utf8().get_data());
|
||||||
mono_trace_set_log_handler(mono_log_callback, this);
|
mono_trace_set_log_handler(mono_log_callback, this);
|
||||||
} else {
|
} else {
|
||||||
OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
|
OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
|
||||||
|
|
|
@ -68,6 +68,6 @@ public:
|
||||||
} // namespace gdmono
|
} // namespace gdmono
|
||||||
|
|
||||||
#define SCOPE_EXIT \
|
#define SCOPE_EXIT \
|
||||||
auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]()
|
auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]() -> void
|
||||||
|
|
||||||
#endif // UTIL_MACROS_H
|
#endif // UTIL_MACROS_H
|
||||||
|
|
Loading…
Reference in New Issue