C#: Update C# code-generation to use a new ICustomMarshaler in some string usages that were leaking

When `C` calls a function that return/has an out string and it was overwritten by `C#` inherit class the `C` portion
wasn't cleaning its copy. Now, when a `C` calls a `C#` delegate function, `Strings` that are `out` values or `return`
values use a new marshaler (specific to this case) that uses Eina short lived strings (`Eina_Slstr`) instead of
duplicating it with `strdup`, so at some point, the string passed to `C` is deleted.

To do so, a `direction_context` (a new `Context` at `generation_contexts.hh`) was created. It is only used when a C#
delegate is being called from C (so this context is only set in `function_definition.hh` and `property_definition.hh`,
where it is set to `native_to_manage` to indicate that it is a native call to a managed function).

When this `direction_context` is set and the `String` being marshaled is not marked with an `@move` tag and it is an
`out` or `return` value, the new `StringOutMarshaler` (implemented at `iwrapper.cs`) is used (instead of
`StringKeepOwnershipMarshaler`).

When marshaling a managed data to native this marshaler uses eina short lived string (`Eina_Slstr`) that will be
automatically deleted. This delete is bounded to "the loop of the current thread or until the clear function is called
explicitly" as said at `src/lib/eina/eina_slstr.h`.

Reviewed-by: Felipe Magno de Almeida <felipe@expertisesolutions.com.br>
Differential Revision: https://phab.enlightenment.org/D11434
This commit is contained in:
Lucas Cavalcante de Sousa 2020-02-28 15:58:08 +00:00 committed by Felipe Magno de Almeida
parent e5cec5dacc
commit 117450e3fa
6 changed files with 106 additions and 25 deletions

View File

@ -90,7 +90,9 @@ struct native_function_definition_generator
(marshall_annotation << " " << marshall_parameter)
) % ", ")
<< ");\n\n")
.generate(sink, std::make_tuple(f.return_type, f.return_type, f.c_name, f.parameters), context))
.generate(sink,
std::make_tuple(f.return_type, f.return_type, f.c_name, f.parameters),
context_add_tag(direction_context{direction_context::native_to_managed}, context)))
return false;
// API delegate is the wrapper for the Eo methods exported from C that we will use from C#.
@ -186,7 +188,7 @@ struct native_function_definition_generator
, f.c_name
, f.parameters
)
, context))
, context_add_tag(direction_context{direction_context::native_to_managed}, context)))
return false;
// Static functions do not need to be called from C
@ -196,7 +198,9 @@ struct native_function_definition_generator
// This is the delegate that will be passed to Eo to be called from C.
if(!as_generator(
indent << "private static " << f.c_name << "_delegate " << f.c_name << "_static_delegate;\n\n"
).generate(sink, attributes::unused, context))
).generate(sink,
attributes::unused,
context_add_tag(direction_context{direction_context::native_to_managed}, context)))
return false;
return true;

View File

@ -50,6 +50,19 @@ struct class_context
{}
};
struct direction_context
{
enum direction {
native_to_managed,
managed_to_native,
};
direction current_direction;
direction_context(direction current_direction)
: current_direction(current_direction)
{}
};
struct indentation_context
{
constexpr indentation_context(indentation_context const& other) = default;

View File

@ -30,6 +30,7 @@ namespace eina = efl::eina;
namespace detail {
template <typename OutputIterator, typename Context>
struct marshall_annotation_visitor_generate
{
mutable OutputIterator sink;
@ -60,7 +61,13 @@ struct marshall_annotation_visitor_generate
{"string", true, [] {
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringPassOwnershipMarshaler))";
}},
{"string", false, [] {
{"string", false, [this] {
auto is_native_to_managed = false;
if constexpr (efl::eolian::grammar::tag_check<direction_context, Context>::value)
is_native_to_managed = context_find_tag<direction_context>(*context).current_direction == direction_context::native_to_managed;
if((is_out || is_return) && is_native_to_managed)
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringOutMarshaler))";
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringKeepOwnershipMarshaler))";
}},
{"mstring", true, [] {
@ -98,7 +105,13 @@ struct marshall_annotation_visitor_generate
{"string", true, [] {
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringPassOwnershipMarshaler))";
}},
{"string", false, [] {
{"string", false, [this] {
auto is_native_to_managed = false;
if constexpr (efl::eolian::grammar::tag_check<direction_context, Context>::value)
is_native_to_managed = context_find_tag<direction_context>(*context).current_direction == direction_context::native_to_managed;
if((is_out || is_return) && is_native_to_managed)
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringOutMarshaler))";
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringKeepOwnershipMarshaler))";
}},
{"mstring", true, [] {

View File

@ -192,7 +192,9 @@ struct native_property_function_definition_generator
(marshall_annotation << " " << marshall_parameter)
) % ", ")
<< ");\n\n")
.generate(sink, std::make_tuple(f.return_type, f.return_type, f.c_name, f.parameters), context))
.generate(sink,
std::make_tuple(f.return_type, f.return_type, f.c_name, f.parameters),
context_add_tag(direction_context{direction_context::native_to_managed}, context)))
return false;
// API delegate is the wrapper for the Eo methods exported from C that we will use from C#.
@ -340,7 +342,9 @@ struct native_property_function_definition_generator
// This is the delegate that will be passed to Eo to be called from C.
if(!as_generator(
indent << "private static " << f.c_name << "_delegate " << f.c_name << "_static_delegate;\n\n"
).generate(sink, attributes::unused, context))
).generate(sink,
attributes::unused,
context_add_tag(direction_context{direction_context::native_to_managed}, context)))
return false;
return true;
};

View File

@ -55,6 +55,9 @@ internal static partial class NativeCustomExportFunctions
efl_mono_native_free_addr_get();
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
efl_mono_native_efl_unref_addr_get();
[DllImport(efl.Libs.Eina)] public static extern IntPtr
eina_slstr_copy_new(string str);
}
/// <summary>Wrapper around native memory DllImport'd functions.
@ -94,6 +97,11 @@ public static class MemoryNative
return NativeCustomExportFunctions.efl_mono_native_strdup(str);
}
public static IntPtr SlstrCopyNew(string str)
{
return NativeCustomExportFunctions.eina_slstr_copy_new(str);
}
/// <summary>
/// Retrieves an instance of a string for use in program.
/// <para>Since EFL 1.23.</para>

View File

@ -1508,6 +1508,45 @@ class StringPassOwnershipMarshaler : ICustomMarshaler
static private StringPassOwnershipMarshaler marshaler;
}
class StringOutMarshaler: ICustomMarshaler
{
public object MarshalNativeToManaged(IntPtr pNativeData)
{
return Eina.StringConversion.NativeUtf8ToManagedString(pNativeData);
}
public IntPtr MarshalManagedToNative(object managedObj)
{
return Eina.MemoryNative.SlstrCopyNew((string)managedObj);
}
public void CleanUpNativeData(IntPtr pNativeData)
{
}
public void CleanUpManagedData(object managedObj)
{
}
public int GetNativeDataSize()
{
return -1;
}
internal static ICustomMarshaler GetInstance(string cookie)
{
if (marshaler == null)
{
marshaler = new StringOutMarshaler();
}
return marshaler;
}
static private StringOutMarshaler marshaler;
}
class StringKeepOwnershipMarshaler: ICustomMarshaler
{
public object MarshalNativeToManaged(IntPtr pNativeData)