C#: Restructure code prior move to .NET Core

The main focus here was to remove the majority of code that relied on
Mono's embedding APIs, specially the reflection APIs. The embedding
APIs we still use are the bare minimum we need for things to work.
A lot of code was moved to C#. We no longer deal with any managed
objects (`MonoObject*`, and such) in native code, and all marshaling
is done in C#.

The reason for restructuring the code and move away from embedding APIs
is that once we move to .NET Core, we will be limited by the much more
minimal .NET hosting.

PERFORMANCE REGRESSIONS
-----------------------

Some parts of the code were written with little to no concern about
performance. This includes code that calls into script methods and
accesses script fields, properties and events.
The reason for this is that all of that will be moved to source
generators, so any work prior to that would be a waste of time.

DISABLED FEATURES
-----------------

Some code was removed as it no longer makes sense (or won't make sense
in the future).
Other parts were commented out with `#if 0`s and TODO warnings because
it doesn't make much sense to work on them yet as those parts will
change heavily when we switch to .NET Core but also when we start
introducing source generators.
As such, the following features were disabled temporarily:
- Assembly-reloading (will be done with ALCs in .NET Core).
- Properties/fields exports and script method listing (will be
  handled by source generators in the future).
- Exception logging in the editor and stack info for errors.
- Exporting games.
- Building of C# projects. We no longer copy the Godot API assemblies
  to the project directory, so MSBuild won't be able to find them. The
  idea is to turn them into NuGet packages in the future, which could
  also be obtained from local NuGet sources during development.
This commit is contained in:
Ignacio Roldán Etcheverry 2021-09-12 20:21:15 +02:00
parent 3f1a620102
commit f744d99179
79 changed files with 2514 additions and 5125 deletions

File diff suppressed because it is too large Load diff

View file

@ -39,7 +39,6 @@
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
#include "mono_gd/gd_mono_header.h"
#include "mono_gd/gd_mono_internals.h"
#ifdef TOOLS_ENABLED
@ -66,18 +65,6 @@ TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
#define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst))
struct DotNetScriptLookupInfo {
String class_namespace;
String class_name;
GDMonoClass *script_class = nullptr;
DotNetScriptLookupInfo() {} // Required by HashMap...
DotNetScriptLookupInfo(const String &p_class_namespace, const String &p_class_name, GDMonoClass *p_script_class) :
class_namespace(p_class_namespace), class_name(p_class_name), script_class(p_script_class) {
}
};
class CSharpScript : public Script {
GDCLASS(CSharpScript, Script);
@ -88,26 +75,13 @@ public:
bool nil_is_variant = false;
};
struct EventSignal {
GDMonoField *field = nullptr;
GDMonoMethod *invoke_method = nullptr;
Vector<SignalParameter> parameters;
};
private:
friend class CSharpInstance;
friend class CSharpLanguage;
friend struct CSharpScriptDepSort;
bool tool = false;
bool valid = false;
bool builtin;
GDMonoClass *base = nullptr;
GDMonoClass *native = nullptr;
GDMonoClass *script_class = nullptr;
Ref<CSharpScript> base_cache; // TODO what's this for?
Set<Object *> instances;
@ -128,14 +102,9 @@ private:
#endif
String source;
StringName name;
SelfList<CSharpScript> script_list = this;
Map<StringName, Vector<SignalParameter>> _signals;
Map<StringName, EventSignal> event_signals;
bool signals_invalidated = true;
Vector<MultiplayerAPI::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
@ -158,34 +127,26 @@ private:
void _clear();
void _update_name();
void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
#warning TODO
#if 0
bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
#ifdef TOOLS_ENABLED
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
#endif
#endif
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
static void update_script_class_info(Ref<CSharpScript> p_script);
static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
static void initialize_for_managed_type(Ref<CSharpScript> p_script);
protected:
static void _bind_methods();
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
void _resource_path_changed() override;
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
void _get_property_list(List<PropertyInfo> *p_properties) const;
@ -270,22 +231,21 @@ class CSharpInstance : public ScriptInstance {
bool _unreference_owner_unsafe();
/*
* If nullptr is returned, the caller must destroy the script instance by removing it from its owner.
* If false is returned, the caller must destroy the script instance by removing it from its owner.
*/
MonoObject *_internal_new_managed();
bool _internal_new_managed();
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle);
void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
public:
MonoObject *get_mono_object() const;
_FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; }
_FORCE_INLINE_ GCHandleIntPtr get_gchandle_intptr() { return gchandle.get_intptr(); }
Object *get_owner() override;
bool set(const StringName &p_name, const Variant &p_value) override;
@ -297,15 +257,15 @@ public:
bool has_method(const StringName &p_method) const override;
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
void mono_object_disposed(MonoObject *p_obj);
void mono_object_disposed();
/*
* If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
void mono_object_disposed_baseref(bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
void connect_event_signals();
void connect_event_signal(const StringName &p_event_signal);
void disconnect_event_signals();
void refcount_incremented() override;
@ -329,7 +289,6 @@ public:
struct CSharpScriptBinding {
bool inited = false;
StringName type_name;
GDMonoClass *wrapper_class = nullptr;
MonoGCHandleData gchandle;
Object *owner = nullptr;
@ -367,26 +326,15 @@ class CSharpLanguage : public ScriptLanguage {
ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman);
struct StringNameCache {
StringName _signal_callback;
StringName _set;
StringName _get;
StringName _get_property_list;
StringName _notification;
StringName _script_source;
StringName dotctor; // .ctor
StringName on_before_serialize; // OnBeforeSerialize
StringName on_after_deserialize; // OnAfterDeserialize
StringName delegate_invoke_method_name;
StringNameCache();
};
int lang_idx = -1;
HashMap<String, DotNetScriptLookupInfo> dotnet_script_lookup_map;
void lookup_script_for_class(GDMonoClass *p_class);
// For debug_break and debug_break_parse
int _debug_parse_err_line = -1;
String _debug_parse_err_file;
@ -416,6 +364,7 @@ public:
StringNameCache string_names;
const Mutex &get_language_bind_mutex() { return language_bind_mutex; }
const Mutex &get_script_instances_mutex() { return script_instances_mutex; }
_FORCE_INLINE_ int get_language_index() { return lang_idx; }
void set_language_index(int p_idx);
@ -429,7 +378,7 @@ public:
#endif
static void release_script_gchandle(MonoGCHandleData &p_gchandle);
static void release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle);
static void release_script_gchandle(void *p_expected_mono_obj_unused, MonoGCHandleData &p_gchandle);
bool debug_break(const String &p_error, bool p_allow_continue = true);
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
@ -441,12 +390,6 @@ public:
_FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
void lookup_scripts_in_assembly(GDMonoAssembly *p_assembly);
const DotNetScriptLookupInfo *lookup_dotnet_script(const String &p_script_path) const {
return dotnet_script_lookup_map.getptr(p_script_path);
}
String get_name() const override;
/* LANGUAGE FUNCTIONS */
@ -521,6 +464,10 @@ public:
Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
static void tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted);
static void tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted);
static void tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged);
#ifdef DEBUG_ENABLED
Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
#endif

View file

@ -178,17 +178,6 @@ namespace GodotTools.Build
if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
// Make sure the API assemblies are up to date before building the project.
// We may not have had the chance to update the release API assemblies, and the debug ones
// may have been deleted by the user at some point after they were loaded by the Godot editor.
string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
{
ShowBuildErrorDialog("Failed to update the Godot API assemblies");
return false;
}
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
{
pr.Step("Building project solution", 0);

View file

@ -1,4 +1,5 @@
using Godot;
using Godot.NativeInterop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -100,7 +101,9 @@ namespace GodotTools.Export
return;
if (Path.GetExtension(path) != Internal.CSharpLanguageExtension)
throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
throw new ArgumentException(
$"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}",
nameof(path));
// TODO What if the source file is not part of the game's C# project
@ -194,7 +197,8 @@ namespace GodotTools.Export
{
string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll");
if (!File.Exists(thisWasmFrameworkAssemblyPath))
throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath);
throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'",
thisWasmFrameworkAssemblyPath);
assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath;
}
@ -206,18 +210,21 @@ namespace GodotTools.Export
foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf)
{
string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
string thisWasmFrameworkAssemblyPath =
Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
if (File.Exists(thisWasmFrameworkAssemblyPath))
{
assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath;
}
else
{
thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
thisWasmFrameworkAssemblyPath =
Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
if (!File.Exists(thisWasmFrameworkAssemblyPath))
{
throw new FileNotFoundException("Expected one of the following assemblies but none were found: " +
$"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
throw new FileNotFoundException(
"Expected one of the following assemblies but none were found: " +
$"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
thisWasmFrameworkAssemblyPath);
}
@ -227,7 +234,12 @@ namespace GodotTools.Export
}
var initialAssemblies = assemblies.Duplicate();
internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
godot_dictionary initialAssembliesAux = ((Godot.Collections.Dictionary)initialAssemblies).NativeValue;
using godot_string buildConfigAux = Marshaling.mono_string_to_godot(buildConfig);
using godot_string bclDirAux = Marshaling.mono_string_to_godot(bclDir);
godot_dictionary assembliesAux = ((Godot.Collections.Dictionary)assemblies).NativeValue;
internal_GetExportedAssemblyDependencies(initialAssembliesAux, buildConfigAux, bclDirAux,
ref assembliesAux);
AddI18NAssemblies(assemblies, bclDir);
@ -237,9 +249,12 @@ namespace GodotTools.Export
outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
string apiConfig = isDebug ? "Debug" : "Release";
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
// TODO
throw new NotImplementedException();
string resAssembliesDir = null; // Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null;
bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") ||
outputDataDir == null;
if (!assembliesInsidePck)
{
@ -277,7 +292,8 @@ namespace GodotTools.Export
}
// AOT compilation
bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
bool aotEnabled = platform == OS.Platforms.iOS ||
(bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
if (aotEnabled)
{
@ -296,14 +312,19 @@ namespace GodotTools.Export
LLVMOnly = false,
LLVMPath = "",
LLVMOutputPath = "",
FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
FullAot = platform == OS.Platforms.iOS ||
(bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"),
ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? Array.Empty<string>(),
ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? Array.Empty<string>(),
ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ??
Array.Empty<string>(),
ExtraOptimizerOptions =
(string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ??
Array.Empty<string>(),
ToolchainPath = aotToolchainPath
};
AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies);
AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir,
outputDataDir, assemblies);
}
}
@ -317,7 +338,9 @@ namespace GodotTools.Export
Directory.Delete(aotTempDir, recursive: true);
// TODO: Just a workaround until the export plugins can be made to abort with errors
if (!string.IsNullOrEmpty(maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
if (
!string.IsNullOrEmpty(
maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
{
string lastExportError = maybeLastExportError;
maybeLastExportError = null;
@ -381,7 +404,8 @@ namespace GodotTools.Export
private static bool PlatformHasTemplateDir(string platform)
{
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }
.Contains(platform);
}
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
@ -476,7 +500,8 @@ namespace GodotTools.Export
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies,
string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies);
private static extern void internal_GetExportedAssemblyDependencies(
in godot_dictionary initialAssemblies, in godot_string buildConfig,
in godot_string customBclDir, ref godot_dictionary dependencyAssemblies);
}
}

View file

@ -52,7 +52,7 @@ namespace GodotTools
private bool CreateProjectSolution()
{
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
{
pr.Step("Generating C# project...".TTR());
@ -89,24 +89,6 @@ namespace GodotTools
return false;
}
pr.Step("Updating Godot API assemblies...".TTR());
string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug");
if (!string.IsNullOrEmpty(debugApiAssembliesError))
{
ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError);
return false;
}
string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release");
if (!string.IsNullOrEmpty(releaseApiAssembliesError))
{
ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError);
return false;
}
pr.Step("Done".TTR());
// Here, after all calls to progress_task_step
@ -528,8 +510,9 @@ namespace GodotTools
public static GodotSharpEditor Instance { get; private set; }
[UsedImplicitly]
private GodotSharpEditor()
private static IntPtr InternalCreateInstance()
{
return new GodotSharpEditor().NativeInstance;
}
}
}

View file

@ -2,12 +2,13 @@
<PropertyGroup>
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
<TargetFramework>net472</TargetFramework>
<LangVersion>7.2</LangVersion>
<LangVersion>8</LangVersion>
<!-- The Godot editor uses the Debug Godot API assemblies -->
<GodotApiConfiguration>Debug</GodotApiConfiguration>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
<GodotApiAssembliesDir>$(GodotOutputDataDir)/Api/$(GodotApiConfiguration)</GodotApiAssembliesDir>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" Exists('$(GodotApiAssembliesDir)/GodotSharp.dll') ">
<!-- The project is part of the Godot source tree -->

View file

@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using Godot;
using Godot.NativeInterop;
namespace GodotTools.Internals
{
@ -9,18 +10,22 @@ namespace GodotTools.Internals
public string Task { get; }
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_Create(string task, string label, int amount, bool canCancel);
private static extern void internal_Create(in godot_string task, in godot_string label, int amount,
bool canCancel);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_Dispose(string task);
private static extern void internal_Dispose(in godot_string task);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_Step(string task, string state, int step, bool forceRefresh);
private static extern bool internal_Step(in godot_string task, in godot_string state, int step,
bool forceRefresh);
public EditorProgress(string task, string label, int amount, bool canCancel = false)
{
Task = task;
internal_Create(task, label, amount, canCancel);
using godot_string taskIn = Marshaling.mono_string_to_godot(task);
using godot_string labelIn = Marshaling.mono_string_to_godot(label);
internal_Create(taskIn, labelIn, amount, canCancel);
}
~EditorProgress()
@ -33,18 +38,23 @@ namespace GodotTools.Internals
public void Dispose()
{
internal_Dispose(Task);
using godot_string taskIn = Marshaling.mono_string_to_godot(Task);
internal_Dispose(taskIn);
GC.SuppressFinalize(this);
}
public void Step(string state, int step = -1, bool forceRefresh = true)
{
internal_Step(Task, state, step, forceRefresh);
using godot_string taskIn = Marshaling.mono_string_to_godot(Task);
using godot_string stateIn = Marshaling.mono_string_to_godot(state);
internal_Step(taskIn, stateIn, step, forceRefresh);
}
public bool TryStep(string state, int step = -1, bool forceRefresh = true)
{
return internal_Step(Task, state, step, forceRefresh);
using godot_string taskIn = Marshaling.mono_string_to_godot(Task);
using godot_string stateIn = Marshaling.mono_string_to_godot(state);
return internal_Step(taskIn, stateIn, step, forceRefresh);
}
}
}

View file

