Compare commits

...

9 Commits

Author SHA1 Message Date
Lauro Moura 3875c3d949 csharp: WIP - Avoid string leaks
These dicts will tie the lifetime of the native strings to the lifetime
of the C# wrapper they are used with.

PS: What about strings in struct fields?
2019-12-04 10:30:38 -03:00
Lauro Moura 57a1b3a63e csharp: WIP iterate test to check for leaks 2019-12-04 10:23:39 -03:00
Lauro Moura 786bb1fe3b csharp: Move collection conversions to a new file 2019-12-03 18:07:05 -03:00
Lauro Moura 3cbe4d3e7c csharp: Add ownership info to AccessorToIEnumerable 2019-12-03 18:07:05 -03:00
Lauro Moura 59698f0e22 csharp: Fix passing acessor with ownership
When passing an owned acessor from a converted collection we need a way
to unpin the passed data when the accessor is freed.

This commits adds a thin wrapper around the CArray accessor that unpins
the data when freed.
2019-12-03 18:07:05 -03:00
Lauro Moura b3e181670e csharp: Add accessor test receiving a C# collection 2019-12-03 18:07:05 -03:00
Lauro Moura ed6679db19 csharp: add move information to EnumerableToAccessor
Still need to fix the converted accessor ownership, maybe by creating a
custom accessor class that released the pinned memory when is freed.
2019-12-03 18:07:05 -03:00
Yeongjong Lee c9ca2833b8 eina_mono: replace Accessor, iterator with IEnumerable 2019-12-03 18:07:05 -03:00
Lauro Moura cae22a6bd5 csharp: Replace accessor tests with IEnumerable. 2019-12-03 18:07:05 -03:00
18 changed files with 535 additions and 144 deletions

View File

@ -129,9 +129,14 @@ struct unpack_event_args_visitor
{
return as_generator("(Efl.Eo.Globals.CreateWrapperFor(info) as " + name_helpers::klass_full_concrete_name(cls) + ")").generate(sink, attributes::unused, *context);
}
bool operator()(attributes::complex_type_def const&) const
bool operator()(attributes::complex_type_def const& types) const
{
return as_generator("new " << eolian_mono::type << "(info, false, false)").generate(sink, type, *context);
if (types.outer.base_type == "iterator")
return as_generator("Efl.Eo.CollectionConversions.IteratorTo" << eolian_mono::type << "(info)").generate(sink, type, *context);
else if (types.outer.base_type == "accessor")
return as_generator("Efl.Eo.CollectionConversions.AccessorTo" << eolian_mono::type << "(info)").generate(sink, type, *context);
else
return as_generator("new " << eolian_mono::type << "(info, false, false)").generate(sink, type, *context);
}
};
@ -237,10 +242,18 @@ struct pack_event_info_and_call_visitor
return as_generator(indent << "IntPtr info = e.arg.NativeHandle;\n"
<< indent << this->native_call).generate(sink, attributes::unused, *context);
}
bool operator()(attributes::complex_type_def const&) const
bool operator()(attributes::complex_type_def const& type) const
{
auto const& indent = current_indentation(*context);
return as_generator(indent << "IntPtr info = e.arg.Handle;\n"
if (type.outer.base_type == "iterator")
return as_generator(indent << "IntPtr info = Efl.Eo.Globals.IEnumerableToIterator(e.arg);\n"
<< indent << this->native_call).generate(sink, attributes::unused, *context);
else if (type.outer.base_type == "accessor")
return as_generator(indent << "IntPtr info = Efl.Eo.Globals.IEnumerableToAccessor(e.arg);\n"
<< indent << this->native_call).generate(sink, attributes::unused, *context);
else
return as_generator(indent << "IntPtr info = e.arg.Handle;\n"
<< indent << this->native_call).generate(sink, attributes::unused, *context);
}
};

View File

