efl_mono: Add support for Eina.Error/Empty in eina.Value

Summary:
eina.Value.Empty now means that we have an zeroed (empty) eina value.
For optional values that are empty use eina.Value.OptionalEmpty.

This was required to support the empty values passed with
EINA_VALUE_EMPTY in some Ecore futures.

Also, returning an eina_value by value is not supported in eolian
for safety reasons, so we removed some tests that tried to use this
behavior.
Depends on D6171

Reviewers: felipealmeida

Reviewed By: felipealmeida

Subscribers: cedric, zmike

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D6172
This commit is contained in:
Lauro Moura 2018-04-23 16:20:12 -03:00
parent 2b8cbfe2f4
commit f8a033fd70
7 changed files with 172 additions and 60 deletions

View File

@ -240,7 +240,7 @@ struct to_internal_field_convert_generator
else if (field.type.c_type == "Eina_Value *" || field.type.c_type == "const Eina_Value *")
{
if (!as_generator(
scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << ".Handle;\n"
scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << ".NativeHandle;\n"
).generate(sink, std::make_tuple(field_name, field_name), context))
return false;
}

View File

@ -22,6 +22,8 @@ internal static class NativeCustomExportFunctions
efl_mono_native_free_ref(IntPtr ptr);
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
efl_mono_native_alloc(uint count);
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
efl_mono_native_memset(IntPtr ptr, uint fill, uint count);
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
efl_mono_native_alloc_copy(IntPtr val, uint size);
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr
@ -56,6 +58,11 @@ public static class MemoryNative {
return NativeCustomExportFunctions.efl_mono_native_alloc(Convert.ToUInt32(count));
}
public static void Memset(IntPtr ptr, int fill, int count)
{
NativeCustomExportFunctions.efl_mono_native_memset(ptr, Convert.ToUInt32(fill), Convert.ToUInt32(count));
}
public static IntPtr AllocCopy(IntPtr ptr, int count)
{
return NativeCustomExportFunctions.efl_mono_native_alloc_copy(ptr, Convert.ToUInt32(count));

View File

@ -325,7 +325,7 @@ static internal class UnsafeNativeMethods {
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_pset_wrapper(IntPtr handle, ref eina.EinaNative.Value_List ptr);
[DllImport(efl.Libs.CustomExports)]
[DllImport(efl.Libs.Eina)]
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_copy(IntPtr src, IntPtr dest);
@ -379,6 +379,10 @@ static internal class UnsafeNativeMethods {
// Optional
[DllImport(efl.Libs.CustomExports)]
internal static extern IntPtr type_optional();
// Error
[DllImport(efl.Libs.CustomExports)]
internal static extern IntPtr type_error();
}
}
@ -386,8 +390,8 @@ static internal class UnsafeNativeMethods {
[StructLayout(LayoutKind.Sequential)]
public struct Value_Native
{
IntPtr type;
IntPtr value; // Atually an Eina_Value_Union, but it is padded to 8 bytes.
public IntPtr Type;
public IntPtr Value; // Atually an Eina_Value_Union, but it is padded to 8 bytes.
}
@ -470,6 +474,10 @@ public enum ValueType {
Hash,
/// <summary>Optional (aka empty) values.</summary>
Optional,
/// <summary>Error values.</summary>
Error,
/// <summary>Empty values.</summary>
Empty,
}
static class ValueTypeMethods {
@ -521,6 +529,11 @@ static class ValueTypeMethods {
return val == ValueType.Optional;
}
public static bool IsError(this ValueType val)
{
return val == ValueType.Error;
}
/// <summary>Returns the Marshal.SizeOf for the given ValueType native structure.</summary>
public static int MarshalSizeOf(this ValueType val)
{
@ -608,6 +621,12 @@ static class ValueTypeBridge
ManagedToNative.Add(ValueType.Optional, type_optional());
NativeToManaged.Add(type_optional(), ValueType.Optional);
ManagedToNative.Add(ValueType.Error, type_error());
NativeToManaged.Add(type_error(), ValueType.Error);
ManagedToNative.Add(ValueType.Empty, IntPtr.Zero);
NativeToManaged.Add(IntPtr.Zero, ValueType.Empty);
TypesLoaded = true;
}
}
@ -647,10 +666,13 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
// EINA_VALUE_TYPE_STRUCT: Eina_Value_Struct -- FIXME
public IntPtr Handle { get; protected set;}
internal IntPtr Handle { get; set;}
/// <summary> Whether this wrapper owns (can free) the native value. </summary>
public Ownership Ownership { get; protected set;}
private bool Disposed;
/// <summary> Whether this wrapper has already freed the native value. </summary>
public bool Flushed { get; protected set;}
/// <summary> Whether this is an Optional value (meaning it can have a value or not). </summary>
public bool Optional {
get {
return GetValueType() == eina.ValueType.Optional;
@ -663,7 +685,16 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
// }
} */
}
/// <summary> Whether this wrapper is actually empty/uninitialized (zeroed). This is different from an empty optional value. </summary>
public bool Empty {
get {
SanityChecks();
return GetValueType() == eina.ValueType.Empty;
}
}
/// <summary> Whether this optional value is empty. </summary>
public bool OptionalEmpty {
get {
OptionalSanityChecks();
bool empty;
@ -690,7 +721,14 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
{
if (type.IsContainer())
throw new ArgumentException("To use container types you must provide a subtype");
this.Handle = MemoryNative.Alloc(eina_value_sizeof());
if (this.Handle == IntPtr.Zero)
throw new OutOfMemoryException("Failed to allocate memory for eina.Value");
// Initialize to EINA_VALUE_EMPTY before performing any other operation on this value.
MemoryNative.Memset(this.Handle, 0, eina_value_sizeof());
this.Ownership = Ownership.Managed;
Setup(type);
}
@ -710,12 +748,22 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
/// <summary>Constructor to build value from Values_Natives passed by value from C.</summary>
public Value(Value_Native value)
{
this.Handle = MemoryNative.Alloc(Marshal.SizeOf(typeof(Value_Native)));
IntPtr tmp = MemoryNative.Alloc(Marshal.SizeOf(typeof(Value_Native)));
try {
Marshal.StructureToPtr(value, tmp, false); // Can't get the address of a struct directly.
if (!eina_value_copy(tmp, this.Handle))
throw new System.InvalidOperationException("Failed to copy value to managed memory.");
if (value.Type == IntPtr.Zero) // Got an EINA_VALUE_EMPTY by value.
{
this.Handle = tmp;
tmp = IntPtr.Zero;
}
else
{
this.Handle = MemoryNative.Alloc(Marshal.SizeOf(typeof(Value_Native)));
// Copy is used to deep copy the pointed payload (e.g. strings) inside this struct, so we can own this value.
if (!eina_value_copy(tmp, this.Handle))
throw new System.InvalidOperationException("Failed to copy value to managed memory.");
}
} catch {
MemoryNative.Free(this.Handle);
throw;
@ -786,7 +834,7 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
}
if (!Disposed && (Handle != IntPtr.Zero)) {
if (!Flushed)
if (!Flushed && !Empty)
eina_value_flush_wrapper(this.Handle);
MemoryNative.Free(this.Handle);
@ -801,11 +849,12 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
}
/// <summary>Returns the native handle wrapped by this object.</summary>
public IntPtr NativeHandle()
{
if (Disposed)
throw new ObjectDisposedException(base.GetType().Name);
return this.Handle;
public IntPtr NativeHandle {
get {
if (Disposed)
throw new ObjectDisposedException(base.GetType().Name);
return this.Handle;
}
}
/// <summary>Converts this storage to type 'type'</summary>
@ -814,6 +863,18 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
if (Disposed)
throw new ObjectDisposedException(base.GetType().Name);
// Can't call setup with Empty value type (would give an eina error)
if (type == eina.ValueType.Empty)
{
// Need to cleanup as it may point to payload outside the underlying Eina_Value (like arrays and strings).
if (!Empty)
eina_value_flush_wrapper(this.Handle);
MemoryNative.Memset(this.Handle, 0, eina_value_sizeof());
return true;
}
if (type.IsContainer())
throw new ArgumentException("To setup a container you must provide a subtype.");
@ -1075,6 +1136,21 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
return eina_value_set_wrapper_string(this.Handle, value);
}
/// <summary>Stores the given error value.</summary>
public bool Set(eina.Error value)
{
SanityChecks();
int error_code = value;
if (this.Optional)
return eina_value_optional_pset(this.Handle,
ValueTypeBridge.GetNative(ValueType.Error),
ref error_code);
return eina_value_set_wrapper_int(this.Handle, error_code);
}
/// <summary>Stores the given value into this value. The target value must be an optional.</summary>
public bool Set(Value value)
{
@ -1228,6 +1304,22 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
return true;
}
/// <summary>Gets the currently stored value as an eina.Error.</summary>
public bool Get(out eina.Error value)
{
SanityChecks();
bool ret;
int intermediate; // It seems out doesn't play well with implicit operators...
if (this.Optional)
ret = eina_value_optional_pget(this.Handle, out intermediate);
else
ret = eina_value_get_wrapper(this.Handle, out intermediate);
value = intermediate;
return ret;
}
/// <summary>Gets the currently stored value as an complex (e.g. container) eina.Value.</summary>
public bool Get(out Value value)
{

View File

@ -27,6 +27,11 @@ EAPI void *efl_mono_native_alloc(unsigned int size)
return malloc(size);
}
EAPI void efl_mono_native_memset(void *ptr, unsigned int fill, unsigned int count)
{
memset(ptr, fill, count);
}
EAPI void efl_mono_native_free(void *ptr)
{
free(ptr);
@ -313,6 +318,9 @@ EAPI const Eina_Value_Type *type_array() {
EAPI const Eina_Value_Type *type_list() {
return EINA_VALUE_TYPE_LIST;
}
EAPI const Eina_Value_Type *type_error() {
return EINA_VALUE_TYPE_ERROR;
}
EAPI const Eina_Value_Type *type_optional() {
return EINA_VALUE_TYPE_OPTIONAL;
@ -354,6 +362,11 @@ EAPI void eina_value_flush_wrapper(Eina_Value *value)
EAPI const Eina_Value_Type *eina_value_type_get_wrapper(const Eina_Value *value)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(value, NULL);
// Can't pass null value type (for Empty values) to value_type_get.
if (value->type == NULL)
return NULL;
return eina_value_type_get(value);
}

View File

@ -135,6 +135,17 @@ public static class TestEinaValue {
}
}
public static void TestErrorSimple()
{
using (eina.Value v = new eina.Value(eina.ValueType.Error)) {
eina.Error error = new eina.Error(eina.Error.NO_ERROR);
Test.Assert(v.Set(error));
eina.Error x;
Test.Assert(v.Get(out x));
Test.AssertEquals(error, x);
}
}
public static void TestSetWrongType()
{
using (eina.Value v = new eina.Value(eina.ValueType.String)) {
@ -209,20 +220,20 @@ public static class TestEinaValue {
{
using (eina.Value a = new eina.Value(eina.ValueType.Optional)) {
Test.Assert(a.Optional);
Test.Assert(a.Empty); // By default, optional values are empty
Test.Assert(a.OptionalEmpty); // By default, optional values are empty
// Sets expectation
int expected = 1984;
Test.Assert(a.Set(expected));
Test.Assert(a.Optional);
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
Test.Assert(a.Reset());
Test.Assert(a.Empty);
Test.Assert(a.OptionalEmpty);
expected = -4891;
Test.Assert(a.Set(expected)); // Set() automatically infers the subtype from the argument.
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
int actual = 0;
Test.Assert(a.Get(out actual));
@ -233,20 +244,20 @@ public static class TestEinaValue {
{
using (eina.Value a = new eina.Value(eina.ValueType.Optional)) {
Test.Assert(a.Optional);
Test.Assert(a.Empty); // By default, optional values are empty
Test.Assert(a.OptionalEmpty); // By default, optional values are empty
// Sets expectation
uint expected = 1984;
Test.Assert(a.Set(expected));
Test.Assert(a.Optional);
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
Test.Assert(a.Reset());
Test.Assert(a.Empty);
Test.Assert(a.OptionalEmpty);
expected = 0xdeadbeef;
Test.Assert(a.Set(expected));
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
uint actual = 0;
Test.Assert(a.Get(out actual));
@ -257,26 +268,26 @@ public static class TestEinaValue {
{
using (eina.Value a = new eina.Value(eina.ValueType.Int32)) {
Test.Assert(!a.Optional);
BoolRet dummy = () => a.Empty;
BoolRet dummy = () => a.OptionalEmpty;
Test.AssertRaises<eina.InvalidValueTypeException>(() => dummy());
}
using (eina.Value a = new eina.Value(eina.ValueType.Optional)) {
Test.Assert(a.Optional);
Test.Assert(a.Empty); // By default, optional values are empty
Test.Assert(a.OptionalEmpty); // By default, optional values are empty
// Sets expectation
string expected = "Hello, world!";
Test.Assert(a.Set(expected));
Test.Assert(a.Optional);
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
Test.Assert(a.Reset());
Test.Assert(a.Empty);
Test.Assert(a.OptionalEmpty);
expected = "!dlrow olleH";
Test.Assert(a.Set(expected));
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
string actual = String.Empty;
Test.Assert(a.Get(out actual));
@ -291,7 +302,7 @@ public static class TestEinaValue {
{
Test.Assert(a.Optional);
Test.Assert(a.Empty); // By default, optional values are empty
Test.Assert(a.OptionalEmpty); // By default, optional values are empty
// Sets expectation
Test.Assert(expected.Append(-1));
@ -300,14 +311,14 @@ public static class TestEinaValue {
Test.Assert(a.Set(expected));
Test.Assert(a.Optional);
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
Test.Assert(a.Reset());
Test.Assert(a.Empty);
Test.Assert(a.OptionalEmpty);
expected.Append(-42);
Test.Assert(a.Set(expected));
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
eina.Value actual = null;
Test.Assert(a.Get(out actual));
@ -325,7 +336,7 @@ public static class TestEinaValue {
{
Test.Assert(a.Optional);
Test.Assert(a.Empty); // By default, optional values are empty
Test.Assert(a.OptionalEmpty); // By default, optional values are empty
// Sets expectation
Test.Assert(expected.Append(-1));
@ -334,14 +345,14 @@ public static class TestEinaValue {
Test.Assert(a.Set(expected));
Test.Assert(a.Optional);
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
Test.Assert(a.Reset());
Test.Assert(a.Empty);
Test.Assert(a.OptionalEmpty);
expected.Append(-42);
Test.Assert(a.Set(expected));
Test.Assert(!a.Empty);
Test.Assert(!a.OptionalEmpty);
eina.Value actual = null;
Test.Assert(a.Get(out actual));
@ -807,9 +818,23 @@ public static class TestEinaValue {
value_ptr.Set(payload);
eina.Value_Native byvalue = value_ptr;
eina.Value another_value_ptr = byvalue;
Test.AssertEquals(value_ptr, another_value_ptr);
}
}
public static void TestValueEmpty() {
using (eina.Value empty = new eina.Value(eina.ValueType.Empty)) {
Test.Assert(empty.Empty, "Value must be empty");
empty.Setup(eina.ValueType.Int32);
// Values already set-up are not empty. For this kind of empty, use Optional
Test.Assert(!empty.Empty, "Values already set-up must not be empty.");
empty.Set(42);
Test.Assert(!empty.Empty, "Values with payload must not be empty.");
}
}
// FIXME Add remaining list tests

View File

@ -77,23 +77,6 @@ public static class TestEinaValueEolian {
}
}
public static void TestEolianEinaValueInReturnByValue()
{
test.ITesting obj = new test.Testing();
using (eina.Value v = new eina.Value(eina.ValueType.Int32)) {
v.Set(42);
obj.SetValue(v);
Test.AssertEquals(eina.Ownership.Managed, v.Ownership);
// Using get_value_ptr while get_value() is not supported.
eina.Value v_received = obj.GetValuePtrOwn();
Test.AssertEquals(eina.Ownership.Managed, v_received.Ownership);
Test.AssertEquals(v, v_received);
v_received.Dispose();
}
}
public static void TestEolianEinaValueOutByValue()
{
test.ITesting obj = new test.Testing();

View File

@ -3680,14 +3680,6 @@ Eina_Value *_test_testing_get_value_ptr(EINA_UNUSED Eo *obj, Test_Testing_Data *
return pd->stored_value;
}
/* Currently the Eolian declaration FUNC_BODY in the .eo.c file seems to be broken for
* generic value.
*/
/* Eina_Value _test_testing_get_value(EINA_UNUSED Eo *obj, Test_Testing_Data *pd)
{
return *pd->stored_value;
}*/
void _test_testing_clear_value(EINA_UNUSED Eo *obj, Test_Testing_Data *pd)
{
if (pd->stored_value) {