@ -1,3 +1,4 @@
using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@ -7,14 +8,32 @@ namespace GodotTools.Internals
{
public static float EditorScale => internal_EditorScale();
public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_GlobalDef(setting, defaultValue, restartIfChanged);
public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.mono_string_to_godot(setting);
using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue);
internal_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
using (result)
return Marshaling.variant_to_mono_object(&result);
}
public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_EditorDef(setting, defaultValue, restartIfChanged);
public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.mono_string_to_godot(setting);
using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue);
internal_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
using (result)
return Marshaling.variant_to_mono_object(&result);
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static string TTR(this string text) => internal_TTR(text);
public static string TTR(this string text)
{
using godot_string textIn = Marshaling.mono_string_to_godot(text);
internal_TTR(textIn, out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
// Internal Calls
@ -22,12 +41,14 @@ namespace GodotTools.Internals
private static extern float internal_EditorScale();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
private static extern void internal_GlobalDef(in godot_string setting, in godot_variant defaultValue,
bool restartIfChanged, out godot_variant result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
private static extern void internal_EditorDef(in godot_string setting, in godot_variant defaultValue,
bool restartIfChanged, out godot_variant result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_TTR(string text);
private static extern void internal_TTR(in godot_string text, out godot_string dest);
}
}

View file

@ -1,90 +1,102 @@
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
namespace GodotTools.Internals
{
public static class GodotSharpDirs
{
public static string ResDataDir => internal_ResDataDir();
public static string ResMetadataDir => internal_ResMetadataDir();
public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir();
public static string ResAssembliesDir => internal_ResAssembliesDir();
public static string ResConfigDir => internal_ResConfigDir();
public static string ResTempDir => internal_ResTempDir();
public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir();
public static string ResTempAssembliesDir => internal_ResTempAssembliesDir();
public static string ResMetadataDir
{
get
{
internal_ResMetadataDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string MonoUserDir => internal_MonoUserDir();
public static string MonoLogsDir => internal_MonoLogsDir();
public static string ResTempAssembliesBaseDir
{
get
{
internal_ResTempAssembliesBaseDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
#region Tools-only
public static string MonoSolutionsDir => internal_MonoSolutionsDir();
public static string BuildLogsDirs => internal_BuildLogsDirs();
public static string MonoUserDir
{
get
{
internal_MonoUserDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string ProjectSlnPath => internal_ProjectSlnPath();
public static string ProjectCsProjPath => internal_ProjectCsProjPath();
public static string BuildLogsDirs
{
get
{
internal_BuildLogsDirs(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string DataEditorToolsDir => internal_DataEditorToolsDir();
public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir();
#endregion
public static string ProjectSlnPath
{
get
{
internal_ProjectSlnPath(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string DataMonoEtcDir => internal_DataMonoEtcDir();
public static string DataMonoLibDir => internal_DataMonoLibDir();
#region Windows-only
public static string DataMonoBinDir => internal_DataMonoBinDir();
#endregion
public static string ProjectCsProjPath
{
get
{
internal_ProjectCsProjPath(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string DataEditorToolsDir
{
get
{
internal_DataEditorToolsDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
#region Internal
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResDataDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResMetadataDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResAssembliesBaseDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResAssembliesDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResConfigDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempAssembliesBaseDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempAssembliesDir();
private static extern void internal_ResMetadataDir(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoUserDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoLogsDir();
#region Tools-only
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoSolutionsDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_BuildLogsDirs();
private static extern void internal_ResTempAssembliesBaseDir(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ProjectSlnPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ProjectCsProjPath();
private static extern void internal_MonoUserDir(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataEditorToolsDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataEditorPrebuiltApiDir();
#endregion
private static extern void internal_BuildLogsDirs(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoEtcDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoLibDir();
private static extern void internal_ProjectSlnPath(out godot_string r_dest);
#region Windows-only
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoBinDir();
#endregion
private static extern void internal_ProjectCsProjPath(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_DataEditorToolsDir(out godot_string r_dest);
#endregion
}

View file

@ -1,5 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using Godot;
using Godot.NativeInterop;
using GodotTools.IdeMessaging.Requests;
namespace GodotTools.Internals
@ -9,15 +11,29 @@ namespace GodotTools.Internals
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = ".cs";
public static string UpdateApiAssembliesFromPrebuilt(string config) =>
internal_UpdateApiAssembliesFromPrebuilt(config);
public static string FullTemplatesDir
{
get
{
internal_FullTemplatesDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string FullTemplatesDir =>
internal_FullTemplatesDir();
public static string SimplifyGodotPath(this string path)
{
using godot_string pathIn = Marshaling.mono_string_to_godot(path);
internal_SimplifyGodotPath(pathIn, out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId);
public static bool IsOsxAppBundleInstalled(string bundleId)
{
using godot_string bundleIdIn = Marshaling.mono_string_to_godot(bundleId);
return internal_IsOsxAppBundleInstalled(bundleIdIn);
}
public static bool GodotIs32Bits() => internal_GodotIs32Bits();
@ -25,10 +41,6 @@ namespace GodotTools.Internals
public static void GodotMainIteration() => internal_GodotMainIteration();
public static ulong GetCoreApiHash() => internal_GetCoreApiHash();
public static ulong GetEditorApiHash() => internal_GetEditorApiHash();
public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded();
public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
@ -36,11 +48,19 @@ namespace GodotTools.Internals
public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
internal_ScriptEditorEdit(resource, line, col, grabFocus);
internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus);
public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
public static string MonoWindowsInstallRoot
{
get
{
internal_MonoWindowsInstallRoot(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static void EditorRunPlay() => internal_EditorRunPlay();
@ -48,22 +68,25 @@ namespace GodotTools.Internals
public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts();
public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, string scriptFile) =>
internal_CodeCompletionRequest((int)kind, scriptFile);
public static unsafe string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind,
string scriptFile)
{
using godot_string scriptFileIn = Marshaling.mono_string_to_godot(scriptFile);
internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res);
using (res)
return Marshaling.PackedStringArray_to_mono_array(&res);
}
#region Internal
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
private static extern void internal_FullTemplatesDir(out godot_string dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_FullTemplatesDir();
private static extern void internal_SimplifyGodotPath(in godot_string path, out godot_string dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_SimplifyGodotPath(this string path);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_IsOsxAppBundleInstalled(string bundleId);
private static extern bool internal_IsOsxAppBundleInstalled(in godot_string bundleId);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_GodotIs32Bits();
@ -74,12 +97,6 @@ namespace GodotTools.Internals
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_GodotMainIteration();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern ulong internal_GetCoreApiHash();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern ulong internal_GetEditorApiHash();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_IsAssembliesReloadingNeeded();
@ -90,13 +107,13 @@ namespace GodotTools.Internals
private static extern void internal_EditorDebuggerNodeReloadScripts();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
private static extern bool internal_ScriptEditorEdit(IntPtr resource, int line, int col, bool grabFocus);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_EditorNodeShowScriptScreen();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoWindowsInstallRoot();
private static extern void internal_MonoWindowsInstallRoot(out godot_string dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_EditorRunPlay();
@ -108,7 +125,8 @@ namespace GodotTools.Internals
private static extern void internal_ScriptEditorDebugger_ReloadScripts();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string[] internal_CodeCompletionRequest(int kind, string scriptFile);
private static extern void internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
out godot_packed_string_array res);
#endregion
}

View file

@ -1,3 +1,4 @@
using Godot.NativeInterop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -13,10 +14,10 @@ namespace GodotTools.Utils
public static class OS
{
[MethodImpl(MethodImplOptions.InternalCall)]
static extern string GetPlatformName();
static extern void GetPlatformName(out godot_string dest);
[MethodImpl(MethodImplOptions.InternalCall)]
static extern bool UnixFileHasExecutableAccess(string filePath);
static extern bool UnixFileHasExecutableAccess(in godot_string filePath);
public static class Names
{
@ -63,14 +64,24 @@ namespace GodotTools.Utils
[Names.HTML5] = Platforms.HTML5
};
private static bool IsOS(string name)
private static unsafe bool IsOS(string name)
{
return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
GetPlatformName(out godot_string dest);
using (dest)
{
string platformName = Marshaling.mono_string_from_godot(dest);
return name.Equals(platformName, StringComparison.OrdinalIgnoreCase);
}
}
private static bool IsAnyOS(IEnumerable<string> names)
private static unsafe bool IsAnyOS(IEnumerable<string> names)
{
return names.Any(p => p.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase));
GetPlatformName(out godot_string dest);
using (dest)
{
string platformName = Marshaling.mono_string_from_godot(dest);
return names.Any(p => p.Equals(platformName, StringComparison.OrdinalIgnoreCase));
}
}
private static readonly IEnumerable<string> LinuxBSDPlatforms =
@ -111,7 +122,8 @@ namespace GodotTools.Utils
private static string PathWhichWindows([NotNull] string name)
{
string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] windowsExts =
Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
char[] invalidPathChars = Path.GetInvalidPathChars();
@ -129,7 +141,8 @@ namespace GodotTools.Utils
}
string nameExt = Path.GetExtension(name);
bool hasPathExt = !string.IsNullOrEmpty(nameExt) && windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
bool hasPathExt = !string.IsNullOrEmpty(nameExt) &&
windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
@ -137,10 +150,10 @@ namespace GodotTools.Utils
return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists);
return (from dir in searchDirs
select Path.Combine(dir, name)
select Path.Combine(dir, name)
into path
from ext in windowsExts
select path + ext).FirstOrDefault(File.Exists);
from ext in windowsExts
select path + ext).FirstOrDefault(File.Exists);
}
private static string PathWhichUnix([NotNull] string name)
@ -164,7 +177,11 @@ namespace GodotTools.Utils
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
return searchDirs.Select(dir => Path.Combine(dir, name))
.FirstOrDefault(path => File.Exists(path) && UnixFileHasExecutableAccess(path));
.FirstOrDefault(path =>
{
using godot_string pathIn = Marshaling.mono_string_to_godot(path);
return File.Exists(path) && UnixFileHasExecutableAccess(pathIn);
});
}
public static void RunProcess(string command, IEnumerable<string> arguments)

View file

@ -96,9 +96,9 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define C_CLASS_NATIVE_FUNCS "NativeFuncs"
#define C_NS_MONOUTILS "InteropUtils"
#define C_METHOD_TIE_MANAGED_TO_UNMANAGED "NativeInterop." C_NS_MONOUTILS ".TieManagedToUnmanaged"
#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOUTILS ".TieManagedToUnmanaged"
#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged"
#define C_METHOD_ENGINE_GET_SINGLETON "NativeInterop." C_NS_MONOUTILS ".EngineGetSingleton"
#define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton"
#define C_NS_MONOMARSHAL "Marshaling"
#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL ".mono_object_to_variant"
@ -1278,6 +1278,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append("using System;\n"); // IntPtr
output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
output.append("using Godot.NativeInterop;\n");
output.append("\n"
"#pragma warning disable CS1591 // Disable warning: "
@ -1310,7 +1311,11 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
if (itype.is_singleton) {
output.append("static partial class ");
} else {
output.append(itype.is_instantiable ? "partial class " : "abstract partial class ");
// Even if the class is not instantiable, we can't declare it abstract because
// the engine can still instantiate them and return them via the scripting API.
// Example: `SceneTreeTimer` returned from `SceneTree.create_timer`.
// See the reverted commit: ef5672d3f94a7321ed779c922088bb72adbb1521
output.append("partial class ");
}
output.append(itype.proxy_name);
@ -1430,6 +1435,10 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Add native name static field
if (is_derived_type) {
output << MEMBER_BEGIN "private static readonly System.Type _cachedType = typeof(" << itype.proxy_name << ");\n";
}
output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
@ -1437,27 +1446,19 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
if (itype.is_instantiable) {
// Add native constructor static field
String get_constructor_method = ICALL_CLASSDB_GET_CONSTRUCTOR;
if (itype.is_singleton) {
// Singletons are static classes. They don't derive Godot.Object,
// so we need to specify the type to call the static method.
get_constructor_method = "Object." + get_constructor_method;
}
output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
<< "#if NET\n"
<< INDENT2 "private static unsafe readonly delegate* unmanaged<IntPtr> "
<< CS_STATIC_FIELD_NATIVE_CTOR " = " << get_constructor_method
<< CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR
<< "(" BINDINGS_NATIVE_NAME_FIELD ");\n"
<< "#else\n"
// Get rid of this one once we switch to .NET 5/6
<< INDENT2 "private static readonly IntPtr " CS_STATIC_FIELD_NATIVE_CTOR
<< " = " << get_constructor_method << "(" BINDINGS_NATIVE_NAME_FIELD ");\n"
<< " = " ICALL_CLASSDB_GET_CONSTRUCTOR "(" BINDINGS_NATIVE_NAME_FIELD ");\n"
<< "#endif\n";
}
@ -1486,8 +1487,12 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
<< "#endif\n"
<< INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ");\n"
<< CLOSE_BLOCK_L3
<< INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ", "
<< BINDINGS_NATIVE_NAME_FIELD << ", refCounted: " << (itype.is_ref_counted ? "true" : "false")
<< ", ((object)this).GetType(), _cachedType);\n" CLOSE_BLOCK_L3
<< INDENT3 "else\n" INDENT3 OPEN_BLOCK
<< INDENT4 "InteropUtils.TieManagedToUnmanagedWithPreSetup(this, "
<< BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L3
<< INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2;
} else {
// Hide the constructor
@ -1503,6 +1508,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
}
// Methods
int method_bind_count = 0;
for (const MethodInterface &imethod : itype.methods) {
Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
@ -1510,12 +1517,82 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
}
// Signals
for (const SignalInterface &isignal : itype.signals_) {
Error method_err = _generate_cs_signal(itype, isignal, output);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
}
// Script calls
if (!itype.is_singleton && (is_derived_type || itype.has_virtual_methods)) {
// TODO: string is ok for now. But should be replaced with StringName in the future for performance.
output << MEMBER_BEGIN "internal " << (is_derived_type ? "override" : "virtual")
<< " unsafe bool InternalGodotScriptCall(string method, godot_variant** args, "
<< "int argCount, out godot_variant ret)\n"
<< INDENT2 "{\n";
for (const MethodInterface &imethod : itype.methods) {
if (!imethod.is_virtual) {
continue;
}
// TODO:
// Compare with cached StringName. We already have a cached StringName
// field for the proxy name. We need one for the original snake_case name.
output << INDENT3 "if ((method == nameof(" << imethod.proxy_name << ") || method == \"" << imethod.name
<< "\") && argCount == " << itos(imethod.arguments.size()) << ")\n"
<< INDENT3 "{\n";
if (imethod.return_type.cname != name_cache.type_void) {
output << INDENT4 "object retBoxed = ";
} else {
output << INDENT4;
}
output << imethod.proxy_name << "(";
for (int i = 0; i < imethod.arguments.size(); i++) {
const ArgumentInterface &iarg = imethod.arguments[i];
const TypeInterface *arg_type = _get_type_or_null(iarg.type);
ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
if (i != 0) {
output << ", ";
}
// TODO: static marshaling (no reflection, no runtime type checks)
output << "(" << arg_type->cs_type << ")Marshaling.variant_to_mono_object_of_type(args["
<< itos(i) << "], typeof(" << arg_type->cs_type << "))";
}
output << ");\n";
if (imethod.return_type.cname != name_cache.type_void) {
// TODO: static marshaling (no reflection, no runtime type checks)
output << INDENT4 "ret = Marshaling.mono_object_to_variant(retBoxed);\n";
output << INDENT4 "return true;\n";
} else {
output << INDENT4 "ret = default;\n";
output << INDENT4 "return true;\n";
}
output << INDENT3 "}\n";
}
if (is_derived_type) {
output << INDENT3 "return base.InternalGodotScriptCall(method, args, argCount, out ret);\n";
} else {
output << INDENT3 "return InternalGodotScriptCallViaReflection(method, args, argCount, out ret);\n";
}
output << INDENT2 "}\n";
}
output.append(INDENT1 CLOSE_BLOCK /* class */
CLOSE_BLOCK /* namespace */);
@ -2556,6 +2633,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
if (method_info.flags & METHOD_FLAG_VIRTUAL) {
imethod.is_virtual = true;
itype.has_virtual_methods = true;
}
PropertyInfo return_info = method_info.return_val;
@ -2689,7 +2767,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ERR_FAIL_COND_V_MSG(itype.find_property_by_name(imethod.cname), false,
"Method name conflicts with property: '" + itype.name + "." + imethod.name + "'.");
// Classes starting with an underscore are ignored unless they're used as a property setter or getter
// Methods starting with an underscore are ignored unless they're used as a property setter or getter
if (!imethod.is_virtual && imethod.name[0] == '_') {
for (const PropertyInterface &iprop : itype.properties) {
if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
@ -3140,7 +3218,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.proxy_name = "string";
itype.cs_type = itype.proxy_name;
itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(&%1);\n";
itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n";
itype.c_arg_in = "&%s_in";
itype.c_type = "godot_string";
itype.c_type_in = itype.cs_type;

View file

@ -358,6 +358,8 @@ class BindingsGenerator {
List<MethodInterface> methods;
List<SignalInterface> signals_;
bool has_virtual_methods = false;
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
for (const MethodInterface &E : methods) {
if (E.cname == p_cname) {

View file

@ -44,177 +44,112 @@
#include "../csharp_script.h"
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/osx_utils.h"
#include "code_completion.h"
#include "godotsharp_export.h"
MonoString *godot_icall_GodotSharpDirs_ResDataDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir());
#include <gdnative/gdnative.h>
void godot_icall_GodotSharpDirs_ResMetadataDir(godot_string *r_dest) {
memnew_placement(r_dest, String(GodotSharpDirs::get_res_metadata_dir()));
}
MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir());
void godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(godot_string *r_dest) {
memnew_placement(r_dest, String(GodotSharpDirs::get_res_temp_assemblies_base_dir()));
}
MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir());
void godot_icall_GodotSharpDirs_MonoUserDir(godot_string *r_dest) {
memnew_placement(r_dest, String(GodotSharpDirs::get_mono_user_dir()));
}
MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResConfigDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResTempDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir());
}
MonoString *godot_icall_GodotSharpDirs_MonoUserDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir());
}
MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir());
}
MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() {
void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir());
memnew_placement(r_dest, String(GodotSharpDirs::get_build_logs_dir()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
void godot_icall_GodotSharpDirs_ProjectSlnPath(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir());
memnew_placement(r_dest, String(GodotSharpDirs::get_project_sln_path()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
void godot_icall_GodotSharpDirs_ProjectCsProjPath(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
memnew_placement(r_dest, String(GodotSharpDirs::get_project_csproj_path()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() {
void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path());
memnew_placement(r_dest, String(GodotSharpDirs::get_data_editor_tools_dir()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir());
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir());
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir());
}
MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir());
}
MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() {
#ifdef WINDOWS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir());
#else
return nullptr;
#endif
}
void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) {
String task = GDMonoMarshal::mono_string_to_godot(p_task);
String label = GDMonoMarshal::mono_string_to_godot(p_label);
void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) {
String task = *reinterpret_cast<const String *>(p_task);
String label = *reinterpret_cast<const String *>(p_label);
EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel);
}
void godot_icall_EditorProgress_Dispose(MonoString *p_task) {
String task = GDMonoMarshal::mono_string_to_godot(p_task);
void godot_icall_EditorProgress_Dispose(const godot_string *p_task) {
String task = *reinterpret_cast<const String *>(p_task);
EditorNode::progress_end_task(task);
}
MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) {
String task = GDMonoMarshal::mono_string_to_godot(p_task);
String state = GDMonoMarshal::mono_string_to_godot(p_state);
bool godot_icall_EditorProgress_Step(const godot_string *p_task, const godot_string *p_state, int32_t p_step, bool p_force_refresh) {
String task = *reinterpret_cast<const String *>(p_task);
String state = *reinterpret_cast<const String *>(p_state);
return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
}
uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies,
MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) {
Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies);
String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies);
uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(const godot_dictionary *p_initial_assemblies,
const godot_string *p_build_config, const godot_string *p_custom_bcl_dir, godot_dictionary *r_assembly_dependencies) {
Dictionary initial_dependencies = *reinterpret_cast<const Dictionary *>(p_initial_assemblies);
String build_config = *reinterpret_cast<const String *>(p_build_config);
String custom_bcl_dir = *reinterpret_cast<const String *>(p_custom_bcl_dir);
Dictionary assembly_dependencies = *reinterpret_cast<Dictionary *>(r_assembly_dependencies);
return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies);
}
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
String config = GDMonoMarshal::mono_string_to_godot(p_config);
String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config);
return GDMonoMarshal::mono_string_from_godot(error_str);
}
MonoString *godot_icall_Internal_FullTemplatesDir() {
void godot_icall_Internal_FullTemplatesDir(godot_string *r_dest) {
String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
memnew_placement(r_dest, String(full_templates_dir));
}
MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) {
String path = GDMonoMarshal::mono_string_to_godot(p_path);
return GDMonoMarshal::mono_string_from_godot(path.simplify_path());
void godot_icall_Internal_SimplifyGodotPath(const godot_string *p_path, godot_string *r_dest) {
String path = *reinterpret_cast<const String *>(p_path);
memnew_placement(r_dest, String(path.simplify_path()));
}
MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id) {
bool godot_icall_Internal_IsOsxAppBundleInstalled(const godot_string *p_bundle_id) {
#ifdef OSX_ENABLED
String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id);
return (MonoBoolean)osx_is_app_bundle_installed(bundle_id);
String bundle_id = *reinterpret_cast<const String *>(p_bundle_id);
return (bool)osx_is_app_bundle_installed(bundle_id);
#else
(void)p_bundle_id; // UNUSED
return (MonoBoolean) false;
return (bool)false;
#endif
}
MonoBoolean godot_icall_Internal_GodotIs32Bits() {
bool godot_icall_Internal_GodotIs32Bits() {
return sizeof(void *) == 4;
}
MonoBoolean godot_icall_Internal_GodotIsRealTDouble() {
bool godot_icall_Internal_GodotIsRealTDouble() {
#ifdef REAL_T_IS_DOUBLE
return (MonoBoolean) true;
return (bool)true;
#else
return (MonoBoolean) false;
return (bool)false;
#endif
}
@ -222,23 +157,15 @@ void godot_icall_Internal_GodotMainIteration() {
Main::iteration();
}
uint64_t godot_icall_Internal_GetCoreApiHash() {
return ClassDB::get_api_hash(ClassDB::API_CORE);
}
uint64_t godot_icall_Internal_GetEditorApiHash() {
return ClassDB::get_api_hash(ClassDB::API_EDITOR);
}
MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() {
bool godot_icall_Internal_IsAssembliesReloadingNeeded() {
#ifdef GD_MONO_HOT_RELOAD
return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
return (bool)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
#else
return (MonoBoolean) false;
return (bool)false;
#endif
}
void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload);
#endif
@ -248,21 +175,22 @@ void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
EditorDebuggerNode::get_singleton()->reload_scripts();
}
MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource);
return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) {
Ref<Resource> resource = p_resource;
return (bool)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
}
void godot_icall_Internal_EditorNodeShowScriptScreen() {
EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
}
MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
void godot_icall_Internal_MonoWindowsInstallRoot(godot_string *r_dest) {
#ifdef WINDOWS_ENABLED
String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
return GDMonoMarshal::mono_string_from_godot(install_root_dir);
memnew_placement(r_dest, String(install_root_dir));
#else
return nullptr;
memnew_placement(r_dest, String);
return;
#endif
}
@ -281,43 +209,43 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
}
}
MonoArray *godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, MonoString *p_script_file) {
String script_file = GDMonoMarshal::mono_string_to_godot(p_script_file);
void godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, const godot_string *p_script_file, godot_packed_string_array *r_ret) {
String script_file = *reinterpret_cast<const String *>(p_script_file);
PackedStringArray suggestions = gdmono::get_code_completion((gdmono::CompletionKind)p_kind, script_file);
return GDMonoMarshal::PackedStringArray_to_mono_array(suggestions);
memnew_placement(r_ret, PackedStringArray(suggestions));
}
float godot_icall_Globals_EditorScale() {
return EDSCALE;
}
MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
void godot_icall_Globals_GlobalDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
String setting = *reinterpret_cast<const String *>(p_setting);
Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
return GDMonoMarshal::variant_to_mono_object(result);
memnew_placement(r_result, Variant(result));
}
MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
String setting = *reinterpret_cast<const String *>(p_setting);
Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
return GDMonoMarshal::variant_to_mono_object(result);
memnew_placement(r_result, Variant(result));
}
MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
String text = GDMonoMarshal::mono_string_to_godot(p_text);
return GDMonoMarshal::mono_string_from_godot(TTR(text));
void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) {
String text = *reinterpret_cast<const String *>(p_text);
memnew_placement(r_dest, String(TTR(text)));
}
MonoString *godot_icall_Utils_OS_GetPlatformName() {
void godot_icall_Utils_OS_GetPlatformName(godot_string *r_dest) {
String os_name = OS::get_singleton()->get_name();
return GDMonoMarshal::mono_string_from_godot(os_name);
memnew_placement(r_dest, String(os_name));
}
MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_path) {
bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file_path) {
#ifdef UNIX_ENABLED
String file_path = GDMonoMarshal::mono_string_to_godot(p_file_path);
String file_path = *reinterpret_cast<const String *>(p_file_path);
return access(file_path.utf8().get_data(), X_OK) == 0;
#else
ERR_FAIL_V(false);
@ -326,25 +254,13 @@ MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_
void register_editor_internal_calls() {
// GodotSharpDirs
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir);
// EditorProgress
GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create);
@ -355,15 +271,12 @@ void register_editor_internal_calls() {
GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
// Internals
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", godot_icall_Internal_IsOsxAppBundleInstalled);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts);

View file

@ -92,7 +92,7 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *re
mono_assembly_get_assemblyref(image, i, reusable_aname);
GDMonoAssembly *ref_assembly = nullptr;
if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, p_search_dirs)) {
ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
}
@ -126,7 +126,7 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
String assembly_path = p_initial_assemblies[*key];
GDMonoAssembly *assembly = nullptr;
bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly);
ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");

View file

@ -35,12 +35,8 @@
#include "core/string/ustring.h"
#include "core/variant/dictionary.h"
#include "../mono_gd/gd_mono_header.h"
namespace GodotSharpExport {
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies);
} // namespace GodotSharpExport

View file

@ -14,7 +14,7 @@ namespace Godot.Collections
/// </summary>
public sealed class Array : IList, IDisposable
{
internal godot_array NativeValue;
public godot_array NativeValue;
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
@ -307,7 +307,7 @@ namespace Godot.Collections
{
using godot_string str = default;
NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str);
return Marshaling.mono_string_from_godot(&str);
return Marshaling.mono_string_from_godot(str);
}
/// <summary>

View file

@ -1,22 +1,27 @@
using System;
#nullable enable
namespace Godot
{
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
private readonly bool requiresLookup;
private readonly System.Type[] scriptTypes;
public bool RequiresLookup { get; private set; }
public Type[]? ScriptTypes { get; private set; }
public AssemblyHasScriptsAttribute()
{
requiresLookup = true;
RequiresLookup = true;
ScriptTypes = null;
}
public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
public AssemblyHasScriptsAttribute(Type[] scriptTypes)
{
requiresLookup = false;
this.scriptTypes = scriptTypes;
RequiresLookup = false;
ScriptTypes = scriptTypes;
}
}
}
#nullable restore

View file

@ -5,11 +5,11 @@ namespace Godot
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
private string path;
public string Path { get; private set; }
public ScriptPathAttribute(string path)
{
this.path = path;
Path = path;
}
}
}

View file

@ -0,0 +1,113 @@
using System;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot.Bridge
{
internal static class CSharpInstanceBridge
{
private static unsafe void Call(IntPtr godotObjectGCHandle, godot_string_name* method,
godot_variant** args, int argCount, godot_variant_call_error* ref_callError, godot_variant* r_ret)
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
{
*r_ret = default;
(*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL;
return;
}
using godot_string dest = default;
NativeFuncs.godotsharp_string_name_as_string(&dest, method);
string methodStr = Marshaling.mono_string_from_godot(dest);
_ = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant outRet);
*r_ret = outRet;
}
private static unsafe bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
throw new InvalidOperationException();
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value))
return true;
object valueManaged = Marshaling.variant_to_mono_object(value);
return godotObject._Set(nameManaged, valueManaged);
}
private static unsafe bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* r_retValue)
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
throw new InvalidOperationException();
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(),
out godot_variant outRet))
{
*r_retValue = outRet;
return true;
}
object ret = godotObject._Get(nameManaged);
if (ret == null)
{
*r_retValue = default;
return false;
}
*r_retValue = Marshaling.mono_object_to_variant(ret);
return true;
}
private static void CallDispose(IntPtr godotObjectGCHandle, bool okIfNull)
{
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (okIfNull)
godotObject?.Dispose();
else
godotObject!.Dispose();
}
private static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* r_res, bool* r_valid)
{
var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (self == null)
{
*r_res = default;
*r_valid = false;
return;
}
var resultStr = self.ToString();
if (resultStr == null)
{
*r_res = default;
*r_valid = false;
return;
}
*r_res = Marshaling.mono_string_to_godot(resultStr);
*r_valid = true;
}
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Runtime.InteropServices;
namespace Godot.Bridge
{
internal static class GCHandleBridge
{
private static void FreeGCHandle(IntPtr gcHandlePtr)
=> GCHandle.FromIntPtr(gcHandlePtr).Free();
}
}

View file

@ -0,0 +1,511 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using Godot.Collections;
using Godot.NativeInterop;
namespace Godot.Bridge
{
internal static class ScriptManagerBridge
{
private static System.Collections.Generic.Dictionary<string, ScriptLookupInfo> _scriptLookupMap = new();
private static System.Collections.Generic.Dictionary<IntPtr, Type> _scriptBridgeMap = new();
private struct ScriptLookupInfo
{
public string ClassNamespace { get; private set; }
public string ClassName { get; private set; }
public Type ScriptType { get; private set; }
public ScriptLookupInfo(string classNamespace, string className, Type scriptType)
{
ClassNamespace = classNamespace;
ClassName = className;
ScriptType = scriptType;
}
};
internal static void FrameCallback()
{
Dispatcher.DefaultGodotTaskScheduler?.Activate();
}
internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
IntPtr godotObject)
{
Type nativeType = TypeGetProxyClass(nativeTypeName);
var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
obj.NativePtr = godotObject;
var ctor = nativeType.GetConstructor(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null);
_ = ctor!.Invoke(obj, null);
return GCHandle.ToIntPtr(GCHandle.Alloc(obj));
}
internal static unsafe void CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, IntPtr godotObject,
godot_variant** args, int argCount)
{
// Performance is not critical here as this will be replaced with source generators.
Type scriptType = _scriptBridgeMap[scriptPtr];
var obj = (Object)FormatterServices.GetUninitializedObject(scriptType);
obj.NativePtr = godotObject;
var ctor = scriptType
.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(c => c.GetParameters().Length == argCount)
.FirstOrDefault();
if (ctor == null)
{
if (argCount == 0)
{
throw new MissingMemberException(
$"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor.");
}
else
{
throw new MissingMemberException(
$"The class '{scriptType.FullName}' does not define a constructor that takes x parameters.");
}
}
var parameters = ctor.GetParameters();
int paramCount = parameters.Length;
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
{
invokeParams[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameters[i].ParameterType);
}
ctor.Invoke(obj, invokeParams);
}
private static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* r_res)
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
{
*r_res = default;
return;
}
var native = Object.InternalGetClassNativeBase(scriptType);
var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
{
*r_res = default;
return;
}
var nativeName = (StringName)field.GetValue(null);
*r_res = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue);
}
private static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
{
var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
if (target != null)
target.NativePtr = newPtr;
}
private static unsafe Type TypeGetProxyClass(godot_string_name* nativeTypeName)
{
// Performance is not critical here as this will be replaced with a generated dictionary.
using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(nativeTypeName));
string nativeTypeNameStr = stringName.ToString();
if (nativeTypeNameStr[0] == '_')
nativeTypeNameStr = nativeTypeNameStr.Substring(1);
Type wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr);
if (wrapperType == null)
{
wrapperType = AppDomain.CurrentDomain.GetAssemblies()
.First(a => a.GetName().Name == "GodotSharpEditor")
.GetType("Godot." + nativeTypeNameStr);
}
static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed;
if (wrapperType != null && IsStatic(wrapperType))
{
// A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
return typeof(Object);
}
return wrapperType;
}
internal static void LookupScriptsInAssembly(Assembly assembly)
{
static void LookupScriptForClass(Type type)
{
var scriptPathAttr = type.GetCustomAttributes(inherit: false)
.OfType<ScriptPathAttribute>()
.FirstOrDefault();
if (scriptPathAttr == null)
return;
_scriptLookupMap[scriptPathAttr.Path] = new ScriptLookupInfo(type.Namespace, type.Name, type);
}
var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false)
.OfType<AssemblyHasScriptsAttribute>()
.FirstOrDefault();
if (assemblyHasScriptsAttr == null)
return;
if (assemblyHasScriptsAttr.RequiresLookup)
{
// This is supported for scenarios where specifying all types would be cumbersome,
// such as when disabling C# source generators (for whatever reason) or when using a
// language other than C# that has nothing similar to source generators to automate it.
var typeOfGodotObject = typeof(Object);
foreach (var type in assembly.GetTypes())
{
if (type.IsNested)
continue;
if (!typeOfGodotObject.IsAssignableFrom(type))
continue;
LookupScriptForClass(type);
}
}
else
{
// This is the most likely scenario as we use C# source generators
var scriptTypes = assemblyHasScriptsAttr.ScriptTypes;
if (scriptTypes != null)
{
for (int i = 0; i < scriptTypes.Length; i++)
{
LookupScriptForClass(scriptTypes[i]);
}
}
}
}
internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr,
godot_string_name* eventSignalName, godot_variant** args, int argCount, bool* r_ownerIsNull)
{
var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target;
if (owner == null)
{
*r_ownerIsNull = true;
return;
}
*r_ownerIsNull = false;
owner.InternalRaiseEventSignal(eventSignalName, args, argCount);
}
internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* r_retSignals)
{
// Performance is not critical here as this will be replaced with source generators.
using var signals = new Dictionary();
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
// Legacy signals
foreach (var signalDelegate in top
.GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public)
.Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType))
.Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any()))
{
var invokeMethod = signalDelegate.GetMethod("Invoke");
if (invokeMethod == null)
throw new MissingMethodException(signalDelegate.FullName, "Invoke");
var signalParams = new Collections.Array();
foreach (var parameters in invokeMethod.GetParameters())
{
var paramType = Marshaling.managed_to_variant_type(
parameters.ParameterType, out bool nilIsVariant);
signalParams.Add(new Dictionary()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(signalDelegate.Name, signalParams);
}
// Event signals
var foundEventSignals = top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Select(ev => ev.Name);
var fields = top.GetFields(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
foreach (var eventSignalField in fields
.Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
.Where(f => foundEventSignals.Contains(f.Name)))
{
var delegateType = eventSignalField.FieldType;
var invokeMethod = delegateType.GetMethod("Invoke");
if (invokeMethod == null)
throw new MissingMethodException(delegateType.FullName, "Invoke");
var signalParams = new Collections.Array();
foreach (var parameters in invokeMethod.GetParameters())
{
var paramType = Marshaling.managed_to_variant_type(
parameters.ParameterType, out bool nilIsVariant);
signalParams.Add(new Dictionary()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(eventSignalField.Name, signalParams);
}
top = top.BaseType;
}
*r_retSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue);
}
internal static unsafe bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName)
{
// Performance is not critical here as this will be replaced with source generators.
using var signals = new Dictionary();
string signalNameStr = Marshaling.mono_string_from_godot(*signalName);
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
// Legacy signals
if (top
.GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public)
.Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType))
.Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Any(signalDelegate => signalDelegate.Name == signalNameStr)
)
{
return true;
}
// Event signals
if (top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Any(eventSignal => eventSignal.Name == signalNameStr)
)
{
return true;
}
top = top.BaseType;
}
return false;
}
internal static unsafe bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, bool deep)
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false;
string methodStr = Marshaling.mono_string_from_godot(*method);
if (deep)
{
Type top = scriptType;
Type native = Object.InternalGetClassNativeBase(scriptType);
while (top != null && top != native)
{
var methodInfo = top.GetMethod(methodStr,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (methodInfo != null)
return true;
top = top.BaseType;
}
return false;
}
else
{
var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
return methodInfo != null;
}
}
internal static bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
{
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false;
if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType))
return false;
return scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType);
}
internal static unsafe bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath)
{
string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath);
if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo))
return false;
_scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType);
return true;
}
internal static void AddScriptBridgeWithType(IntPtr scriptPtr, Type scriptType)
=> _scriptBridgeMap.Add(scriptPtr, scriptType);
internal static void RemoveScriptBridge(IntPtr scriptPtr)
=> _scriptBridgeMap.Remove(scriptPtr);
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, bool* r_tool,
godot_dictionary* r_rpcFunctionsDest)
{
// Performance is not critical here as this will be replaced with source generators.
var scriptType = _scriptBridgeMap[scriptPtr];
*r_tool = scriptType.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any();
if (!*r_tool && scriptType.IsNested)
{
*r_tool = scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any() ?? false;
}
if (!*r_tool && scriptType.Assembly.GetName().Name == "GodotTools")
*r_tool = true;
// RPC functions
static MultiplayerAPI.RPCMode MemberGetRpcMode(MemberInfo memberInfo)
{
if (memberInfo.GetCustomAttributes(inherit: false).OfType<RemoteAttribute>().Any())
return MultiplayerAPI.RPCMode.Remote;
if (memberInfo.GetCustomAttributes(inherit: false).OfType<MasterAttribute>().Any())
return MultiplayerAPI.RPCMode.Master;
if (memberInfo.GetCustomAttributes(inherit: false).OfType<PuppetAttribute>().Any())
return MultiplayerAPI.RPCMode.Puppet;
return MultiplayerAPI.RPCMode.Disabled;
}
Dictionary<string, Dictionary> rpcFunctions = new();
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public))
{
if (method.IsStatic)
continue;
string methodName = method.Name;
if (rpcFunctions.ContainsKey(methodName))
continue;
var rpcMode = MemberGetRpcMode(method);
if (rpcMode == MultiplayerAPI.RPCMode.Disabled)
continue;
var rpcConfig = new Dictionary();
rpcConfig["rpc_mode"] = (int)rpcMode;
// TODO Transfer mode, channel
rpcConfig["transfer_mode"] = (int)MultiplayerPeer.TransferModeEnum.Reliable;
rpcConfig["channel"] = 0;
rpcFunctions.Add(methodName, rpcConfig);
}
top = top.BaseType;
}
*r_rpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue);
}
internal static unsafe bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* r_newGCHandlePtr,
bool createWeak)
{
var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
object target = oldGCHandle.Target;
if (target == null)
{
*r_newGCHandlePtr = IntPtr.Zero;
return false; // Called after the managed side was collected, so nothing to do here
}
// Release the current weak handle and replace it with a strong handle.
var newGCHandle = GCHandle.Alloc(target, createWeak ? GCHandleType.Weak : GCHandleType.Normal);
*r_newGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
return true;
}
}
}

