Skip to content

Commit

Permalink
Fix exported typed dictionaries in .NET not being marshalled correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
juanjp600 committed Oct 7, 2024
1 parent 00d763e commit 8aabfe0
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
79 changes: 79 additions & 0 deletions modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,82 @@ private static godot_variant ToVariantFunc(in Dictionary<TKey, TValue> godotDict
private static Dictionary<TKey, TValue> FromVariantFunc(in godot_variant variant) =>
VariantUtils.ConvertToDictionary<TKey, TValue>(variant);

private void SetTypedForUnderlyingDict()
{
if (Count > 0) { return; }

var self = (godot_dictionary)NativeValue;

godot_bool isTypedKey = NativeFuncs.godotsharp_dictionary_is_typed_key(ref self);
godot_bool isTypedValue = NativeFuncs.godotsharp_dictionary_is_typed_value(ref self);

if (isTypedKey == godot_bool.True || isTypedValue == godot_bool.True) { return; }

static void GetMarshalTypeInfo(
Type type,
out uint outVariantType,
out StringName outClassName,
out godot_ref outScript)
{
Variant.Type variantType = GD.TypeToVariantType(type);

if (variantType != Variant.Type.Object)
{
outVariantType = (uint)variantType;
outClassName = new StringName();
outScript = default;
return;
}

godot_ref scriptRef = default;

if (type.Assembly != typeof(Godot.GodotObject).Assembly)
{
unsafe
{
Godot.Bridge.ScriptManagerBridge.GetOrLoadOrCreateScriptForType(type, &scriptRef);
}
}

Type classNameType = type;
while (classNameType.BaseType != null && classNameType.Assembly != typeof(Godot.GodotObject).Assembly)
{
classNameType = classNameType.BaseType;
}

StringName className = classNameType.Name;

outVariantType = (uint)variantType;
outClassName = className;
outScript = scriptRef;
}

GetMarshalTypeInfo(typeof(TKey), out var keyVariantType, out var keyClassName, out var keyScriptRef);
GetMarshalTypeInfo(typeof(TValue), out var valueVariantType, out var valueClassName, out var valueScriptRef);

godot_string_name keyClassNameNative = (godot_string_name)keyClassName.NativeValue;
NativeFuncs.godotsharp_ref_to_var(ref keyScriptRef, out godot_variant keyScriptVariant);
godot_string_name valueClassNameNative = (godot_string_name)valueClassName.NativeValue;
NativeFuncs.godotsharp_ref_to_var(ref valueScriptRef, out godot_variant valueScriptVariant);

using (keyClassName)
using (valueClassName)
using (keyScriptRef)
using (valueScriptRef)
using (keyScriptVariant)
using (valueScriptVariant)
{
NativeFuncs.godotsharp_dictionary_set_typed(
ref self,
keyVariantType,
keyClassNameNative,
keyScriptVariant,
valueVariantType,
valueClassNameNative,
valueScriptVariant);
}
}

static unsafe Dictionary()
{
VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.ToVariantCb = &ToVariantFunc;
Expand All @@ -519,6 +595,7 @@ internal ref godot_dictionary.movable NativeValue
public Dictionary()
{
_underlyingDict = new Dictionary();
SetTypedForUnderlyingDict();
}

/// <summary>
Expand All @@ -535,6 +612,7 @@ public Dictionary(IDictionary<TKey, TValue> dictionary)
throw new ArgumentNullException(nameof(dictionary));

_underlyingDict = new Dictionary();
SetTypedForUnderlyingDict();

foreach (KeyValuePair<TKey, TValue> entry in dictionary)
Add(entry.Key, entry.Value);
Expand All @@ -554,6 +632,7 @@ public Dictionary(Dictionary dictionary)
throw new ArgumentNullException(nameof(dictionary));

_underlyingDict = dictionary;
SetTypedForUnderlyingDict();
}

// Explicit name to make it very clear
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ public static partial void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref

public static partial void godotsharp_ref_destroy(ref godot_ref p_instance);

public static partial void godotsharp_ref_to_var(ref godot_ref p_ref, out godot_variant r_dest);

public static partial void godotsharp_string_name_new_from_string(out godot_string_name r_dest,
in godot_string p_name);

Expand Down Expand Up @@ -463,6 +465,24 @@ public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dict

public static partial void godotsharp_dictionary_make_read_only(ref godot_dictionary p_self);

public static partial void godotsharp_dictionary_set_typed(ref godot_dictionary p_self, uint p_key_type, in godot_string_name p_key_class_name, in godot_variant p_key_script, uint p_value_type, in godot_string_name p_value_class_name, in godot_variant p_value_script);

public static partial godot_bool godotsharp_dictionary_is_typed_key(ref godot_dictionary p_self);

public static partial godot_bool godotsharp_dictionary_is_typed_value(ref godot_dictionary p_self);

public static partial uint godotsharp_dictionary_get_typed_key_builtin(ref godot_dictionary p_self);

public static partial uint godotsharp_dictionary_get_typed_value_builtin(ref godot_dictionary p_self);

public static partial void godotsharp_dictionary_get_typed_key_class_name(ref godot_dictionary p_self, out godot_string_name r_dest);

public static partial void godotsharp_dictionary_get_typed_value_class_name(ref godot_dictionary p_self, out godot_string_name r_dest);

public static partial void godotsharp_dictionary_get_typed_key_script(ref godot_dictionary p_self, out godot_variant r_dest);

public static partial void godotsharp_dictionary_get_typed_value_script(ref godot_dictionary p_self, out godot_variant r_dest);

public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str);