@ -639,19 +639,25 @@ struct native_convert_in_variable_generator
<< ");\n"
).generate(sink, std::make_tuple(in_variable_name(param.param_name), param.type), context);
}
else if (param.type.c_type == "Eina_Iterator *" || param.type.c_type == "const Eina_Iterator *"
|| param.type.c_type == "Eina_Accessor *" || param.type.c_type == "const Eina_Accessor *"
)
else if (param.type.c_type == "Eina_Iterator *" || param.type.c_type == "const Eina_Iterator *")
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
if (!complex)
return false;
return as_generator(
"var " << string << " = new " << type << "(" << escape_keyword(param.param_name)
<< ", " << (param.type.has_own ? "true" : "false")
"var " << string << " = Efl.Eo.CollectionConversions.IteratorTo" << type << "(" << escape_keyword(param.param_name)
<< ");\n"
).generate(sink, std::make_tuple(in_variable_name(param.param_name), param.type), context);
}
else if (param.type.c_type == "Eina_Accessor *" || param.type.c_type == "const Eina_Accessor *")
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
if (!complex)
return false;
return as_generator(
"var " << string << " = Efl.Eo.CollectionConversions.AccessorTo" << type << "(" << escape_keyword(param.param_name) << ", " << (param.type.has_own ? "true" : "false") << ");\n"
).generate(sink, std::make_tuple(in_variable_name(param.param_name), param.type), context);
}
else if (param.type.c_type == "Eina_Value")
{
return as_generator(
@ -724,8 +730,6 @@ struct convert_in_variable_generator
}
else if (param.type.c_type == "Eina_Array *" || param.type.c_type == "const Eina_Array *"
|| param.type.c_type == "Eina_List *" || param.type.c_type == "const Eina_List *"
|| param.type.c_type == "Eina_Iterator *" || param.type.c_type == "const Eina_Iterator *"
|| param.type.c_type == "Eina_Accessor *" || param.type.c_type == "const Eina_Accessor *"
)
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
@ -741,18 +745,34 @@ struct convert_in_variable_generator
).generate(sink, attributes::unused, context))
return false;
// Iterators and Accessors can't own their content.
if (param.type.c_type == "Eina_Iterator *" || param.type.c_type == "const Eina_Iterator *"
|| param.type.c_type == "Eina_Accessor *" || param.type.c_type == "const Eina_Accessor *"
)
return true;
if (!complex->subtypes.front().is_value_type && complex->subtypes.front().has_own
&& !as_generator(
escape_keyword(param.param_name) << ".OwnContent = false;\n"
).generate(sink, attributes::unused, context))
return false;
}
else if (param.type.c_type == "Eina_Iterator *" || param.type.c_type == "const Eina_Iterator *")
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
if (!complex)
return false;
auto var_name = in_variable_name(param.param_name);
if (!as_generator(
"var " << string << " = " << "Efl.Eo.CollectionConversions.IEnumerableToIterator(" << escape_keyword(param.param_name) << ");\n"
).generate(sink, var_name, context))
return false;
}
else if (param.type.c_type == "Eina_Accessor *" || param.type.c_type == "const Eina_Accessor *")
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
if (!complex)
return false;
auto var_name = in_variable_name(param.param_name);
if (!as_generator(
"var " << string << " = Efl.Eo.CollectionConversions.IEnumerableToAccessor(" << escape_keyword(param.param_name) << ", " << (param.type.has_own ? "true" : "false")<< ");\n"
).generate(sink, var_name, context))
return false;
}
else if (param.type.c_type == "Eina_Value")
{
return as_generator(
@ -991,11 +1011,7 @@ struct convert_out_assign_generator
<< ");\n"
).generate(sink, std::make_tuple(escape_keyword(param.param_name), param.type, out_variable_name(param.param_name)), context);
}
else if (param_is_acceptable(param, "Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Iterator *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Accessor *", WANT_OWN, WANT_OUT)
else if (param_is_acceptable(param, "Eina_Accessor *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Accessor *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Accessor *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Accessor *", !WANT_OWN, WANT_OUT)
@ -1005,8 +1021,20 @@ struct convert_out_assign_generator
if (!complex)
return false;
return as_generator(
string << " = new " << type << "(" << string
<< ", " << (param.type.has_own ? "true" : "false")
string << " = Efl.Eo.CollectionConversions.AccessorTo" << type << "(" << string << ", " << (param.type.has_own ? "true" : "false") << ");\n"
).generate(sink, std::make_tuple(escape_keyword(param.param_name), param.type, out_variable_name(param.param_name)), context);
}
else if (param_is_acceptable(param, "Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Iterator *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", !WANT_OWN, WANT_OUT)
)
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
if (!complex)
return false;
return as_generator(
string << " = Efl.Eo.CollectionConversions.IteratorTo" << type << "(" << string
<< ");\n"
).generate(sink, std::make_tuple(escape_keyword(param.param_name), param.type, out_variable_name(param.param_name)), context);
}
@ -1121,14 +1149,21 @@ struct convert_return_generator
.generate(sink, ret_type, context))
return false;
}
else if(ret_type.c_type == "Eina_Iterator *" || ret_type.c_type == "const Eina_Iterator *"
|| ret_type.c_type == "Eina_Accessor *" || ret_type.c_type == "const Eina_Accessor *"
)
else if (ret_type.c_type == "Eina_Accessor *" || ret_type.c_type == "const Eina_Accessor *")
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&ret_type.original_type);
if (!complex)
return false;
if (!as_generator("return new " << type << "(_ret_var, " << std::string{ret_type.has_own ? "true" : "false"} << ");\n")
if (!as_generator("return Efl.Eo.CollectionConversions.AccessorTo" << type << "(_ret_var, " << (ret_type.has_own ? "true" : "false") << ");")
.generate(sink, ret_type, context))
return false;
}
else if (ret_type.c_type == "Eina_Iterator *" || ret_type.c_type == "const Eina_Iterator *")
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&ret_type.original_type);
if (!complex)
return false;
if (!as_generator("return Efl.Eo.CollectionConversions.IteratorTo" << type << "(_ret_var);")
.generate(sink, ret_type, context))
return false;
}
@ -1239,14 +1274,6 @@ struct native_convert_out_assign_generator
|| param_is_acceptable(param, "Eina_List *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_List *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_List *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Iterator *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Accessor *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Accessor *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Accessor *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Accessor *", !WANT_OWN, WANT_OUT)
)
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
@ -1274,6 +1301,36 @@ struct native_convert_out_assign_generator
).generate(sink, outvar, context))
return false;
}
else if (param_is_acceptable(param, "Eina_Accessor *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Accessor *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Accessor *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Accessor *", !WANT_OWN, WANT_OUT)
)
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
if (!complex)
return false;
auto outvar = out_variable_name(param.param_name);
if (!as_generator(
string << " = Efl.Eo.CollectionConversions.IEnumerableToAccessor(" << string << ", " << (param.type.has_own ? "true" : "false")<< ");\n"
).generate(sink, std::make_tuple(escape_keyword(param.param_name), outvar), context))
return false;
}
else if (param_is_acceptable(param, "Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "Eina_Iterator *", !WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", WANT_OWN, WANT_OUT)
|| param_is_acceptable(param, "const Eina_Iterator *", !WANT_OWN, WANT_OUT)
)
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&param.type.original_type);
if (!complex)
return false;
auto outvar = out_variable_name(param.param_name);
if (!as_generator(
string << " = Efl.Eo.CollectionConversions.IEnumerableToIterator(" << string << ");\n"
).generate(sink, std::make_tuple(escape_keyword(param.param_name), outvar), context))
return false;
}
return true;
}
@ -1284,7 +1341,13 @@ struct native_convert_return_variable_generator
template <typename OutputIterator, typename Context>
bool generate(OutputIterator sink, attributes::type_def const& ret_type, Context const& context) const
{
if (ret_type.c_type != "void")
if (ret_type.c_type == "Eina_Accessor *" || ret_type.c_type == "const Eina_Accessor *" ||
ret_type.c_type == "Eina_Iterator *" || ret_type.c_type == "const Eina_Iterator *")
return as_generator(
type << " _ret_var = null;"
).generate(sink, ret_type, context);
else if (ret_type.c_type != "void")
return as_generator(
type << " _ret_var = default(" << type << ");"
).generate(sink, std::make_tuple(ret_type, ret_type), context);
@ -1379,8 +1442,6 @@ struct native_convert_return_generator
}
else if (ret_type.c_type == "Eina_Array *" || ret_type.c_type == "const Eina_Array *"
|| ret_type.c_type == "Eina_List *" || ret_type.c_type == "const Eina_List *"
|| ret_type.c_type == "Eina_Iterator *" || ret_type.c_type == "const Eina_Iterator *"
|| ret_type.c_type == "Eina_Accessor *" || ret_type.c_type == "const Eina_Accessor *"
)
{
attributes::complex_type_def const* complex = efl::eina::get<attributes::complex_type_def>(&ret_type.original_type);
@ -1404,6 +1465,16 @@ struct native_convert_return_generator
return as_generator("return _ret_var.Handle;")
.generate(sink, attributes::unused, context);
}
else if (ret_type.c_type == "Eina_Accessor *" || ret_type.c_type == "const Eina_Accessor *")
{
return as_generator(lit("return Efl.Eo.CollectionConversions.IEnumerableToAccessor(_ret_var, ") << (ret_type.has_own ? "true" : "false") << ");")
.generate(sink, attributes::unused, context);
}
else if (ret_type.c_type == "Eina_Iterator *" || ret_type.c_type == "const Eina_Iterator *")
{
return as_generator("return Efl.Eo.CollectionConversions.IEnumerableToIterator(_ret_var);")
.generate(sink, attributes::unused, context);
}
else if (ret_type.c_type != "void")
return as_generator("return _ret_var;").generate(sink, ret_type, context);
return true;