View file

@ -11,10 +11,6 @@ namespace Godot
{
internal static class DelegateUtils
{
// TODO: Move somewhere else once we need to for things other than delegates
internal static void FreeGCHandle(IntPtr delegateGCHandle)
=> GCHandle.FromIntPtr(delegateGCHandle).Free();
internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
{
var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target;
@ -22,14 +18,14 @@ namespace Godot
return @delegateA == @delegateB;
}
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, godot_variant* ret)
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc,
godot_variant* ret)
{
// TODO: Optimize
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target;
var managedArgs = new object[argc];
var parameterInfos = @delegate.Method.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
@ -260,7 +256,8 @@ namespace Godot
}
}
private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, out IntPtr delegateGCHandle)
private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData,
out IntPtr delegateGCHandle)
{
bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate);
delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate));
@ -368,7 +365,8 @@ namespace Godot
int valueBufferLength = reader.ReadInt32();
byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
FieldInfo fieldInfo =
targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
}

View file

@ -15,7 +15,7 @@ namespace Godot.Collections
IDictionary,
IDisposable
{
internal godot_dictionary NativeValue;
public godot_dictionary NativeValue;
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
@ -319,7 +319,7 @@ namespace Godot.Collections
{
using godot_string str = default;
NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str);
return Marshaling.mono_string_from_godot(&str);
return Marshaling.mono_string_from_godot(str);
}
}

View file

@ -1,4 +1,4 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using Godot.Collections;
using Godot.NativeInterop;
@ -7,14 +7,64 @@ namespace Godot
{
public partial class SceneTree
{
public Array<T> GetNodesInGroup<T>(StringName group) where T : class
public unsafe Array<T> GetNodesInGroup<T>(StringName group) where T : class
{
godot_array array;
godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), ref group.NativeValue, typeof(T), out array);
return Array<T>.CreateTakingOwnershipOfDisposableValue(array);
var array = GetNodesInGroup(group);
if (array.Count == 0)
return new Array<T>(array);
var typeOfT = typeof(T);
bool nativeBase = InternalIsClassNativeBase(typeOfT);
if (nativeBase)
{
// Native type
var field = typeOfT.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
var nativeName = (StringName)field!.GetValue(null);
godot_string_name nativeNameAux = nativeName.NativeValue;
godot_array inputAux = array.NativeValue;
godot_array filteredArray;
godotsharp_array_filter_godot_objects_by_native(&nativeNameAux, &inputAux, &filteredArray);
return Array<T>.CreateTakingOwnershipOfDisposableValue(filteredArray);
}
else
{
// Custom derived type
godot_array inputAux = array.NativeValue;
godot_array filteredArray;
godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray);
var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray);
// Re-use first array as its size is the same or greater than the filtered one
var resWrapped = new Array<T>(array);
int j = 0;
for (int i = 0; i < filteredArrayWrapped.Count; i++)
{
if (filteredArrayWrapped[i] is T t)
{
resWrapped[j] = t;
j++;
}
}
// Remove trailing elements, since this was re-used
resWrapped.Resize(j);
return resWrapped;
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, ref godot_string_name group, Type elemType, out godot_array dest);
internal extern unsafe void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name,
godot_array* p_input, godot_array* r_output);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern unsafe void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input,
godot_array* r_output);
}
}

View file

@ -192,7 +192,7 @@ namespace Godot
using var whatGodotArray = Marshaling.mono_array_to_Array(what);
using godot_string ret = default;
NativeFuncs.godotsharp_str(&whatGodotArray, &ret);
return Marshaling.mono_string_from_godot(&ret);
return Marshaling.mono_string_from_godot(ret);
}
public static unsafe object Str2Var(string str)
@ -217,7 +217,7 @@ namespace Godot
using var variant = Marshaling.mono_object_to_variant(var);
using godot_string ret = default;
NativeFuncs.godotsharp_var2str(&variant, &ret);
return Marshaling.mono_string_from_godot(&ret);
return Marshaling.mono_string_from_godot(ret);
}
public static Variant.Type TypeToVariantType(Type type)

View file

@ -17,10 +17,7 @@ namespace Godot
public override void Fail(string message, string detailMessage)
{
GD.PrintErr("Assertion failed: ", message);
if (detailMessage != null)
{
GD.PrintErr(" Details: ", detailMessage);
}
GD.PrintErr(" Details: ", detailMessage);
try
{

View file

@ -13,7 +13,7 @@ namespace Godot.NativeInterop
{
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_bool
public struct godot_bool
{
public byte _value;
@ -25,7 +25,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_ref : IDisposable
public struct godot_ref : IDisposable
{
internal IntPtr _reference;
@ -41,7 +41,7 @@ namespace Godot.NativeInterop
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal enum godot_variant_call_error_error
public enum godot_variant_call_error_error
{
GODOT_CALL_ERROR_CALL_OK = 0,
GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD,
@ -53,16 +53,16 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_variant_call_error
public struct godot_variant_call_error
{
godot_variant_call_error_error error;
int argument;
Godot.Variant.Type expected;
public godot_variant_call_error_error error;
public int argument;
public Godot.Variant.Type expected;
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal struct godot_variant : IDisposable
public struct godot_variant : IDisposable
{
[FieldOffset(0)] public Godot.Variant.Type _type;
@ -152,7 +152,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_string : IDisposable
public struct godot_string : IDisposable
{
internal IntPtr _ptr;
@ -170,7 +170,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_string_name : IDisposable
public struct godot_string_name : IDisposable
{
internal IntPtr _data;
@ -191,7 +191,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_node_path : IDisposable
public struct godot_node_path : IDisposable
{
internal IntPtr _data;
@ -212,7 +212,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal struct godot_signal : IDisposable
public struct godot_signal : IDisposable
{
[FieldOffset(0)] public godot_string_name _name;
@ -231,7 +231,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal struct godot_callable : IDisposable
public struct godot_callable : IDisposable
{
[FieldOffset(0)] public godot_string_name _method;
@ -255,7 +255,7 @@ namespace Godot.NativeInterop
// be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_array : IDisposable
public struct godot_array : IDisposable
{
internal unsafe ArrayPrivate* _p;
@ -294,7 +294,7 @@ namespace Godot.NativeInterop
// be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_dictionary : IDisposable
public struct godot_dictionary : IDisposable
{
internal IntPtr _p;
@ -309,7 +309,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_byte_array : IDisposable
public struct godot_packed_byte_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe byte* _ptr;
@ -327,7 +327,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_int32_array : IDisposable
public struct godot_packed_int32_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe int* _ptr;
@ -345,7 +345,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_int64_array : IDisposable
public struct godot_packed_int64_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe long* _ptr;
@ -363,7 +363,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_float32_array : IDisposable
public struct godot_packed_float32_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe float* _ptr;
@ -381,7 +381,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_float64_array : IDisposable
public struct godot_packed_float64_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe double* _ptr;
@ -399,7 +399,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_string_array : IDisposable
public struct godot_packed_string_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe godot_string* _ptr;
@ -417,7 +417,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_vector2_array : IDisposable
public struct godot_packed_vector2_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Vector2* _ptr;
@ -435,7 +435,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_vector3_array : IDisposable
public struct godot_packed_vector3_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Vector3* _ptr;
@ -453,7 +453,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_color_array : IDisposable
public struct godot_packed_color_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Color* _ptr;

View file

@ -1,5 +1,9 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.Bridge;
// ReSharper disable InconsistentNaming
namespace Godot.NativeInterop
{
@ -7,21 +11,101 @@ namespace Godot.NativeInterop
{
public static Object UnmanagedGetManaged(IntPtr unmanaged)
{
// TODO: Move to C#
return internal_unmanaged_get_managed(unmanaged);
// The native pointer may be null
if (unmanaged == IntPtr.Zero)
return null;
IntPtr gcHandlePtr;
bool has_cs_script_instance = false;
// First try to get the tied managed instance from a CSharpInstance script instance
unsafe
{
gcHandlePtr = unmanaged_get_script_instance_managed(unmanaged, &has_cs_script_instance);
}
if (gcHandlePtr != IntPtr.Zero)
return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
// Otherwise, if the object has a CSharpInstance script instance, return null
if (has_cs_script_instance)
return null;
// If it doesn't have a CSharpInstance script instance, try with native instance bindings
gcHandlePtr = unmanaged_get_instance_binding_managed(unmanaged);
object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null;
if (target != null)
return (Object)target;
// If the native instance binding GC handle target was collected, create a new one
gcHandlePtr = unmanaged_instance_binding_create_managed(unmanaged, gcHandlePtr);
return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null;
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern Object internal_unmanaged_get_managed(IntPtr unmanaged);
private static extern unsafe IntPtr unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
bool* r_has_cs_script_instance);
public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged)
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
IntPtr oldGCHandlePtr);
public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged,
StringName nativeName, bool refCounted, Type type, Type nativeType)
{
// TODO: Move to C#
internal_tie_managed_to_unmanaged(managed, unmanaged);
var gcHandle = GCHandle.Alloc(managed, refCounted ? GCHandleType.Weak : GCHandleType.Normal);
if (type == nativeType)
{
unsafe
{
godot_string_name nativeNameAux = nativeName.NativeValue;
internal_tie_native_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged,
&nativeNameAux, refCounted);
}
}
else
{
IntPtr scriptPtr = internal_new_csharp_script();
ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type);
// IMPORTANT: This must be called after AddScriptWithTypeBridge
internal_tie_user_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged,
scriptPtr, refCounted);
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_tie_managed_to_unmanaged(Object managed, IntPtr unmanaged);
private static extern unsafe void internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, godot_string_name* nativeName, bool refCounted);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, IntPtr scriptPtr, bool refCounted);
public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged)
{
var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal);
internal_tie_managed_to_unmanaged_with_pre_setup(GCHandle.ToIntPtr(strongGCHandle), unmanaged);
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_tie_managed_to_unmanaged_with_pre_setup(
IntPtr gcHandleIntPtr, IntPtr unmanaged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr internal_new_csharp_script();
public static unsafe Object EngineGetSingleton(string name)
{

View file

@ -10,14 +10,8 @@ namespace Godot.NativeInterop
{
// We want to use full name qualifiers here even if redundant for clarity
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
internal static class Marshaling
public static class Marshaling
{
public static unsafe void SetFieldValue(FieldInfo fieldInfo, object obj, godot_variant* value)
{
var valueObj = variant_to_mono_object_of_type(value, fieldInfo.FieldType);
fieldInfo.SetValue(obj, valueObj);
}
public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant)
{
r_nil_is_variant = false;
@ -233,10 +227,6 @@ namespace Godot.NativeInterop
return mono_object_to_variant_impl(p_obj);
}
// TODO: Only called from C++. Remove once no longer needed.
private static unsafe void mono_object_to_variant_out(object p_obj, bool p_fail_with_err, godot_variant* r_ret)
=> *r_ret = mono_object_to_variant_impl(p_obj, p_fail_with_err);
private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true)
{
if (p_obj == null)
@ -442,7 +432,7 @@ namespace Godot.NativeInterop
// TODO: Validate element type is compatible with Variant
#if NET
var nativeGodotArray =
mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj));
(godot_array)mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj));
#else
// With .NET Standard we need a package reference for Microsoft.CSharp in order to
// use dynamic, so we have this workaround for now until we switch to .NET 5/6.
@ -485,12 +475,12 @@ namespace Godot.NativeInterop
case Variant.Type.String:
{
// We avoid the internal call if the stored type is the same we want.
return mono_string_from_godot(&(*p_var)._data._m_string);
return mono_string_from_godot((*p_var)._data._m_string);
}
default:
{
using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
return mono_string_from_godot(&godotString);
return mono_string_from_godot(godotString);
}
}
}
@ -853,7 +843,7 @@ namespace Godot.NativeInterop
#endif
}
case Variant.Type.String:
return mono_string_from_godot(&(*p_var)._data._m_string);
return mono_string_from_godot((*p_var)._data._m_string);
case Variant.Type.Vector2:
return (*p_var)._data._m_vector2;
case Variant.Type.Vector2i:
@ -977,14 +967,14 @@ namespace Godot.NativeInterop
}
}
public static unsafe string mono_string_from_godot(godot_string* p_string)
public static unsafe string mono_string_from_godot(in godot_string p_string)
{
if ((*p_string)._ptr == IntPtr.Zero)
if (p_string._ptr == IntPtr.Zero)
return string.Empty;
const int sizeOfChar32 = 4;
byte* bytes = (byte*)(*p_string)._ptr;
int size = (*p_string).Size;
byte* bytes = (byte*)p_string._ptr;
int size = p_string.Size;
if (size == 0)
return string.Empty;
size -= 1; // zero at the end
@ -1253,7 +1243,7 @@ namespace Godot.NativeInterop
int size = (*p_array).Size;
var array = new string[size];
for (int i = 0; i < size; i++)
array[i] = mono_string_from_godot(&buffer[i]);
array[i] = mono_string_from_godot(buffer[i]);
return array;
}

View file

@ -10,7 +10,7 @@ namespace Godot.NativeInterop
// The attribute is not available with .NET Core and it's not needed there.
[System.Security.SuppressUnmanagedCodeSecurity]
#endif
internal static unsafe partial class NativeFuncs
public static unsafe partial class NativeFuncs
{
private const string GodotDllName = "__Internal";

View file

@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
namespace Godot.NativeInterop
{
internal static unsafe partial class NativeFuncs
public static unsafe partial class NativeFuncs
{
public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src)
{

View file

@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
namespace Godot.NativeInterop
{
internal static class VariantUtils
public static class VariantUtils
{
public static godot_variant CreateFromRID(RID from)
=> new() {_type = Variant.Type.Rid, _data = {_m_rid = from}};

View file

@ -6,7 +6,7 @@ namespace Godot
{
public sealed class NodePath : IDisposable
{
internal godot_node_path NativeValue;
public godot_node_path NativeValue;
~NodePath()
{
@ -57,7 +57,7 @@ namespace Godot
godot_node_path src = NativeValue;
NativeFuncs.godotsharp_node_path_as_string(&dest, &src);
using (dest)
return Marshaling.mono_string_from_godot(&dest);
return Marshaling.mono_string_from_godot(dest);
}
public NodePath GetAsPropertyPath()
@ -71,14 +71,14 @@ namespace Godot
{
using godot_string subNames = default;
NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames);
return Marshaling.mono_string_from_godot(&subNames);
return Marshaling.mono_string_from_godot(subNames);
}
public unsafe string GetName(int idx)
{
using godot_string name = default;
NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name);
return Marshaling.mono_string_from_godot(&name);
return Marshaling.mono_string_from_godot(name);
}
public int GetNameCount()
@ -90,7 +90,7 @@ namespace Godot
{
using godot_string subName = default;
NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName);
return Marshaling.mono_string_from_godot(&subName);
return Marshaling.mono_string_from_godot(subName);
}
public int GetSubNameCount()

View file

@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
@ -7,6 +9,7 @@ namespace Godot
public partial class Object : IDisposable
{
private bool _disposed = false;
private Type _cachedType = typeof(Object);
internal IntPtr NativePtr;
internal bool MemoryOwn;
@ -18,12 +21,17 @@ namespace Godot
#if NET
unsafe
{
ptr = NativeCtor();
NativePtr = NativeCtor();
}
#else
NativePtr = _gd__invoke_class_constructor(NativeCtor);
#endif
NativeInterop.InteropUtils.TieManagedToUnmanaged(this, NativePtr);
InteropUtils.TieManagedToUnmanaged(this, NativePtr,
NativeName, refCounted: false, GetType(), _cachedType);
}
else
{
InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr);
}
_InitializeGodotScriptInstanceInternals();
@ -31,12 +39,32 @@ namespace Godot
internal void _InitializeGodotScriptInstanceInternals()
{
godot_icall_Object_ConnectEventSignals(NativePtr);
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
foreach (var eventSignal in top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()))
{
unsafe
{
using var eventSignalName = new StringName(eventSignal.Name);
godot_string_name eventSignalNameAux = eventSignalName.NativeValue;
godot_icall_Object_ConnectEventSignal(NativePtr, &eventSignalNameAux);
}
}
top = top.BaseType;
}
}
internal Object(bool memoryOwn)
{
this.MemoryOwn = memoryOwn;
MemoryOwn = memoryOwn;
}
public IntPtr NativeInstance => NativePtr;
@ -73,11 +101,11 @@ namespace Godot
if (MemoryOwn)
{
MemoryOwn = false;
godot_icall_RefCounted_Disposed(this, NativePtr, !disposing);
godot_icall_RefCounted_Disposed(NativePtr, !disposing);
}
else
{
godot_icall_Object_Disposed(this, NativePtr);
godot_icall_Object_Disposed(NativePtr);
}
this.NativePtr = IntPtr.Zero;
@ -90,7 +118,7 @@ namespace Godot
{
using godot_string str = default;
NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str);
return Marshaling.mono_string_from_godot(&str);
return Marshaling.mono_string_from_godot(str);
}
/// <summary>
@ -121,13 +149,219 @@ namespace Godot
return new SignalAwaiter(source, signal, this);
}
internal static Type InternalGetClassNativeBase(Type t)
{
do
{
var assemblyName = t.Assembly.GetName();
if (assemblyName.Name == "GodotSharp")
return t;
if (assemblyName.Name == "GodotSharpEditor")
return t;
} while ((t = t.BaseType) != null);
return null;
}
internal static bool InternalIsClassNativeBase(Type t)
{
var assemblyName = t.Assembly.GetName();
return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor";
}
internal unsafe bool InternalGodotScriptCallViaReflection(string method, godot_variant** args, int argCount,
out godot_variant ret)
{
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var methodInfo = top.GetMethod(method,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (methodInfo != null)
{
var parameters = methodInfo.GetParameters();
int paramCount = parameters.Length;
if (argCount == paramCount)
{
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
{
invokeParams[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameters[i].ParameterType);
}
object retObj = methodInfo.Invoke(this, invokeParams);
ret = Marshaling.mono_object_to_variant(retObj);
return true;
}
}
top = top.BaseType;
}
ret = default;
return false;
}
internal unsafe bool InternalGodotScriptSetFieldOrPropViaReflection(string name, godot_variant* value)
{
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var fieldInfo = top.GetField(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (fieldInfo != null)
{
object valueManaged = Marshaling.variant_to_mono_object_of_type(value, fieldInfo.FieldType);
fieldInfo.SetValue(this, valueManaged);
return true;
}
var propertyInfo = top.GetProperty(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (propertyInfo != null)
{
object valueManaged = Marshaling.variant_to_mono_object_of_type(value, propertyInfo.PropertyType);
propertyInfo.SetValue(this, valueManaged);
return true;
}
top = top.BaseType;
}
return false;
}
internal bool InternalGodotScriptGetFieldOrPropViaReflection(string name, out godot_variant value)
{
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var fieldInfo = top.GetField(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (fieldInfo != null)
{
object valueManaged = fieldInfo.GetValue(this);
value = Marshaling.mono_object_to_variant(valueManaged);
return true;
}
var propertyInfo = top.GetProperty(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (propertyInfo != null)
{
object valueManaged = propertyInfo.GetValue(this);
value = Marshaling.mono_object_to_variant(valueManaged);
return true;
}
top = top.BaseType;
}
value = default;
return false;
}
internal unsafe void InternalRaiseEventSignal(godot_string_name* eventSignalName, godot_variant** args,
int argc)
{
// Performance is not critical here as this will be replaced with source generators.
using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(eventSignalName));
string eventSignalNameStr = stringName.ToString();
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var foundEventSignals = top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Select(ev => ev.Name);
var fields = top.GetFields(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
var eventSignalField = fields
.Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
.Where(f => foundEventSignals.Contains(f.Name))
.FirstOrDefault(f => f.Name == eventSignalNameStr);
if (eventSignalField != null)
{
var @delegate = (Delegate)eventSignalField.GetValue(this);
if (@delegate == null)
continue;
var delegateType = eventSignalField.FieldType;
var invokeMethod = delegateType.GetMethod("Invoke");
if (invokeMethod == null)
throw new MissingMethodException(delegateType.FullName, "Invoke");
var parameterInfos = invokeMethod.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
{
throw new InvalidOperationException(
$"The event delegate expects {paramsLength} arguments, but received {argc}.");
}
var managedArgs = new object[argc];
for (uint i = 0; i < argc; i++)
{
managedArgs[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameterInfos[i].ParameterType);
}
invokeMethod.Invoke(@delegate, managedArgs);
return;
}
top = top.BaseType;
}
}
internal static unsafe IntPtr ClassDB_get_method(StringName type, string method)
{
IntPtr methodBind;
fixed (char* methodChars = method)
{
methodBind = NativeInterop.NativeFuncs
.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars);
methodBind = NativeFuncs.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars);
}
if (methodBind == IntPtr.Zero)
@ -137,11 +371,10 @@ namespace Godot
}
#if NET
internal static unsafe delegate* unmanaged<IntPtr> _gd__ClassDB_get_constructor(StringName type)
internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
{
// for some reason the '??' operator doesn't support 'delegate*'
var nativeConstructor = NativeInterop.NativeFuncs
.godotsharp_get_class_constructor(ref type.NativeValue);
var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue);
if (nativeConstructor == null)
throw new NativeConstructorNotFoundException(type);
@ -152,8 +385,7 @@ namespace Godot
internal static IntPtr ClassDB_get_constructor(StringName type)
{
// for some reason the '??' operator doesn't support 'delegate*'
var nativeConstructor = NativeInterop.NativeFuncs
.godotsharp_get_class_constructor(ref type.NativeValue);
var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue);
if (nativeConstructor == IntPtr.Zero)
throw new NativeConstructorNotFoundException(type);
@ -162,16 +394,17 @@ namespace Godot
}
internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr)
=> NativeInterop.NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr);
=> NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr);
#endif
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
internal static extern void godot_icall_Object_Disposed(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
internal static extern void godot_icall_RefCounted_Disposed(IntPtr ptr, bool isFinalizer);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
internal static extern unsafe void godot_icall_Object_ConnectEventSignal(IntPtr obj,
godot_string_name* eventSignal);
}
}

