From 2705ea853108a64edc9264c8e5d4c79e55aea001 Mon Sep 17 00:00:00 2001 From: Lauro Moura Date: Thu, 15 Mar 2018 20:32:39 -0300 Subject: [PATCH] csharp: Fix support for ptr(structs) In general, ptr(struct) parameters behavior depends whether the parameter has the @owned modifier or not. If there is no @owned parameter (meaning no transfer of ownership happens) and it is a "complex" struct, with reference type fields (like strings), the struct is converted to the respective Internal struct and passed with "ref" to the DllImport'd function. For @in parameters, after the function it returns, this intermediate struct is converted to the public struct type and assigned to the original parameter, updating it to the external world. When we have ownership transfers, the structure is copied to unmanaged memory and given to the callee. We can't send managed memory directly as the callee may try to free it. On the managed side, the original struct is left to be garbage collected normally. --- src/Makefile_Efl_Mono.am | 1 + .../eolian/mono/function_helpers.hh | 8 +- src/bin/eolian_mono/eolian/mono/helpers.hh | 24 ++ .../eolian_mono/eolian/mono/marshall_type.hh | 17 ++ .../eolian/mono/marshall_type_impl.hh | 4 +- src/bin/eolian_mono/eolian/mono/parameter.hh | 122 +++++++-- .../eolian/mono/struct_definition.hh | 27 +- src/tests/efl_mono/BasicDirection.cs | 34 +++ src/tests/efl_mono/Structs.cs | 244 ++++++++++++++++-- src/tests/efl_mono/libefl_mono_native_test.c | 128 +++++++-- src/tests/efl_mono/test_testing.eo | 154 +++++++---- 11 files changed, 640 insertions(+), 123 deletions(-) create mode 100644 src/tests/efl_mono/BasicDirection.cs diff --git a/src/Makefile_Efl_Mono.am b/src/Makefile_Efl_Mono.am index e442d09fb5..1f718ecc28 100644 --- a/src/Makefile_Efl_Mono.am +++ b/src/Makefile_Efl_Mono.am @@ -419,6 +419,7 @@ TESTS += tests/efl_mono/mono_test_driver.sh tests_efl_mono_efl_mono_SOURCES = \ tests/efl_mono/Main.cs \ + tests/efl_mono/BasicDirection.cs \ tests/efl_mono/Eina.cs \ tests/efl_mono/Eldbus.cs \ tests/efl_mono/Eo.cs \ diff --git a/src/bin/eolian_mono/eolian/mono/function_helpers.hh b/src/bin/eolian_mono/eolian/mono/function_helpers.hh index e7af6f9dc0..2b8c912d44 100644 --- a/src/bin/eolian_mono/eolian/mono/function_helpers.hh +++ b/src/bin/eolian_mono/eolian/mono/function_helpers.hh @@ -98,9 +98,11 @@ struct native_function_definition_epilogue_generator if (!as_generator( scope_tab << scope_tab << "//Assigning out variables\n" << *(scope_tab << scope_tab << native_convert_out_assign(*klass) << "\n") + << scope_tab << scope_tab << "//Placeholder in ptr variables that need to be updated\n" + << *(scope_tab << scope_tab << native_convert_in_ptr_assign << "\n") << scope_tab << scope_tab << "//Converting return variable\n" << scope_tab << scope_tab << native_convert_return(*klass) - ).generate(sink, std::make_tuple(f.parameters, f.return_type), context)) + ).generate(sink, std::make_tuple(f.parameters, f.parameters, f.return_type), context)) return false; return true; @@ -117,9 +119,11 @@ struct function_definition_epilogue_generator scope_tab << scope_tab << "eina.Error.RaiseIfOccurred();\n" << scope_tab << scope_tab << "//Assigning out variables\n" << *(scope_tab << scope_tab << convert_out_assign << "\n") + << scope_tab << scope_tab << "//Placeholder in ptr variables that need to be updated\n" + << *(scope_tab << scope_tab << convert_in_ptr_assign << "\n") << scope_tab << scope_tab << "//Converting return variable\n" << scope_tab << scope_tab << convert_return - ).generate(sink, std::make_tuple(f.parameters, f.return_type), context)) + ).generate(sink, std::make_tuple(f.parameters, f.parameters, f.return_type), context)) return false; return true; diff --git a/src/bin/eolian_mono/eolian/mono/helpers.hh b/src/bin/eolian_mono/eolian/mono/helpers.hh index e02fd05b70..d1bebeee14 100644 --- a/src/bin/eolian_mono/eolian/mono/helpers.hh +++ b/src/bin/eolian_mono/eolian/mono/helpers.hh @@ -53,6 +53,30 @@ inline bool need_struct_conversion(attributes::regular_type_def const* regular) return regular && regular->is_struct() && !is_struct_blacklisted(*regular); } +inline bool need_struct_conversion(attributes::parameter_def const& param, attributes::regular_type_def const* regular) +{ + if (param.direction == attributes::parameter_direction::in && param.type.has_own) + return false; + + return need_struct_conversion(regular); +} + +inline bool need_struct_conversion_in_return(attributes::type_def const& ret_type, attributes::parameter_direction const& direction) +{ + auto regular = efl::eina::get(&ret_type.original_type); + + if (!regular->is_struct()) + return false; + + if (regular->is_struct() && (direction == attributes::parameter_direction::out || direction == attributes::parameter_direction::unknown)) + return false; + + if (ret_type.has_own) + return false; + + return true; +} + inline bool need_pointer_conversion(attributes::regular_type_def const* regular) { if (!regular) diff --git a/src/bin/eolian_mono/eolian/mono/marshall_type.hh b/src/bin/eolian_mono/eolian/mono/marshall_type.hh index a946e15ea8..79ce8b6380 100644 --- a/src/bin/eolian_mono/eolian/mono/marshall_type.hh +++ b/src/bin/eolian_mono/eolian/mono/marshall_type.hh @@ -16,6 +16,16 @@ struct marshall_annotation_visitor_generate; template struct marshall_native_annotation_visitor_generate; } + +/* + * Converts a given type/parameter to the type used in the DllImport signatures. + * + * For example, Eina.Value can be marshaled either as an eina.Value instance through + * CustomMarshallers if we have a ptr(Eina.Value) or through the intermediate + * eina.Value_Native blittable struct if it is passed by value. + * + * For details, check marshall_type_impl.h with the actual conversion rules. + */ struct marshall_type_generator { marshall_type_generator(bool is_return = false) @@ -26,6 +36,7 @@ struct marshall_type_generator { return type.original_type.visit(detail::marshall_type_visitor_generate{sink, &context, type.c_type, false, is_return, type.is_ptr }); } + /* Some types may require a different conversion when they are in @out parameters. */ template bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const { @@ -36,6 +47,12 @@ struct marshall_type_generator bool is_return; }; +/* + * Generates the "[MarshalAs(...)]" rules for the given type. + * + * For example, the CustomMarshallers definitions for String and eina.Values and the + * boolean size defintion (Eina_Value is 1 byte, while C# bool has 4 bytes). + */ struct marshall_annotation_generator { marshall_annotation_generator(bool is_return = false) diff --git a/src/bin/eolian_mono/eolian/mono/marshall_type_impl.hh b/src/bin/eolian_mono/eolian/mono/marshall_type_impl.hh index 89efc7f3e2..61a53745ca 100644 --- a/src/bin/eolian_mono/eolian/mono/marshall_type_impl.hh +++ b/src/bin/eolian_mono/eolian/mono/marshall_type_impl.hh @@ -149,8 +149,10 @@ struct marshall_type_visitor_generate }} }; - if (!is_ptr && regular.is_struct() && !is_struct_blacklisted(regular)) + if (regular.is_struct() && !is_struct_blacklisted(regular) && !(bool)(regular.base_qualifier & qualifier_info::is_own)) { + if ((is_out || is_return) && is_ptr) + return as_generator(" System.IntPtr").generate(sink, attributes::unused, *context); return as_generator(*(lower_case[string] << ".") << string << "_StructInternal") .generate(sink, std::make_tuple(eolian_mono::escape_namespace(regular.namespaces), regular.base_type), *context); } diff --git a/src/bin/eolian_mono/eolian/mono/parameter.hh b/src/bin/eolian_mono/eolian/mono/parameter.hh index d836124fe0..0487ee22b3 100644 --- a/src/bin/eolian_mono/eolian/mono/parameter.hh +++ b/src/bin/eolian_mono/eolian/mono/parameter.hh @@ -22,6 +22,8 @@ namespace eolian_mono { struct native_convert_out_variable_generator; struct convert_out_variable_generator; struct convert_out_assign_generator; + struct native_convert_in_ptr_assign_generator; + struct convert_in_ptr_assign_generator; struct native_convert_out_assign_parameterized; struct native_convert_out_assign_generator; struct convert_return_generator; @@ -135,6 +137,26 @@ template <> struct attributes_needed< ::eolian_mono::convert_out_assign_generator> : std::integral_constant {}; } +template <> +struct is_eager_generator< ::eolian_mono::native_convert_in_ptr_assign_generator> : std::true_type {}; +template <> +struct is_generator< ::eolian_mono::native_convert_in_ptr_assign_generator> : std::true_type {}; + +namespace type_traits { +template <> +struct attributes_needed< ::eolian_mono::native_convert_in_ptr_assign_generator> : std::integral_constant {}; +} + +template <> +struct is_eager_generator< ::eolian_mono::convert_in_ptr_assign_generator> : std::true_type {}; +template <> +struct is_generator< ::eolian_mono::convert_in_ptr_assign_generator> : std::true_type {}; + +namespace type_traits { +template <> +struct attributes_needed< ::eolian_mono::convert_in_ptr_assign_generator> : std::integral_constant {}; +} + template <> struct is_eager_generator< ::eolian_mono::convert_return_variable_generator> : std::true_type {}; template <> @@ -341,16 +363,31 @@ inline std::string direction_modifier(attributes::parameter_def const& param) } else if (param.direction != attributes::parameter_direction::in) { - auto regular = efl::eina::get(¶m.type.original_type); - if (param.type.c_type == "Eina_Slice" || param.type.c_type == "Eina_Rw_Slice" - || need_struct_conversion(regular)) + if (param.type.c_type == "Eina_Slice" || param.type.c_type == "Eina_Rw_Slice") return " ref "; else return " out "; } + else if (param.direction == attributes::parameter_direction::in && param.type.is_ptr) + { + auto regular = efl::eina::get(¶m.type.original_type); + if (need_struct_conversion(regular)) + return " ref "; // Don't add ref on Marshal if it is ptr + } return " "; } +std::string marshall_direction_modifier(attributes::parameter_def const& param) +{ + if (param.direction == attributes::parameter_direction::in && param.type.is_ptr) + { + auto regular = efl::eina::get(¶m.type.original_type); + if (need_struct_conversion(regular) && param.type.has_own) + return " "; // Don't add ref on Marshal if it is ptr + } + return direction_modifier(param); +} + struct is_fp_visitor { typedef is_fp_visitor visitor_type; @@ -412,7 +449,7 @@ struct marshall_parameter_generator if (!param.type.original_type.visit(is_fp_visitor{})) return as_generator( - direction_modifier(param) << marshall_type << " " << string + marshall_direction_modifier(param) << marshall_type << " " << string ).generate(sink, std::make_tuple(param, param_name), context); return as_generator( @@ -421,14 +458,15 @@ struct marshall_parameter_generator ).generate(sink, param, context); } } const marshall_parameter {}; - + +// FIXME This seems to be used only in the else branch of the native function definition. Is it really needed? struct argument_generator { template bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const { std::string param_name = escape_keyword(param.param_name); - std::string direction = direction_modifier(param); + std::string direction = marshall_direction_modifier(param); if (!param.type.original_type.visit(is_fp_visitor{})) return as_generator( @@ -475,7 +513,7 @@ struct argument_invocation_generator template bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const { - std::string arg = direction_modifier(param); + std::string arg = marshall_direction_modifier(param); if (use_conversion_vars && param_should_use_out_var(param, false)) arg += out_variable_name(param.param_name); @@ -507,7 +545,7 @@ struct native_convert_in_variable_generator return true; auto regular = efl::eina::get(¶m.type.original_type); - if (param.type.is_ptr && need_pointer_conversion(regular)) + if (param.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion(param, regular)) { return as_generator( "var " << string << " = eina.PrimitiveConversion.PointerToManaged<" << type << ">(" << escape_keyword(param.param_name) << ");\n" @@ -575,7 +613,7 @@ struct convert_in_variable_generator return true; auto regular = efl::eina::get(¶m.type.original_type); - if (param.type.is_ptr && need_pointer_conversion(regular)) + if (param.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion(param, regular)) { return as_generator( "var " << string << " = eina.PrimitiveConversion.ManagedToPointerAlloc(" << escape_keyword(param.param_name) << ");\n" @@ -659,7 +697,8 @@ struct convert_in_variable_generator } } const convert_in_variable {}; - + +/* Some types require an intermediate variable to be filled as out parameter in the marshalled function */ struct convert_out_variable_generator { template @@ -669,7 +708,7 @@ struct convert_out_variable_generator return true; auto regular = efl::eina::get(¶m.type.original_type); - if (param.type.is_ptr && need_pointer_conversion(regular)) + if (param.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion(regular)) { return as_generator( "System.IntPtr " << string << " = System.IntPtr.Zero;\n" @@ -679,7 +718,7 @@ struct convert_out_variable_generator { return as_generator( "var " << string << " = new " << marshall_type << "();\n" - ).generate(sink, std::make_tuple(out_variable_name(param.param_name), param.type), context); + ).generate(sink, std::make_tuple(out_variable_name(param.param_name), param), context); } else if (param_is_acceptable(param, "Eina_Binbuf *", WANT_OWN, WANT_OUT) || param_is_acceptable(param, "Eina_Binbuf *", !WANT_OWN, WANT_OUT) @@ -742,7 +781,7 @@ struct native_convert_out_variable_generator return true; auto regular = efl::eina::get(¶m.type.original_type); - if (param.type.is_ptr && need_pointer_conversion(regular)) + if (param.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion(regular)) { return as_generator( type << " " << string << " = default(" << type << ");\n" @@ -819,6 +858,7 @@ struct native_convert_out_variable_generator } const native_convert_out_variable {}; +/* Assign the Managed out variables from the marshalled intermediate ones if needed. */ struct convert_out_assign_generator { template @@ -828,11 +868,17 @@ struct convert_out_assign_generator return true; auto regular = efl::eina::get(¶m.type.original_type); - if (param.type.is_ptr && need_pointer_conversion(regular)) + if (param.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion_in_return(param.type, param.direction)) { - return as_generator( + bool ret = as_generator( string << " = eina.PrimitiveConversion.PointerToManaged<" << type << ">(" << out_variable_name(param.param_name) << ");\n" ).generate(sink, std::make_tuple(escape_keyword(param.param_name), param.type), context); + + if (param.type.has_own) + ret = ret && as_generator(scope_tab << scope_tab << "Marshal.FreeHGlobal(" << out_variable_name(param.param_name) << ");\n" + ).generate(sink, attributes::unused, context); + + return ret; } else if (need_struct_conversion(regular)) { @@ -911,6 +957,40 @@ struct convert_out_assign_generator } const convert_out_assign {}; +struct native_convert_in_ptr_assign_generator +{ + template + bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const + { + auto regular = efl::eina::get(¶m.type.original_type); + if (param_should_use_in_var(param, true) && param.type.is_ptr && !param.type.has_own && need_struct_conversion(regular)) + { + return as_generator( + string << " = " << type << "_StructConvertion.ToInternal(" << in_variable_name(param.param_name) << ");\n" + ).generate(sink, std::make_tuple(escape_keyword(param.param_name), param.type), context); + } + + return true; + } +} const native_convert_in_ptr_assign {}; + +struct convert_in_ptr_assign_generator +{ + template + bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const + { + auto regular = efl::eina::get(¶m.type.original_type); + if (param_should_use_in_var(param, true) && param.type.is_ptr && !param.type.has_own && need_struct_conversion(regular)) + { + return as_generator( + string << " = " << type << "_StructConvertion.ToExternal(" << in_variable_name(param.param_name) << ");\n" + ).generate(sink, std::make_tuple(escape_keyword(param.param_name), param.type), context); + } + + return true; + } +} const convert_in_ptr_assign {}; + struct convert_return_variable_generator { template @@ -923,17 +1003,19 @@ struct convert_return_variable_generator } const convert_return_variable {}; - +/* Converts the intermediate return variable to the proper API type */ struct convert_return_generator { template bool generate(OutputIterator sink, attributes::type_def const& ret_type, Context const& context) const { auto regular = efl::eina::get(&ret_type.original_type); - if (ret_type.is_ptr && need_pointer_conversion(regular)) + if (ret_type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion_in_return(ret_type, attributes::parameter_direction::unknown)) { return as_generator( - "return eina.PrimitiveConversion.PointerToManaged<" << type << ">(_ret_var);\n" + "var __ret_tmp = eina.PrimitiveConversion.PointerToManaged<" << type << ">(_ret_var);\n" + << scope_tab << scope_tab << (ret_type.has_own ? ("Marshal.FreeHGlobal(_ret_var);\n"): "\n") + << scope_tab << scope_tab << "return __ret_tmp;\n" ).generate(sink, ret_type, context); } else if (need_struct_conversion(regular)) @@ -998,7 +1080,7 @@ struct native_convert_out_assign_generator return true; auto regular = efl::eina::get(¶m.type.original_type); - if (param.type.is_ptr && need_pointer_conversion(regular)) + if (param.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion_in_return(param.type, param.direction)) { return as_generator( string << " = eina.PrimitiveConversion.ManagedToPointerAlloc(" << string << ");\n" @@ -1131,7 +1213,7 @@ struct native_convert_return_generator bool generate(OutputIterator sink, attributes::type_def const& ret_type, Context const& context) const { auto regular = efl::eina::get(&ret_type.original_type); - if (ret_type.is_ptr && need_pointer_conversion(regular)) + if (ret_type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion_in_return(ret_type, attributes::parameter_direction::unknown) ) { return as_generator( "return eina.PrimitiveConversion.ManagedToPointerAlloc(_ret_var);\n" diff --git a/src/bin/eolian_mono/eolian/mono/struct_definition.hh b/src/bin/eolian_mono/eolian/mono/struct_definition.hh index a0ff03315e..0b45da95b2 100644 --- a/src/bin/eolian_mono/eolian/mono/struct_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/struct_definition.hh @@ -94,10 +94,11 @@ struct struct_internal_definition_generator { if (!as_generator ( + "///Internal wrapper for struct " << string << ".\n" "[StructLayout(LayoutKind.Sequential)]\n" - "internal struct " << string << "\n{\n" + "public struct " << string << "\n{\n" ) - .generate(sink, binding_struct_internal_name(struct_), context)) + .generate(sink, std::make_tuple<>(binding_struct_name(struct_), binding_struct_internal_name(struct_)), context)) return false; // iterate struct fields @@ -132,6 +133,24 @@ struct struct_internal_definition_generator return false; } + auto external_name = binding_struct_name(struct_); + auto internal_name = binding_struct_internal_name(struct_); + + if(!as_generator( + scope_tab << "///Implicit conversion to the internal/marshalling representation.\n" + << scope_tab << "public static implicit operator " << string << "(" << string << " struct_)\n" + << scope_tab << "{\n" + << scope_tab << scope_tab << "return " << string << "_StructConvertion.ToExternal(struct_);\n" + << scope_tab << "}\n" + << scope_tab << "///Implicit conversion to the managed representation.\n" + << scope_tab << "public static implicit operator " << string << "(" << string << " struct_)\n" + << scope_tab << "{\n" + << scope_tab << scope_tab << "return " << string << "_StructConvertion.ToInternal(struct_);\n" + << scope_tab << "}\n" + ).generate(sink, std::make_tuple(external_name, internal_name, external_name, + internal_name, external_name, external_name), context)) + return false; + if(!as_generator("}\n").generate(sink, attributes::unused, context)) return false; return true; @@ -172,7 +191,7 @@ struct to_internal_field_convert_generator .generate(sink, std::make_tuple(field_name, field_name), context)) return false; } - else if (field.type.is_ptr && need_pointer_conversion(regular)) + else if (field.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion(regular)) { if (!as_generator( scope_tab << scope_tab << "_internal_struct." << string << " = eina.PrimitiveConversion.ManagedToPointerAlloc(_external_struct." << string << ");\n") @@ -282,7 +301,7 @@ struct to_external_field_convert_generator .generate(sink, std::make_tuple(field_name, field.type, field_name), context)) return false; } - else if (field.type.is_ptr && need_pointer_conversion(regular)) + else if (field.type.is_ptr && need_pointer_conversion(regular) && !need_struct_conversion(regular)) { if (!as_generator( scope_tab << scope_tab << "_external_struct." << string << " = eina.PrimitiveConversion.PointerToManaged<" << type << ">(_internal_struct." << string << ");\n") diff --git a/src/tests/efl_mono/BasicDirection.cs b/src/tests/efl_mono/BasicDirection.cs new file mode 100644 index 0000000000..cf7634d09e --- /dev/null +++ b/src/tests/efl_mono/BasicDirection.cs @@ -0,0 +1,34 @@ +using System; +using System.Runtime.InteropServices; +using System.Linq; + +namespace TestSuite +{ + +class TestIntDirections +{ + public static void simple_out() + { + int original = 1984; + int received; + test.Testing t = new test.TestingConcrete(); + + t.IntOut(original, out received); + + Test.AssertEquals(-original, received); + } + + public static void simple_ptr_out() + { + int original = 1984; + int received; + test.Testing t = new test.TestingConcrete(); + + t.IntPtrOut(original, out received); + + Test.AssertEquals(original*2, received); + } +} + +} + diff --git a/src/tests/efl_mono/Structs.cs b/src/tests/efl_mono/Structs.cs index 07917d0c56..20707c141b 100644 --- a/src/tests/efl_mono/Structs.cs +++ b/src/tests/efl_mono/Structs.cs @@ -266,38 +266,54 @@ class TestStructs Test.Assert(r, "Function returned false"); } - // public static void simple_ptr_in() - // { - // var simple = structSimpleWithValues(); - // test.Testing t = new test.TestingConcrete(); - // bool r = t.struct_simple_ptr_in(simple); - // Test.Assert(r, "Function returned false"); - // } + public static void simple_ptr_in() + { + var simple = structSimpleWithValues(); + int original = simple.Fint; + simple.Fstring = "Struct Ptr In"; + test.Testing t = new test.TestingConcrete(); + Test.Assert(t.StructSimplePtrIn(ref simple)); + Test.AssertEquals(-original, simple.Fint); + Test.AssertEquals("nI rtP tcurtS", simple.Fstring); + } - // public static void simple_ptr_in_own() - // { - // var simple = structSimpleWithValues(); - // test.Testing t = new test.TestingConcrete(); - // bool r = t.struct_simple_ptr_in_own(simple); - // Test.Assert(r, "Function returned false"); - // } + public static void simple_ptr_in_own() + { + var simple = structSimpleWithValues(); + int original = simple.Fint; + simple.Fstring = "Struct Ptr In Own"; + test.Testing t = new test.TestingConcrete(); + test.StructSimple result = t.StructSimplePtrInOwn(ref simple); + Test.AssertEquals(-original, result.Fint); + Test.AssertEquals("nwO nI rtP tcurtS", result.Fstring); + } public static void simple_out() { var simple = new test.StructSimple(); test.Testing t = new test.TestingConcrete(); - bool r = t.StructSimpleOut(ref simple); + bool r = t.StructSimpleOut(out simple); Test.Assert(r, "Function returned false"); checkStructSimple(simple); } - // public static void simple_ptr_out() - // { - // } + public static void simple_ptr_out() + { + test.StructSimple simple; + test.Testing t = new test.TestingConcrete(); + test.StructSimple result = t.StructSimplePtrOut(out simple); + Test.AssertEquals(result.Fint, simple.Fint); + Test.AssertEquals(result.Fstring, simple.Fstring); + } - // public static void simple_ptr_out_own() - // { - // } + public static void simple_ptr_out_own() + { + test.StructSimple simple; + test.Testing t = new test.TestingConcrete(); + test.StructSimple result = t.StructSimplePtrOutOwn(out simple); + Test.AssertEquals(result.Fint, simple.Fint); + Test.AssertEquals(simple.Fstring, "Ptr Out Own"); + } public static void simple_return() { @@ -306,14 +322,186 @@ class TestStructs checkStructSimple(simple); } - // public static void simple_ptr_return() - // { - // } + public static void simple_ptr_return() + { + test.Testing t = new test.TestingConcrete(); + var simple = t.StructSimplePtrReturn(); + Test.AssertEquals(simple.Fstring, "Ret Ptr"); + } - // public static void simple_ptr_return_own() - // { - // } + public static void simple_ptr_return_own() + { + test.Testing t = new test.TestingConcrete(); + var simple = t.StructSimplePtrReturnOwn(); + Test.AssertEquals(simple.Fstring, "Ret Ptr Own"); + } + public class StructReturner : test.TestingInherit + { + public test.StructSimple received; + public bool called; + + public StructReturner() : base(null) + { + called = false; + received = default(test.StructSimple); + } + + public override bool StructSimpleIn(test.StructSimple simple) + { + called = true; + received = simple; + + return true; + } + + public override bool StructSimplePtrIn(ref test.StructSimple simple) + { + called = true; + simple.Fstring = "Virtual Struct Ptr In"; + return true; + } + + public override test.StructSimple StructSimplePtrInOwn(ref test.StructSimple simple) + { + called = true; + received = simple; + return received; + } + + public override bool StructSimpleOut(out test.StructSimple simple) { + called = true; + simple = new test.StructSimple(); + simple.Fstring = "Virtual Struct Out"; + return true; + } + + public override test.StructSimple StructSimplePtrOut(out test.StructSimple simple) { + called = true; + // No way to explicitly define the ownership of the parameter. + simple = new test.StructSimple(); + simple.Fstring = "Virtual Struct Ptr Out"; + return simple; + } + + public override test.StructSimple StructSimplePtrOutOwn(out test.StructSimple simple) { + called = true; + // No way to explicitly define the ownership of the parameter. + simple = new test.StructSimple(); + simple.Fstring = "Virtual Struct Ptr Out Own"; + return simple; + } + + public override test.StructSimple StructSimpleReturn() + { + called = true; + var simple = new test.StructSimple(); + simple.Fstring = "Virtual Struct Return"; + return simple; + } + + public override test.StructSimple StructSimplePtrReturn() + { + called = true; + var simple = new test.StructSimple(); + simple.Fstring = "Virtual Struct Ptr Return"; + return simple; + } + + public override test.StructSimple StructSimplePtrReturnOwn() + { + called = true; + var simple = new test.StructSimple(); + simple.Fstring = "Virtual Struct Ptr Return Own"; + return simple; + } + } + + public static void simple_in_virtual() + { + StructReturner t = new StructReturner(); + var simple = structSimpleWithValues(); + simple.Fstring = "Virtual Struct In"; + + t.CallStructSimpleIn(simple); + Test.Assert(t.called); + Test.AssertEquals(simple.Fstring, t.received.Fstring); + } + + public static void simple_ptr_in_virtual() + { + StructReturner t = new StructReturner(); + var simple = structSimpleWithValues(); + string reference = "Virtual Struct Ptr In"; + + t.CallStructSimplePtrIn(ref simple); + Test.Assert(t.called); + Test.AssertEquals(simple.Fstring, reference); + } + + public static void simple_ptr_in_own_virtual() + { + StructReturner t = new StructReturner(); + var simple = structSimpleWithValues(); + simple.Fstring = "Virtual Struct Ptr In Own"; + + t.CallStructSimplePtrInOwn(ref simple); + Test.Assert(t.called); + Test.AssertEquals(t.received.Fstring, simple.Fstring); + } + + public static void simple_out_virtual() + { + StructReturner t = new StructReturner(); + test.StructSimple simple; + t.CallStructSimpleOut(out simple); + Test.Assert(t.called, "override was not called"); + Test.AssertEquals("Virtual Struct Out", simple.Fstring); + } + + public static void simple_ptr_out_virtual() + { + StructReturner t = new StructReturner(); + test.StructSimple simple; + t.CallStructSimplePtrOut(out simple); + Test.Assert(t.called, "override was not called"); + Test.AssertEquals("Virtual Struct Ptr Out", simple.Fstring); + } + + public static void simple_ptr_out_own_virtual() + { + StructReturner t = new StructReturner(); + test.StructSimple simple; + t.CallStructSimplePtrOutOwn(out simple); + Test.Assert(t.called, "override was not called"); + Test.AssertEquals("Virtual Struct Ptr Out Own", simple.Fstring); + } + + public static void simple_return_virtual() + { + StructReturner t = new StructReturner(); + test.StructSimple simple = t.CallStructSimpleReturn(); + Test.Assert(t.called, "override was not called"); + Test.AssertEquals("Virtual Struct Return", simple.Fstring); + } + + public static void simple_ptr_return_virtual() + { + StructReturner t = new StructReturner(); + test.StructSimple simple = t.CallStructSimplePtrReturn(); + Test.Assert(t.called, "override was not called"); + Test.AssertEquals("Virtual Struct Ptr Return", simple.Fstring); + } + + public static void simple_ptr_return_own_virtual() + { + StructReturner t = new StructReturner(); + test.StructSimple simple = t.CallStructSimplePtrReturnOwn(); + Test.Assert(t.called, "override was not called"); + Test.AssertEquals("Virtual Struct Ptr Return Own", simple.Fstring); + } + + // Complex Structs public static void complex_in() { var complex = structComplexWithValues(); @@ -334,7 +522,7 @@ class TestStructs { var complex = new test.StructComplex(); test.Testing t = new test.TestingConcrete(); - bool r = t.StructComplexOut(ref complex); + bool r = t.StructComplexOut(out complex); Test.Assert(r, "Function returned false"); checkStructComplex(complex); } diff --git a/src/tests/efl_mono/libefl_mono_native_test.c b/src/tests/efl_mono/libefl_mono_native_test.c index f96666380b..04fd12053f 100644 --- a/src/tests/efl_mono/libefl_mono_native_test.c +++ b/src/tests/efl_mono/libefl_mono_native_test.c @@ -44,7 +44,8 @@ typedef struct Test_Testing_Data Eina_Free_Cb free_cb; Eina_Error error_code; Eina_Value *stored_value; - + Test_StructSimple stored_struct; + int stored_int; } Test_Testing_Data; typedef struct Test_Numberwrapper_Data @@ -100,6 +101,17 @@ Efl_Object *_test_testing_return_object(Eo *obj, EINA_UNUSED Test_Testing_Data * return obj; } +void _test_testing_int_out(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, int x, int *y) +{ + *y = -x; +} + +void _test_testing_int_ptr_out(EINA_UNUSED Eo *obj, Test_Testing_Data *pd, int x, int **y) +{ + pd->stored_int = x * 2; + *y = &pd->stored_int; +} + const char *_test_testing_in_string(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, const char *str) { const char *ret = malloc(sizeof(char)*(strlen(str) + 1)); @@ -3342,20 +3354,34 @@ Eina_Bool _test_testing_struct_simple_in(EINA_UNUSED Eo *obj, EINA_UNUSED Test_T return check_and_modify_struct_simple(&simple); } -EOLIAN -Eina_Bool _test_testing_struct_simple_ptr_in(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple *simple) +static void _reverse_string(char *str) { - (void) simple; - EINA_LOG_ERR("Not implemented!"); - return EINA_FALSE; + int len = strlen(str); + if (len > 0) { + for (int i=0, k=len-1; i < len/2; i++, k--) { + char tmp = str[k]; + str[k] = str[i]; + str[i] = tmp; + } + } } EOLIAN -Eina_Bool _test_testing_struct_simple_ptr_in_own(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple *simple) +Eina_Bool _test_testing_struct_simple_ptr_in(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple *simple) { - (void) simple; - EINA_LOG_ERR("Not implemented!"); - return EINA_FALSE; + simple->fint = -simple->fint; + _reverse_string(simple->fstring); + return EINA_TRUE; +} + +EOLIAN +Test_StructSimple _test_testing_struct_simple_ptr_in_own(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple *simple) +{ + Test_StructSimple ret = *simple; + free(simple); + ret.fint = -ret.fint; + _reverse_string(ret.fstring); + return ret; } EOLIAN @@ -3373,19 +3399,20 @@ Eina_Bool _test_testing_struct_simple_out(EINA_UNUSED Eo *obj, EINA_UNUSED Test_ } EOLIAN -Eina_Bool _test_testing_struct_simple_ptr_out(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple **simple) +Test_StructSimple _test_testing_struct_simple_ptr_out(EINA_UNUSED Eo *obj, Test_Testing_Data *pd, Test_StructSimple **simple) { - (void) simple; - EINA_LOG_ERR("Not implemented!"); - return EINA_FALSE; + struct_simple_with_values(&pd->stored_struct); + *simple = &pd->stored_struct; + return **simple; } EOLIAN -Eina_Bool _test_testing_struct_simple_ptr_out_own(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple **simple) +Test_StructSimple _test_testing_struct_simple_ptr_out_own(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple **simple) { - (void) simple; - EINA_LOG_ERR("Not implemented!"); - return EINA_FALSE; + *simple = malloc(sizeof(Test_StructSimple)); + struct_simple_with_values(*simple); + (*simple)->fstring = "Ptr Out Own"; + return **simple; } EOLIAN @@ -3399,15 +3426,72 @@ Test_StructSimple _test_testing_struct_simple_return(EINA_UNUSED Eo *obj, EINA_U EOLIAN Test_StructSimple *_test_testing_struct_simple_ptr_return(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd) { - EINA_LOG_ERR("Not implemented!"); - return NULL; + struct_simple_with_values(&pd->stored_struct); + pd->stored_struct.fstring = "Ret Ptr"; + return &pd->stored_struct; } EOLIAN Test_StructSimple *_test_testing_struct_simple_ptr_return_own(EINA_UNUSED Eo *obj, EINA_UNUSED Test_Testing_Data *pd) { - EINA_LOG_ERR("Not implemented!"); - return NULL; + Test_StructSimple *ret = malloc(sizeof(Test_StructSimple)); + struct_simple_with_values(ret); + ret->fstring = "Ret Ptr Own"; + return ret; +} + +EOLIAN +void _test_testing_call_struct_simple_in(Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple simple) +{ + test_testing_struct_simple_in(obj, simple); +} + +EOLIAN +void _test_testing_call_struct_simple_ptr_in(Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple *simple) +{ + test_testing_struct_simple_ptr_in(obj, simple); +} + +EOLIAN +void _test_testing_call_struct_simple_ptr_in_own(Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple *simple) +{ + test_testing_struct_simple_ptr_in_own(obj, simple); +} + +EOLIAN +void _test_testing_call_struct_simple_out(Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple *simple) +{ + test_testing_struct_simple_out(obj, simple); +} + +EOLIAN +void _test_testing_call_struct_simple_ptr_out(Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple **simple) +{ + test_testing_struct_simple_ptr_out(obj, simple); +} + +EOLIAN +void _test_testing_call_struct_simple_ptr_out_own(Eo *obj, EINA_UNUSED Test_Testing_Data *pd, Test_StructSimple **simple) +{ + test_testing_struct_simple_ptr_out_own(obj, simple); +} + +EOLIAN +Test_StructSimple _test_testing_call_struct_simple_return(Eo *obj, EINA_UNUSED Test_Testing_Data *pd) +{ + return test_testing_struct_simple_return(obj); +} + +EOLIAN +Test_StructSimple *_test_testing_call_struct_simple_ptr_return(Eo *obj, EINA_UNUSED Test_Testing_Data *pd) +{ + return test_testing_struct_simple_ptr_return(obj); +} + +EOLIAN +Test_StructSimple *_test_testing_call_struct_simple_ptr_return_own(Eo *obj, EINA_UNUSED Test_Testing_Data *pd) +{ + return test_testing_struct_simple_ptr_return_own(obj); } // with complex types diff --git a/src/tests/efl_mono/test_testing.eo b/src/tests/efl_mono/test_testing.eo index a65b4db8da..c2d3edcd2e 100644 --- a/src/tests/efl_mono/test_testing.eo +++ b/src/tests/efl_mono/test_testing.eo @@ -87,6 +87,20 @@ class Test.Testing (Efl.Object) { return: Test.Testing; } + int_out { + params { + @in x: int; + @out y: int; + } + } + + int_ptr_out { + params { + @in x: int; + @out y: ptr(int); + } + } + in_stringshare { params { @in v: stringshare; @@ -1355,19 +1369,19 @@ class Test.Testing (Efl.Object) { return: bool; } - // struct_simple_ptr_in { - // params { - // @in simple: ptr(Test.StructSimple); - // } - // return: bool; - // } - // - // struct_simple_ptr_in_own { - // params { - // @in simple: ptr(Test.StructSimple) @owned; - // } - // return: bool; - // } + struct_simple_ptr_in { + params { + @in simple: ptr(Test.StructSimple); + } + return: bool; + } + + struct_simple_ptr_in_own { + params { + @in simple: ptr(Test.StructSimple) @owned; + } + return: Test.StructSimple; + } struct_simple_out { params { @@ -1376,31 +1390,79 @@ class Test.Testing (Efl.Object) { return: bool; } - // struct_simple_ptr_out { - // params { - // @out simple: ptr(Test.StructSimple); - // } - // return: bool; - // } - // - // struct_simple_ptr_out_own { - // params { - // @out simple: ptr(Test.StructSimple) @owned; - // } - // return: bool; - // } + struct_simple_ptr_out { + params { + @out simple: ptr(Test.StructSimple); + } + return: Test.StructSimple; + } + + struct_simple_ptr_out_own { + params { + @out simple: ptr(Test.StructSimple) @owned; + } + return: Test.StructSimple; + } struct_simple_return { return: Test.StructSimple; } - // struct_simple_ptr_return { - // return: ptr(Test.StructSimple); - // } - // - // struct_simple_ptr_return_own { - // return: ptr(Test.StructSimple) @owned; - // } + struct_simple_ptr_return { + return: ptr(Test.StructSimple); + } + + struct_simple_ptr_return_own { + return: ptr(Test.StructSimple) @owned; + } + + call_struct_simple_in { + params { + @in simple: Test.StructSimple; + } + } + + call_struct_simple_ptr_in { + params { + @in simple: ptr(Test.StructSimple); + } + } + + call_struct_simple_ptr_in_own { + params { + @in simple: ptr(Test.StructSimple) @owned; + } + } + + call_struct_simple_out { + params { + @out simple: Test.StructSimple; + } + } + + call_struct_simple_ptr_out { + params { + @out simple: ptr(Test.StructSimple); + } + } + + call_struct_simple_ptr_out_own { + params { + @out simple: ptr(Test.StructSimple) @owned; + } + } + + call_struct_simple_return { + return: Test.StructSimple; + } + + call_struct_simple_ptr_return { + return: ptr(Test.StructSimple); + } + + call_struct_simple_ptr_return_own { + return: ptr(Test.StructSimple) @owned; + } struct_complex_in { params { @@ -1409,19 +1471,19 @@ class Test.Testing (Efl.Object) { return: bool; } - // struct_complex_ptr_in { - // params { - // @in complex: ptr(Test.StructComplex); - // } - // return: bool; - // } - // - // struct_complex_ptr_in_own { - // params { - // @in complex: ptr(Test.StructComplex) @owned; - // } - // return: bool; - // } + struct_complex_ptr_in { + params { + @in complex: ptr(Test.StructComplex); + } + return: bool; + } + + struct_complex_ptr_in_own { + params { + @in complex: ptr(Test.StructComplex) @owned; + } + return: bool; + } struct_complex_out { params {