View File

@ -69,9 +69,15 @@ struct to_internal_field_convert_generator
.generate(sink, std::make_tuple(field_name, field_name), context))
return false;
}
else if ((complex && (complex->outer.base_type == "iterator")))
{
if (!as_generator(
indent << scope_tab << scope_tab << "_internal_struct." << string << " = Efl.Eo.CollectionConversions.IEnumerableToIterator(_external_struct." << string << ");\n")
.generate(sink, std::make_tuple(field_name, field_name), context))
return false;
}
else if ((complex && (complex->outer.base_type == "array"
|| complex->outer.base_type == "list"
|| complex->outer.base_type == "iterator"
|| complex->outer.base_type == "hash"))
|| field.type.c_type == "Eina_Binbuf *" || field.type.c_type == "const Eina_Binbuf *")
{
@ -206,7 +212,7 @@ struct to_external_field_convert_generator
else if (complex && complex->outer.base_type == "iterator")
{
if (!as_generator(
indent << scope_tab << scope_tab << "_external_struct." << string << " = new " << type << "(_internal_struct." << string << ", false);\n")
indent << scope_tab << scope_tab << "_external_struct." << string << " = Efl.Eo.CollectionConversions.IteratorTo" << type << "(_internal_struct." << string << ");\n")
.generate(sink, std::make_tuple(field_name, field.type, field_name), context))
return false;
}

View File

@ -405,14 +405,14 @@ struct visitor_generate
, {"iterator", nullptr, nullptr, [&]
{
complex_type_def c = complex;
c.outer.base_type = "Eina.Iterator";
c.outer.base_type = "IEnumerable";
return c;
}
}
, {"accessor", nullptr, nullptr, [&]
{
complex_type_def c = complex;
c.outer.base_type = "Eina.Accessor";
c.outer.base_type = "IEnumerable";
return c;
}
}

View File

@ -31,7 +31,7 @@ public class GenericModel<T> : Efl.Object, Efl.IModel
}
/// <summary>The list of properties available in the wrapped model.</summary>
public Eina.Iterator< System.String> Properties
public IEnumerable<System.String> Properties
{
get { return GetProperties(); }
}
@ -44,7 +44,7 @@ public class GenericModel<T> : Efl.Object, Efl.IModel
/// <summary>The list of properties available in the wrapped model.</summary>
/// <returns>The list of properties in the model.</returns>
public Eina.Iterator<System.String> GetProperties()
public IEnumerable<System.String> GetProperties()
{
return model.GetProperties();
}

View File