View file

@ -1,10 +0,0 @@
namespace Godot
{
internal class ScriptManager
{
internal static void FrameCallback()
{
Dispatcher.DefaultGodotTaskScheduler?.Activate();
}
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot
@ -12,11 +13,13 @@ namespace Godot
public SignalAwaiter(Object source, StringName signal, Object target)
{
godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), this);
godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue,
Object.GetPtr(target), GCHandle.ToIntPtr(GCHandle.Alloc(this)));
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, IntPtr target, SignalAwaiter awaiter);
internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal,
IntPtr target, IntPtr awaiterHandlePtr);
public bool IsCompleted => _completed;
@ -29,14 +32,32 @@ namespace Godot
public IAwaiter<object[]> GetAwaiter() => this;
internal void SignalCallback(object[] args)
internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr,
godot_variant** args, int argCount,
bool* r_awaiterIsNull)
{
_completed = true;
_result = args;
var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target;
if (_action != null)
if (awaiter == null)
{
_action();
*r_awaiterIsNull = true;
return;
}
*r_awaiterIsNull = false;
awaiter._completed = true;
object[] signalArgs = new object[argCount];
for (int i = 0; i < argCount; i++)
signalArgs[i] = Marshaling.variant_to_mono_object(args[i]);
awaiter._result = signalArgs;
if (awaiter._action != null)
{
awaiter._action();
}
}
}

View file

@ -872,7 +872,7 @@ namespace Godot
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_string md5Text = default;
NativeFuncs.godotsharp_string_md5_text(&instanceStr, &md5Text);
return Marshaling.mono_string_from_godot(&md5Text);
return Marshaling.mono_string_from_godot(md5Text);
}
/// <summary>
@ -1065,7 +1065,7 @@ namespace Godot
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_string sha256Text = default;
NativeFuncs.godotsharp_string_sha256_text(&instanceStr, &sha256Text);
return Marshaling.mono_string_from_godot(&sha256Text);
return Marshaling.mono_string_from_godot(sha256Text);
}
/// <summary>

View file

@ -6,7 +6,7 @@ namespace Godot
{
public sealed class StringName : IDisposable
{
internal godot_string_name NativeValue;
public godot_string_name NativeValue;
~StringName()
{
@ -57,7 +57,7 @@ namespace Godot
godot_string_name src = NativeValue;
NativeFuncs.godotsharp_string_name_as_string(&dest, &src);
using (dest)
return Marshaling.mono_string_from_godot(&dest);
return Marshaling.mono_string_from_godot(dest);
}
public bool IsEmpty => godot_string_name.IsEmpty(in NativeValue);

View file

@ -32,6 +32,9 @@
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
<Compile Include="Core\Basis.cs" />
<Compile Include="Core\Bridge\CSharpInstanceBridge.cs" />
<Compile Include="Core\Bridge\GCHandleBridge.cs" />
<Compile Include="Core\Bridge\ScriptManagerBridge.cs" />
<Compile Include="Core\Callable.cs" />
<Compile Include="Core\Color.cs" />
<Compile Include="Core\Colors.cs" />
@ -69,7 +72,6 @@
<Compile Include="Core\NativeInterop\NativeFuncs.cs" />
<Compile Include="Core\NativeInterop\InteropStructs.cs" />
<Compile Include="Core\NativeInterop\Marshaling.cs" />
<Compile Include="Core\ScriptManager.cs" />
<Compile Include="Core\SignalInfo.cs" />
<Compile Include="Core\SignalAwaiter.cs" />
<Compile Include="Core\StringExtensions.cs" />

View file

@ -34,13 +34,11 @@
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../mono_gd/gd_mono_utils.h"
#include "../signal_awaiter_utils.h"
void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
void godot_icall_Object_Disposed(Object *p_ptr) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == nullptr);
#endif
@ -49,7 +47,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
if (cs_instance) {
if (!cs_instance->is_destructing_script_instance()) {
cs_instance->mono_object_disposed(p_obj);
cs_instance->mono_object_disposed();
p_ptr->set_script_instance(nullptr);
}
return;
@ -63,13 +61,13 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
CSharpLanguage::release_script_gchandle(nullptr, gchandle);
}
}
}
}
void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
void godot_icall_RefCounted_Disposed(Object *p_ptr, MonoBoolean p_is_finalizer) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == nullptr);
// This is only called with RefCounted derived classes
@ -85,7 +83,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole
bool delete_owner;
bool remove_script_instance;
cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance);
cs_instance->mono_object_disposed_baseref(p_is_finalizer, delete_owner, remove_script_instance);
if (delete_owner) {
memdelete(rc);
@ -110,28 +108,28 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
CSharpLanguage::release_script_gchandle(nullptr, gchandle);
}
}
}
}
}
void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
void godot_icall_Object_ConnectEventSignal(Object *p_ptr, const StringName *p_event_signal) {
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
if (csharp_instance) {
csharp_instance->connect_event_signals();
csharp_instance->connect_event_signal(*p_event_signal);
}
}
int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
StringName signal = p_signal ? *p_signal : StringName();
return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr);
}
void godot_register_object_icalls() {
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed);
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_RefCounted_Disposed", godot_icall_RefCounted_Disposed);
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals);
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignal", godot_icall_Object_ConnectEventSignal);
GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect);
}

View file

@ -30,22 +30,165 @@
#include "core/object/object.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_utils.h"
MonoObject *godot_icall_InteropUtils_unmanaged_get_managed(Object *unmanaged) {
return GDMonoUtils::unmanaged_get_managed(unmanaged);
GCHandleIntPtr unmanaged_get_script_instance_managed(Object *p_unmanaged, bool *r_has_cs_script_instance) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_unmanaged);
CRASH_COND(!r_has_cs_script_instance);
#endif
if (p_unmanaged->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance());
if (cs_instance) {
*r_has_cs_script_instance = true;
return cs_instance->get_gchandle_intptr();
}
}
*r_has_cs_script_instance = false;
return GCHandleIntPtr();
}
void godot_icall_InteropUtils_tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
GDMonoInternals::tie_managed_to_unmanaged(managed, unmanaged);
GCHandleIntPtr unmanaged_get_instance_binding_managed(Object *p_unmanaged) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_unmanaged);
#endif
void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
ERR_FAIL_NULL_V(data, GCHandleIntPtr());
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr());
return script_binding.gchandle.get_intptr();
}
GCHandleIntPtr unmanaged_instance_binding_create_managed(Object *p_unmanaged, GCHandleIntPtr p_old_gchandle) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_unmanaged);
#endif
void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
ERR_FAIL_NULL_V(data, GCHandleIntPtr());
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr());
MonoGCHandleData &gchandle = script_binding.gchandle;
// TODO: Possible data race?
CRASH_COND(gchandle.get_intptr().value != p_old_gchandle.value);
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
// Create a new one
#ifdef DEBUG_ENABLED
CRASH_COND(script_binding.type_name == StringName());
#endif
bool parent_is_object_class = ClassDB::is_parent_class(p_unmanaged->get_class_name(), script_binding.type_name);
ERR_FAIL_COND_V_MSG(!parent_is_object_class, GCHandleIntPtr(),
"Type inherits from native type '" + script_binding.type_name + "', so it can't be instantiated in object of type: '" + p_unmanaged->get_class() + "'.");
MonoException *exc = nullptr;
GCHandleIntPtr strong_gchandle =
GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding
.invoke(&script_binding.type_name, p_unmanaged, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return GCHandleIntPtr();
}
ERR_FAIL_NULL_V(strong_gchandle.value, GCHandleIntPtr());
gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
// Tie managed to unmanaged
RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
// See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
rc->reference();
CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
return gchandle.get_intptr();
}
void godot_icall_InteropUtils_tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) {
CSharpLanguage::tie_native_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_native_name, p_ref_counted);
}
void godot_icall_InteropUtils_tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted) {
CSharpLanguage::tie_user_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_script, p_ref_counted);
}
void godot_icall_InteropUtils_tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(p_gchandle_intptr, p_unmanaged);
}
CSharpScript *godot_icall_InteropUtils_internal_new_csharp_script() {
CSharpScript *script = memnew(CSharpScript);
CRASH_COND(!script);
return script;
}
void godotsharp_array_filter_godot_objects_by_native(StringName *p_native_name, const Array *p_input, Array *r_output) {
memnew_placement(r_output, Array);
for (int i = 0; i < p_input->size(); ++i) {
if (ClassDB::is_parent_class(((Object *)(*p_input)[i])->get_class(), *p_native_name)) {
r_output->push_back(p_input[i]);
}
}
}
void godotsharp_array_filter_godot_objects_by_non_native(const Array *p_input, Array *r_output) {
memnew_placement(r_output, Array);
for (int i = 0; i < p_input->size(); ++i) {
CSharpInstance *si = CAST_CSHARP_INSTANCE(((Object *)(*p_input)[i])->get_script_instance());
if (si != nullptr) {
r_output->push_back(p_input[i]);
}
}
}
void godot_register_placeholder_icalls() {
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_unmanaged_get_managed",
godot_icall_InteropUtils_unmanaged_get_managed);
"Godot.NativeInterop.InteropUtils::unmanaged_get_script_instance_managed",
unmanaged_get_script_instance_managed);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_tie_managed_to_unmanaged",
godot_icall_InteropUtils_tie_managed_to_unmanaged);
"Godot.NativeInterop.InteropUtils::unmanaged_get_instance_binding_managed",
unmanaged_get_instance_binding_managed);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::unmanaged_instance_binding_create_managed",
unmanaged_instance_binding_create_managed);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_tie_native_managed_to_unmanaged",
godot_icall_InteropUtils_tie_native_managed_to_unmanaged);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_tie_user_managed_to_unmanaged",
godot_icall_InteropUtils_tie_user_managed_to_unmanaged);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_tie_managed_to_unmanaged_with_pre_setup",
godot_icall_InteropUtils_tie_managed_to_unmanaged_with_pre_setup);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_new_csharp_script",
godot_icall_InteropUtils_internal_new_csharp_script);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.SceneTree::godotsharp_array_filter_godot_objects_by_native",
godotsharp_array_filter_godot_objects_by_native);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.SceneTree::godotsharp_array_filter_godot_objects_by_non_native",
godotsharp_array_filter_godot_objects_by_non_native);
}

View file

