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:
parent
e5cec5dacc
commit
117450e3fa
|
@ -47,7 +47,7 @@ struct native_function_definition_generator
|
||||||
{
|
{
|
||||||
attributes::klass_def const* klass;
|
attributes::klass_def const* klass;
|
||||||
std::vector<attributes::property_def> properties;
|
std::vector<attributes::property_def> properties;
|
||||||
|
|
||||||
template <typename OutputIterator, typename Context>
|
template <typename OutputIterator, typename Context>
|
||||||
bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const
|
bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const
|
||||||
{
|
{
|
||||||
|
@ -72,7 +72,7 @@ struct native_function_definition_generator
|
||||||
if (property_generate_wrapper_setter (*it, context))
|
if (property_generate_wrapper_setter (*it, context))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const& indent = current_indentation(context);
|
auto const& indent = current_indentation(context);
|
||||||
|
|
||||||
// Delegate for the C# method we will export to EO as a method implementation.
|
// Delegate for the C# method we will export to EO as a method implementation.
|
||||||
|
@ -90,7 +90,9 @@ struct native_function_definition_generator
|
||||||
(marshall_annotation << " " << marshall_parameter)
|
(marshall_annotation << " " << marshall_parameter)
|
||||||
) % ", ")
|
) % ", ")
|
||||||
<< ");\n\n")
|
<< ");\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;
|
return false;
|
||||||
|
|
||||||
// API delegate is the wrapper for the Eo methods exported from C that we will use from C#.
|
// 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.c_name
|
||||||
, f.parameters
|
, f.parameters
|
||||||
)
|
)
|
||||||
, context))
|
, context_add_tag(direction_context{direction_context::native_to_managed}, context)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Static functions do not need to be called from C
|
// Static functions do not need to be called from C
|
||||||
|
@ -196,13 +198,15 @@ struct native_function_definition_generator
|
||||||
// This is the delegate that will be passed to Eo to be called from C.
|
// This is the delegate that will be passed to Eo to be called from C.
|
||||||
if(!as_generator(
|
if(!as_generator(
|
||||||
indent << "private static " << f.c_name << "_delegate " << f.c_name << "_static_delegate;\n\n"
|
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 false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct function_definition_generator
|
struct function_definition_generator
|
||||||
{
|
{
|
||||||
template <typename OutputIterator, typename Context>
|
template <typename OutputIterator, typename Context>
|
||||||
|
@ -232,7 +236,7 @@ struct function_definition_generator
|
||||||
if (property_generate_wrapper_setter (*it, context))
|
if (property_generate_wrapper_setter (*it, context))
|
||||||
function_scope = "internal ";
|
function_scope = "internal ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not generate static function for concrete class
|
// Do not generate static function for concrete class
|
||||||
if (is_concrete && f.is_static)
|
if (is_concrete && f.is_static)
|
||||||
return true;
|
return true;
|
||||||
|
@ -402,7 +406,7 @@ struct property_wrapper_definition_generator
|
||||||
return true;
|
return true;
|
||||||
if (property.setter && !property.setter->keys.empty())
|
if (property.setter && !property.setter->keys.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (property.getter && property.setter)
|
if (property.getter && property.setter)
|
||||||
{
|
{
|
||||||
if (property.setter->values.size() != property.getter->values.size())
|
if (property.setter->values.size() != property.getter->values.size())
|
||||||
|
@ -498,7 +502,7 @@ struct property_wrapper_definition_generator
|
||||||
|
|
||||||
if (property.setter && property.setter->explicit_return_type.c_type == "Eina_Success_Flag")
|
if (property.setter && property.setter->explicit_return_type.c_type == "Eina_Success_Flag")
|
||||||
set_has_return_error = true;
|
set_has_return_error = true;
|
||||||
|
|
||||||
if (parameters.size() == 1)
|
if (parameters.size() == 1)
|
||||||
{
|
{
|
||||||
if (!as_generator(
|
if (!as_generator(
|
||||||
|
@ -674,7 +678,7 @@ struct attributes_needed< ::eolian_mono::property_wrapper_definition_generator>
|
||||||
template <>
|
template <>
|
||||||
struct attributes_needed< ::eolian_mono::property_wrapper_definition_parameterized> : std::integral_constant<int, 1> {};
|
struct attributes_needed< ::eolian_mono::property_wrapper_definition_parameterized> : std::integral_constant<int, 1> {};
|
||||||
}
|
}
|
||||||
|
|
||||||
} } }
|
} } }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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
|
struct indentation_context
|
||||||
{
|
{
|
||||||
constexpr indentation_context(indentation_context const& other) = default;
|
constexpr indentation_context(indentation_context const& other) = default;
|
||||||
|
|
|
@ -28,8 +28,9 @@ namespace eolian_mono {
|
||||||
namespace eina = efl::eina;
|
namespace eina = efl::eina;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename OutputIterator, typename Context>
|
template <typename OutputIterator, typename Context>
|
||||||
|
|
||||||
struct marshall_annotation_visitor_generate
|
struct marshall_annotation_visitor_generate
|
||||||
{
|
{
|
||||||
mutable OutputIterator sink;
|
mutable OutputIterator sink;
|
||||||
|
@ -41,7 +42,7 @@ struct marshall_annotation_visitor_generate
|
||||||
|
|
||||||
typedef marshall_type_visitor_generate<OutputIterator, Context> visitor_type;
|
typedef marshall_type_visitor_generate<OutputIterator, Context> visitor_type;
|
||||||
typedef bool result_type;
|
typedef bool result_type;
|
||||||
|
|
||||||
bool operator()(attributes::regular_type_def const& regular) const
|
bool operator()(attributes::regular_type_def const& regular) const
|
||||||
{
|
{
|
||||||
using attributes::regular_type_def;
|
using attributes::regular_type_def;
|
||||||
|
@ -60,7 +61,13 @@ struct marshall_annotation_visitor_generate
|
||||||
{"string", true, [] {
|
{"string", true, [] {
|
||||||
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringPassOwnershipMarshaler))";
|
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))";
|
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringKeepOwnershipMarshaler))";
|
||||||
}},
|
}},
|
||||||
{"mstring", true, [] {
|
{"mstring", true, [] {
|
||||||
|
@ -98,7 +105,13 @@ struct marshall_annotation_visitor_generate
|
||||||
{"string", true, [] {
|
{"string", true, [] {
|
||||||
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringPassOwnershipMarshaler))";
|
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))";
|
return "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(Efl.Eo.StringKeepOwnershipMarshaler))";
|
||||||
}},
|
}},
|
||||||
{"mstring", true, [] {
|
{"mstring", true, [] {
|
||||||
|
|
|
@ -37,7 +37,7 @@ struct compare_get_and_set_value_type
|
||||||
inline bool operator () (attributes::parameter_def const& get, attributes::parameter_def const& set) const;
|
inline bool operator () (attributes::parameter_def const& get, attributes::parameter_def const& set) const;
|
||||||
inline bool operator () (attributes::type_def const& get, attributes::type_def const& set) const;
|
inline bool operator () (attributes::type_def const& get, attributes::type_def const& set) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct compare_get_and_set_value_type_overload
|
struct compare_get_and_set_value_type_overload
|
||||||
{
|
{
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
|
@ -64,7 +64,7 @@ struct compare_get_and_set_value_type_overload
|
||||||
|
|
||||||
typedef bool result_type;
|
typedef bool result_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool compare_get_and_set_value_type::operator () (attributes::parameter_def const& get, attributes::parameter_def const& set) const
|
inline bool compare_get_and_set_value_type::operator () (attributes::parameter_def const& get, attributes::parameter_def const& set) const
|
||||||
{
|
{
|
||||||
return efl::eina::visit(compare_get_and_set_value_type_overload{}, get.type.original_type, set.type.original_type);
|
return efl::eina::visit(compare_get_and_set_value_type_overload{}, get.type.original_type, set.type.original_type);
|
||||||
|
@ -73,7 +73,7 @@ inline bool compare_get_and_set_value_type::operator () (attributes::type_def co
|
||||||
{
|
{
|
||||||
return efl::eina::visit(compare_get_and_set_value_type_overload{}, get.original_type, set.original_type);
|
return efl::eina::visit(compare_get_and_set_value_type_overload{}, get.original_type, set.original_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Context>
|
template <typename Context>
|
||||||
bool property_generate_wrapper_both_check(attributes::property_def const& property, Context const& context)
|
bool property_generate_wrapper_both_check(attributes::property_def const& property, Context const& context)
|
||||||
{
|
{
|
||||||
|
@ -87,7 +87,7 @@ bool property_generate_wrapper_both_check(attributes::property_def const& proper
|
||||||
|
|
||||||
if ((is_concrete || is_interface) && is_static)
|
if ((is_concrete || is_interface) && is_static)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!property.getter)
|
if (!property.getter)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ bool property_generate_wrapper_both_check(attributes::property_def const& proper
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ bool property_generate_wrapper_setter (attributes::property_def const& property,
|
||||||
|
|
||||||
if (property.setter->explicit_return_type != attributes::void_)
|
if (property.setter->explicit_return_type != attributes::void_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!property.setter->keys.empty())
|
if (!property.setter->keys.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -192,7 +192,9 @@ struct native_property_function_definition_generator
|
||||||
(marshall_annotation << " " << marshall_parameter)
|
(marshall_annotation << " " << marshall_parameter)
|
||||||
) % ", ")
|
) % ", ")
|
||||||
<< ");\n\n")
|
<< ");\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;
|
return false;
|
||||||
|
|
||||||
// API delegate is the wrapper for the Eo methods exported from C that we will use from C#.
|
// API delegate is the wrapper for the Eo methods exported from C that we will use from C#.
|
||||||
|
@ -279,7 +281,7 @@ struct native_property_function_definition_generator
|
||||||
|
|
||||||
if(!f.keys.empty() && !as_generator(lit("[(") << (native_argument_invocation % ", ") << ")]").generate (sink, f.keys, context))
|
if(!f.keys.empty() && !as_generator(lit("[(") << (native_argument_invocation % ", ") << ")]").generate (sink, f.keys, context))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!as_generator
|
if(!as_generator
|
||||||
(" = ("
|
(" = ("
|
||||||
<< (native_tuple_argument_invocation % ", ") << ");\n"
|
<< (native_tuple_argument_invocation % ", ") << ");\n"
|
||||||
|
@ -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.
|
// This is the delegate that will be passed to Eo to be called from C.
|
||||||
if(!as_generator(
|
if(!as_generator(
|
||||||
indent << "private static " << f.c_name << "_delegate " << f.c_name << "_static_delegate;\n\n"
|
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 false;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -377,10 +381,10 @@ template <>
|
||||||
struct is_generator< ::eolian_mono::native_property_function_definition_generator> : std::true_type {};
|
struct is_generator< ::eolian_mono::native_property_function_definition_generator> : std::true_type {};
|
||||||
|
|
||||||
namespace type_traits {
|
namespace type_traits {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct attributes_needed< ::eolian_mono::native_property_function_definition_generator> : std::integral_constant<int, 1> {};
|
struct attributes_needed< ::eolian_mono::native_property_function_definition_generator> : std::integral_constant<int, 1> {};
|
||||||
|
|
||||||
} } } }
|
} } } }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -55,6 +55,9 @@ internal static partial class NativeCustomExportFunctions
|
||||||
efl_mono_native_free_addr_get();
|
efl_mono_native_free_addr_get();
|
||||||
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
|
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
|
||||||
efl_mono_native_efl_unref_addr_get();
|
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.
|
/// <summary>Wrapper around native memory DllImport'd functions.
|
||||||
|
@ -94,6 +97,11 @@ public static class MemoryNative
|
||||||
return NativeCustomExportFunctions.efl_mono_native_strdup(str);
|
return NativeCustomExportFunctions.efl_mono_native_strdup(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IntPtr SlstrCopyNew(string str)
|
||||||
|
{
|
||||||
|
return NativeCustomExportFunctions.eina_slstr_copy_new(str);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves an instance of a string for use in program.
|
/// Retrieves an instance of a string for use in program.
|
||||||
/// <para>Since EFL 1.23.</para>
|
/// <para>Since EFL 1.23.</para>
|
||||||
|
|
|
@ -1508,6 +1508,45 @@ class StringPassOwnershipMarshaler : ICustomMarshaler
|
||||||
static private StringPassOwnershipMarshaler marshaler;
|
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
|
class StringKeepOwnershipMarshaler: ICustomMarshaler
|
||||||
{
|
{
|
||||||
public object MarshalNativeToManaged(IntPtr pNativeData)
|
public object MarshalNativeToManaged(IntPtr pNativeData)
|
||||||
|
|
Loading…
Reference in New Issue