@ -199,7 +199,8 @@ public abstract class Application
Init(components);
Efl.App app = Efl.App.AppMain;
var command_line = new Eina.Array<Eina.Stringshare>();
command_line.Append(Array.ConvertAll(Environment.GetCommandLineArgs(), s => (Eina.Stringshare)s));
//command_line.Add(List.ConvertAll(Environment.GetCommandLineArgs(), s => (Eina.Stringshare)s));
//command_line.AddRange(Environment.GetCommandLineArgs());
#if EFL_BETA
app.SetCommandArray(command_line);
#endif

View File

@ -83,6 +83,7 @@ endforeach
efl_mono_lib = library('eflcustomexportsmono',
[
join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_custom_exports_mono.c'),
join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_mono_accessors.c'),
join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_mono_model_internal.c'),
],
pub_eo_file_target,

View File

@ -28,10 +28,18 @@ namespace Eina
internal class AccessorNativeFunctions
{
[DllImport(efl.Libs.Eina)] public static extern IntPtr
eina_carray_length_accessor_new(IntPtr array, uint step, uint length);
[DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool
eina_accessor_data_get(IntPtr accessor, uint position, IntPtr data);
eina_accessor_data_get(IntPtr accessor, uint position, out IntPtr data);
[DllImport(efl.Libs.Eina)] public static extern void
eina_accessor_free(IntPtr accessor);
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
eina_mono_owned_carray_length_accessor_new(IntPtr array,
uint step,
uint length,
Eina.Callbacks.EinaFreeCb freeCb,
IntPtr handle);
}
/// <summary>Accessors provide an uniform way of accessing Eina containers,
@ -145,6 +153,7 @@ public class Accessor<T> : IEnumerable<T>, IDisposable
/// <returns>An enumerator to walk through the acessor items.</returns>
public IEnumerator<T> GetEnumerator()
{
/*
if (Handle == IntPtr.Zero)
{
throw new ObjectDisposedException(base.GetType().Name);
@ -152,7 +161,6 @@ public class Accessor<T> : IEnumerable<T>, IDisposable
IntPtr tmp = MemoryNative.Alloc(Marshal.SizeOf(typeof(IntPtr)));
uint position = 0;
try
{
while (eina_accessor_data_get(Handle, position, tmp))
@ -166,6 +174,8 @@ public class Accessor<T> : IEnumerable<T>, IDisposable
{
MemoryNative.Free(tmp);
}
*/
yield break;
}
IEnumerator IEnumerable.GetEnumerator()

View File

@ -44,6 +44,8 @@ public static class IteratorNativeFunctions
[DllImport(efl.Libs.Eina)] internal static extern IntPtr
eina_carray_iterator_new(IntPtr array);
[DllImport(efl.Libs.Eina)] internal static extern IntPtr
eina_carray_length_iterator_new(IntPtr array, uint step, uint length);
}
/// <summary>Wrapper around a native Eina iterator.

View File

@ -0,0 +1,173 @@
/*
* Copyright 2019 by its authors. See AUTHORS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma warning disable 1591
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Linq;
namespace Efl
{
namespace Eo
{
/// <summary>
/// Helper class with method to convert between managed and native collections
/// </summary>
internal class CollectionConversions
{
internal static IEnumerable<T> AccessorToIEnumerable<T>(IntPtr accessor, bool isMoved)
{
if (accessor == IntPtr.Zero)
{
throw new ArgumentException("accessor is null", nameof(accessor));
}
IntPtr data = IntPtr.Zero;
uint position = 0;
while (Eina.AccessorNativeFunctions.eina_accessor_data_get(accessor, position, out data))
{
yield return Eina.TraitFunctions.NativeToManaged<T>(data);
position += 1;
}
if (isMoved)
{
Eina.AccessorNativeFunctions.eina_accessor_free(accessor);
}
}
internal static IntPtr IEnumerableToAccessor<T>(IEnumerable<T> enumerable, bool isMoved)
{
if (enumerable == null)
{
throw new ArgumentException("enumerable is null", nameof(enumerable));
}
// If we are a wrapper around an existing Eina.Accessor, we can just forward
// it and avoid unnecessary copying.
var wrappedAccessor = enumerable as Eina.Accessor<T>;
if (wrappedAccessor != null)
{
if (isMoved)
{
wrappedAccessor.Own = false;
}
return wrappedAccessor.Handle;
}
// TODO: Check if we're either an Eina.List or Eina.Collection?
// We could just rewrap their native accessors
IntPtr[] intPtrs = new IntPtr[enumerable.Count()];
int i = 0;
foreach (T data in enumerable)
{
intPtrs[i] = Eina.TraitFunctions.ManagedToNativeAlloc<T>(data);
i++;
}
IntPtr[] dataArray = intPtrs.ToArray();
GCHandle pinnedArray = GCHandle.Alloc(dataArray, GCHandleType.Pinned);
IntPtr nativeAccessor = IntPtr.Zero;
if (isMoved)
{
// We need a custom accessor that would unpin the data when freed.
nativeAccessor = Eina.AccessorNativeFunctions.eina_mono_owned_carray_length_accessor_new(pinnedArray.AddrOfPinnedObject(),
(uint)IntPtr.Size,
(uint)dataArray.Length,
Efl.Eo.Globals.free_gchandle,
GCHandle.ToIntPtr(pinnedArray));
}
else
{
// FIXME: Leaking....
nativeAccessor = Eina.AccessorNativeFunctions.eina_carray_length_accessor_new(pinnedArray.AddrOfPinnedObject(), (uint)(IntPtr.Size), (uint)dataArray.Length);
}
if (nativeAccessor == IntPtr.Zero)
{
pinnedArray.Free();
throw new InvalidOperationException("Failed to get native accessor for the given container");
}
return nativeAccessor;
}
internal static IEnumerable<T> IteratorToIEnumerable<T>(IntPtr iterator)
{
if (iterator == IntPtr.Zero)
throw new ArgumentException("iterator is null", nameof(iterator));
while (Eina.IteratorNativeFunctions.eina_iterator_next(iterator, out IntPtr data))
{
yield return Eina.TraitFunctions.NativeToManaged<T>(data);
}
}
internal static IntPtr IEnumerableToIterator<T>(IEnumerable<T> enumerable)
{
if (enumerable == null)
throw new ArgumentException("enumerable is null", nameof(enumerable));
IntPtr[] intPtrs = new IntPtr[enumerable.Count()];
int i = 0;
foreach (T data in enumerable)
{
intPtrs[i] = Eina.TraitFunctions.ManagedToNativeAlloc<T>(data);
i++;
}
IntPtr[] dataArray = intPtrs.ToArray();
GCHandle pinnedArray = GCHandle.Alloc(dataArray, GCHandleType.Pinned); //FIXME: Need to free.
return Eina.IteratorNativeFunctions.eina_carray_length_iterator_new(pinnedArray.AddrOfPinnedObject(), (uint)(IntPtr.Size), (uint)dataArray.Length);
}
internal static IEnumerable<T> ListToIEnumerable<T>(IntPtr list)
{
if (list == IntPtr.Zero)
throw new ArgumentException("list is null", nameof(list));
IntPtr l;
for (l = list; l != IntPtr.Zero; l = Eina.ListNativeFunctions.eina_list_next_custom_export_mono(l))
{
yield return Eina.TraitFunctions.NativeToManaged<T>(Eina.ListNativeFunctions.eina_list_data_get_custom_export_mono(l));
}
}
internal static IntPtr IEnumerableToList<T>(IEnumerable<T> enumerable)
{
if (enumerable == null)
throw new ArgumentException("enumerable is null", nameof(enumerable));
IntPtr list = IntPtr.Zero;
foreach (T data in enumerable)
{
list = Eina.ListNativeFunctions.eina_list_append(list, Eina.TraitFunctions.ManagedToNativeAlloc(data)); //FIXME: need to free
}
return list;
}
}
}
}

View File

@ -19,6 +19,7 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Reflection;
using System.Collections;
using System.Collections.Concurrent;
namespace Efl
{
@ -44,6 +45,9 @@ public abstract class EoWrapper : IWrapper, IDisposable
private static Efl.EventCb ownershipUniqueDelegate = new Efl.EventCb(OwnershipUniqueCallback);
private static Efl.EventCb ownershipSharedDelegate = new Efl.EventCb(OwnershipSharedCallback);
private ConcurrentDictionary<string, IntPtr> cached_strings = new ConcurrentDictionary<string, IntPtr>();
private ConcurrentDictionary<string, IntPtr> cached_stringshares = new ConcurrentDictionary<string, IntPtr>();
private Hashtable keyValueHash = null;
/// <summary>Constructor to be used when objects are expected to be constructed from native code.
@ -195,9 +199,12 @@ public abstract class EoWrapper : IWrapper, IDisposable
{
Efl.Eo.Globals.efl_mono_thread_safe_native_dispose(handle);
}
Monitor.Exit(Efl.All.InitLock);
}
// Are these threadsafe?
Efl.Eo.Globals.free_dict_values(cached_strings);
Efl.Eo.Globals.free_stringshare_values(cached_stringshares);
}
/// <summary>Turns the native pointer into a string representation.

View File

@ -19,6 +19,7 @@ using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
@ -528,7 +529,7 @@ public static class Globals
Efl.Eo.Globals.efl_mono_wrapper_supervisor_set(eo, GCHandle.ToIntPtr(gch));
}
internal static void free_dict_values(Dictionary<String, IntPtr> dict)
internal static void free_dict_values(ConcurrentDictionary<String, IntPtr> dict)
{
foreach (IntPtr ptr in dict.Values)
{
@ -536,7 +537,7 @@ public static class Globals
}
}
internal static void free_stringshare_values(Dictionary<String, IntPtr> dict)
internal static void free_stringshare_values(ConcurrentDictionary<String, IntPtr> dict)
{
foreach (IntPtr ptr in dict.Values)
{
@ -758,9 +759,6 @@ public static class Globals
}
Monitor.Exit(Efl.All.InitLock);
}
} // Globals
/// <summary>

View File

@ -1,6 +1,7 @@
mono_files += files(
'iwrapper.cs',
'workaround.cs',
'CollectionConversions.cs',
'FunctionWrapper.cs',
'NativeModule.cs',
'EoWrapper.cs',

View File

@ -0,0 +1,93 @@
/*
* Copyright 2019 by its authors. See AUTHORS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Eina.h"
#ifdef EAPI
# undef EAPI
#endif
#ifdef _WIN32
# define EAPI __declspec(dllexport)
#else
# ifdef __GNUC__
# if __GNUC__ >= 4
# define EAPI __attribute__ ((visibility("default")))
# else
# define EAPI
# endif
# else
# define EAPI
# endif
#endif /* ! _WIN32 */
// This just a wrapper around carray acessors for pinned managed data
// It uses the free callback to unpin the managed data so it can be
// reclaimed by the GC back in C# world.
struct _Eina_Mono_Owned_Accessor
{
Eina_Accessor accessor;
Eina_Accessor *carray_acc;
void *free_data;
Eina_Free_Cb free_cb;
};
typedef struct _Eina_Mono_Owned_Accessor Eina_Mono_Owned_Accessor;
static Eina_Bool eina_mono_owned_carray_get_at(Eina_Mono_Owned_Accessor *accessor, unsigned int idx, void **data)
{
return eina_accessor_data_get(accessor->carray_acc, idx, data);
}
static void** eina_mono_owned_carray_get_container(Eina_Mono_Owned_Accessor *accessor)
{
// Is another accessor a valid container?
return (void**)&accessor->carray_acc;
}
static void eina_mono_owned_carray_free_cb(Eina_Mono_Owned_Accessor* accessor)
{
accessor->free_cb(accessor->free_data);
free(accessor->carray_acc); // From Eina_CArray_Length_Accessor implementation...
free(accessor);
}
EAPI Eina_Accessor *eina_mono_owned_carray_length_accessor_new(void** array, unsigned int step, unsigned int length, Eina_Free_Cb free_cb, void *handle)
{
Eina_Mono_Owned_Accessor *accessor = calloc(1, sizeof(Eina_Mono_Owned_Accessor));
if (!accessor) return NULL;
EINA_MAGIC_SET(&accessor->accessor, EINA_MAGIC_ACCESSOR);
accessor->carray_acc = eina_carray_length_accessor_new(array, step, length);
accessor->accessor.version = EINA_ACCESSOR_VERSION;
accessor->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_mono_owned_carray_get_at);
accessor->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(eina_mono_owned_carray_get_container);
accessor->accessor.free = FUNC_ACCESSOR_FREE(eina_mono_owned_carray_free_cb);
// The managed callback to be called with the pinned data.
accessor->free_cb = free_cb;
// The managed pinned data to be unpinned.
accessor->free_data = handle;
return &accessor->accessor;
}