@ -187,14 +187,14 @@ GD_PINVOKE_EXPORT void godotsharp_packed_string_array_add(PackedStringArray *r_d
r_dest->append(*p_element);
}
GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(void *p_delegate_handle, Callable *r_callable) {
GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) {
// TODO: Use pooling for ManagedCallable instances.
CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle));
memnew_placement(r_callable, Callable(managed_callable));
}
GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable,
void **r_delegate_handle, Object **r_object, StringName *r_name) {
GCHandleIntPtr *r_delegate_handle, Object **r_object, StringName *r_name) {
if (p_callable->is_custom()) {
CallableCustom *custom = p_callable->get_custom();
CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
@ -207,25 +207,25 @@ GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callab
return true;
} else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object());
memnew_placement(r_name, StringName(signal_awaiter_callable->get_signal()));
return true;
} else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = ObjectDB::get_instance(event_signal_callable->get_object());
memnew_placement(r_name, StringName(event_signal_callable->get_signal()));
return true;
}
// Some other CallableCustom. We only support ManagedCallable.
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = nullptr;
memnew_placement(r_name, StringName());
return false;
} else {
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = ObjectDB::get_instance(p_callable->get_object_id());
memnew_placement(r_name, StringName(p_callable->get_method()));
return true;

View file

@ -1,81 +0,0 @@
/*************************************************************************/
/* scene_tree_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 "core/object/class_db.h"
#include "core/string/string_name.h"
#include "core/variant/array.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../mono_gd/gd_mono_utils.h"
void godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype, Array *r_dest) {
memnew_placement(r_dest, Array);
List<Node *> nodes;
// Retrieve all the nodes in the group
ptr->get_nodes_in_group(*group, &nodes);
// No need to bother if the group is empty
if (!nodes.is_empty()) {
MonoType *elem_type = mono_reflection_type_get_type(refltype);
MonoClass *mono_class = mono_class_from_mono_type(elem_type);
GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class);
if (klass == GDMonoUtils::get_class_native_base(klass)) {
// If we're trying to get native objects, just check the inheritance list
StringName native_class_name = GDMonoUtils::get_native_godot_class_name(klass);
for (int i = 0; i < nodes.size(); ++i) {
if (ClassDB::is_parent_class(nodes[i]->get_class(), native_class_name)) {
r_dest->push_back(nodes[i]);
}
}
} else {
// If we're trying to get csharpscript instances, get the mono object and compare the classes
for (int i = 0; i < nodes.size(); ++i) {
CSharpInstance *si = CAST_CSHARP_INSTANCE(nodes[i]->get_script_instance());
if (si != nullptr) {
MonoObject *obj = si->get_mono_object();
if (obj != nullptr && mono_object_get_class(obj) == mono_class) {
r_dest->push_back(nodes[i]);
}
}
}
}
}
}
void godot_register_scene_tree_icalls() {
GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic);
}

View file

@ -33,6 +33,7 @@
#define BINDINGS_NAMESPACE "Godot"
#define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections"
#define BINDINGS_NAMESPACE_BRIDGE BINDINGS_NAMESPACE ".Bridge"
#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
#define BINDINGS_PTR_FIELD "NativePtr"
#define BINDINGS_NATIVE_NAME_FIELD "NativeName"

View file

@ -89,8 +89,6 @@ class _GodotSharpDirs {
public:
String res_data_dir;
String res_metadata_dir;
String res_assemblies_base_dir;
String res_assemblies_dir;
String res_config_dir;
String res_temp_dir;
String res_temp_assemblies_base_dir;
@ -98,6 +96,9 @@ public:
String mono_user_dir;
String mono_logs_dir;
String api_assemblies_base_dir;
String api_assemblies_dir;
#ifdef TOOLS_ENABLED
String mono_solutions_dir;
String build_logs_dir;
@ -106,7 +107,6 @@ public:
String csproj_filepath;
String data_editor_tools_dir;
String data_editor_prebuilt_api_dir;
#else
// Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
// Only defined on export templates. Used when exporting assemblies outside of PCKs.
@ -124,8 +124,6 @@ private:
_GodotSharpDirs() {
res_data_dir = "res://.godot/mono";
res_metadata_dir = res_data_dir.plus_file("metadata");
res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
// TODO use paths from csproj
@ -133,6 +131,8 @@ private:
res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin");
res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config());
api_assemblies_base_dir = res_data_dir.plus_file("assemblies");
#ifdef JAVASCRIPT_ENABLED
mono_user_dir = "user://";
#else
@ -162,7 +162,7 @@ private:
String data_dir_root = exe_dir.plus_file("GodotSharp");
data_editor_tools_dir = data_dir_root.plus_file("Tools");
data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api");
api_assemblies_base_dir = data_dir_root.plus_file("Api");
String data_mono_root_dir = data_dir_root.plus_file("Mono");
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
@ -182,8 +182,8 @@ private:
data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools");
}
if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
data_editor_prebuilt_api_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
if (!DirAccess::exists(api_assemblies_base_dir)) {
api_assemblies_base_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
}
if (!DirAccess::exists(data_mono_root_dir)) {
@ -227,6 +227,8 @@ private:
#endif
#endif
api_assemblies_dir = api_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
}
_GodotSharpDirs(const _GodotSharpDirs &);
@ -247,14 +249,6 @@ String get_res_metadata_dir() {
return _GodotSharpDirs::get_singleton().res_metadata_dir;
}
String get_res_assemblies_base_dir() {
return _GodotSharpDirs::get_singleton().res_assemblies_base_dir;
}
String get_res_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_assemblies_dir;
}
String get_res_config_dir() {
return _GodotSharpDirs::get_singleton().res_config_dir;
}
@ -271,6 +265,14 @@ String get_res_temp_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
}
String get_api_assemblies_dir() {
return _GodotSharpDirs::get_singleton().api_assemblies_dir;
}
String get_api_assemblies_base_dir() {
return _GodotSharpDirs::get_singleton().api_assemblies_base_dir;
}
String get_mono_user_dir() {
return _GodotSharpDirs::get_singleton().mono_user_dir;
}
@ -299,10 +301,6 @@ String get_project_csproj_path() {
String get_data_editor_tools_dir() {
return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
}
String get_data_editor_prebuilt_api_dir() {
return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
}
#else
String get_data_game_assemblies_dir() {
return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;

View file

@ -37,13 +37,14 @@ namespace GodotSharpDirs {
String get_res_data_dir();
String get_res_metadata_dir();
String get_res_assemblies_base_dir();
String get_res_assemblies_dir();
String get_res_config_dir();
String get_res_temp_dir();
String get_res_temp_assemblies_base_dir();
String get_res_temp_assemblies_dir();
String get_api_assemblies_dir();
String get_api_assemblies_base_dir();
String get_mono_user_dir();
String get_mono_logs_dir();
@ -55,7 +56,6 @@ String get_project_sln_path();
String get_project_csproj_path();
String get_data_editor_tools_dir();
String get_data_editor_prebuilt_api_dir();
#else
String get_data_game_assemblies_dir();
#endif

View file

@ -32,7 +32,6 @@
#include "csharp_script.h"
#include "mono_gd/gd_mono_cache.h"
#include "mono_gd/gd_mono_marshal.h"
#include "mono_gd/gd_mono_utils.h"
#ifdef GD_MONO_HOT_RELOAD
@ -45,8 +44,8 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus
const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a);
const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b);
if (!a->delegate_handle || !b->delegate_handle) {
if (!a->delegate_handle && !b->delegate_handle) {
if (!a->delegate_handle.value || !b->delegate_handle.value) {
if (!a->delegate_handle.value && !b->delegate_handle.value) {
return true;
}
return false;
@ -54,7 +53,7 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus
// Call Delegate's 'Equals'
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(DelegateUtils, DelegateEquals)
MonoBoolean res = GDMonoCache::cached_data.methodthunk_DelegateUtils_DelegateEquals
.invoke(a->delegate_handle,
b->delegate_handle, &exc);
UNHANDLED_EXCEPTION(exc);
@ -69,7 +68,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust
}
uint32_t ManagedCallable::hash() const {
return hash_djb2_one_64((uint64_t)delegate_handle);
return hash_djb2_one_64((uint64_t)delegate_handle.value);
}
String ManagedCallable::get_as_text() const {
@ -94,7 +93,7 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant
r_return_value = Variant();
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(DelegateUtils, InvokeWithVariantArgs)
GDMonoCache::cached_data.methodthunk_DelegateUtils_InvokeWithVariantArgs
.invoke(delegate_handle, p_arguments,
p_argcount, &r_return_value, &exc);
@ -106,21 +105,21 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant
}
void ManagedCallable::release_delegate_handle() {
if (delegate_handle) {
if (delegate_handle.value) {
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(DelegateUtils, FreeGCHandle).invoke(delegate_handle, &exc);
GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.invoke(delegate_handle, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
}
delegate_handle = nullptr;
delegate_handle = GCHandleIntPtr();
}
}
// Why you do this clang-format...
/* clang-format off */
ManagedCallable::ManagedCallable(void *p_delegate_handle) : delegate_handle(p_delegate_handle) {
ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(instances_mutex);

View file

@ -38,11 +38,10 @@
#include "core/variant/callable.h"
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono_method.h"
class ManagedCallable : public CallableCustom {
friend class CSharpLanguage;
void *delegate_handle;
GCHandleIntPtr delegate_handle;
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
@ -59,7 +58,7 @@ public:
ObjectID get_object() const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
_FORCE_INLINE_ void *get_delegate() const { return delegate_handle; }
_FORCE_INLINE_ GCHandleIntPtr get_delegate() const { return delegate_handle; }
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
@ -69,7 +68,7 @@ public:
void release_delegate_handle();
ManagedCallable(void *p_delegate_handle);
ManagedCallable(GCHandleIntPtr p_delegate_handle);
~ManagedCallable();
};

View file

@ -31,34 +31,24 @@
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
#include "mono_gd/gd_mono_cache.h"
void MonoGCHandleData::release() {
#ifdef DEBUG_ENABLED
CRASH_COND(handle && GDMono::get_singleton() == nullptr);
CRASH_COND(handle.value && GDMono::get_singleton() == nullptr);
#endif
if (handle && GDMono::get_singleton()->is_runtime_initialized()) {
GDMonoUtils::free_gchandle(handle);
handle = 0;
if (handle.value && !GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.is_null() &&
GDMono::get_singleton()->is_runtime_initialized()) {
free_gchandle(handle);
handle.value = nullptr;
}
}
MonoGCHandleData MonoGCHandleData::new_strong_handle(MonoObject *p_object) {
return MonoGCHandleData(GDMonoUtils::new_strong_gchandle(p_object), gdmono::GCHandleType::STRONG_HANDLE);
}
MonoGCHandleData MonoGCHandleData::new_strong_handle_pinned(MonoObject *p_object) {
return MonoGCHandleData(GDMonoUtils::new_strong_gchandle_pinned(p_object), gdmono::GCHandleType::STRONG_HANDLE);
}
MonoGCHandleData MonoGCHandleData::new_weak_handle(MonoObject *p_object) {
return MonoGCHandleData(GDMonoUtils::new_weak_gchandle(p_object), gdmono::GCHandleType::WEAK_HANDLE);
}
Ref<MonoGCHandleRef> MonoGCHandleRef::create_strong(MonoObject *p_object) {
return memnew(MonoGCHandleRef(MonoGCHandleData::new_strong_handle(p_object)));
}
Ref<MonoGCHandleRef> MonoGCHandleRef::create_weak(MonoObject *p_object) {
return memnew(MonoGCHandleRef(MonoGCHandleData::new_weak_handle(p_object)));
void MonoGCHandleData::free_gchandle(GCHandleIntPtr p_gchandle) {
CRASH_COND(GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.is_null());
MonoException *exc = nullptr;
GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.invoke(p_gchandle, &exc);
if (exc) {
GDMonoUtils::debug_unhandled_exception(exc);
}
}

View file

@ -31,8 +31,6 @@
#ifndef CSHARP_GC_HANDLE_H
#define CSHARP_GC_HANDLE_H
#include <mono/jit/jit.h>
#include "core/object/ref_counted.h"
namespace gdmono {
@ -44,18 +42,25 @@ enum class GCHandleType : char {
};
}
struct GCHandleIntPtr {
void *value = nullptr;
};
static_assert(sizeof(GCHandleIntPtr) == sizeof(void *));
// Manual release of the GC handle must be done when using this struct
struct MonoGCHandleData {
uint32_t handle = 0;
GCHandleIntPtr handle;
gdmono::GCHandleType type = gdmono::GCHandleType::NIL;
_FORCE_INLINE_ bool is_released() const { return !handle; }
_FORCE_INLINE_ bool is_released() const { return !handle.value; }
_FORCE_INLINE_ bool is_weak() const { return type == gdmono::GCHandleType::WEAK_HANDLE; }
_FORCE_INLINE_ MonoObject *get_target() const { return handle ? mono_gchandle_get_target(handle) : nullptr; }
_FORCE_INLINE_ GCHandleIntPtr get_intptr() const { return handle; }
void release();
static void free_gchandle(GCHandleIntPtr p_gchandle);
MonoGCHandleData &operator=(const MonoGCHandleData &p_other) {
#ifdef DEBUG_ENABLED
CRASH_COND(!is_released());
@ -69,40 +74,10 @@ struct MonoGCHandleData {
MonoGCHandleData() {}
MonoGCHandleData(uint32_t p_handle, gdmono::GCHandleType p_type) :
MonoGCHandleData(GCHandleIntPtr p_handle, gdmono::GCHandleType p_type) :
handle(p_handle),
type(p_type) {
}
static MonoGCHandleData new_strong_handle(MonoObject *p_object);
static MonoGCHandleData new_strong_handle_pinned(MonoObject *p_object);
static MonoGCHandleData new_weak_handle(MonoObject *p_object);
};
class MonoGCHandleRef : public RefCounted {
GDCLASS(MonoGCHandleRef, RefCounted);
MonoGCHandleData data;
public:
static Ref<MonoGCHandleRef> create_strong(MonoObject *p_object);
static Ref<MonoGCHandleRef> create_weak(MonoObject *p_object);
_FORCE_INLINE_ bool is_released() const { return data.is_released(); }
_FORCE_INLINE_ bool is_weak() const { return data.is_weak(); }
_FORCE_INLINE_ MonoObject *get_target() const { return data.get_target(); }
void release() { data.release(); }
_FORCE_INLINE_ void set_handle(uint32_t p_handle, gdmono::GCHandleType p_handle_type) {
data = MonoGCHandleData(p_handle, p_handle_type);
}
MonoGCHandleRef(const MonoGCHandleData &p_gc_handle_data) :
data(p_gc_handle_data) {
}
~MonoGCHandleRef() { release(); }
};
#endif // CSHARP_GC_HANDLE_H

View file

@ -48,8 +48,6 @@
#include "../godotsharp_dirs.h"
#include "../utils/path_utils.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
#ifdef TOOLS_ENABLED
@ -413,7 +411,9 @@ void GDMono::initialize_load_assemblies() {
// Load assemblies. The API and tools assemblies are required,
// the application is aborted if these assemblies cannot be loaded.
_load_api_assemblies();
if (!_try_load_api_assemblies()) {
CRASH_NOW_MSG("Failed to load one of the API assemblies.");
}
#if defined(TOOLS_ENABLED)
bool tool_assemblies_loaded = _load_tools_assemblies();
@ -434,24 +434,12 @@ void GDMono::initialize_load_assemblies() {
}
}
bool GDMono::_are_api_assemblies_out_of_sync() {
bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoCache::cached_data.godot_api_cache_updated);
#ifdef TOOLS_ENABLED
if (!out_of_sync) {
out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync;
}
#endif
return out_of_sync;
}
void godot_register_object_icalls();
void godot_register_scene_tree_icalls();
void godot_register_placeholder_icalls();
void GDMono::_register_internal_calls() {
// Registers internal calls that were not generated.
godot_register_object_icalls();
godot_register_scene_tree_icalls();
godot_register_placeholder_icalls();
}
@ -492,35 +480,35 @@ GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
return result ? *result : nullptr;
}
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly) {
#ifdef DEBUG_ENABLED
CRASH_COND(!r_assembly);
#endif
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
bool result = load_assembly(p_name, aname, r_assembly);
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) {
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly) {
#ifdef DEBUG_ENABLED
CRASH_COND(!r_assembly);
#endif
return load_assembly(p_name, p_aname, r_assembly, p_refonly, GDMonoAssembly::get_default_search_dirs());
return load_assembly(p_name, p_aname, r_assembly, GDMonoAssembly::get_default_search_dirs());
}
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs) {
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, const Vector<String> &p_search_dirs) {
#ifdef DEBUG_ENABLED
CRASH_COND(!r_assembly);
#endif
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
print_verbose("Mono: Loading assembly " + p_name + "...");
GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, p_refonly, p_search_dirs);
GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, /* refonly: */ false, p_search_dirs);
if (!assembly) {
return false;
@ -528,17 +516,17 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo
*r_assembly = assembly;
print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path());
return true;
}
bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) {
bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly) {
CRASH_COND(!r_assembly);
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
print_verbose("Mono: Loading assembly " + p_name + "...");
GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly);
GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, /* refonly: */ false);
if (!assembly) {
return false;
@ -546,284 +534,41 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo
*r_assembly = assembly;
print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path());
return true;
}
ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) {
ApiAssemblyInfo::Version api_assembly_version;
const char *nativecalls_name = p_api_type == ApiAssemblyInfo::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(nullptr));
}
}
return api_assembly_version;
}
String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) {
return p_type == ApiAssemblyInfo::API_CORE ? "API_CORE" : "API_EDITOR";
}
bool GDMono::_load_corlib_assembly() {
if (corlib_assembly) {
return true;
}
bool success = load_assembly("mscorlib", &corlib_assembly);
if (success) {
GDMonoCache::update_corlib_cache();
}
return success;
return load_assembly("mscorlib", &corlib_assembly);
}
#ifdef TOOLS_ENABLED
bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config) {
String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String dst_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
String assembly_name = p_api_type == ApiAssemblyInfo::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
// Create destination directory if needed
if (!DirAccess::exists(dst_dir)) {
DirAccess *da = DirAccess::create_for_path(dst_dir);
Error err = da->make_dir_recursive(dst_dir);
memdelete(da);
if (err != OK) {
ERR_PRINT("Failed to create destination directory for the API assemblies. Error: " + itos(err) + ".");
return false;
}
}
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = assembly_name + ".xml";
if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) {
WARN_PRINT("Failed to copy '" + xml_file + "'.");
}
String pdb_file = assembly_name + ".pdb";
if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) {
WARN_PRINT("Failed to copy '" + pdb_file + "'.");
}
String assembly_file = assembly_name + ".dll";
if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) {
ERR_PRINT("Failed to copy '" + assembly_file + "'.");
return false;
}
return true;
}
static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool &r_out_of_sync) {
String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
if (!FileAccess::exists(core_api_assembly_path) || !FileAccess::exists(editor_api_assembly_path)) {
return false;
}
String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
if (!FileAccess::exists(cached_api_hash_path)) {
return false;
}
Ref<ConfigFile> cfg;
cfg.instantiate();
Error cfg_err = cfg->load(cached_api_hash_path);
ERR_FAIL_COND_V(cfg_err != OK, false);
// Checking the modified time is good enough
if (FileAccess::get_modified_time(core_api_assembly_path) != (uint64_t)cfg->get_value("core", "modified_time") ||
FileAccess::get_modified_time(editor_api_assembly_path) != (uint64_t)cfg->get_value("editor", "modified_time")) {
return false;
}
r_out_of_sync = GDMono::get_singleton()->get_api_core_hash() != (uint64_t)cfg->get_value("core", "api_hash") ||
GDMono::get_singleton()->get_api_editor_hash() != (uint64_t)cfg->get_value("editor", "api_hash");
return true;
}
static void create_cached_api_hash_for(const String &p_api_assemblies_dir) {
String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
Ref<ConfigFile> cfg;
cfg.instantiate();
cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path));
cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path));
// This assumes the prebuilt api assemblies we copied to the project are not out of sync
cfg->set_value("core", "api_hash", GDMono::get_singleton()->get_api_core_hash());
cfg->set_value("editor", "api_hash", GDMono::get_singleton()->get_api_editor_hash());
Error err = cfg->save(cached_api_hash_path);
ERR_FAIL_COND(err != OK);
}
bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config) {
MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.CheckApiAssemblies");
ERR_FAIL_NULL_V(temp_domain, "Failed to create temporary domain to check API assemblies");
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain);
_GDMONO_SCOPE_DOMAIN_(temp_domain);
GDMono::LoadedApiAssembly temp_core_api_assembly;
GDMono::LoadedApiAssembly temp_editor_api_assembly;
if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly,
p_config, /* refonly: */ true, /* loaded_callback: */ nullptr)) {
return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync;
}
return true; // Failed to load, assume they're outdated assemblies
}
String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) {
#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \
( \
(m_out_of_sync ? \
String("The assembly is invalidated ") : \
String("The assembly was not found ")) + \
(m_prebuilt_exists ? \
String("and the prebuilt assemblies are missing.") : \
String("and we failed to copy the prebuilt assemblies.")))
String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
String core_assembly_path = dst_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
String editor_assembly_path = dst_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
bool api_assemblies_out_of_sync = false;
if (p_core_api_out_of_sync && p_editor_api_out_of_sync) {
api_assemblies_out_of_sync = *p_core_api_out_of_sync || *p_editor_api_out_of_sync;
} else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) {
// Determine if they're out of sync
if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) {
api_assemblies_out_of_sync = _temp_domain_load_are_assemblies_out_of_sync(p_config);
}
}
// Note: Even if only one of the assemblies if missing or out of sync, we update both
if (!api_assemblies_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) {
return String(); // No update needed
}
print_verbose("Updating '" + p_config + "' API assemblies");
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) {
return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ false);
}
// Copy the prebuilt Api
if (!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_CORE, p_config) ||
!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_EDITOR, p_config)) {
return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ true);
}
// Cache the api hash of the assemblies we just copied
create_cached_api_hash_for(dst_assemblies_dir);
return String(); // Updated successfully
#undef FAIL_REASON
}
#endif
bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
if (r_loaded_api_assembly.assembly) {
bool GDMono::_load_core_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) {
if (*r_loaded_api_assembly) {
return true;
}
#ifdef TOOLS_ENABLED
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
// If running the project manager, load it from the prebuilt API directory
String assembly_dir = !Main::is_project_manager() ?
GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) :
GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
bool success = FileAccess::exists(assembly_path) &&
load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
#else
bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly);
#endif
#ifdef DEBUG_METHODS_ENABLED
if (success) {
ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE);
r_loaded_api_assembly.out_of_sync = get_api_core_hash() != api_assembly_ver.godot_api_hash;
} else {
r_loaded_api_assembly.out_of_sync = false;
}
#else
r_loaded_api_assembly.out_of_sync = false;
#endif
return success;
return load_assembly(CORE_API_ASSEMBLY_NAME, r_loaded_api_assembly);
}
#ifdef TOOLS_ENABLED
bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
if (r_loaded_api_assembly.assembly) {
bool GDMono::_load_editor_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) {
if (*r_loaded_api_assembly) {
return true;
}
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
// If running the project manager, load it from the prebuilt API directory
String assembly_dir = !Main::is_project_manager() ?
GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) :
GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
bool success = FileAccess::exists(assembly_path) &&
load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
#ifdef DEBUG_METHODS_ENABLED
if (success) {
ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR);
r_loaded_api_assembly.out_of_sync = get_api_editor_hash() != api_assembly_ver.godot_api_hash;
} else {
r_loaded_api_assembly.out_of_sync = false;
}
#else
r_loaded_api_assembly.out_of_sync = false;
#endif
return success;
return load_assembly(EDITOR_API_ASSEMBLY_NAME, r_loaded_api_assembly);
}
#endif
bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback) {
if (!_load_core_api_assembly(r_core_api_assembly, p_config, p_refonly)) {
bool GDMono::_try_load_api_assemblies() {
String config = get_expected_api_build_config();
if (!_load_core_api_assembly(&core_api_assembly, config)) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Mono: Failed to load Core API assembly");
}
@ -831,30 +576,15 @@ bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, Lo
}
#ifdef TOOLS_ENABLED
if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) {
if (!_load_editor_api_assembly(&editor_api_assembly, config)) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Mono: Failed to load Editor API assembly");
}
return false;
}
if (r_editor_api_assembly.out_of_sync) {
return false;
}
#endif
// Check if the core API assembly is out of sync only after trying to load the
// editor API assembly. Otherwise, if both assemblies are out of sync, we would
// only update the former as we won't know the latter also needs to be updated.
if (r_core_api_assembly.out_of_sync) {
return false;
}
if (p_callback) {
return p_callback();
}
return true;
return _on_core_api_assembly_loaded();
}
bool GDMono::_on_core_api_assembly_loaded() {
@ -869,72 +599,14 @@ bool GDMono::_on_core_api_assembly_loaded() {
return true;
}
bool GDMono::_try_load_api_assemblies_preset() {
return _try_load_api_assemblies(core_api_assembly, editor_api_assembly,
get_expected_api_build_config(), /* refonly: */ false, _on_core_api_assembly_loaded);
}
void GDMono::_load_api_assemblies() {
bool api_assemblies_loaded = _try_load_api_assemblies_preset();
#if defined(TOOLS_ENABLED) && !defined(GD_MONO_SINGLE_APPDOMAIN)
if (!api_assemblies_loaded) {
// The API assemblies are out of sync or some other error happened. Fine, try one more time, but
// this time update them from the prebuilt assemblies directory before trying to load them again.
// Shouldn't happen. The project manager loads the prebuilt API assemblies
CRASH_COND_MSG(Main::is_project_manager(), "Failed to load one of the prebuilt API assemblies.");
// 1. Unload the scripts domain
Error domain_unload_err = _unload_scripts_domain();
CRASH_COND_MSG(domain_unload_err != OK, "Mono: Failed to unload scripts domain.");
// 2. Update the API assemblies
String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync);
CRASH_COND_MSG(!update_error.is_empty(), update_error);
// 3. Load the scripts domain again
Error domain_load_err = _load_scripts_domain();
CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
// 4. Try loading the updated assemblies
api_assemblies_loaded = _try_load_api_assemblies_preset();
}
#endif
if (!api_assemblies_loaded) {
// welp... too bad
if (_are_api_assemblies_out_of_sync()) {
if (core_api_assembly.out_of_sync) {
ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync.");
} else if (!GDMonoCache::cached_data.godot_api_cache_updated) {
ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed.");
}
#ifdef TOOLS_ENABLED
if (editor_api_assembly.out_of_sync) {
ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync.");
}
#endif
CRASH_NOW();
} else {
CRASH_NOW_MSG("Failed to load one of the API assemblies.");
}
}
}
#ifdef TOOLS_ENABLED
bool GDMono::_load_tools_assemblies() {
if (tools_assembly && tools_project_editor_assembly) {
return true;
}
bool success = load_assembly(TOOLS_ASM_NAME, &tools_assembly) &&
load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly);
return success;
return load_assembly(TOOLS_ASM_NAME, &tools_assembly) &&
load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly);
}
#endif
@ -953,7 +625,12 @@ bool GDMono::_load_project_assembly() {
if (success) {
mono_assembly_set_main(project_assembly->get_assembly());
CSharpLanguage::get_singleton()->lookup_scripts_in_assembly(project_assembly);
MonoException *exc = nullptr;
GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_LookupScriptsInAssembly.invoke(
mono_assembly_get_object(mono_domain_get(), project_assembly->get_assembly()), &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
}
}
return success;
@ -962,11 +639,8 @@ bool GDMono::_load_project_assembly() {
void GDMono::_install_trace_listener() {
#ifdef DEBUG_ENABLED
// Install the trace listener now before the project assembly is loaded
GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils");
GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener");
MonoException *exc = nullptr;
install_func->invoke_raw(nullptr, nullptr, &exc);
GDMonoCache::cached_data.methodthunk_DebuggingUtils_InstallTraceListener.invoke(&exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");
@ -1014,9 +688,9 @@ Error GDMono::_unload_scripts_domain() {
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
core_api_assembly.assembly = nullptr;
core_api_assembly = nullptr;
#ifdef TOOLS_ENABLED
editor_api_assembly.assembly = nullptr;
editor_api_assembly = nullptr;
#endif
project_assembly = nullptr;
@ -1058,7 +732,9 @@ Error GDMono::reload_scripts_domain() {
// Load assemblies. The API and tools assemblies are required,
// the application is aborted if these assemblies cannot be loaded.
_load_api_assemblies();
if (!_try_load_api_assemblies()) {
CRASH_NOW_MSG("Failed to load one of the API assemblies.");
}
#if defined(TOOLS_ENABLED)
bool tools_assemblies_loaded = _load_tools_assemblies();
@ -1111,51 +787,6 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
}
#endif
GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
MonoImage *image = mono_class_get_image(p_raw_class);
if (image == corlib_assembly->get_image()) {
return corlib_assembly->get_class(p_raw_class);
}
int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
const String *k = nullptr;
while ((k = domain_assemblies.next(k))) {
GDMonoAssembly *assembly = domain_assemblies.get(*k);
if (assembly->get_image() == image) {
GDMonoClass *klass = assembly->get_class(p_raw_class);
if (klass) {
return klass;
}
}
}
return nullptr;
}
GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) {
GDMonoClass *klass = corlib_assembly->get_class(p_namespace, p_name);
if (klass) {
return klass;
}
int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
const String *k = nullptr;
while ((k = domain_assemblies.next(k))) {
GDMonoAssembly *assembly = domain_assemblies.get(*k);
klass = assembly->get_class(p_namespace, p_name);
if (klass) {
return klass;
}
}
return nullptr;
}
void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];

View file

@ -41,31 +41,6 @@
#include "../utils/mono_reg_utils.h"
#endif
namespace ApiAssemblyInfo {
enum Type {
API_CORE,
API_EDITOR
};
struct Version {
uint64_t godot_api_hash = 0;
bool operator==(const Version &p_other) const {
return godot_api_hash == p_other.godot_api_hash;
}
Version() {}
Version(uint64_t p_godot_api_hash) :
godot_api_hash(p_godot_api_hash) {
}
static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type);
};
String to_string(Type p_type);
} // namespace ApiAssemblyInfo
class GDMono {
public:
enum UnhandledExceptionPolicy {
@ -73,13 +48,6 @@ public:
POLICY_LOG_ERROR
};
struct LoadedApiAssembly {
GDMonoAssembly *assembly = nullptr;
bool out_of_sync = false;
LoadedApiAssembly() {}
};
private:
bool runtime_initialized;
bool finalizing_scripts_domain;
@ -98,17 +66,12 @@ private:
GDMonoAssembly *tools_project_editor_assembly;
#endif
LoadedApiAssembly core_api_assembly;
LoadedApiAssembly editor_api_assembly;
GDMonoAssembly *core_api_assembly;
GDMonoAssembly *editor_api_assembly;
typedef bool (*CoreApiAssemblyLoadedCallback)();
bool _are_api_assemblies_out_of_sync();
bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config);
bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
bool _load_core_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config);
#ifdef TOOLS_ENABLED
bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
bool _load_editor_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config);
#endif
static bool _on_core_api_assembly_loaded();
@ -119,10 +82,7 @@ private:
#endif
bool _load_project_assembly();
bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback);
bool _try_load_api_assemblies_preset();
void _load_api_assemblies();
bool _try_load_api_assemblies();
void _install_trace_listener();
@ -184,11 +144,6 @@ public:
#endif
}
#ifdef TOOLS_ENABLED
bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config);
String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = nullptr, const bool *p_editor_api_out_of_sync = nullptr);
#endif
static GDMono *get_singleton() { return singleton; }
[[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
@ -206,29 +161,24 @@ public:
_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.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.assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; }
#endif
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; }
#endif
GDMonoClass *get_class(MonoClass *p_raw_class);
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
#ifdef GD_MONO_HOT_RELOAD
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);
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs);
bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly);
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly);
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, const Vector<String> &p_search_dirs);
bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly);
Error finalize_and_unload_domain(MonoDomain *p_domain);

View file

@ -40,8 +40,8 @@
#include "core/templates/list.h"
#include "../godotsharp_dirs.h"
#include "gd_mono.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
Vector<String> GDMonoAssembly::search_dirs;
@ -73,21 +73,18 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
}
if (p_custom_config.is_empty()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_dir());
} else {
String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug";
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_base_dir().plus_file(api_config));
}
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir());
r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_base_dir());
r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
#ifdef TOOLS_ENABLED
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
// For GodotTools to find the api assemblies
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"));
#endif
}
@ -330,13 +327,6 @@ no_pdb:
void GDMonoAssembly::unload() {
ERR_FAIL_NULL(image); // Should not be called if already unloaded
for (const KeyValue<MonoClass *, GDMonoClass *> &E : cached_raw) {
memdelete(E.value);
}
cached_classes.clear();
cached_raw.clear();
assembly = nullptr;
image = nullptr;
}
@ -345,90 +335,6 @@ String GDMonoAssembly::get_path() const {
return String::utf8(mono_image_get_filename(image));
}
bool GDMonoAssembly::has_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, false);
#endif
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return false;
}
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
MonoObject *GDMonoAssembly::get_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, nullptr);
#endif
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return nullptr;
}
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoAssembly::fetch_attributes() {
ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_assembly(assembly);
attrs_fetched = true;
}
GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
ERR_FAIL_NULL_V(image, nullptr);
ClassKey key(p_namespace, p_name);
GDMonoClass **match = cached_classes.getptr(key);
if (match) {
return *match;
}
MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
if (!mono_class) {
return nullptr;
}
GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
cached_classes[key] = wrapped_class;
cached_raw[mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
ERR_FAIL_NULL_V(image, nullptr);
Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
if (match) {
return match->value();
}
StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
StringName class_name = String::utf8(mono_class_get_name(p_mono_class));
GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
cached_raw[p_mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) {
return GDMono::get_singleton()->get_corlib_assembly();

View file

@ -40,47 +40,14 @@
#include "gd_mono_utils.h"
class GDMonoAssembly {
struct ClassKey {
struct Hasher {
static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) {
uint32_t hash = 0;
GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash());
GDMonoUtils::hash_combine(hash, p_key.class_name.hash());
return hash;
}
};
_FORCE_INLINE_ bool operator==(const ClassKey &p_a) const {
return p_a.class_name == class_name && p_a.namespace_name == namespace_name;
}
ClassKey() {}
ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) {
namespace_name = p_namespace_name;
class_name = p_class_name;
}
StringName namespace_name;
StringName class_name;
};
String name;
MonoImage *image;
MonoAssembly *assembly;
bool attrs_fetched = false;
MonoCustomAttrInfo *attributes = nullptr;
#ifdef GD_MONO_HOT_RELOAD
uint64_t modified_time = 0;
#endif
HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
Map<MonoClass *, GDMonoClass *> cached_raw;
static Vector<String> search_dirs;
static void assembly_load_hook(MonoAssembly *assembly, void *user_data);
@ -111,14 +78,6 @@ public:
String get_path() const;
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
void fetch_attributes();
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
GDMonoClass *get_class(MonoClass *p_mono_class);
static String find_assembly(const String &p_name);
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());

View file