// StringExtensions
Expand Down
50 changes: 50 additions & 0 deletions modules/mono/glue/runtime_interop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ void godotsharp_ref_destroy(Ref<RefCounted> *p_instance) {
p_instance->~Ref();
}

void godotsharp_ref_to_var(const Ref<RefCounted> *p_ref, Variant *r_dest) {
*r_dest = Variant(p_ref->ptr());
}

void godotsharp_string_name_new_from_string(StringName *r_dest, const String *p_name) {
memnew_placement(r_dest, StringName(*p_name));
}
Expand Down Expand Up @@ -1207,6 +1211,42 @@ void godotsharp_dictionary_make_read_only(Dictionary *p_self) {
p_self->make_read_only();
}

void godotsharp_dictionary_set_typed(Dictionary *p_self, uint32_t p_key_type, const StringName *p_key_class_name, const Variant *p_key_script, uint32_t p_value_type, const StringName *p_value_class_name, const Variant *p_value_script) {
p_self->set_typed(p_key_type, *p_key_class_name, *p_key_script, p_value_type, *p_value_class_name, *p_value_script);
}

bool godotsharp_dictionary_is_typed_key(const Dictionary *p_self) {
return p_self->is_typed_key();
}

bool godotsharp_dictionary_is_typed_value(const Dictionary *p_self) {
return p_self->is_typed_value();
}

uint32_t godotsharp_dictionary_get_typed_key_builtin(const Dictionary *p_self) {
return p_self->get_typed_key_builtin();
}

uint32_t godotsharp_dictionary_get_typed_value_builtin(const Dictionary *p_self) {
return p_self->get_typed_value_builtin();
}

void godotsharp_dictionary_get_typed_key_class_name(const Dictionary *p_self, StringName *r_dest) {
memnew_placement(r_dest, StringName(p_self->get_typed_key_class_name()));
}

void godotsharp_dictionary_get_typed_value_class_name(const Dictionary *p_self, StringName *r_dest) {
memnew_placement(r_dest, StringName(p_self->get_typed_value_class_name()));
}

void godotsharp_dictionary_get_typed_key_script(const Dictionary *p_self, Variant *r_dest) {
memnew_placement(r_dest, Variant(p_self->get_typed_key_script()));
}

void godotsharp_dictionary_get_typed_value_script(const Dictionary *p_self, Variant *r_dest) {
memnew_placement(r_dest, Variant(p_self->get_typed_value_script()));
}

void godotsharp_dictionary_to_string(const Dictionary *p_self, String *r_str) {
*r_str = Variant(*p_self).operator String();
}
Expand Down Expand Up @@ -1470,6 +1510,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_array_filter_godot_objects_by_non_native,
(void *)godotsharp_ref_new_from_ref_counted_ptr,
(void *)godotsharp_ref_destroy,
(void *)godotsharp_ref_to_var,
(void *)godotsharp_string_name_new_from_string,
(void *)godotsharp_node_path_new_from_string,
(void *)godotsharp_string_name_as_string,
Expand Down Expand Up @@ -1610,6 +1651,15 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_dictionary_recursive_equal,
(void *)godotsharp_dictionary_remove_key,
(void *)godotsharp_dictionary_make_read_only,
(void *)godotsharp_dictionary_set_typed,
(void *)godotsharp_dictionary_is_typed_key,
(void *)godotsharp_dictionary_is_typed_value,
(void *)godotsharp_dictionary_get_typed_key_builtin,
(void *)godotsharp_dictionary_get_typed_value_builtin,
(void *)godotsharp_dictionary_get_typed_key_class_name,
(void *)godotsharp_dictionary_get_typed_value_class_name,
(void *)godotsharp_dictionary_get_typed_key_script,
(void *)godotsharp_dictionary_get_typed_value_script,
(void *)godotsharp_dictionary_to_string,
(void *)godotsharp_string_simplify_path,
(void *)godotsharp_string_to_camel_case,
Expand Down

0 comments on commit 8aabfe0

Please sign in to comment.