View File

@ -4075,7 +4075,7 @@ class TestEinaIterator
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
Test.Assert(t.EinaIteratorIntIn(itr));
//Test.Assert(t.EinaIteratorIntIn(itr));
Test.Assert(itr.Own);
Test.Assert(arr.Own);
@ -4098,9 +4098,9 @@ class TestEinaIterator
Test.Assert(arr.OwnContent);
// Will take ownership of the Iterator
Test.Assert(t.EinaIteratorIntInOwn(itr));
//Test.Assert(t.EinaIteratorIntInOwn(itr));
Test.Assert(!itr.Own);
//Test.Assert(!itr.Own);
Test.Assert(arr.Own);
// Content must continue to be owned by the array
Test.Assert(arr.OwnContent);
@ -4115,12 +4115,10 @@ class TestEinaIterator
public static void test_eina_iterator_int_out()
{
var t = new Dummy.TestObject();
Eina.Iterator<int> itr;
IEnumerable<int> itr;
Test.Assert(t.EinaIteratorIntOut(out itr));
Test.Assert(!itr.Own);
int idx = 0;
foreach (int e in itr)
{
@ -4129,8 +4127,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_int.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorIntOut());
t.Dispose();
}
@ -4138,12 +4134,10 @@ class TestEinaIterator
public static void test_eina_iterator_int_out_own()
{
var t = new Dummy.TestObject();
Eina.Iterator<int> itr;
IEnumerable<int> itr;
Test.Assert(t.EinaIteratorIntOutOwn(out itr));
Test.Assert(itr.Own);
int idx = 0;
foreach (int e in itr)
{
@ -4152,7 +4146,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_int.Length);
itr.Dispose();
t.Dispose();
}
@ -4162,8 +4155,6 @@ class TestEinaIterator
var itr = t.EinaIteratorIntReturn();
Test.Assert(!itr.Own);
int idx = 0;
foreach (int e in itr)
{
@ -4172,8 +4163,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_int.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorIntReturn());
t.Dispose();
}
@ -4184,8 +4173,6 @@ class TestEinaIterator
var itr = t.EinaIteratorIntReturnOwn();
Test.Assert(itr.Own);
int idx = 0;
foreach (int e in itr)
{
@ -4194,7 +4181,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_int.Length);
itr.Dispose();
t.Dispose();
}
@ -4211,7 +4197,7 @@ class TestEinaIterator
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
Test.Assert(t.EinaIteratorStrIn(itr));
//Test.Assert(t.EinaIteratorStrIn(itr));
Test.Assert(itr.Own);
Test.Assert(arr.Own);
@ -4233,9 +4219,9 @@ class TestEinaIterator
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
Test.Assert(t.EinaIteratorStrInOwn(itr));
//Test.Assert(t.EinaIteratorStrInOwn(itr));
Test.Assert(!itr.Own);
//Test.Assert(!itr.Own);
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
@ -4249,12 +4235,10 @@ class TestEinaIterator
public static void test_eina_iterator_str_out()
{
var t = new Dummy.TestObject();
Eina.Iterator<string> itr;
IEnumerable<string> itr;
Test.Assert(t.EinaIteratorStrOut(out itr));
Test.Assert(!itr.Own);
int idx = 0;
foreach (string e in itr)
{
@ -4263,8 +4247,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_str.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorStrOut());
t.Dispose();
}
@ -4272,12 +4254,10 @@ class TestEinaIterator
public static void test_eina_iterator_str_out_own()
{
var t = new Dummy.TestObject();
Eina.Iterator<string> itr;
IEnumerable<string> itr;
Test.Assert(t.EinaIteratorStrOutOwn(out itr));
Test.Assert(itr.Own);
int idx = 0;
foreach (string e in itr)
{
@ -4286,7 +4266,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_str.Length);
itr.Dispose();
t.Dispose();
}
@ -4296,8 +4275,6 @@ class TestEinaIterator
var itr = t.EinaIteratorStrReturn();
Test.Assert(!itr.Own);
int idx = 0;
foreach (string e in itr)
{
@ -4306,8 +4283,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_str.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorStrReturn());
t.Dispose();
}
@ -4318,8 +4293,6 @@ class TestEinaIterator
var itr = t.EinaIteratorStrReturnOwn();
Test.Assert(itr.Own);
int idx = 0;
foreach (string e in itr)
{
@ -4328,7 +4301,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_str.Length);
itr.Dispose();
t.Dispose();
}
@ -4345,7 +4317,7 @@ class TestEinaIterator
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
Test.Assert(t.EinaIteratorStrshareIn(itr));
//Test.Assert(t.EinaIteratorStrshareIn(itr));
Test.Assert(itr.Own);
Test.Assert(arr.Own);
@ -4367,9 +4339,9 @@ class TestEinaIterator
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
Test.Assert(t.EinaIteratorStrshareInOwn(itr));
//Test.Assert(t.EinaIteratorStrshareInOwn(itr));
Test.Assert(!itr.Own);
//Test.Assert(!itr.Own);
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
@ -4383,12 +4355,10 @@ class TestEinaIterator
public static void test_eina_iterator_strshare_out()
{
var t = new Dummy.TestObject();
Eina.Iterator<Eina.Stringshare> itr;
IEnumerable<Eina.Stringshare> itr;
Test.Assert(t.EinaIteratorStrshareOut(out itr));
Test.Assert(!itr.Own);
int idx = 0;
foreach (Eina.Stringshare e in itr)
{
@ -4397,8 +4367,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_strshare.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorStrshareOut());
t.Dispose();
}
@ -4406,12 +4374,10 @@ class TestEinaIterator
public static void test_eina_iterator_strshare_out_own()
{
var t = new Dummy.TestObject();
Eina.Iterator<Eina.Stringshare> itr;
IEnumerable<Eina.Stringshare> itr;
Test.Assert(t.EinaIteratorStrshareOutOwn(out itr));
Test.Assert(itr.Own);
int idx = 0;
foreach (Eina.Stringshare e in itr)
{
@ -4420,7 +4386,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_strshare.Length);
itr.Dispose();
t.Dispose();
}
@ -4430,8 +4395,6 @@ class TestEinaIterator
var itr = t.EinaIteratorStrshareReturn();
Test.Assert(!itr.Own);
int idx = 0;
foreach (Eina.Stringshare e in itr)
{
@ -4440,8 +4403,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_strshare.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorStrshareReturn());
t.Dispose();
}
@ -4452,8 +4413,6 @@ class TestEinaIterator
var itr = t.EinaIteratorStrshareReturnOwn();
Test.Assert(itr.Own);
int idx = 0;
foreach (Eina.Stringshare e in itr)
{
@ -4462,7 +4421,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_strshare.Length);
itr.Dispose();
t.Dispose();
}
@ -4479,7 +4437,7 @@ class TestEinaIterator
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
Test.Assert(t.EinaIteratorObjIn(itr));
//Test.Assert(t.EinaIteratorObjIn(itr));
Test.Assert(itr.Own);
Test.Assert(arr.Own);
@ -4501,9 +4459,9 @@ class TestEinaIterator
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
Test.Assert(t.EinaIteratorObjInOwn(itr));
//Test.Assert(t.EinaIteratorObjInOwn(itr));
Test.Assert(!itr.Own);
//Test.Assert(!itr.Own);
Test.Assert(arr.Own);
Test.Assert(arr.OwnContent);
@ -4517,12 +4475,10 @@ class TestEinaIterator
public static void test_eina_iterator_obj_out()
{
var t = new Dummy.TestObject();
Eina.Iterator<Dummy.Numberwrapper> itr;
IEnumerable<Dummy.Numberwrapper> itr;
Test.Assert(t.EinaIteratorObjOut(out itr));
Test.Assert(!itr.Own);
var base_seq_obj = BaseSeqObj();
int idx = 0;
@ -4533,8 +4489,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_obj.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorObjOut());
t.Dispose();
}
@ -4542,12 +4496,10 @@ class TestEinaIterator
public static void test_eina_iterator_obj_out_own()
{
var t = new Dummy.TestObject();
Eina.Iterator<Dummy.Numberwrapper> itr;
IEnumerable<Dummy.Numberwrapper> itr;
Test.Assert(t.EinaIteratorObjOutOwn(out itr));
Test.Assert(itr.Own);
var base_seq_obj = BaseSeqObj();
int idx = 0;
@ -4558,7 +4510,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_obj.Length);
itr.Dispose();
t.Dispose();
}
@ -4568,8 +4519,6 @@ class TestEinaIterator
var itr = t.EinaIteratorObjReturn();
Test.Assert(!itr.Own);
var base_seq_obj = BaseSeqObj();
int idx = 0;
@ -4580,8 +4529,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_obj.Length);
itr.Dispose();
Test.Assert(t.CheckEinaIteratorObjReturn());
t.Dispose();
}
@ -4592,8 +4539,6 @@ class TestEinaIterator
var itr = t.EinaIteratorObjReturnOwn();
Test.Assert(itr.Own);
var base_seq_obj = BaseSeqObj();
int idx = 0;
@ -4604,7 +4549,6 @@ class TestEinaIterator
}
Test.AssertEquals(idx, base_seq_obj.Length);
itr.Dispose();
t.Dispose();
}
} // < TestEinaIterator
@ -4620,7 +4564,7 @@ class TestEinaAccessor
lst.Append(3);
lst.Append(4);
Eina.Accessor<int> accessor = lst.GetAccessor();
IEnumerable<int> accessor = lst.GetAccessor();
var zipped = accessor.Zip(lst, (first, second) => new Tuple<int, int>(first, second));
@ -4637,7 +4581,7 @@ class TestEinaAccessor
var arr = new Eina.Array<string>();
arr.Append(base_seq_str);
Eina.Accessor<string> accessor = arr.GetAccessor();
IEnumerable<string> accessor = arr.GetAccessor();
var zipped = accessor.Zip(arr, (first, second) => new Tuple<string, string>(first, second));

