summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/SCsub33
-rw-r--r--modules/mono/config.py26
-rw-r--r--modules/mono/csharp_script.cpp35
-rw-r--r--modules/mono/csharp_script.h7
-rw-r--r--modules/mono/doc_classes/@C#.xml2
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml2
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml2
-rw-r--r--modules/mono/editor/bindings_generator.cpp92
-rw-r--r--modules/mono/editor/bindings_generator.h3
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp61
-rw-r--r--modules/mono/editor/godotsharp_builds.h16
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp17
-rw-r--r--modules/mono/editor/godotsharp_editor.h1
-rw-r--r--modules/mono/editor/godotsharp_export.cpp166
-rw-r--r--modules/mono/editor/godotsharp_export.h57
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp2
-rw-r--r--modules/mono/glue/cs_files/Basis.cs6
-rw-r--r--modules/mono/glue/cs_files/StringExtensions.cs32
-rw-r--r--modules/mono/glue/cs_files/Transform.cs4
-rwxr-xr-xmodules/mono/glue/cs_files/VERSION.txt1
-rw-r--r--modules/mono/godotsharp_defs.h3
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp357
-rw-r--r--modules/mono/mono_gd/gd_mono.h63
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp110
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h17
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp67
28 files changed, 952 insertions, 249 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index aa8626e6da..a1dfcf6377 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -31,11 +31,18 @@ def make_cs_files_header(src, dst):
if i > 0:
header.write(', ')
header.write(byte_to_str(buf[buf_idx]))
- inserted_files += '\tr_files.insert(\"' + file + '\", ' \
+ inserted_files += '\tr_files.insert("' + file + '", ' \
'CompressedFile(_cs_' + name + '_compressed_size, ' \
'_cs_' + name + '_uncompressed_size, ' \
'_cs_' + name + '_compressed));\n'
header.write(' };\n')
+ version_file = os.path.join(src, 'VERSION.txt')
+ with open(version_file, 'r') as content_file:
+ try:
+ glue_version = int(content_file.read()) # make sure the format is valid
+ header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
+ except ValueError:
+ raise ValueError('Invalid C# glue version in: ' + version_file)
header.write('\nstruct CompressedFile\n' '{\n'
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
'\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
@@ -80,23 +87,23 @@ def find_msbuild_unix(filename):
import sys
hint_dirs = ['/opt/novell/mono/bin']
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path):
return hint_path
- elif os.path.isfile(hint_path + ".exe"):
- return hint_path + ".exe"
+ elif os.path.isfile(hint_path + '.exe'):
+ return hint_path + '.exe'
- for hint_dir in os.environ["PATH"].split(os.pathsep):
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
- return hint_path + ".exe"
+ if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
+ return hint_path + '.exe'
return None
@@ -152,7 +159,7 @@ def mono_build_solution(source, target, env):
xbuild_fallback = env['xbuild_fallback']
if xbuild_fallback and os.name == 'nt':
- print("Option 'xbuild_fallback' not supported on Windows")
+ print('Option \'xbuild_fallback\' not supported on Windows')
xbuild_fallback = False
if xbuild_fallback:
@@ -202,10 +209,16 @@ def mono_build_solution(source, target, env):
copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
+output_dir = Dir('#bin').abspath
+assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
-mono_sln_builder = Builder(action = mono_build_solution)
+mono_sln_builder = Builder(action=mono_build_solution)
env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
env_mono.MonoBuildSolution(
- os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'),
+ os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'),
'editor/GodotSharpTools/GodotSharpTools.sln'
)
+
+if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir):
+ rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir)
+ env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir})
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 7c1846dcc2..831d849ea6 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -2,8 +2,9 @@
import imp
import os
import sys
+import subprocess
-from SCons.Script import BoolVariable, Environment, Variables
+from SCons.Script import BoolVariable, Dir, Environment, PathVariable, Variables
monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
@@ -29,20 +30,16 @@ def is_enabled():
return False
-def copy_file_no_replace(src_dir, dst_dir, name):
+def copy_file(src_dir, dst_dir, name):
from shutil import copyfile
src_path = os.path.join(src_dir, name)
dst_path = os.path.join(dst_dir, name)
- need_copy = True
if not os.path.isdir(dst_dir):
os.mkdir(dst_dir)
- elif os.path.exists(dst_path):
- need_copy = False
- if need_copy:
- copyfile(src_path, dst_path)
+ copyfile(src_path, dst_path)
def configure(env):
@@ -51,11 +48,13 @@ def configure(env):
envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
+ envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', PathVariable.PathIsDirCreate))
envvars.Update(env)
bits = env['bits']
mono_static = env['mono_static']
+ assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
@@ -99,11 +98,14 @@ def configure(env):
if not mono_dll_name:
raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
- copy_file_no_replace(mono_bin_path, 'bin', mono_dll_name + '.dll')
+ copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
+
+ copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
else:
sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so'
mono_root = ''
+ mono_lib_path = ''
if bits == '32':
if os.getenv('MONO32_PREFIX'):
@@ -148,7 +150,9 @@ def configure(env):
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
- copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+
+ copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
else:
if mono_static:
raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')
@@ -157,6 +161,7 @@ def configure(env):
mono_lib_path = ''
mono_so_name = ''
+ mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@@ -172,7 +177,8 @@ def configure(env):
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
- copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ copy_file(os.path.join(mono_prefix, 'lib', 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
env.Append(LINKFLAGS='-rdynamic')
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index fb45136575..1d0afa7f2d 100644
--- a/modules/mono/csharp_script.cpp
+++ b/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
}
@@ -280,6 +282,15 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("@\" \""); // verbatim string literal
}
+static String get_base_class_name(const String &p_base_class_name, const String p_class_name) {
+
+ String base_class = p_base_class_name;
+ if (p_class_name == base_class) {
+ base_class = "Godot." + base_class;
+ }
+ return base_class;
+}
+
Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
String script_template = "using " BINDINGS_NAMESPACE ";\n"
@@ -306,7 +317,8 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
"// }\n"
"}\n";
- script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name)
+ String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
+ script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name)
.replace("%CLASS_NAME%", p_class_name);
Ref<CSharpScript> script;
@@ -325,12 +337,24 @@ bool CSharpLanguage::is_using_templates() {
void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
String src = p_script->get_source_code();
- src = src.replace("%BASE%", p_base_class_name)
+ String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
+ src = src.replace("%BASE%", base_class_name)
.replace("%CLASS%", p_class_name)
.replace("%TS%", _get_indentation());
p_script->set_source_code(src);
}
+String CSharpLanguage::validate_path(const String &p_path) const {
+
+ String class_name = p_path.get_file().get_basename();
+ List<String> keywords;
+ get_reserved_words(&keywords);
+ if (keywords.find(class_name)) {
+ return TTR("Class name can't be a reserved keyword");
+ }
+ return "";
+}
+
Script *CSharpLanguage::create_script() const {
return memnew(CSharpScript);
@@ -454,7 +478,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
// Printing an error here will result in endless recursion, so we must be careful
- if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
+ if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
return Vector<StackInfo>();
MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
@@ -1550,7 +1574,6 @@ bool CSharpScript::_update_exports() {
}
bool CSharpScript::_update_signals() {
-#ifdef TOOLS_ENABLED
if (!valid)
return false;
@@ -1581,8 +1604,6 @@ bool CSharpScript::_update_signals() {
}
return changed;
-#endif
- return false;
}
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
@@ -2135,9 +2156,7 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
}
void CSharpScript::update_signals() {
-#ifdef TOOLS_ENABLED
_update_signals();
-#endif
}
Ref<Script> CSharpScript::get_base_script() const {
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index ffb1d2e0f4..8666149111 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -90,15 +90,15 @@ class CSharpScript : public Script {
Variant::Type type;
};
+ Map<StringName, Vector<Argument> > _signals;
+ bool signals_invalidated;
+
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
Set<PlaceHolderScriptInstance *> placeholders;
bool source_changed_cache;
bool exports_invalidated;
- Map<StringName, Vector<Argument> > _signals;
- bool signals_invalidated;
-
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
@@ -294,6 +294,7 @@ public:
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; }
+ virtual String validate_path(const String &p_path) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml
index 0f33c76eb2..082bc30fd8 100644
--- a/modules/mono/doc_classes/@C#.xml
+++ b/modules/mono/doc_classes/@C#.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@C#" category="Core" version="3.0-stable">
+<class name="@C#" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index 3efe71f1b3..a1f7399653 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="CSharpScript" inherits="Script" category="Core" version="3.0-stable">
+<class name="CSharpScript" inherits="Script" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 1e5edf2a2a..985c66464b 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GodotSharp" inherits="Object" category="Core" version="3.0-stable">
+<class name="GodotSharp" inherits="Object" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 62c7a94755..a210b8e480 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -70,8 +70,6 @@
#define LOCAL_RET "ret"
-#define CS_CLASS_NATIVECALLS "NativeCalls"
-#define CS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
#define CS_FIELD_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
@@ -105,6 +103,8 @@
#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
+#define BINDINGS_GENERATOR_VERSION UINT32_C(2)
+
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n";
bool BindingsGenerator::verbose_output = false;
@@ -529,7 +529,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
"using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+
+ cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = ");
+ cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = ");
+ cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.push_back("\n");
#define ADD_INTERNAL_CALL(m_icall) \
if (!m_icall.editor_only) { \
@@ -551,7 +559,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS ".cs");
+ String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
if (err != OK)
@@ -626,7 +634,15 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
"using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
+
+ cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = ");
+ cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = ");
+ cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.push_back("\n");
#define ADD_INTERNAL_CALL(m_icall) \
if (m_icall.editor_only) { \
@@ -648,7 +664,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS_EDITOR ".cs");
+ String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
if (err != OK)
@@ -714,7 +730,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.push_back(INDENT1 "public ");
- output.push_back(itype.is_singleton ? "static class " : "class ");
+ bool is_abstract = !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled'
+ output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class "));
output.push_back(itype.proxy_name);
if (itype.is_singleton) {
@@ -882,7 +899,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.push_back("\";\n");
output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.push_back("." ICALL_PREFIX);
output.push_back(itype.name);
output.push_back(SINGLETON_ICALL_SUFFIX "();\n");
@@ -912,7 +929,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// The engine will initialize the pointer field of the managed side before calling the constructor
// This is why we only allocate a new native object from the constructor if the pointer field is not set
output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.push_back("." + ctor_method);
output.push_back("(this);\n" CLOSE_BLOCK_L2);
} else {
@@ -956,7 +973,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"if (disposed) return;\n" INDENT3
"if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3
"if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN
- " = false;\n" INDENT5 CS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
+ " = false;\n" INDENT5 BINDINGS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
"(this, " BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
"this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3
"GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
@@ -1229,7 +1246,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
{
if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) {
p_output.push_back(MEMBER_BEGIN "private static IntPtr ");
- p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
+ p_output.push_back(method_bind_field + " = " BINDINGS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
p_output.push_back(p_imethod.name);
p_output.push_back("\");\n");
}
@@ -1310,7 +1327,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
const InternalCall *im_icall = match->value();
- String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS;
+ String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
im_call += "." + im_icall->name + "(" + icall_params + ");\n";
if (p_imethod.arguments.size())
@@ -1400,25 +1417,33 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
}
output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK);
+
output.push_back("uint64_t get_core_api_hash() { return ");
- output.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
+ output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
+
output.push_back("#ifdef TOOLS_ENABLED\n"
"uint64_t get_editor_api_hash() { return ");
- output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) +
+ output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) +
"; }\n#endif // TOOLS_ENABLED\n");
+
+ output.push_back("uint32_t get_bindings_version() { return ");
+ output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
+ output.push_back("uint32_t get_cs_glue_version() { return ");
+ output.push_back(String::num_uint64(CS_GLUE_VERSION) + "; }\n");
+
output.push_back("void register_generated_icalls() " OPEN_BLOCK);
output.push_back("\tgodot_register_header_icalls();");
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- output.push_back("\tmono_add_internal_call("); \
- output.push_back("\"" BINDINGS_NAMESPACE "."); \
- output.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \
- output.push_back("::"); \
- output.push_back(m_icall.name); \
- output.push_back("\", (void*)"); \
- output.push_back(m_icall.name); \
- output.push_back(");\n"); \
+#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
+ { \
+ output.push_back("\tmono_add_internal_call("); \
+ output.push_back("\"" BINDINGS_NAMESPACE "."); \
+ output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
+ output.push_back("::"); \
+ output.push_back(m_icall.name); \
+ output.push_back("\", (void*)"); \
+ output.push_back(m_icall.name); \
+ output.push_back(");\n"); \
}
bool tools_sequence = false;
@@ -1486,6 +1511,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
return OK;
}
+uint32_t BindingsGenerator::get_version() {
+ return BINDINGS_GENERATOR_VERSION;
+}
+
+uint32_t BindingsGenerator::get_cs_glue_version() {
+ return CS_GLUE_VERSION;
+}
+
Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) {
FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
@@ -1528,9 +1561,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
if (p_imethod.is_vararg) {
if (i < p_imethod.arguments.size() - 1) {
c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
- c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, ";
- c_in_statements += sformat("&%s_in", c_param_name);
- c_in_statements += ");\n";
+ c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(";
+ c_in_statements += itos(i);
+ c_in_statements += sformat(", &%s_in);\n", c_param_name);
}
} else {
if (i > 0)
@@ -2231,7 +2264,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
"this." BINDINGS_PTR_FIELD " = NativeCalls.godot_icall_NodePath_Ctor(path);\n" CLOSE_BLOCK_L2
MEMBER_BEGIN "public static implicit operator NodePath(string from)\n" OPEN_BLOCK_L2 "return new NodePath(from);\n" CLOSE_BLOCK_L2
MEMBER_BEGIN "public static implicit operator string(NodePath from)\n" OPEN_BLOCK_L2
- "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2);
+ "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2
+ MEMBER_BEGIN "public override string ToString()\n" OPEN_BLOCK_L2 "return (string)this;\n" CLOSE_BLOCK_L2);
builtin_types.insert(itype.cname, itype);
// RID
@@ -2365,7 +2399,7 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
imethod.name = mi.name;
imethod.cname = imethod.name;
- imethod.proxy_name = mi.name;
+ imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(mi.name));
for (int i = 0; i < mi.arguments.size(); i++) {
ArgumentInterface iarg;
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 9b5a9cea88..f6194139af 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -536,6 +536,9 @@ public:
Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true);
Error generate_glue(const String &p_output_dir);
+ static uint32_t get_version();
+ static uint32_t get_cs_glue_version();
+
void initialize();
_FORCE_INLINE_ static BindingsGenerator *get_singleton() {
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 6b41b10981..ad07f043b2 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -33,7 +33,6 @@
#include "main/main.h"
#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
@@ -178,13 +177,15 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s
return true;
}
-bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) {
+bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
String assembly_file = p_assembly_name + ".dll";
String assembly_src = p_src_dir.plus_file(assembly_file);
String assembly_dst = p_dst_dir.plus_file(assembly_file);
- if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst)) {
+ if (!FileAccess::exists(assembly_dst) ||
+ FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
+ GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = p_assembly_name + ".xml";
@@ -200,36 +201,49 @@ 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;
}
+
+ GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
}
return true;
}
-bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
+String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
+
+ uint64_t api_hash = p_api_type == APIAssembly::API_CORE ?
+ GDMono::get_singleton()->get_api_core_hash() :
+ GDMono::get_singleton()->get_api_editor_hash();
+ return String::num_uint64(api_hash) +
+ "_" + String::num_uint64(BindingsGenerator::get_version()) +
+ "_" + String::num_uint64(BindingsGenerator::get_cs_glue_version());
+}
+
+bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
- String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+ String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
String api_build_config = "Release";
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
pr.step("Generating " + api_name + " solution");
- uint64_t core_hash = GDMono::get_singleton()->get_api_core_hash();
- uint64_t editor_hash = GDMono::get_singleton()->get_api_editor_hash();
+ String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_CORE))
+ .plus_file(API_ASSEMBLY_NAME);
+ String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_EDITOR))
+ .plus_file(EDITOR_API_ASSEMBLY_NAME);
- String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash));
- String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash));
-
- String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir;
+ String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir;
String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
String core_api_assembly;
- if (p_api_type == API_EDITOR) {
+ if (p_api_type == APIAssembly::API_EDITOR) {
core_api_assembly = core_api_sln_dir.plus_file("bin")
.plus_file(api_build_config)
.plus_file(API_ASSEMBLY_NAME ".dll");
@@ -242,7 +256,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
BindingsGenerator *gen = BindingsGenerator::get_singleton();
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
- Error err = p_api_type == API_CORE ?
+ Error err = p_api_type == APIAssembly::API_CORE ?
gen->generate_cs_core_project(api_sln_dir, gen_verbose) :
gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose);
@@ -275,7 +289,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
// Copy the built assembly to the assemblies directory
String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
- if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name))
+ if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
return false;
pr.step("Done");
@@ -283,22 +297,22 @@ 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
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
return false;
EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
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 +323,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 +381,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 +481,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 +502,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);
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index 5d2390ecd9..27b771e324 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -31,6 +31,7 @@
#ifndef GODOTSHARP_BUILDS_H
#define GODOTSHARP_BUILDS_H
+#include "../mono_gd/gd_mono.h"
#include "mono_bottom_panel.h"
#include "mono_build_info.h"
@@ -56,17 +57,14 @@ private:
HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
+ static String _api_folder_name(APIAssembly::Type p_api_type);
+
static GodotSharpBuilds *singleton;
friend class GDMono;
static void _register_internal_calls();
public:
- enum APIType {
- API_CORE,
- API_EDITOR
- };
-
enum BuildTool {
MSBUILD_MONO,
#ifdef WINDOWS_ENABLED
@@ -89,11 +87,13 @@ public:
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config);
- static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name);
+ static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type);
+
+ static bool make_api_sln(APIAssembly::Type p_api_type);
- static bool make_api_sln(APIType p_api_type);
+ static bool build_project_blocking(const String &p_config);
- static bool build_project_blocking();
+ static bool editor_build_callback();
GodotSharpBuilds();
~GodotSharpBuilds();
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 0ef3adfdd0..998da8bda3 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/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
@@ -84,10 +85,10 @@ bool GodotSharpEditor::_create_project_solution() {
return false;
}
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
return false;
pr.step(TTR("Done"));
@@ -278,10 +279,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
String about_text =
String("C# support in Godot Engine is a brand new feature and a work in progress.\n") +
"It is at the alpha stage and thus not suitable for use in production.\n\n" +
- "As of Godot 3.0, C# support is not feature-complete and can crash in some situations. " +
- "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases.\n" +
+ "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " +
+ "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " +
+ "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
"The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " +
- "but not to share them as standalone binaries. This feature is of course high on the priority list and should be available in 3.0.1.\n\n" +
+ "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" +
"If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" +
" https://github.com/godotengine/godot/issues\n\n" +
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
@@ -315,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() {
diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h
index 81c49aec30..66da814c8b 100644
--- a/modules/mono/editor/godotsharp_editor.h
+++ b/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 {
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
new file mode 100644
index 0000000000..cd09e6516a
--- /dev/null
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -0,0 +1,166 @@
+/*************************************************************************/
+/* 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
+
+ ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
+ ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain());
+
+ 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);
+}
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
new file mode 100644
index 0000000000..b38db9660c
--- /dev/null
+++ b/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
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 20378a0162..32aec2a3b5 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/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);
diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs
index ea92b1641b..c6cdc069ef 100644
--- a/modules/mono/glue/cs_files/Basis.cs
+++ b/modules/mono/glue/cs_files/Basis.cs
@@ -452,9 +452,9 @@ namespace Godot
public Basis(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz)
{
- this.x = new Vector3(xx, xy, xz);
- this.y = new Vector3(yx, yy, yz);
- this.z = new Vector3(zx, zy, zz);
+ this.x = new Vector3(xx, yx, zx);
+ this.y = new Vector3(xy, yy, zy);
+ this.z = new Vector3(xz, yz, zz);
}
public static Basis operator *(Basis left, Basis right)
diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs
index 5c3ceff97d..cbc337ab19 100644
--- a/modules/mono/glue/cs_files/StringExtensions.cs
+++ b/modules/mono/glue/cs_files/StringExtensions.cs
@@ -287,7 +287,7 @@ namespace Godot
if (sep == -1)
return @base;
- return @base + rs.substr(0, sep);
+ return @base + rs.Substr(0, sep);
}
// <summary>
@@ -478,7 +478,7 @@ namespace Godot
// </summary>
public static bool IsValidIpAddress(this string instance)
{
- string[] ip = instance.split(".");
+ string[] ip = instance.Split(".");
if (ip.Length != 4)
return false;
@@ -489,7 +489,7 @@ namespace Godot
if (!n.IsValidInteger())
return false;
- int val = n.to_int();
+ int val = n.ToInt();
if (val < 0 || val > 255)
return false;
}
@@ -571,7 +571,7 @@ namespace Godot
// <summary>
// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
// </summary>
- public static bool matchn(this string instance, string expr)
+ public static bool Matchn(this string instance, string expr)
{
return instance.ExprMatch(expr, false);
}
@@ -830,7 +830,7 @@ namespace Godot
// <summary>
// Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
// </summary>
- public static string[] split(this string instance, string divisor, bool allow_empty = true)
+ public static string[] Split(this string instance, string divisor, bool allow_empty = true)
{
return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries);
}
@@ -838,7 +838,7 @@ namespace Godot
// <summary>
// Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",".
// </summary>
- public static float[] split_floats(this string instance, string divisor, bool allow_empty = true)
+ public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true)
{
List<float> ret = new List<float>();
int from = 0;
@@ -872,7 +872,7 @@ namespace Godot
// <summary>
// Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively.
// </summary>
- public static string strip_edges(this string instance, bool left = true, bool right = true)
+ public static string StripEdges(this string instance, bool left = true, bool right = true)
{
if (left)
{
@@ -890,7 +890,7 @@ namespace Godot
// <summary>
// Return part of the string from the position [code]from[/code], with length [code]len[/code].
// </summary>
- public static string substr(this string instance, int from, int len)
+ public static string Substr(this string instance, int from, int len)
{
return instance.Substring(from, len);
}
@@ -898,7 +898,7 @@ namespace Godot
// <summary>
// Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters.
// </summary>
- public static byte[] to_ascii(this string instance)
+ public static byte[] ToAscii(this string instance)
{
return Encoding.ASCII.GetBytes(instance);
}
@@ -906,7 +906,7 @@ namespace Godot
// <summary>
// Convert a string, containing a decimal number, into a [code]float[/code].
// </summary>
- public static float to_float(this string instance)
+ public static float ToFloat(this string instance)
{
return float.Parse(instance);
}
@@ -914,7 +914,7 @@ namespace Godot
// <summary>
// Convert a string, containing an integer number, into an [code]int[/code].
// </summary>
- public static int to_int(this string instance)
+ public static int ToInt(this string instance)
{
return int.Parse(instance);
}
@@ -922,7 +922,7 @@ namespace Godot
// <summary>
// Return the string converted to lowercase.
// </summary>
- public static string to_lower(this string instance)
+ public static string ToLower(this string instance)
{
return instance.ToLower();
}
@@ -930,7 +930,7 @@ namespace Godot
// <summary>
// Return the string converted to uppercase.
// </summary>
- public static string to_upper(this string instance)
+ public static string ToUpper(this string instance)
{
return instance.ToUpper();
}
@@ -938,7 +938,7 @@ namespace Godot
// <summary>
// Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii().
// </summary>
- public static byte[] to_utf8(this string instance)
+ public static byte[] ToUtf8(this string instance)
{
return Encoding.UTF8.GetBytes(instance);
}
@@ -946,7 +946,7 @@ namespace Godot
// <summary>
// Return a copy of the string with special characters escaped using the XML standard.
// </summary>
- public static string xml_escape(this string instance)
+ public static string XmlEscape(this string instance)
{
return SecurityElement.Escape(instance);
}
@@ -954,7 +954,7 @@ namespace Godot
// <summary>
// Return a copy of the string with escaped characters replaced by their meanings according to the XML standard.
// </summary>
- public static string xml_unescape(this string instance)
+ public static string XmlUnescape(this string instance)
{
return SecurityElement.FromString(instance).Text;
}
diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs
index 5214100d36..9853721f98 100644
--- a/modules/mono/glue/cs_files/Transform.cs
+++ b/modules/mono/glue/cs_files/Transform.cs
@@ -24,7 +24,7 @@ namespace Godot
public Transform LookingAt(Vector3 target, Vector3 up)
{
Transform t = this;
- t.set_look_at(origin, target, up);
+ t.SetLookAt(origin, target, up);
return t;
}
@@ -43,7 +43,7 @@ namespace Godot
return new Transform(basis.Scaled(scale), origin * scale);
}
- public void set_look_at(Vector3 eye, Vector3 target, Vector3 up)
+ public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
// Z vector
diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt
new file mode 100755
index 0000000000..0cfbf08886
--- /dev/null
+++ b/modules/mono/glue/cs_files/VERSION.txt
@@ -0,0 +1 @@
+2
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index 4c26c3e6bd..f604464e8f 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -39,4 +39,7 @@
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
+#define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
+#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
+
#endif // GODOTSHARP_DEFS_H
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index f5febd415b..0646580eaa 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -42,11 +42,15 @@
#include "project_settings.h"
#include "../csharp_script.h"
+#include "../godotsharp_dirs.h"
#include "../utils/path_utils.h"
+#include "gd_mono_class.h"
+#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
#ifdef TOOLS_ENABLED
#include "../editor/godotsharp_editor.h"
+#include "main/main.h"
#endif
void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) {
@@ -70,7 +74,31 @@ void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
GDMono *GDMono::singleton = NULL;
+namespace {
+
+void setup_runtime_main_args() {
+ CharString execpath = OS::get_singleton()->get_executable_path().utf8();
+
+ List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
+
+ List<CharString> cmdline_args_utf8;
+ Vector<char *> main_args;
+ main_args.resize(cmdline_args.size() + 1);
+
+ main_args[0] = execpath.ptrw();
+
+ int i = 1;
+ for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
+ CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get();
+ main_args[i] = stored.ptrw();
+ i++;
+ }
+
+ mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
+}
+
#ifdef DEBUG_ENABLED
+
static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
do {
@@ -92,24 +120,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
return mono_is_debugger_attached();
}
-#endif
-
-#ifdef TOOLS_ENABLED
-// temporary workaround. should be provided from Main::setup/setup2 instead
-bool _is_project_manager_requested() {
-
- List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
- for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
- const String &arg = E->get();
- if (arg == "-p" || arg == "--project-manager")
- return true;
- }
-
- return false;
-}
-#endif
-#ifdef DEBUG_ENABLED
void gdmono_debug_init() {
mono_debug_init(MONO_DEBUG_FORMAT_MONO);
@@ -121,7 +132,7 @@ void gdmono_debug_init() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() ||
ProjectSettings::get_singleton()->get_resource_path().empty() ||
- _is_project_manager_requested()) {
+ Main::is_project_manager()) {
return;
}
#endif
@@ -136,8 +147,11 @@ void gdmono_debug_init() {
};
mono_jit_parse_options(2, (char **)options);
}
+
#endif
+} // namespace
+
void GDMono::initialize() {
ERR_FAIL_NULL(Engine::get_singleton());
@@ -190,6 +204,8 @@ void GDMono::initialize() {
GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
+ setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
+
runtime_initialized = true;
OS::get_singleton()->print("Mono: Runtime initialized\n");
@@ -222,7 +238,46 @@ void GDMono::initialize() {
_register_internal_calls();
// The following assemblies are not required at initialization
- _load_all_script_assemblies();
+#ifndef MONO_GLUE_DISABLED
+ if (_load_api_assemblies()) {
+ if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ // Everything is fine with the api assemblies, load the project assembly
+ _load_project_assembly();
+ } else {
+#ifdef TOOLS_ENABLED
+ // The assembly was successfuly loaded, but the full api could not be cached.
+ // This is most likely an outdated assembly loaded because of an invalid version in the metadata,
+ // so we invalidate the version in the metadata and unload the script domain.
+
+ if (core_api_assembly_out_of_sync) {
+ ERR_PRINT("The loaded Core API assembly is out of sync");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ }
+
+ if (editor_api_assembly_out_of_sync) {
+ ERR_PRINT("The loaded Editor API assembly is out of sync");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
+ }
+
+ OS::get_singleton()->print("Mono: Proceeding to unload scripts domain because of invalid API assemblies\n");
+
+ Error err = _unload_scripts_domain();
+ if (err != OK) {
+ WARN_PRINT("Mono: Failed to unload scripts domain");
+ }
+#else
+ ERR_PRINT("The loaded API assembly is invalid");
+ CRASH_NOW();
+#endif
+ }
+ }
+#else
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
+#endif
mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
@@ -233,7 +288,11 @@ void GDMono::initialize() {
namespace GodotSharpBindings {
uint64_t get_core_api_hash();
+#ifdef TOOLS_ENABLED
uint64_t get_editor_api_hash();
+#endif // TOOLS_ENABLED
+uint32_t get_bindings_version();
+uint32_t get_cs_glue_version();
void register_generated_icalls();
} // namespace GodotSharpBindings
@@ -285,43 +344,84 @@ 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;
}
+APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) {
+ APIAssembly::Version api_assembly_version;
+
+ const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ?
+ BINDINGS_CLASS_NATIVECALLS :
+ BINDINGS_CLASS_NATIVECALLS_EDITOR;
+
+ GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name);
+
+ if (nativecalls_klass) {
+ GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
+ if (api_hash_field)
+ api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL));
+
+ GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
+ if (binds_ver_field)
+ api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL));
+
+ GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
+ if (cs_glue_ver_field)
+ api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL));
+ }
+
+ return api_assembly_version;
+}
+
+String APIAssembly::to_string(APIAssembly::Type p_type) {
+ return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR";
+}
+
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();
@@ -331,13 +431,25 @@ bool GDMono::_load_corlib_assembly() {
bool GDMono::_load_core_api_assembly() {
- if (api_assembly)
+ if (core_api_assembly)
return true;
- bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly);
+#ifdef TOOLS_ENABLED
+ if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE))
+ return false;
+#endif
- if (success)
+ bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly);
+
+ if (success) {
+#ifndef MONO_GLUE_DISABLED
+ APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE);
+ core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
+#endif
GDMonoUtils::update_godot_api_cache();
+ }
return success;
}
@@ -348,7 +460,23 @@ bool GDMono::_load_editor_api_assembly() {
if (editor_api_assembly)
return true;
- return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
+#ifdef TOOLS_ENABLED
+ if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR))
+ return false;
+#endif
+
+ bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
+
+ if (success) {
+#ifndef MONO_GLUE_DISABLED
+ APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR);
+ editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
+#endif
+ }
+
+ return success;
}
#endif
@@ -360,7 +488,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
@@ -374,17 +502,20 @@ bool GDMono::_load_project_assembly() {
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());
+ } else {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
+ }
return success;
}
-bool GDMono::_load_all_script_assemblies() {
+bool GDMono::_load_api_assemblies() {
-#ifndef MONO_GLUE_DISABLED
if (!_load_core_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n");
@@ -399,20 +530,72 @@ bool GDMono::_load_all_script_assemblies() {
#endif
}
- if (!_load_project_assembly()) {
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
- return false;
+ return true;
+}
+
+#ifdef TOOLS_ENABLED
+String GDMono::_get_api_assembly_metadata_path() {
+
+ return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg");
+}
+
+void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) {
+
+ String section = APIAssembly::to_string(p_api_type);
+ String path = _get_api_assembly_metadata_path();
+
+ Ref<ConfigFile> metadata;
+ metadata.instance();
+ metadata->load(path);
+
+ metadata->set_value(section, "invalidated", p_invalidated);
+
+ String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
+ .plus_file(p_api_type == APIAssembly::API_CORE ?
+ API_ASSEMBLY_NAME ".dll" :
+ EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ ERR_FAIL_COND(!FileAccess::exists(assembly_path));
+
+ uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
+
+ metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time));
+
+ String dir = path.get_base_dir();
+ if (!DirAccess::exists(dir)) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND(!da);
+ Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir));
+ ERR_FAIL_COND(err != OK);
}
- return true;
-#else
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n");
+ Error save_err = metadata->save(path);
+ ERR_FAIL_COND(save_err != OK);
+}
- return true;
-#endif
+bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) {
+
+ String section = APIAssembly::to_string(p_api_type);
+
+ Ref<ConfigFile> metadata;
+ metadata.instance();
+ metadata->load(_get_api_assembly_metadata_path());
+
+ String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
+ .plus_file(p_api_type == APIAssembly::API_CORE ?
+ API_ASSEMBLY_NAME ".dll" :
+ EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ if (!FileAccess::exists(assembly_path))
+ return false;
+
+ uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
+
+ uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0);
+
+ return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
}
+#endif
Error GDMono::_load_scripts_domain() {
@@ -455,12 +638,15 @@ Error GDMono::_unload_scripts_domain() {
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
- api_assembly = NULL;
+ core_api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
editor_api_assembly = NULL;
#endif
+ core_api_assembly_out_of_sync = false;
+ editor_api_assembly_out_of_sync = false;
+
MonoDomain *domain = scripts_domain;
scripts_domain = NULL;
@@ -515,16 +701,80 @@ Error GDMono::reload_scripts_domain() {
return err;
}
- if (!_load_all_script_assemblies()) {
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Failed to load script assemblies\n");
+#ifndef MONO_GLUE_DISABLED
+ if (!_load_api_assemblies()) {
return ERR_CANT_OPEN;
}
+ if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ // Everything is fine with the api assemblies, load the project assembly
+ _load_project_assembly();
+ } else {
+ // The assembly was successfuly loaded, but the full api could not be cached.
+ // This is most likely an outdated assembly loaded because of an invalid version in the metadata,
+ // so we invalidate the version in the metadata and unload the script domain.
+
+ if (core_api_assembly_out_of_sync) {
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ ERR_PRINT("Core API assembly is in sync, but the cache update failed");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ }
+
+ if (editor_api_assembly_out_of_sync) {
+ metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
+ }
+
+ Error err = _unload_scripts_domain();
+ if (err != OK) {
+ WARN_PRINT("Mono: Failed to unload scripts domain");
+ }
+
+ return ERR_CANT_RESOLVE;
+ }
+
+ if (!_load_project_assembly())
+ return ERR_CANT_OPEN;
+#else
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
+#endif
+
return OK;
}
#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);
@@ -576,8 +826,11 @@ GDMono::GDMono() {
tools_domain = NULL;
#endif
+ core_api_assembly_out_of_sync = false;
+ editor_api_assembly_out_of_sync = false;
+
corlib_assembly = NULL;
- api_assembly = NULL;
+ core_api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
editor_api_assembly = NULL;
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 67251778c6..5e01152870 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -31,6 +31,8 @@
#ifndef GD_MONO_H
#define GD_MONO_H
+#include "core/io/config_file.h"
+
#include "../godotsharp_defs.h"
#include "gd_mono_assembly.h"
#include "gd_mono_log.h"
@@ -39,6 +41,43 @@
#include "../utils/mono_reg_utils.h"
#endif
+namespace APIAssembly {
+enum Type {
+ API_CORE,
+ API_EDITOR
+};
+
+struct Version {
+ uint64_t godot_api_hash;
+ uint32_t bindings_version;
+ uint32_t cs_glue_version;
+
+ bool operator==(const Version &p_other) const {
+ return godot_api_hash == p_other.godot_api_hash &&
+ bindings_version == p_other.bindings_version &&
+ cs_glue_version == p_other.cs_glue_version;
+ }
+
+ Version() :
+ godot_api_hash(0),
+ bindings_version(0),
+ cs_glue_version(0) {
+ }
+
+ Version(uint64_t p_godot_api_hash,
+ uint32_t p_bindings_version,
+ uint32_t p_cs_glue_version) :
+ godot_api_hash(p_godot_api_hash),
+ bindings_version(p_bindings_version),
+ cs_glue_version(p_cs_glue_version) {
+ }
+
+ static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type);
+};
+
+String to_string(Type p_type);
+} // namespace APIAssembly
+
#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
#ifdef TOOLS_ENABLED
#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
@@ -55,8 +94,11 @@ class GDMono {
MonoDomain *tools_domain;
#endif
+ bool core_api_assembly_out_of_sync;
+ bool editor_api_assembly_out_of_sync;
+
GDMonoAssembly *corlib_assembly;
- GDMonoAssembly *api_assembly;
+ GDMonoAssembly *core_api_assembly;
GDMonoAssembly *project_assembly;
#ifdef TOOLS_ENABLED
GDMonoAssembly *editor_api_assembly;
@@ -75,7 +117,11 @@ class GDMono {
#endif
bool _load_project_assembly();
- bool _load_all_script_assemblies();
+ bool _load_api_assemblies();
+
+#ifdef TOOLS_ENABLED
+ String _get_api_assembly_metadata_path();
+#endif
void _register_internal_calls();
@@ -94,8 +140,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
@@ -113,6 +157,11 @@ public:
#endif
#endif
+#ifdef TOOLS_ENABLED
+ void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated);
+ bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type);
+#endif
+
static GDMono *get_singleton() { return singleton; }
// Do not use these, unless you know what you're doing
@@ -128,7 +177,7 @@ public:
#endif
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_api_assembly() const { return api_assembly; }
+ _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
@@ -145,6 +194,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();
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index ef39b8549d..d062d56dcf 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/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,44 +84,49 @@ 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);
- break;
+ res = _load_assembly_from(name.get_basename(), path, refonly);
+ if (res != NULL)
+ break;
}
} else {
path = search_dir.plus_file(name + ".dll");
if (FileAccess::exists(path)) {
- res = _load_assembly_from(name, path);
- break;
+ res = _load_assembly_from(name, path, refonly);
+ if (res != NULL)
+ break;
}
path = search_dir.plus_file(name + ".exe");
if (FileAccess::exists(path)) {
- res = _load_assembly_from(name, path);
- break;
+ res = _load_assembly_from(name, path, refonly);
+ if (res != NULL)
+ break;
}
}
}
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());
+#ifdef GD_MONO_EDITOR_ASSEMBLIES_DIR
+ search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir().plus_file(_MKSTR(GD_MONO_EDITOR_ASSEMBLIES_DIR)).simplify_path());
+#endif
const char *rootdir = mono_assembly_getrootdir();
if (rootdir) {
search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
+ search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5").plus_file("Facades"));
}
if (assemblies_path) {
@@ -121,10 +142,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 +154,57 @@ 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);
- break;
+ res = _load_assembly_from(name.get_basename(), path, refonly);
+ if (res != NULL)
+ break;
}
} else {
path = search_dir.plus_file(name + ".dll");
if (FileAccess::exists(path)) {
- res = _load_assembly_from(name, path);
- break;
+ res = _load_assembly_from(name, path, refonly);
+ if (res != NULL)
+ 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);
@@ -186,14 +212,15 @@ Error GDMonoAssembly::load(MonoDomain *p_domain) {
String image_filename(path);
- MonoImageOpenStatus status;
+ MonoImageOpenStatus status = MONO_IMAGE_OK;
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);
+ ERR_FAIL_COND_V(status != MONO_IMAGE_OK, ERR_FILE_CANT_OPEN);
+ ERR_FAIL_NULL_V(image, ERR_FILE_CANT_OPEN);
#ifdef DEBUG_ENABLED
String pdb_path(path + ".pdb");
@@ -213,15 +240,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 +395,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;
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 8a5fa19626..5cf744a5a2 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/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();
};
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 6572408ab5..4e28622adb 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -195,13 +195,13 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
// Transform
#define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \
- m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \
- m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \
- m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \
+ m_in.basis[0].x, m_in.basis[1].x, m_in.basis[2].x, \
+ m_in.basis[0].y, m_in.basis[1].y, m_in.basis[2].y, \
+ m_in.basis[0].z, m_in.basis[1].z, m_in.basis[2].z, \
m_in.origin.x, m_in.origin.y, m_in.origin.z \
};
#define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \
- Basis(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]), \
+ Basis(m_in[0], m_in[3], m_in[6], m_in[1], m_in[4], m_in[7], m_in[2], m_in[5], m_in[8]), \
Vector3(m_in[9], m_in[10], m_in[11]));
// AABB
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index df0985f6ac..ad52904945 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -230,10 +230,13 @@ String GDMonoMethod::get_signature_desc(bool p_namespaces) const {
}
void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
- const char *_names[params_count];
- mono_method_get_param_names(mono_method, _names);
- for (int i = 0; i < params_count; ++i) {
- names.push_back(StringName(_names[i]));
+ if (params_count > 0) {
+ const char **_names = memnew_arr(const char *, params_count);
+ mono_method_get_param_names(mono_method, _names);
+ for (int i = 0; i < params_count; ++i) {
+ names.push_back(StringName(_names[i]));
+ }
+ memdelete_arr(_names);
}
}
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 42e307cf08..db136a1313 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -143,7 +143,7 @@ void MonoCache::cleanup() {
godot_api_cache_updated = false;
}
-#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
+#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
void update_corlib_cache() {
@@ -245,7 +245,7 @@ void update_godot_api_cache() {
mono_runtime_object_init(task_scheduler);
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
- mono_cache.corlib_cache_updated = true;
+ mono_cache.godot_api_cache_updated = true;
}
void clear_cache() {
@@ -305,7 +305,7 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) {
if (class_name[0] == '_')
class_name = class_name.substr(1, class_name.length());
- GDMonoClass *klass = GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
+ GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
#ifdef TOOLS_ENABLED
if (!klass) {
@@ -321,7 +321,7 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
do {
const GDMonoAssembly *assembly = klass->get_assembly();
- if (assembly == GDMono::get_singleton()->get_api_assembly())
+ if (assembly == GDMono::get_singleton()->get_core_api_assembly())
return klass;
#ifdef TOOLS_ENABLED
if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
@@ -420,37 +420,56 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
if (!ScriptDebugger::get_singleton())
return;
- GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
- MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
+ ScriptLanguage::StackInfo separator;
+ separator.file = "";
+ separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
+ separator.line = 0;
+
+ Vector<ScriptLanguage::StackInfo> si;
+ String exc_msg = "";
+
+ while (p_exc != NULL) {
+ GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
+ MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
- MonoBoolean need_file_info = true;
- void *ctor_args[2] = { p_exc, &need_file_info };
+ MonoBoolean need_file_info = true;
+ void *ctor_args[2] = { p_exc, &need_file_info };
- MonoObject *unexpected_exc = NULL;
- CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
+ MonoObject *unexpected_exc = NULL;
+ CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
- if (unexpected_exc != NULL) {
- mono_print_unhandled_exception(unexpected_exc);
+ if (unexpected_exc != NULL) {
+ mono_print_unhandled_exception(unexpected_exc);
- if (p_recursion_caution) {
- // Called from CSharpLanguage::get_current_stack_info,
- // so printing an error here could result in endless recursion
- OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
- return;
- } else {
- ERR_FAIL();
+ if (p_recursion_caution) {
+ // Called from CSharpLanguage::get_current_stack_info,
+ // so printing an error here could result in endless recursion
+ OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
+ return;
+ } else {
+ ERR_FAIL();
+ }
}
- }
- Vector<ScriptLanguage::StackInfo> si;
- if (stack_trace != NULL && !p_recursion_caution)
- si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
+ Vector<ScriptLanguage::StackInfo> _si;
+ if (stack_trace != NULL && !p_recursion_caution) {
+ _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
+ for (int i = _si.size() - 1; i >= 0; i--)
+ si.insert(0, _si[i]);
+ }
+
+ exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
+
+ GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException");
+ p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL;
+ if (p_exc != NULL)
+ si.insert(0, separator);
+ }
String file = si.size() ? si[0].file : __FILE__;
String func = si.size() ? si[0].func : FUNCTION_STR;
int line = si.size() ? si[0].line : __LINE__;
String error_msg = "Unhandled exception";
- String exc_msg = GDMonoUtils::get_exception_name_and_message(p_exc);
ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
#endif