@ -31,199 +31,76 @@
#include "gd_mono_cache.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_method.h"
#include "gd_mono_utils.h"
namespace GDMonoCache {
CachedData cached_data;
#define CACHE_AND_CHECK(m_var, m_val) \
{ \
CRASH_COND(m_var != nullptr); \
m_var = m_val; \
ERR_FAIL_COND_MSG(m_var == nullptr, "Mono Cache: Member " #m_var " is null."); \
}
static MonoMethod *get_mono_method(MonoClass *p_mono_class, const char *p_method_name, int p_param_count) {
ERR_FAIL_NULL_V(p_mono_class, nullptr);
return mono_class_get_method_from_name(p_mono_class, p_method_name, p_param_count);
}
#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_class, m_val)
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(cached_data.field_##m_class##_##m_field, m_val)
#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(cached_data.method_##m_class##_##m_method, m_val)
static MonoClass *get_mono_class(GDMonoAssembly *p_assembly, const char *p_namespace, const char *p_name) {
ERR_FAIL_NULL_V(p_assembly->get_image(), nullptr);
return mono_class_from_name(p_assembly->get_image(), p_namespace, p_name);
}
#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \
{ \
CRASH_COND(!m_var.is_null()); \
ERR_FAIL_COND_MSG(m_val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \
m_var.set_from_method(m_val); \
ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \
void update_godot_api_cache() {
#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \
{ \
CRASH_COND(!m_var.is_null()); \
val = m_val; \
ERR_FAIL_COND_MSG(val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \
m_var.set_from_method(val); \
ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \
}
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_METHOD_THUNK_AND_CHECK_IMPL(cached_data.methodthunk_##m_class##_##m_method, m_val)
void CachedData::clear_corlib_cache() {
corlib_cache_updated = false;
#define GODOT_API_CLASS(m_class) (get_mono_class(GDMono::get_singleton()->get_core_api_assembly(), BINDINGS_NAMESPACE, #m_class))
#define GODOT_API_BRIDGE_CLASS(m_class) (get_mono_class(GDMono::get_singleton()->get_core_api_assembly(), BINDINGS_NAMESPACE_BRIDGE, #m_class))
class_MonoObject = nullptr;
class_String = nullptr;
MonoMethod *val = nullptr;
#ifdef DEBUG_ENABLED
class_System_Diagnostics_StackTrace = nullptr;
methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify();
method_System_Diagnostics_StackTrace_ctor_bool = nullptr;
method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr;
#endif
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, get_mono_method(GODOT_API_CLASS(SignalAwaiter), "SignalCallback", 4));
class_KeyNotFoundException = nullptr;
}
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, get_mono_method(GODOT_API_CLASS(DelegateUtils), "InvokeWithVariantArgs", 4));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, get_mono_method(GODOT_API_CLASS(DelegateUtils), "DelegateEquals", 2));
void CachedData::clear_godot_api_cache() {
godot_api_cache_updated = false;
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, FrameCallback, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "FrameCallback", 0));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, CreateManagedForGodotObjectBinding, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "CreateManagedForGodotObjectBinding", 2));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "CreateManagedForGodotObjectScriptInstance", 4));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, GetScriptNativeName, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "GetScriptNativeName", 2));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, LookupScriptsInAssembly, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "LookupScriptsInAssembly", 1));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, SetGodotObjectPtr, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "SetGodotObjectPtr", 2));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, RaiseEventSignal, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "RaiseEventSignal", 5));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, GetScriptSignalList, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "GetScriptSignalList", 2));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, HasScriptSignal, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "HasScriptSignal", 2));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, HasMethodUnknownParams, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "HasMethodUnknownParams", 3));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, ScriptIsOrInherits, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "ScriptIsOrInherits", 2));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, AddScriptBridge, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "AddScriptBridge", 2));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, RemoveScriptBridge, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "RemoveScriptBridge", 1));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, UpdateScriptClassInfo, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "UpdateScriptClassInfo", 3));
CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, SwapGCHandleForType, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "SwapGCHandleForType", 3));
class_GodotObject = nullptr;
class_GodotResource = nullptr;
class_Control = nullptr;
class_Callable = nullptr;
class_SignalInfo = nullptr;
class_ISerializationListener = nullptr;
CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Call, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Call", 6));
CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Set, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Set", 3));
CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Get, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Get", 3));
CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, CallDispose, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "CallDispose", 2));
CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, CallToString, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "CallToString", 3));
#ifdef DEBUG_ENABLED
class_DebuggingUtils = nullptr;
methodthunk_DebuggingUtils_GetStackFrameInfo.nullify();
#endif
CACHE_METHOD_THUNK_AND_CHECK(GCHandleBridge, FreeGCHandle, get_mono_method(GODOT_API_BRIDGE_CLASS(GCHandleBridge), "FreeGCHandle", 1));
class_ExportAttribute = nullptr;
field_ExportAttribute_hint = nullptr;
field_ExportAttribute_hintString = nullptr;
class_SignalAttribute = nullptr;
class_ToolAttribute = nullptr;
class_RemoteAttribute = nullptr;
class_MasterAttribute = nullptr;
class_PuppetAttribute = nullptr;
class_GodotMethodAttribute = nullptr;
field_GodotMethodAttribute_methodName = nullptr;
class_ScriptPathAttribute = nullptr;
field_ScriptPathAttribute_path = nullptr;
class_AssemblyHasScriptsAttribute = nullptr;
field_AssemblyHasScriptsAttribute_requiresLookup = nullptr;
field_AssemblyHasScriptsAttribute_scriptTypes = nullptr;
field_GodotObject_ptr = nullptr;
methodthunk_GodotObject_Dispose.nullify();
methodthunk_SignalAwaiter_SignalCallback.nullify();
methodthunk_Delegate_Equals.nullify();
methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle.nullify();
methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle.nullify();
methodthunk_DelegateUtils_TrySerializeDelegate.nullify();
methodthunk_DelegateUtils_TryDeserializeDelegate.nullify();
methodthunk_DelegateUtils_InvokeWithVariantArgs.nullify();
methodthunk_DelegateUtils_DelegateEquals.nullify();
methodthunk_DelegateUtils_FreeGCHandle.nullify();
methodthunk_Marshaling_managed_to_variant_type.nullify();
methodthunk_Marshaling_try_get_array_element_type.nullify();
methodthunk_Marshaling_variant_to_mono_object_of_type.nullify();
methodthunk_Marshaling_variant_to_mono_object.nullify();
methodthunk_Marshaling_mono_object_to_variant_out.nullify();
methodthunk_Marshaling_SetFieldValue.nullify();
}
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames"));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
#endif
CACHE_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", true));
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
cached_data.corlib_cache_updated = true;
}
void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable));
CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo));
CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
#endif
// Attributes
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute));
CACHE_FIELD_AND_CHECK(ScriptPathAttribute, path, CACHED_CLASS(ScriptPathAttribute)->get_field("path"));
CACHE_CLASS_AND_CHECK(AssemblyHasScriptsAttribute, GODOT_API_CLASS(AssemblyHasScriptsAttribute));
CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, requiresLookup, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("requiresLookup"));
CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes"));
CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegateWithGCHandle", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegateWithGCHandle", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, GODOT_API_CLASS(DelegateUtils)->get_method("InvokeWithVariantArgs", 4));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, GODOT_API_CLASS(DelegateUtils)->get_method("DelegateEquals", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, FreeGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("FreeGCHandle", 1));
GDMonoClass *gd_mono_marshal_class = GDMono::get_singleton()->get_core_api_assembly()->get_class(
"Godot.NativeInterop", "Marshaling");
ERR_FAIL_COND_MSG(gd_mono_marshal_class == nullptr,
"Mono Cache: Class `Godot.NativeInterop.Marshaling` not found.");
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, managed_to_variant_type,
gd_mono_marshal_class->get_method("managed_to_variant_type", 2));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, try_get_array_element_type,
gd_mono_marshal_class->get_method("try_get_array_element_type", 2));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object_of_type,
gd_mono_marshal_class->get_method("variant_to_mono_object_of_type", 2));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object,
gd_mono_marshal_class->get_method("variant_to_mono_object", 1));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, mono_object_to_variant_out,
gd_mono_marshal_class->get_method("mono_object_to_variant_out", 3));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, SetFieldValue,
gd_mono_marshal_class->get_method("SetFieldValue", 3));
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4));
#endif
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, InstallTraceListener, get_mono_method(GODOT_API_CLASS(DebuggingUtils), "InstallTraceListener", 0));
MonoException *exc = nullptr;
GDMono::get_singleton()
->get_core_api_assembly()
->get_class("Godot", "Dispatcher")
->get_method("InitializeDefaultGodotTaskScheduler")
->invoke(nullptr, &exc);
MonoMethod *init_default_godot_task_scheduler =
get_mono_method(GODOT_API_CLASS(Dispatcher), "InitializeDefaultGodotTaskScheduler", 0);
ERR_FAIL_COND_MSG(init_default_godot_task_scheduler == nullptr,
"Mono Cache: InitializeDefaultGodotTaskScheduler is null.");
mono_runtime_invoke(init_default_godot_task_scheduler, nullptr, nullptr, (MonoObject **)&exc);
if (exc) {
GDMonoUtils::debug_unhandled_exception(exc);

View file

@ -31,108 +31,57 @@
#ifndef GD_MONO_CACHE_H
#define GD_MONO_CACHE_H
#include "gd_mono_header.h"
#include "gd_mono_method_thunk.h"
class CSharpScript;
namespace GDMonoCache {
struct CachedData {
// -----------------------------------------------
// corlib classes
// Mono method thunks require structs to be boxed, even if passed by ref (out, ref, in).
// As such we need to use pointers instead for now, instead of by ref parameters.
// Let's use the no-namespace format for these too
GDMonoClass *class_MonoObject; // object
GDMonoClass *class_String; // string
GDMonoMethodThunk<GCHandleIntPtr, const Variant **, int, bool *> methodthunk_SignalAwaiter_SignalCallback;
#ifdef DEBUG_ENABLED
GDMonoClass *class_System_Diagnostics_StackTrace;
GDMonoMethodThunkR<MonoArray *, MonoObject *> methodthunk_System_Diagnostics_StackTrace_GetFrames;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
GDMonoMethodThunk<GCHandleIntPtr, const Variant **, uint32_t, const Variant *> methodthunk_DelegateUtils_InvokeWithVariantArgs;
GDMonoMethodThunkR<bool, GCHandleIntPtr, GCHandleIntPtr> methodthunk_DelegateUtils_DelegateEquals;
GDMonoClass *class_KeyNotFoundException;
// -----------------------------------------------
GDMonoMethodThunk<> methodthunk_ScriptManagerBridge_FrameCallback;
GDMonoMethodThunkR<GCHandleIntPtr, const StringName *, Object *> methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding;
GDMonoMethodThunk<const CSharpScript *, Object *, const Variant **, int> methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
GDMonoMethodThunk<const CSharpScript *, StringName *> methodthunk_ScriptManagerBridge_GetScriptNativeName;
GDMonoMethodThunk<MonoReflectionAssembly *> methodthunk_ScriptManagerBridge_LookupScriptsInAssembly;
GDMonoMethodThunk<GCHandleIntPtr, Object *> methodthunk_ScriptManagerBridge_SetGodotObjectPtr;
GDMonoMethodThunk<GCHandleIntPtr, const StringName *, const Variant **, int, bool *> methodthunk_ScriptManagerBridge_RaiseEventSignal;
GDMonoMethodThunk<const CSharpScript *, Dictionary *> methodthunk_ScriptManagerBridge_GetScriptSignalList;
GDMonoMethodThunkR<bool, const CSharpScript *, const String *> methodthunk_ScriptManagerBridge_HasScriptSignal;
GDMonoMethodThunkR<bool, const CSharpScript *, const String *, bool> methodthunk_ScriptManagerBridge_HasMethodUnknownParams;
GDMonoMethodThunkR<bool, const CSharpScript *, const CSharpScript *> methodthunk_ScriptManagerBridge_ScriptIsOrInherits;
GDMonoMethodThunkR<bool, const CSharpScript *, const String *> methodthunk_ScriptManagerBridge_AddScriptBridge;
GDMonoMethodThunk<const CSharpScript *> methodthunk_ScriptManagerBridge_RemoveScriptBridge;
GDMonoMethodThunk<const CSharpScript *, bool *, Dictionary *> methodthunk_ScriptManagerBridge_UpdateScriptClassInfo;
GDMonoMethodThunkR<bool, GCHandleIntPtr, GCHandleIntPtr *, bool> methodthunk_ScriptManagerBridge_SwapGCHandleForType;
GDMonoClass *class_GodotObject;
GDMonoClass *class_GodotResource;
GDMonoClass *class_Control;
GDMonoClass *class_Callable;
GDMonoClass *class_SignalInfo;
GDMonoClass *class_ISerializationListener;
GDMonoMethodThunk<GCHandleIntPtr, const StringName *, const Variant **, int, Callable::CallError *, Variant *> methodthunk_CSharpInstanceBridge_Call;
GDMonoMethodThunkR<bool, GCHandleIntPtr, const StringName *, const Variant *> methodthunk_CSharpInstanceBridge_Set;
GDMonoMethodThunkR<bool, GCHandleIntPtr, const StringName *, Variant *> methodthunk_CSharpInstanceBridge_Get;
GDMonoMethodThunk<GCHandleIntPtr, bool> methodthunk_CSharpInstanceBridge_CallDispose;
GDMonoMethodThunk<GCHandleIntPtr, String *, bool *> methodthunk_CSharpInstanceBridge_CallToString;
#ifdef DEBUG_ENABLED
GDMonoClass *class_DebuggingUtils;
GDMonoMethodThunk<MonoObject *, MonoString **, int *, MonoString **> methodthunk_DebuggingUtils_GetStackFrameInfo;
#endif
GDMonoMethodThunk<GCHandleIntPtr> methodthunk_GCHandleBridge_FreeGCHandle;
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hintString;
GDMonoClass *class_SignalAttribute;
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_RemoteAttribute;
GDMonoClass *class_MasterAttribute;
GDMonoClass *class_PuppetAttribute;
GDMonoClass *class_GodotMethodAttribute;
GDMonoField *field_GodotMethodAttribute_methodName;
GDMonoClass *class_ScriptPathAttribute;
GDMonoField *field_ScriptPathAttribute_path;
GDMonoClass *class_AssemblyHasScriptsAttribute;
GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup;
GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes;
GDMonoMethodThunk<> methodthunk_DebuggingUtils_InstallTraceListener;
GDMonoField *field_GodotObject_ptr;
GDMonoMethodThunk<MonoObject *> methodthunk_GodotObject_Dispose;
GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback;
GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals;
GDMonoMethodThunkR<MonoBoolean, void *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle;
GDMonoMethodThunkR<MonoBoolean, MonoObject *, void **> methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle;
GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate;
GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate;
GDMonoMethodThunk<void *, const Variant **, uint32_t, const Variant *> methodthunk_DelegateUtils_InvokeWithVariantArgs;
GDMonoMethodThunkR<MonoBoolean, void *, void *> methodthunk_DelegateUtils_DelegateEquals;
GDMonoMethodThunk<void *> methodthunk_DelegateUtils_FreeGCHandle;
GDMonoMethodThunkR<int32_t, MonoReflectionType *, MonoBoolean *> methodthunk_Marshaling_managed_to_variant_type;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_Marshaling_try_get_array_element_type;
GDMonoMethodThunkR<MonoObject *, const Variant *, MonoReflectionType *> methodthunk_Marshaling_variant_to_mono_object_of_type;
GDMonoMethodThunkR<MonoObject *, const Variant *> methodthunk_Marshaling_variant_to_mono_object;
GDMonoMethodThunk<MonoObject *, MonoBoolean, Variant *> methodthunk_Marshaling_mono_object_to_variant_out;
GDMonoMethodThunk<MonoReflectionField *, MonoObject *, const Variant *> methodthunk_Marshaling_SetFieldValue;
bool corlib_cache_updated;
bool godot_api_cache_updated;
void clear_corlib_cache();
void clear_godot_api_cache();
CachedData() {
clear_corlib_cache();
clear_godot_api_cache();
}
bool godot_api_cache_updated = false;
};
extern CachedData cached_data;
void update_corlib_cache();
void update_godot_api_cache();
inline void clear_godot_api_cache() {
cached_data.clear_godot_api_cache();
cached_data = CachedData();
}
} // namespace GDMonoCache
#define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class)
#define CACHED_CLASS_RAW(m_class) (GDMonoCache::cached_data.class_##m_class->get_mono_ptr())
#define CACHED_FIELD(m_class, m_field) (GDMonoCache::cached_data.field_##m_class##_##m_field)
#define CACHED_METHOD(m_class, m_method) (GDMonoCache::cached_data.method_##m_class##_##m_method)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method)
#endif // GD_MONO_CACHE_H

View file

@ -1,570 +0,0 @@
/*************************************************************************/
/* gd_mono_class.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 "gd_mono_class.h"
#include <mono/metadata/attrdefs.h>
#include <mono/metadata/debug-helpers.h>
#include "gd_mono_assembly.h"
#include "gd_mono_cache.h"
#include "gd_mono_marshal.h"
String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
// mono_type_get_full_name is not exposed to embedders, but this seems to do the job
MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
MonoException *exc = nullptr;
MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return GDMonoMarshal::mono_string_to_godot(str);
}
MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
return mono_class_get_type(p_mono_class);
}
String GDMonoClass::get_full_name() const {
return get_full_name(mono_class);
}
String GDMonoClass::get_type_desc() const {
return GDMonoUtils::get_type_desc(get_mono_type());
}
MonoType *GDMonoClass::get_mono_type() const {
// Careful, you cannot compare two MonoType*.
// There is mono_metadata_type_equal, how is this different from comparing two MonoClass*?
return get_mono_type(mono_class);
}
uint32_t GDMonoClass::get_flags() const {
return mono_class_get_flags(mono_class);
}
bool GDMonoClass::is_static() const {
uint32_t static_class_flags = MONO_TYPE_ATTR_ABSTRACT | MONO_TYPE_ATTR_SEALED;
return (get_flags() & static_class_flags) == static_class_flags;
}
bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
return mono_class_is_assignable_from(mono_class, p_from->mono_class);
}
StringName GDMonoClass::get_namespace() const {
GDMonoClass *nesting_class = get_nesting_class();
if (!nesting_class) {
return namespace_name;
}
return nesting_class->get_namespace();
}
String GDMonoClass::get_name_for_lookup() const {
GDMonoClass *nesting_class = get_nesting_class();
if (!nesting_class) {
return class_name;
}
return nesting_class->get_name_for_lookup() + "/" + class_name;
}
GDMonoClass *GDMonoClass::get_parent_class() const {
MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : nullptr;
}
GDMonoClass *GDMonoClass::get_nesting_class() const {
MonoClass *nesting_type = mono_class_get_nesting_type(mono_class);
return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : nullptr;
}
#ifdef TOOLS_ENABLED
Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
bool class_is_enum = mono_class_is_enum(mono_class);
ERR_FAIL_COND_V(!class_is_enum, Vector<MonoClassField *>());
Vector<MonoClassField *> enum_fields;
void *iter = nullptr;
MonoClassField *raw_field = nullptr;
while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != nullptr) {
uint32_t field_flags = mono_field_get_flags(raw_field);
// Enums have an instance field named value__ which holds the value of the enum.
// Enum constants are static, so we will use this to ignore the value__ field.
if (field_flags & MONO_FIELD_ATTR_PUBLIC && field_flags & MONO_FIELD_ATTR_STATIC) {
enum_fields.push_back(raw_field);
}
}
return enum_fields;
}
#endif
bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, false);
#endif
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return false;
}
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, nullptr);
#endif
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return nullptr;
}
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoClass::fetch_attributes() {
ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_class(get_mono_ptr());
attrs_fetched = true;
}
void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) {
CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this));
if (methods_fetched) {
return;
}
void *iter = nullptr;
MonoMethod *raw_method = nullptr;
while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
StringName name = String::utf8(mono_method_get_name(raw_method));
// get_method implicitly fetches methods and adds them to this->methods
GDMonoMethod *method = get_method(raw_method, name);
ERR_CONTINUE(!method);
if (method->get_name() != name) {
#ifdef DEBUG_ENABLED
String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")";
WARN_PRINT("Method '" + fullname + "' is hidden by Godot API method. Should be '" +
method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'.");
#endif
continue;
}
#ifdef DEBUG_ENABLED
// For debug builds, we also fetched from native base classes as well before if this is not a native base class.
// This allows us to warn the user here if he is using snake_case by mistake.
if (p_native_base != this) {
GDMonoClass *native_top = p_native_base;
while (native_top) {
GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count());
if (m && m->get_name() != name) {
// found
String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")";
WARN_PRINT("Method '" + fullname + "' should be '" + m->get_full_name_no_class() +
"'. In class '" + namespace_name + "." + class_name + "'.");
break;
}
if (native_top == CACHED_CLASS(GodotObject)) {
break;
}
native_top = native_top->get_parent_class();
}
}
#endif
uint32_t flags = mono_method_get_flags(method->mono_method, nullptr);
if (!(flags & MONO_METHOD_ATTR_VIRTUAL)) {
continue;
}
// Virtual method of Godot Object derived type, let's try to find GodotMethod attribute
GDMonoClass *top = p_native_base;
while (top) {
GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count());
if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) {
// Found base method with GodotMethod attribute.
// We get the original API method name from this attribute.
// This name must point to the virtual method.
MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute));
StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr);
#ifdef DEBUG_ENABLED
CRASH_COND(godot_method_name == StringName());
#endif
MethodKey key = MethodKey(godot_method_name, method->get_parameters_count());
GDMonoMethod **existing_method = methods.getptr(key);
if (existing_method) {
memdelete(*existing_method); // Must delete old one
}
methods.set(key, method);
break;
}
if (top == CACHED_CLASS(GodotObject)) {
break;
}
top = top->get_parent_class();
}
}
methods_fetched = true;
}
GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
ERR_FAIL_COND_V(!methods_fetched, nullptr);
const MethodKey *k = nullptr;
while ((k = methods.next(k))) {
if (k->name == p_name) {
return methods.get(*k);
}
}
return nullptr;
}
bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
return get_fetched_method_unknown_params(p_name) != nullptr;
}
bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr());
}
bool GDMonoClass::has_public_parameterless_ctor() {
GDMonoMethod *ctor = get_method(".ctor", 0);
return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC;
}
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count);
GDMonoMethod **match = methods.getptr(key);
if (match) {
return *match;
}
if (methods_fetched) {
return nullptr;
}
MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count);
if (raw_method) {
GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
methods.set(key, method);
return method;
}
return nullptr;
}
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
MonoMethodSignature *sig = mono_method_signature(p_raw_method);
int params_count = mono_signature_get_param_count(sig);
StringName method_name = String::utf8(mono_method_get_name(p_raw_method));
return get_method(p_raw_method, method_name, params_count);
}
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) {
MonoMethodSignature *sig = mono_method_signature(p_raw_method);
int params_count = mono_signature_get_param_count(sig);
return get_method(p_raw_method, p_name, params_count);
}
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count) {
ERR_FAIL_NULL_V(p_raw_method, nullptr);
MethodKey key = MethodKey(p_name, p_params_count);
GDMonoMethod **match = methods.getptr(key);
if (match) {
return *match;
}
GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
methods.set(key, method);
return method;
}
GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) {
MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace);
MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
mono_method_desc_free(desc);
if (!method) {
return nullptr;
}
ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr);
return get_method(method);
}
GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
if (result) {
return result->value();
}
if (fields_fetched) {
return nullptr;
}
MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
if (raw_field) {
GDMonoField *field = memnew(GDMonoField(raw_field, this));
fields.insert(p_name, field);
return field;
}
return nullptr;
}
const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
if (fields_fetched) {
return fields_list;
}
void *iter = nullptr;
MonoClassField *raw_field = nullptr;
while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) {
StringName name = String::utf8(mono_field_get_name(raw_field));
Map<StringName, GDMonoField *>::Element *match = fields.find(name);
if (match) {
fields_list.push_back(match->get());
} else {
GDMonoField *field = memnew(GDMonoField(raw_field, this));
fields.insert(name, field);
fields_list.push_back(field);
}
}
fields_fetched = true;
return fields_list;
}
GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
Map<StringName, GDMonoProperty *>::Element *result = properties.find(p_name);
if (result) {
return result->value();
}
if (properties_fetched) {
return nullptr;
}
MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data());
if (raw_property) {
GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
properties.insert(p_name, property);
return property;
}
return nullptr;
}
const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
if (properties_fetched) {
return properties_list;
}
void *iter = nullptr;
MonoProperty *raw_property = nullptr;
while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) {
StringName name = String::utf8(mono_property_get_name(raw_property));
Map<StringName, GDMonoProperty *>::Element *match = properties.find(name);
if (match) {
properties_list.push_back(match->get());
} else {
GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
properties.insert(name, property);
properties_list.push_back(property);
}
}
properties_fetched = true;
return properties_list;
}
const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
if (delegates_fetched) {
return delegates_list;
}
void *iter = nullptr;
MonoClass *raw_class = nullptr;
while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != nullptr) {
if (mono_class_is_delegate(raw_class)) {
StringName name = String::utf8(mono_class_get_name(raw_class));
Map<StringName, GDMonoClass *>::Element *match = delegates.find(name);
if (match) {
delegates_list.push_back(match->get());
} else {
GDMonoClass *delegate = memnew(GDMonoClass(String::utf8(mono_class_get_namespace(raw_class)), String::utf8(mono_class_get_name(raw_class)), raw_class, assembly));
delegates.insert(name, delegate);
delegates_list.push_back(delegate);
}
}
}
delegates_fetched = true;
return delegates_list;
}
const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() {
if (!method_list_fetched) {
void *iter = nullptr;
MonoMethod *raw_method = nullptr;
while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
method_list.push_back(memnew(GDMonoMethod(String::utf8(mono_method_get_name(raw_method)), raw_method)));
}
method_list_fetched = true;
}
return method_list;
}
GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) {
namespace_name = p_namespace;
class_name = p_name;
mono_class = p_class;
assembly = p_assembly;
attrs_fetched = false;
attributes = nullptr;
methods_fetched = false;
method_list_fetched = false;
fields_fetched = false;
properties_fetched = false;
delegates_fetched = false;
}
GDMonoClass::~GDMonoClass() {
if (attributes) {
mono_custom_attrs_free(attributes);
}
for (const KeyValue<StringName, GDMonoField *> &E : fields) {
memdelete(E.value);
}
for (const KeyValue<StringName, GDMonoProperty *> &E : properties) {
memdelete(E.value);
}
{
// Ugly workaround...
// We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods).
// This way, we end with both the snake_case name and the PascalCasel name paired with the same method.
// Therefore, we must avoid deleting the same pointer twice.
int offset = 0;
Vector<GDMonoMethod *> deleted_methods;
deleted_methods.resize(methods.size());
const MethodKey *k = nullptr;
while ((k = methods.next(k))) {
GDMonoMethod *method = methods.get(*k);
if (method) {
for (int i = 0; i < offset; i++) {
if (deleted_methods[i] == method) {
// Already deleted
goto already_deleted;
}
}
deleted_methods.write[offset] = method;
++offset;
memdelete(method);
}
already_deleted:;
}
methods.clear();
}
for (int i = 0; i < method_list.size(); ++i) {
memdelete(method_list[i]);
}
}

View file

@ -1,160 +0,0 @@
/*************************************************************************/
/* gd_mono_class.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 GD_MONO_CLASS_H
#define GD_MONO_CLASS_H
#include "core/string/ustring.h"
#include "core/templates/map.h"
#include "gd_mono_field.h"
#include "gd_mono_header.h"
#include "gd_mono_method.h"
#include "gd_mono_property.h"
#include "gd_mono_utils.h"
class GDMonoClass {
struct MethodKey {
struct Hasher {
static _FORCE_INLINE_ uint32_t hash(const MethodKey &p_key) {
uint32_t hash = 0;
GDMonoUtils::hash_combine(hash, p_key.name.hash());
GDMonoUtils::hash_combine(hash, HashMapHasherDefault::hash(p_key.params_count));
return hash;
}
};
_FORCE_INLINE_ bool operator==(const MethodKey &p_a) const {
return p_a.params_count == params_count && p_a.name == name;
}
MethodKey() {}
MethodKey(const StringName &p_name, uint16_t p_params_count) :
name(p_name), params_count(p_params_count) {
}
StringName name;
uint16_t params_count = 0;
};
StringName namespace_name;
StringName class_name;
MonoClass *mono_class;
GDMonoAssembly *assembly;
bool attrs_fetched;
MonoCustomAttrInfo *attributes;
// This contains both the original method names and remapped method names from the native Godot identifiers to the C# functions.
// Most method-related functions refer to this and it's possible this is unintuitive for outside users; this may be a prime location for refactoring or renaming.
bool methods_fetched;
HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods;
bool method_list_fetched;
Vector<GDMonoMethod *> method_list;
bool fields_fetched;
Map<StringName, GDMonoField *> fields;
Vector<GDMonoField *> fields_list;
bool properties_fetched;
Map<StringName, GDMonoProperty *> properties;
Vector<GDMonoProperty *> properties_list;
bool delegates_fetched;
Map<StringName, GDMonoClass *> delegates;
Vector<GDMonoClass *> delegates_list;
friend class GDMonoAssembly;
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
public:
static String get_full_name(MonoClass *p_mono_class);
static MonoType *get_mono_type(MonoClass *p_mono_class);
String get_full_name() const;
String get_type_desc() const;
MonoType *get_mono_type() const;
uint32_t get_flags() const;
bool is_static() const;
bool is_assignable_from(GDMonoClass *p_from) const;
StringName get_namespace() const;
_FORCE_INLINE_ StringName get_name() const { return class_name; }
String get_name_for_lookup() const;
_FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; }
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
GDMonoClass *get_parent_class() const;
GDMonoClass *get_nesting_class() const;
#ifdef TOOLS_ENABLED
Vector<MonoClassField *> get_enum_fields();
#endif
GDMonoMethod *get_fetched_method_unknown_params(const StringName &p_name);
bool has_fetched_method_unknown_params(const StringName &p_name);
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
void fetch_attributes();
void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
bool implements_interface(GDMonoClass *p_interface);
bool has_public_parameterless_ctor();
GDMonoMethod *get_method(const StringName &p_name, uint16_t p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count);
GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
GDMonoField *get_field(const StringName &p_name);
const Vector<GDMonoField *> &get_all_fields();
GDMonoProperty *get_property(const StringName &p_name);
const Vector<GDMonoProperty *> &get_all_properties();
const Vector<GDMonoClass *> &get_all_delegates();
const Vector<GDMonoMethod *> &get_all_methods();
~GDMonoClass();
};
#endif // GD_MONO_CLASS_H

View file

@ -1,149 +0,0 @@
/*************************************************************************/
/* gd_mono_field.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 "gd_mono_field.h"
#include <mono/metadata/attrdefs.h>
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) {
mono_field_set_value(p_object, mono_field, p_value);
}
void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
mono_field_set_value(p_object, mono_field, &p_ptr);
}
void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) {
MonoReflectionField *reflfield = mono_field_get_object(mono_domain_get(), owner->get_mono_ptr(), mono_field);
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(Marshaling, SetFieldValue)
.invoke(reflfield, p_object, &p_value, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
}
}
MonoObject *GDMonoField::get_value(MonoObject *p_object) {
return mono_field_get_value_object(mono_domain_get(), mono_field, p_object);
}
bool GDMonoField::get_bool_value(MonoObject *p_object) {
return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object));
}
int GDMonoField::get_int_value(MonoObject *p_object) {
return GDMonoMarshal::unbox<int32_t>(get_value(p_object));
}
String GDMonoField::get_string_value(MonoObject *p_object) {
MonoObject *val = get_value(p_object);
return GDMonoMarshal::mono_string_to_godot((MonoString *)val);
}
bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return false;
}
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, nullptr);
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return nullptr;
}
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoField::fetch_attributes() {
ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_field(owner->get_mono_ptr(), mono_field);
attrs_fetched = true;
}
bool GDMonoField::is_static() {
return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC;
}
IMonoClassMember::Visibility GDMonoField::get_visibility() {
switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) {
case MONO_FIELD_ATTR_PRIVATE:
return IMonoClassMember::PRIVATE;
case MONO_FIELD_ATTR_FAM_AND_ASSEM:
return IMonoClassMember::PROTECTED_AND_INTERNAL;
case MONO_FIELD_ATTR_ASSEMBLY:
return IMonoClassMember::INTERNAL;
case MONO_FIELD_ATTR_FAMILY:
return IMonoClassMember::PROTECTED;
case MONO_FIELD_ATTR_PUBLIC:
return IMonoClassMember::PUBLIC;
default:
ERR_FAIL_V(IMonoClassMember::PRIVATE);
}
}
GDMonoField::GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner) {
owner = p_owner;
mono_field = p_mono_field;
name = String::utf8(mono_field_get_name(mono_field));
MonoType *field_type = mono_field_get_type(mono_field);
type.type_encoding = mono_type_get_type(field_type);
MonoClass *field_type_class = mono_class_from_mono_type(field_type);
type.type_class = GDMono::get_singleton()->get_class(field_type_class);
attrs_fetched = false;
attributes = nullptr;
}
GDMonoField::~GDMonoField() {
if (attributes) {
mono_custom_attrs_free(attributes);
}
}

View file

@ -1,78 +0,0 @@
/*************************************************************************/
/* gd_mono_field.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 GDMONOFIELD_H
#define GDMONOFIELD_H
#include "gd_mono.h"
#include "gd_mono_header.h"
#include "i_mono_class_member.h"
class GDMonoField : public IMonoClassMember {
GDMonoClass *owner;
MonoClassField *mono_field;
StringName name;
ManagedType type;
bool attrs_fetched;
MonoCustomAttrInfo *attributes;
public:
virtual GDMonoClass *get_enclosing_class() const final { return owner; }
virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; }
virtual StringName get_name() const final { return name; }
virtual bool is_static() final;
virtual Visibility get_visibility() final;
virtual bool has_attribute(GDMonoClass *p_attr_class) final;
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
void fetch_attributes();
_FORCE_INLINE_ ManagedType get_type() const { return type; }
void set_value(MonoObject *p_object, MonoObject *p_value);
void set_value_raw(MonoObject *p_object, void *p_ptr);
void set_value_from_variant(MonoObject *p_object, const Variant &p_value);
MonoObject *get_value(MonoObject *p_object);
bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object);
String get_string_value(MonoObject *p_object);
GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner);
~GDMonoField();
};
#endif // GDMONOFIELD_H

View file

@ -1,52 +0,0 @@
/*************************************************************************/
/* gd_mono_header.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 GD_MONO_HEADER_H
#define GD_MONO_HEADER_H
#include <cstdint>
#ifdef WIN32
#define GD_MONO_STDCALL __stdcall
#else
#define GD_MONO_STDCALL
#endif
class GDMonoAssembly;
class GDMonoClass;
class GDMonoField;
class GDMonoMethod;
class GDMonoProperty;
class IMonoClassMember;
#include "managed_type.h"
#endif // GD_MONO_HEADER_H

View file

@ -31,10 +31,7 @@
#include "gd_mono_internals.h"
#include "../csharp_script.h"
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
#include "core/debugger/engine_debugger.h"
@ -43,73 +40,6 @@
#include <mono/metadata/exception.h>
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
// This method should not fail
CRASH_COND(!unmanaged);
// All mono objects created from the managed world (e.g.: 'new Player()')
// need to have a CSharpScript in order for their methods to be callable from the unmanaged side
RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
GDMonoClass *klass = GDMonoUtils::get_object_class(managed);
CRASH_COND(!klass);
GDMonoClass *native = GDMonoUtils::get_class_native_base(klass);
CRASH_COND(native == nullptr);
if (native == klass) {
// If it's just a wrapper Godot class and not a custom inheriting class, then attach a
// script binding instead. One of the advantages of this is that if a script is attached
// later and it's not a C# script, then the managed object won't have to be disposed.
// Another reason for doing this is that this instance could outlive CSharpLanguage, which would
// be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621
CSharpScriptBinding script_binding;
script_binding.inited = true;
script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
script_binding.wrapper_class = klass;
script_binding.gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
script_binding.owner = unmanaged;
if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
// See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
// May not me referenced yet, so we must use init_ref() instead of reference()
if (rc->init_ref()) {
CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
}
// The object was just created, no script instance binding should have been attached
CRASH_COND(CSharpLanguage::has_instance_binding(unmanaged));
void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);
// Should be thread safe because the object was just created and nothing else should be referencing it
CSharpLanguage::set_instance_binding(unmanaged, data);
return;
}
MonoGCHandleData gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);
CRASH_COND(script.is_null());
CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
unmanaged->set_script_and_instance(script, csharp_instance);
}
void unhandled_exception(MonoException *p_exc) {
mono_print_unhandled_exception((MonoObject *)p_exc);
gd_unhandled_exception_event(p_exc);

View file

@ -37,9 +37,9 @@
#include "core/object/class_db.h"
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
class CSharpScript;
namespace GDMonoInternals {
/**
* Do not call this function directly.
* Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.

View file

@ -1,151 +0,0 @@
/*************************************************************************/
/* gd_mono_marshal.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 "gd_mono_marshal.h"
#include "../signal_awaiter_utils.h"
#include "gd_mono.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
namespace GDMonoMarshal {
// TODO: Those are just temporary until the code that needs them is moved to C#
Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) {
CRASH_COND(p_type.type_class == nullptr);
MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
MonoBoolean nil_is_variant = false;
MonoException *exc = nullptr;
int32_t ret = CACHED_METHOD_THUNK(Marshaling, managed_to_variant_type)
.invoke(refltype, &nil_is_variant, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
return Variant::NIL;
}
if (r_nil_is_variant) {
*r_nil_is_variant = (bool)nil_is_variant;
}
return (Variant::Type)ret;
}
bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
MonoReflectionType *array_refltype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type());
MonoReflectionType *elem_refltype = nullptr;
MonoException *exc = nullptr;
MonoBoolean ret = CACHED_METHOD_THUNK(Marshaling, try_get_array_element_type)
.invoke(array_refltype, &elem_refltype, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
return Variant::NIL;
}
r_elem_type = ManagedType::from_reftype(elem_refltype);
return ret;
}
MonoObject *variant_to_mono_object_of_type(const Variant &p_var, const ManagedType &p_type) {
MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
MonoException *exc = nullptr;
MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object_of_type)
.invoke(&p_var, refltype, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
return nullptr;
}
return ret;
}
MonoObject *variant_to_mono_object(const Variant &p_var) {
MonoException *exc = nullptr;
MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object)
.invoke(&p_var, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
return nullptr;
}
return ret;
}
static Variant mono_object_to_variant_impl(MonoObject *p_obj, bool p_fail_with_err) {
if (!p_obj) {
return Variant();
}
MonoBoolean fail_with_error = p_fail_with_err;
Variant ret;
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(Marshaling, mono_object_to_variant_out)
.invoke(p_obj, fail_with_error, &ret, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
return Variant();
}
return ret;
}
Variant mono_object_to_variant(MonoObject *p_obj) {
return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ true);
}
Variant mono_object_to_variant_no_err(MonoObject *p_obj) {
return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ false);
}
MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) {
const String *r = p_array.ptr();
int length = p_array.size();
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length);
for (int i = 0; i < length; i++) {
MonoString *boxed = mono_string_from_godot(r[i]);
mono_array_setref(ret, i, boxed);
}
return ret;
}
} // namespace GDMonoMarshal

View file

@ -1,97 +0,0 @@
/*************************************************************************/
/* gd_mono_marshal.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 GDMONOMARSHAL_H
#define GDMONOMARSHAL_H
#include "core/variant/variant.h"
#include "gd_mono.h"
#include "gd_mono_utils.h"
namespace GDMonoMarshal {
template <typename T>
T unbox(MonoObject *p_obj) {
return *(T *)mono_object_unbox(p_obj);
}
Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = nullptr);
bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
// String
_FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) {
char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string);
String ret = String(utf32);
mono_free(utf32);
return ret;
}
_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
if (p_mono_string == nullptr) {
return String();
}
return mono_string_to_godot_not_null(p_mono_string);
}
_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data()));
}
// Variant
MonoObject *variant_to_mono_object_of_type(const Variant &p_var, const ManagedType &p_type);
MonoObject *variant_to_mono_object(const Variant &p_var);
// These overloads were added to avoid passing a `const Variant *` to the `const Variant &`
// parameter. That would result in the `Variant(bool)` copy constructor being called as
// pointers are implicitly converted to bool. Implicit conversions are f-ing evil.
_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_type(const Variant *p_var, const ManagedType &p_type) {
return variant_to_mono_object_of_type(*p_var, p_type);
}
_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) {
return variant_to_mono_object(*p_var);
}
Variant mono_object_to_variant(MonoObject *p_obj);
Variant mono_object_to_variant_no_err(MonoObject *p_obj);
// PackedStringArray
MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array);
} // namespace GDMonoMarshal
#endif // GDMONOMARSHAL_H

View file

@ -1,295 +0,0 @@
/*************************************************************************/
/* gd_mono_method.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 "gd_mono_method.h"
#include <mono/metadata/attrdefs.h>
#include <mono/metadata/debug-helpers.h>
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
void GDMonoMethod::_update_signature() {
// Apparently MonoMethodSignature needs not to be freed.
// mono_method_signature caches the result, we don't need to cache it ourselves.
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
_update_signature(method_sig);
}
void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
params_count = mono_signature_get_param_count(p_method_sig);
MonoType *ret_type = mono_signature_get_return_type(p_method_sig);
if (ret_type) {
return_type.type_encoding = mono_type_get_type(ret_type);
if (return_type.type_encoding != MONO_TYPE_VOID) {
MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
return_type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
}
}
void *iter = nullptr;
MonoType *param_raw_type;
while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != nullptr) {
ManagedType param_type;
param_type.type_encoding = mono_type_get_type(param_raw_type);
MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
param_type.type_class = GDMono::get_singleton()->get_class(param_type_class);
param_types.push_back(param_type);
}
// clear the cache
method_info_fetched = false;
method_info = MethodInfo();
}
GDMonoClass *GDMonoMethod::get_enclosing_class() const {
return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method));
}
bool GDMonoMethod::is_static() {
return mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_STATIC;
}
IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
switch (mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
case MONO_METHOD_ATTR_PRIVATE:
return IMonoClassMember::PRIVATE;
case MONO_METHOD_ATTR_FAM_AND_ASSEM:
return IMonoClassMember::PROTECTED_AND_INTERNAL;
case MONO_METHOD_ATTR_ASSEM:
return IMonoClassMember::INTERNAL;
case MONO_METHOD_ATTR_FAMILY:
return IMonoClassMember::PROTECTED;
case MONO_METHOD_ATTR_PUBLIC:
return IMonoClassMember::PUBLIC;
default:
ERR_FAIL_V(IMonoClassMember::PRIVATE);
}
}
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const {
MonoException *exc = nullptr;
MonoObject *ret;
if (params_count > 0) {
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
for (int i = 0; i < params_count; i++) {
MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object_of_type(p_params[i], param_types[i]);
mono_array_setref(params, i, boxed_param);
}
ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
} else {
ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc);
}
if (exc) {
ret = nullptr;
if (r_exc) {
*r_exc = exc;
} else {
GDMonoUtils::set_pending_exception(exc);
}
}
return ret;
}
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const {
ERR_FAIL_COND_V(get_parameters_count() > 0, nullptr);
return invoke_raw(p_object, nullptr, r_exc);
}
MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const {
MonoException *exc = nullptr;
MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
if (exc) {
ret = nullptr;
if (r_exc) {
*r_exc = exc;
} else {
GDMonoUtils::set_pending_exception(exc);
}
}
return ret;
}
bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return false;
}
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, nullptr);
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return nullptr;
}
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoMethod::fetch_attributes() {
ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_method(mono_method);
attrs_fetched = true;
}
String GDMonoMethod::get_full_name(bool p_signature) const {
char *res = mono_method_full_name(mono_method, p_signature);
String full_name(res);
mono_free(res);
return full_name;
}
String GDMonoMethod::get_full_name_no_class() const {
String res;
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
res += ret_str;
mono_free(ret_str);
res += " ";
res += name;
res += "(";
char *sig_desc = mono_signature_get_desc(method_sig, true);
res += sig_desc;
mono_free(sig_desc);
res += ")";
return res;
}
String GDMonoMethod::get_ret_type_full_name() const {
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
String res = ret_str;
mono_free(ret_str);
return res;
}
String GDMonoMethod::get_signature_desc(bool p_namespaces) const {
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
char *sig_desc = mono_signature_get_desc(method_sig, p_namespaces);
String res = sig_desc;
mono_free(sig_desc);
return res;
}
void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
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);
}
}
void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const {
for (int i = 0; i < params_count; ++i) {
types.push_back(param_types[i]);
}
}
const MethodInfo &GDMonoMethod::get_method_info() {
if (!method_info_fetched) {
method_info.name = name;
bool nil_is_variant = false;
if (return_type.type_encoding == MONO_TYPE_VOID) {
method_info.return_val = PropertyInfo(Variant::NIL, "");
} else {
method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), "");
if (method_info.return_val.type == Variant::NIL && nil_is_variant) {
method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
}
}
Vector<StringName> names;
get_parameter_names(names);
for (int i = 0; i < params_count; ++i) {
nil_is_variant = false;
PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]);
if (arg_info.type == Variant::NIL && nil_is_variant) {
arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
}
method_info.arguments.push_back(arg_info);
}
// TODO: default arguments
method_info_fetched = true;
}
return method_info;
}
GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) :
name(p_name), mono_method(p_method) {
_update_signature();
}
GDMonoMethod::~GDMonoMethod() {
if (attributes) {
mono_custom_attrs_free(attributes);
}
}

View file

@ -1,96 +0,0 @@
/*************************************************************************/
/* gd_mono_method.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 GD_MONO_METHOD_H
#define GD_MONO_METHOD_H
#include "gd_mono.h"
#include "gd_mono_header.h"
#include "i_mono_class_member.h"
class GDMonoMethod : public IMonoClassMember {
StringName name;
uint16_t params_count;
ManagedType return_type;
Vector<ManagedType> param_types;
bool method_info_fetched = false;
MethodInfo method_info;
bool attrs_fetched = false;
MonoCustomAttrInfo *attributes = nullptr;
void _update_signature();
void _update_signature(MonoMethodSignature *p_method_sig);
friend class GDMonoClass;
MonoMethod *mono_method;
public:
virtual GDMonoClass *get_enclosing_class() const final;
virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; }
virtual StringName get_name() const final { return name; }
virtual bool is_static() final;
virtual Visibility get_visibility() final;
virtual bool has_attribute(GDMonoClass *p_attr_class) final;
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
void fetch_attributes();
_FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
_FORCE_INLINE_ uint16_t get_parameters_count() const { return params_count; }
_FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const;
MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = nullptr) const;
MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr) const;
String get_full_name(bool p_signature = false) const;
String get_full_name_no_class() const;
String get_ret_type_full_name() const;
String get_signature_desc(bool p_namespaces = false) const;
void get_parameter_names(Vector<StringName> &names) const;
void get_parameter_types(Vector<ManagedType> &types) const;
const MethodInfo &get_method_info();
GDMonoMethod(StringName p_name, MonoMethod *p_method);
~GDMonoMethod();
};
#endif // GD_MONO_METHOD_H

View file

@ -31,20 +31,19 @@
#ifndef GD_MONO_METHOD_THUNK_H
#define GD_MONO_METHOD_THUNK_H
#include <mono/jit/jit.h>
#include <mono/metadata/attrdefs.h>
#include <type_traits>
#include "gd_mono_class.h"
#include "gd_mono_header.h"
#include "gd_mono_marshal.h"
#include "gd_mono_method.h"
#include "core/error/error_macros.h"
#include "gd_mono_utils.h"
#if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED)
#define HAVE_METHOD_THUNKS
#ifdef WIN32
#define GD_MONO_STDCALL __stdcall
#else
#define GD_MONO_STDCALL
#endif
#ifdef HAVE_METHOD_THUNKS
template <class... ParamTypes>
struct GDMonoMethodThunk {
typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
@ -58,33 +57,30 @@ public:
GD_MONO_END_RUNTIME_INVOKE;
}
_FORCE_INLINE_ bool is_null() {
bool is_null() {
return mono_method_thunk == nullptr;
}
_FORCE_INLINE_ void nullify() {
mono_method_thunk = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
void set_from_method(MonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
MonoMethodSignature *method_sig = mono_method_signature(p_mono_method);
MonoType *ret_type = mono_signature_get_return_type(method_sig);
int ret_type_encoding = ret_type ? mono_type_get_type(ret_type) : MONO_TYPE_VOID;
CRASH_COND(ret_type_encoding != MONO_TYPE_VOID);
bool is_static = mono_method_get_flags(p_mono_method, nullptr) & MONO_METHOD_ATTR_STATIC;
CRASH_COND(!is_static);
uint32_t parameters_count = mono_signature_get_param_count(method_sig);
CRASH_COND(parameters_count != sizeof...(ParamTypes));
#endif
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method);
}
GDMonoMethodThunk() {}
explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
set_from_method(p_mono_method);
}
};
template <class R, class... ParamTypes>
@ -101,220 +97,30 @@ public:
return r;
}
_FORCE_INLINE_ bool is_null() {
bool is_null() {
return mono_method_thunk == nullptr;
}
_FORCE_INLINE_ void nullify() {
mono_method_thunk = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
void set_from_method(MonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
MonoMethodSignature *method_sig = mono_method_signature(p_mono_method);
MonoType *ret_type = mono_signature_get_return_type(method_sig);
int ret_type_encoding = ret_type ? mono_type_get_type(ret_type) : MONO_TYPE_VOID;
CRASH_COND(ret_type_encoding == MONO_TYPE_VOID);
bool is_static = mono_method_get_flags(p_mono_method, nullptr) & MONO_METHOD_ATTR_STATIC;
CRASH_COND(!is_static);
uint32_t parameters_count = mono_signature_get_param_count(method_sig);
CRASH_COND(parameters_count != sizeof...(ParamTypes));
#endif
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method);
}
GDMonoMethodThunkR() {}
explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == nullptr);
#endif
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
}
};
#else
template <unsigned int ThunkParamCount, class P1, class... ParamTypes>
struct VariadicInvokeMonoMethodImpl {
static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[ThunkParamCount] = { p_arg1, p_args... };
p_mono_method->invoke_raw(nullptr, args, r_exc);
} else {
void *args[ThunkParamCount] = { p_args... };
p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
}
}
};
template <unsigned int ThunkParamCount, class... ParamTypes>
struct VariadicInvokeMonoMethod {
static void invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
VariadicInvokeMonoMethodImpl<ThunkParamCount, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
}
};
template <>
struct VariadicInvokeMonoMethod<0> {
static void invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_mono_method->is_static());
#endif
p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
}
};
template <class P1>
struct VariadicInvokeMonoMethod<1, P1> {
static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[1] = { p_arg1 };
p_mono_method->invoke_raw(nullptr, args, r_exc);
} else {
p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
}
}
};
template <class R>
R unbox_if_needed(MonoObject *p_val, const ManagedType &, typename std::enable_if<!std::is_pointer<R>::value>::type * = 0) {
return GDMonoMarshal::unbox<R>(p_val);
}
template <class R>
R unbox_if_needed(MonoObject *p_val, const ManagedType &p_type, typename std::enable_if<std::is_pointer<R>::value>::type * = 0) {
if (mono_class_is_valuetype(p_type.type_class->get_mono_ptr())) {
return GDMonoMarshal::unbox<R>(p_val);
} else {
// If it's not a value type, we assume 'R' is a pointer to 'MonoObject' or a compatible type, like 'MonoException'.
return (R)p_val;
}
}
template <unsigned int ThunkParamCount, class R, class P1, class... ParamTypes>
struct VariadicInvokeMonoMethodRImpl {
static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[ThunkParamCount] = { p_arg1, p_args... };
MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
} else {
void *args[ThunkParamCount] = { p_args... };
MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
}
};
template <unsigned int ThunkParamCount, class R, class... ParamTypes>
struct VariadicInvokeMonoMethodR {
static R invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
return VariadicInvokeMonoMethodRImpl<ThunkParamCount, R, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
}
};
template <class R>
struct VariadicInvokeMonoMethodR<0, R> {
static R invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_mono_method->is_static());
#endif
MonoObject *r = p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
};
template <class R, class P1>
struct VariadicInvokeMonoMethodR<1, R, P1> {
static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[1] = { p_arg1 };
MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
} else {
MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
}
};
template <class... ParamTypes>
struct GDMonoMethodThunk {
GDMonoMethod *mono_method = nullptr;
public:
_FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
VariadicInvokeMonoMethod<sizeof...(ParamTypes), ParamTypes...>::invoke(mono_method, p_args..., r_exc);
}
_FORCE_INLINE_ bool is_null() {
return mono_method == nullptr;
}
_FORCE_INLINE_ void nullify() {
mono_method = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
#endif
mono_method = p_mono_method;
}
GDMonoMethodThunk() {}
explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
set_from_method(p_mono_method);
}
};
template <class R, class... ParamTypes>
struct GDMonoMethodThunkR {
GDMonoMethod *mono_method = nullptr;
public:
_FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
return VariadicInvokeMonoMethodR<sizeof...(ParamTypes), R, ParamTypes...>::invoke(mono_method, p_args..., r_exc);
}
_FORCE_INLINE_ bool is_null() {
return mono_method == nullptr;
}
_FORCE_INLINE_ void nullify() {
mono_method = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
#endif
mono_method = p_mono_method;
}
GDMonoMethodThunkR() {}
explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
set_from_method(p_mono_method);
}
};
#endif
#endif // GD_MONO_METHOD_THUNK_H

View file

@ -1,204 +0,0 @@
/*************************************************************************/
/* gd_mono_property.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 "gd_mono_property.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
#include <mono/metadata/attrdefs.h>
GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner) {
owner = p_owner;
mono_property = p_mono_property;
name = String::utf8(mono_property_get_name(mono_property));
MonoMethod *prop_method = mono_property_get_get_method(mono_property);
if (prop_method) {
MonoMethodSignature *getter_sig = mono_method_signature(prop_method);
MonoType *ret_type = mono_signature_get_return_type(getter_sig);
type.type_encoding = mono_type_get_type(ret_type);
MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
} else {
prop_method = mono_property_get_set_method(mono_property);
MonoMethodSignature *setter_sig = mono_method_signature(prop_method);
void *iter = nullptr;
MonoType *param_raw_type = mono_signature_get_params(setter_sig, &iter);
type.type_encoding = mono_type_get_type(param_raw_type);
MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
type.type_class = GDMono::get_singleton()->get_class(param_type_class);
}
attrs_fetched = false;
attributes = nullptr;
}
GDMonoProperty::~GDMonoProperty() {
if (attributes) {
mono_custom_attrs_free(attributes);
}
}
bool GDMonoProperty::is_static() {
MonoMethod *prop_method = mono_property_get_get_method(mono_property);
if (prop_method == nullptr) {
prop_method = mono_property_get_set_method(mono_property);
}
return mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_STATIC;
}
IMonoClassMember::Visibility GDMonoProperty::get_visibility() {
MonoMethod *prop_method = mono_property_get_get_method(mono_property);
if (prop_method == nullptr) {
prop_method = mono_property_get_set_method(mono_property);
}
switch (mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
case MONO_METHOD_ATTR_PRIVATE:
return IMonoClassMember::PRIVATE;
case MONO_METHOD_ATTR_FAM_AND_ASSEM:
return IMonoClassMember::PROTECTED_AND_INTERNAL;
case MONO_METHOD_ATTR_ASSEM:
return IMonoClassMember::INTERNAL;
case MONO_METHOD_ATTR_FAMILY:
return IMonoClassMember::PROTECTED;
case MONO_METHOD_ATTR_PUBLIC:
return IMonoClassMember::PUBLIC;
default:
ERR_FAIL_V(IMonoClassMember::PRIVATE);
}
}
bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return false;
}
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
MonoObject *GDMonoProperty::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, nullptr);
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return nullptr;
}
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoProperty::fetch_attributes() {
ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_property(owner->get_mono_ptr(), mono_property);
attrs_fetched = true;
}
bool GDMonoProperty::has_getter() {
return mono_property_get_get_method(mono_property) != nullptr;
}
bool GDMonoProperty::has_setter() {
return mono_property_get_set_method(mono_property) != nullptr;
}
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
MonoMethod *prop_method = mono_property_get_set_method(mono_property);
void *params[1] = { p_value };
MonoException *exc = nullptr;
GDMonoUtils::runtime_invoke(prop_method, p_object, params, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
GDMonoUtils::set_pending_exception(exc);
}
}
}
void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
MonoException *exc = nullptr;
GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
GDMonoUtils::set_pending_exception(exc);
}
}
}
MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
MonoException *exc = nullptr;
MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, nullptr, &exc);
if (exc) {
ret = nullptr;
if (r_exc) {
*r_exc = exc;
} else {
GDMonoUtils::set_pending_exception(exc);
}
}
return ret;
}
bool GDMonoProperty::get_bool_value(MonoObject *p_object) {
return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object));
}
int GDMonoProperty::get_int_value(MonoObject *p_object) {
return GDMonoMarshal::unbox<int32_t>(get_value(p_object));
}
String GDMonoProperty::get_string_value(MonoObject *p_object) {
MonoObject *val = get_value(p_object);
return GDMonoMarshal::mono_string_to_godot((MonoString *)val);
}

View file

@ -1,79 +0,0 @@
/*************************************************************************/
/* gd_mono_property.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 GD_MONO_PROPERTY_H
#define GD_MONO_PROPERTY_H
#include "gd_mono.h"
#include "gd_mono_header.h"
#include "i_mono_class_member.h"
class GDMonoProperty : public IMonoClassMember {
GDMonoClass *owner;
MonoProperty *mono_property;
StringName name;
ManagedType type;
bool attrs_fetched;
MonoCustomAttrInfo *attributes;
public:
virtual GDMonoClass *get_enclosing_class() const final { return owner; }
virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; }
virtual StringName get_name() const final { return name; }
virtual bool is_static() final;
virtual Visibility get_visibility() final;
virtual bool has_attribute(GDMonoClass *p_attr_class) final;
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
void fetch_attributes();
bool has_getter();
bool has_setter();
_FORCE_INLINE_ ManagedType get_type() const { return type; }
void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = nullptr);
void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr);
MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = nullptr);
bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object);
String get_string_value(MonoObject *p_object);
GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner);
~GDMonoProperty();
};
#endif // GD_MONO_PROPERTY_H

View file

@ -48,68 +48,9 @@
#include "../utils/macros.h"
#include "gd_mono.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
namespace GDMonoUtils {
MonoObject *unmanaged_get_managed(Object *unmanaged) {
if (!unmanaged) {
return nullptr;
}
if (unmanaged->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
if (cs_instance) {
return cs_instance->get_mono_object();
}
}
// If the owner does not have a CSharpInstance...
void *data = CSharpLanguage::get_instance_binding(unmanaged);
ERR_FAIL_NULL_V(data, nullptr);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, nullptr);
MonoGCHandleData &gchandle = script_binding.gchandle;
MonoObject *target = gchandle.get_target();
if (target) {
return target;
}
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
// Create a new one
#ifdef DEBUG_ENABLED
CRASH_COND(script_binding.type_name == StringName());
CRASH_COND(script_binding.wrapper_class == nullptr);
#endif
MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged);
ERR_FAIL_NULL_V(mono_object, nullptr);
gchandle = MonoGCHandleData::new_strong_handle(mono_object);
// Tie managed to unmanaged
RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
// See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
rc->reference();
CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
return mono_object;
}
void set_main_thread(MonoThread *p_thread) {
mono_thread_set_main(p_thread);
}
@ -148,90 +89,6 @@ bool is_thread_attached() {
return mono_domain_get() != nullptr;
}
uint32_t new_strong_gchandle(MonoObject *p_object) {
return mono_gchandle_new(p_object, /* pinned: */ false);
}
uint32_t new_strong_gchandle_pinned(MonoObject *p_object) {
return mono_gchandle_new(p_object, /* pinned: */ true);
}
uint32_t new_weak_gchandle(MonoObject *p_object) {
return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
}
void free_gchandle(uint32_t p_gchandle) {
mono_gchandle_free(p_gchandle);
}
void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) {
GDMonoMethod *ctor = p_class->get_method(".ctor", 0);
ERR_FAIL_NULL(ctor);
ctor->invoke_raw(p_this_obj, nullptr, r_exc);
}
GDMonoClass *get_object_class(MonoObject *p_object) {
return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
}
GDMonoClass *type_get_proxy_class(const StringName &p_type) {
String class_name = p_type;
if (class_name[0] == '_') {
class_name = class_name.substr(1, class_name.length());
}
GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
if (klass && klass->is_static()) {
// A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
return GDMonoCache::cached_data.class_GodotObject;
}
#ifdef TOOLS_ENABLED
if (!klass) {
return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
}
#endif
return klass;
}
GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
GDMonoClass *klass = p_class;
do {
const GDMonoAssembly *assembly = klass->get_assembly();
if (assembly == GDMono::get_singleton()->get_core_api_assembly()) {
return klass;
}
#ifdef TOOLS_ENABLED
if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) {
return klass;
}
#endif
} while ((klass = klass->get_parent_class()) != nullptr);
return nullptr;
}
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), p_native);
ERR_FAIL_COND_V_MSG(!parent_is_object_class, nullptr,
"Type inherits from native type '" + p_native + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'.");
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
ERR_FAIL_NULL_V(mono_object, nullptr);
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct
GDMonoUtils::runtime_object_init(mono_object, p_class);
return mono_object;
}
MonoDomain *create_domain(const String &p_friendly_name) {
print_verbose("Mono: Creating domain '" + p_friendly_name + "'...");
@ -247,14 +104,12 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain;
}
String get_type_desc(MonoType *p_type) {
return mono_type_full_name(p_type);
}
String get_type_desc(MonoReflectionType *p_reftype) {
return get_type_desc(mono_reflection_type_get_type(p_reftype));
}
// TODO:
// Implement all of the disabled exception logging below. Once we move to .NET 6.
// It will have to be done from C# as UnmanagedCallersOnly doesn't allow throwing.
#warning TODO
#if 0
String get_exception_name_and_message(MonoException *p_exc) {
String res;
@ -273,6 +128,7 @@ String get_exception_name_and_message(MonoException *p_exc) {
return res;
}
#endif
void debug_print_unhandled_exception(MonoException *p_exc) {
print_unhandled_exception(p_exc);
@ -284,7 +140,10 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
if (!EngineDebugger::is_active()) {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
#warning TODO
#if 0
ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
#endif
}
#endif
return;
@ -305,6 +164,8 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
Vector<ScriptLanguage::StackInfo> si;
String exc_msg;
#warning TODO
#if 0
while (p_exc != nullptr) {
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
@ -341,6 +202,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
p_exc = (MonoException *)inner_exc;
}
#endif
String file = si.size() ? si[0].file : __FILE__;
String func = si.size() ? si[0].func : FUNCTION_STR;
@ -377,73 +239,6 @@ void set_pending_exception(MonoException *p_exc) {
thread_local int current_invoke_count = 0;
MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
}
MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error) {
r_error = false;
switch (mono_type_get_type(p_enum_basetype)) {
case MONO_TYPE_BOOLEAN:
return (bool)GDMonoMarshal::unbox<MonoBoolean>(p_boxed) ? 1 : 0;
case MONO_TYPE_CHAR:
return GDMonoMarshal::unbox<uint16_t>(p_boxed);
case MONO_TYPE_U1:
return GDMonoMarshal::unbox<uint8_t>(p_boxed);
case MONO_TYPE_U2:
return GDMonoMarshal::unbox<uint16_t>(p_boxed);
case MONO_TYPE_U4:
return GDMonoMarshal::unbox<uint32_t>(p_boxed);
case MONO_TYPE_U8:
return GDMonoMarshal::unbox<uint64_t>(p_boxed);
case MONO_TYPE_I1:
return GDMonoMarshal::unbox<int8_t>(p_boxed);
case MONO_TYPE_I2:
return GDMonoMarshal::unbox<int16_t>(p_boxed);
case MONO_TYPE_I4:
return GDMonoMarshal::unbox<int32_t>(p_boxed);
case MONO_TYPE_I8:
return GDMonoMarshal::unbox<int64_t>(p_boxed);
default:
r_error = true;
return 0;
}
}
void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
CACHED_METHOD_THUNK(GodotObject, Dispose).invoke(p_mono_object, r_exc);
}
ScopeThreadAttach::ScopeThreadAttach() {
if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) {
mono_thread = GDMonoUtils::attach_current_thread();
@ -455,9 +250,4 @@ ScopeThreadAttach::~ScopeThreadAttach() {
GDMonoUtils::detach_current_thread(mono_thread);
}
}
StringName get_native_godot_class_name(GDMonoClass *p_class) {
MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr);
return (StringName)GDMonoMarshal::mono_object_to_variant(native_name_obj);
}
} // namespace GDMonoUtils