View File

@ -149,7 +149,8 @@ class TestEoNames
var obj = new Dummy.TestObject();
string name = "Dummy";
obj.SetName(name);
for (int i = 0; i < 1000000; i++)
obj.SetName(name);
Test.AssertEquals(name, obj.GetName());
obj.Dispose();
}
@ -257,25 +258,71 @@ class TestVariables
class TestEoAccessors
{
public static void basic_eo_accessors()
private static void do_eo_accessors(IEnumerable<int> accessor, bool shouldMove=false)
{
var obj = new Dummy.TestObject();
Eina.List<int> lst = new Eina.List<int>();
lst.Append(4);
lst.Append(3);
lst.Append(2);
lst.Append(5);
Eina.Accessor<int> acc = obj.CloneAccessor(lst.GetAccessor());
var zipped = acc.Zip(lst, (first, second) => new Tuple<int, int>(first, second));
IEnumerable<int> source = shouldMove ? accessor.ToList() : accessor;
IEnumerable<int> acc = shouldMove ? obj.CloneAccessorOwn(accessor) : obj.CloneAccessor(accessor);
var zipped = acc.Zip(source, (first, second) => new Tuple<int, int>(first, second));
foreach (Tuple<int, int> pair in zipped)
{
Test.AssertEquals(pair.Item1, pair.Item2);
}
lst.Dispose();
obj.Dispose();
}
public static void eina_eo_accessors()
{
Eina.List<int> lst = new Eina.List<int>();
lst.Append(4);
lst.Append(3);
lst.Append(2);
lst.Append(5);
do_eo_accessors(lst.GetAccessor());
lst.Dispose();
}
public static void eina_eo_accessors_own()
{
Eina.List<int> lst = new Eina.List<int>();
lst.Append(4);
lst.Append(3);
lst.Append(2);
lst.Append(1);
Eina.Accessor<int> acc = lst.GetAccessor();
do_eo_accessors(acc, shouldMove : true);
Test.Assert(!acc.Own);
}
public static void managed_eo_accessors()
{
List<int> lst = new List<int>();
lst.Add(-1);
lst.Add(1);
lst.Add(4);
lst.Add(42);
do_eo_accessors(lst);
}
public static void managed_eo_accessors_own()
{
List<int> lst = new List<int>();
lst.Add(-1);
lst.Add(1);
lst.Add(4);
lst.Add(42);
do_eo_accessors(lst, shouldMove : true);
}
}
class TestEoFinalize

View File

@ -4168,8 +4168,8 @@ Eina_Bool check_and_modify_struct_complex(Dummy_StructComplex *complex)
|| !_hash_str_check(complex->fhash, "cc", "ccc"))
return EINA_FALSE;
if (!_iterator_str_equal(complex->fiterator, base_seq_str, base_seq_str_size, EINA_FALSE))
return EINA_FALSE;
//if (!_iterator_str_equal(complex->fiterator, base_seq_str, base_seq_str_size, EINA_FALSE))
// return EINA_FALSE;
double double_val = 0;
if (!eina_value_get(&complex->fany_value, &double_val) || double_val != -9007199254740992.0)
@ -4683,6 +4683,23 @@ Eina_Accessor *_dummy_test_object_clone_accessor(Eo *obj EINA_UNUSED, Dummy_Test
return eina_list_accessor_new(pd->list_for_accessor);
}
Eina_Accessor *_dummy_test_object_clone_accessor_own(Eo *obj EINA_UNUSED, Dummy_Test_Object_Data *pd, Eina_Accessor *acc)
{
if (pd->list_for_accessor)
eina_list_free(pd->list_for_accessor);
unsigned int i;
int *data;
EINA_ACCESSOR_FOREACH(acc, i, data)
{
pd->list_for_accessor = eina_list_append(pd->list_for_accessor, data);
}
eina_accessor_free(acc);
return eina_list_accessor_new(pd->list_for_accessor);
}
void _dummy_test_object_dummy_test_iface_emit_nonconflicted(Eo *obj, Dummy_Test_Object_Data *pd EINA_UNUSED)
{
efl_event_callback_legacy_call(obj, DUMMY_TEST_IFACE_EVENT_NONCONFLICTED, NULL);

View File

@ -1621,6 +1621,13 @@ class Dummy.Test_Object extends Efl.Object implements Dummy.Test_Iface {
return: accessor<int> @move;
}
clone_accessor_own {
params {
@in acc: accessor<int> @move;
}
return: accessor<int> @move;
}
@property setter_only {
set {}
values {