View file

@ -35,7 +35,6 @@
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "gd_mono_header.h"
#ifdef JAVASCRIPT_ENABLED
#include "gd_mono_wasm_m2n.h"
#endif
@ -56,13 +55,6 @@ _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash)
p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
}
/**
* If the object has a csharp script, returns the target of the gchandle stored in the script instance
* Otherwise returns a newly constructed MonoObject* which is attached to the object
* Returns nullptr on error
*/
MonoObject *unmanaged_get_managed(Object *unmanaged);
void set_main_thread(MonoThread *p_thread);
MonoThread *attach_current_thread();
void detach_current_thread();
@ -70,24 +62,8 @@ void detach_current_thread(MonoThread *p_mono_thread);
MonoThread *get_current_thread();
bool is_thread_attached();
uint32_t new_strong_gchandle(MonoObject *p_object);
uint32_t new_strong_gchandle_pinned(MonoObject *p_object);
uint32_t new_weak_gchandle(MonoObject *p_object);
void free_gchandle(uint32_t p_gchandle);
void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = nullptr);
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object);
MonoDomain *create_domain(const String &p_friendly_name);
String get_type_desc(MonoType *p_type);
String get_type_desc(MonoReflectionType *p_reftype);
String get_exception_name_and_message(MonoException *p_exc);
void debug_print_unhandled_exception(MonoException *p_exc);
@ -112,18 +88,8 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
return current_invoke_count;
}
MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc);
MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc);
MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc);
void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error);
void dispose(MonoObject *p_mono_object, MonoException **r_exc);
struct ScopeThreadAttach {
ScopeThreadAttach();
~ScopeThreadAttach();
@ -132,8 +98,6 @@ private:
MonoThread *mono_thread = nullptr;
};
StringName get_native_godot_class_name(GDMonoClass *p_class);
template <typename... P>
void add_internal_call(const char *p_name, void (*p_func)(P...)) {
#ifdef JAVASCRIPT_ENABLED
@ -151,8 +115,6 @@ void add_internal_call(const char *p_name, R (*p_func)(P...)) {
}
} // namespace GDMonoUtils
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
#define GD_MONO_BEGIN_RUNTIME_INVOKE \
int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
_runtime_invoke_count_ref += 1; \

View file

@ -1,70 +0,0 @@
/*************************************************************************/
/* i_mono_class_member.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 I_MONO_CLASS_MEMBER_H
#define I_MONO_CLASS_MEMBER_H
#include "gd_mono_header.h"
#include <mono/metadata/object.h>
class IMonoClassMember {
public:
enum Visibility {
PRIVATE,
PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM
INTERNAL, // ASSEMBLY
PROTECTED, // FAMILY
PUBLIC
};
enum MemberType {
MEMBER_TYPE_FIELD,
MEMBER_TYPE_PROPERTY,
MEMBER_TYPE_METHOD
};
virtual ~IMonoClassMember() {}
virtual GDMonoClass *get_enclosing_class() const = 0;
virtual MemberType get_member_type() const = 0;
virtual StringName get_name() const = 0;
virtual bool is_static() = 0;
virtual Visibility get_visibility() = 0;
virtual bool has_attribute(GDMonoClass *p_attr_class) = 0;
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) = 0;
};
#endif // I_MONO_CLASS_MEMBER_H

View file

@ -1,58 +0,0 @@
/*************************************************************************/
/* managed_type.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 "managed_type.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
ManagedType ManagedType::from_class(GDMonoClass *p_class) {
return ManagedType(mono_type_get_type(p_class->get_mono_type()), p_class);
}
ManagedType ManagedType::from_class(MonoClass *p_mono_class) {
GDMonoClass *tclass = GDMono::get_singleton()->get_class(p_mono_class);
ERR_FAIL_COND_V(!tclass, ManagedType());
return ManagedType(mono_type_get_type(tclass->get_mono_type()), tclass);
}
ManagedType ManagedType::from_type(MonoType *p_mono_type) {
MonoClass *mono_class = mono_class_from_mono_type(p_mono_type);
GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_class);
ERR_FAIL_COND_V(!tclass, ManagedType());
return ManagedType(mono_type_get_type(p_mono_type), tclass);
}
ManagedType ManagedType::from_reftype(MonoReflectionType *p_mono_reftype) {
MonoType *mono_type = mono_reflection_type_get_type(p_mono_reftype);
return from_type(mono_type);
}

View file

@ -1,55 +0,0 @@
/*************************************************************************/
/* managed_type.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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 MANAGED_TYPE_H
#define MANAGED_TYPE_H
#include <mono/metadata/object.h>
#include "gd_mono_header.h"
struct ManagedType {
int type_encoding = 0;
GDMonoClass *type_class = nullptr;
static ManagedType from_class(GDMonoClass *p_class);
static ManagedType from_class(MonoClass *p_mono_class);
static ManagedType from_type(MonoType *p_mono_type);
static ManagedType from_reftype(MonoReflectionType *p_mono_reftype);
ManagedType() {}
ManagedType(int p_type_encoding, GDMonoClass *p_type_class) :
type_encoding(p_type_encoding),
type_class(p_type_class) {
}
};
#endif // MANAGED_TYPE_H

View file

@ -346,7 +346,7 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
jsize encodedLength = env->GetArrayLength(encoded);
MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength);
MonoArray *encoded_ret = mono_array_new(mono_domain_get(), mono_get_byte_class(), encodedLength);
uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));

View file

@ -32,16 +32,15 @@
#include "csharp_script.h"
#include "mono_gd/gd_mono_cache.h"
#include "mono_gd/gd_mono_class.h"
#include "mono_gd/gd_mono_marshal.h"
#include "mono_gd/gd_mono_utils.h"
Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) {
Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
// TODO: Use pooling for ManagedCallable instances.
SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal));
MonoGCHandleData awaiter_handle(p_awaiter_handle_ptr, gdmono::GCHandleType::STRONG_HANDLE);
SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, awaiter_handle, p_signal));
Callable callable = Callable(awaiter_callable);
return p_source->connect(p_signal, callable, Vector<Variant>(), Object::CONNECT_ONESHOT);
@ -51,7 +50,7 @@ bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const Calla
// Only called if both instances are of type SignalAwaiterCallable. Static cast is safe.
const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
return a->awaiter_handle.handle == b->awaiter_handle.handle;
return a->awaiter_handle.handle.value == b->awaiter_handle.handle.value;
}
bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@ -105,38 +104,26 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va
"Resumed after await, but class instance is gone.");
#endif
MonoArray *signal_args = nullptr;
if (p_argcount > 0) {
signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
for (int i = 0; i < p_argcount; i++) {
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
mono_array_setref(signal_args, i, boxed);
}
}
MonoObject *awaiter = awaiter_handle.get_target();
if (!awaiter) {
r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter, signal_args, &exc);
bool awaiter_is_null = false;
GDMonoCache::cached_data.methodthunk_SignalAwaiter_SignalCallback.invoke(awaiter_handle.get_intptr(), p_arguments, p_argcount, &awaiter_is_null, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
ERR_FAIL();
} else {
r_call_error.error = Callable::CallError::CALL_OK;
}
if (awaiter_is_null) {
r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
r_call_error.error = Callable::CallError::CALL_OK;
}
SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) :
SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal) :
target_id(p_target->get_instance_id()),
awaiter_handle(MonoGCHandleData::new_strong_handle(p_awaiter)),
awaiter_handle(p_awaiter_handle),
signal(p_signal) {
}
@ -152,7 +139,7 @@ bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const Callabl
return false;
}
if (a->event_signal != b->event_signal) {
if (a->event_signal_name != b->event_signal_name) {
return false;
}
@ -167,7 +154,7 @@ bool EventSignalCallable::compare_less(const CallableCustom *p_a, const Callable
}
uint32_t EventSignalCallable::hash() const {
uint32_t hash = event_signal->field->get_name().hash();
uint32_t hash = event_signal_name.hash();
return hash_djb2_one_64(owner->get_instance_id(), hash);
}
@ -177,8 +164,7 @@ String EventSignalCallable::get_as_text() const {
if (script.is_valid() && script->get_path().is_resource_file()) {
class_name += "(" + script->get_path().get_file() + ")";
}
StringName signal = event_signal->field->get_name();
return class_name + "::EventSignalMiddleman::" + String(signal);
return class_name + "::EventSignalMiddleman::" + String(event_signal_name);
}
CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const {
@ -194,39 +180,38 @@ ObjectID EventSignalCallable::get_object() const {
}
StringName EventSignalCallable::get_signal() const {
return event_signal->field->get_name();
return event_signal_name;
}
void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
r_return_value = Variant();
ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count());
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance());
ERR_FAIL_NULL(csharp_instance);
MonoObject *owner_managed = csharp_instance->get_mono_object();
ERR_FAIL_NULL(owner_managed);
MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed);
if (!delegate_field_value) {
r_call_error.error = Callable::CallError::CALL_OK;
return;
}
GCHandleIntPtr owner_gchandle_intptr = csharp_instance->get_gchandle_intptr();
MonoException *exc = nullptr;
event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc);
bool awaiter_is_null = false;
GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_RaiseEventSignal.invoke(
owner_gchandle_intptr, &event_signal_name,
p_arguments, p_argcount, &awaiter_is_null, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
ERR_FAIL();
} else {
r_call_error.error = Callable::CallError::CALL_OK;
}
if (awaiter_is_null) {
r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
r_call_error.error = Callable::CallError::CALL_OK;
}
EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) :
EventSignalCallable::EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name) :
owner(p_owner),
event_signal(p_event_signal) {
event_signal_name(p_event_signal_name) {
}

View file

@ -36,7 +36,7 @@
#include "csharp_script.h"
#include "mono_gc_handle.h"
Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter);
Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr);
class BaseSignalCallable : public CallableCustom {
public:
@ -68,13 +68,13 @@ public:
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal);
SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal);
~SignalAwaiterCallable();
};
class EventSignalCallable : public BaseSignalCallable {
Object *owner;
const CSharpScript::EventSignal *event_signal;
StringName event_signal_name;
public:
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
@ -96,7 +96,7 @@ public:
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal);
EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name);
};
#endif // SIGNAL_AWAITER_UTILS_H