summaryrefslogtreecommitdiff
path: root/src/bindings/mono/eo_mono/FunctionWrapper.cs
blob: 2f84a9a695da9d7394344ac52540763a16e43881 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * 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.
 */
using System;
using System.Runtime.InteropServices;

namespace Efl
{

namespace Eo
{

///<summary>Class to load functions pointers from a native module.
///
///This class has a platform-dependent implementation on whether it
///is compiled for Windows (using LoadLibrary/GetProcAddress) or Unix
///(dlopen/dlsym).</summary>
public static partial class FunctionInterop
{
    ///<summary>Loads a function pointer from the given module.</summary>
    ///<param name="moduleName">The name of the module containing the function.</param>
    ///<param name="functionName">The name of the function to search for.</param>
    ///<returns>A function pointer that can be used with delegates.</returns>
    public static IntPtr LoadFunctionPointer(string moduleName, string functionName)
    {
        IntPtr module = NativeModule.LoadLibrary(moduleName);
        Eina.Log.Debug($"searching {module} for {functionName}");
        var s = FunctionInterop.dlsym(module, functionName);
        Eina.Log.Debug($"searching {module} for{functionName}, result {s}");
        return s;
    }

    ///<summary>Loads a function pointer from the default module.</summary>
    ///<param name="functionName">The name of the function to search for.</param>
    ///<returns>A function pointer that can be used with delegates.</returns>
    public static IntPtr LoadFunctionPointer(string functionName)
    {
        Eina.Log.Debug($"searching {null} for {functionName}");
        var s = FunctionInterop.dlsym(IntPtr.Zero, functionName);
        Eina.Log.Debug($"searching {null} for {functionName}, result {s}");
        return s;
    }
}

///<summary>Wraps a native function in a portable manner.
///
///This is intended as a workaround DllImport limitations when switching between mono and dotnet.
///
///The parameter T must be a delegate.
///</summary>
public class FunctionWrapper<T> // NOTE: When supporting C# >=7.3, add a where T: System.Delegate?
{
    private Lazy<FunctionLoadResult<T>> loadResult;
#pragma warning disable 0414
    private NativeModule module; // so it doesn't get unloaded
#pragma warning restore 0414

    private static FunctionLoadResult<T> LazyInitialization(NativeModule module, string functionName)
    {
        if (module.Module == IntPtr.Zero)
        {
            return new FunctionLoadResult<T>(FunctionLoadResultKind.LibraryNotFound);
        }
        else
        {
            IntPtr funcptr = FunctionInterop.LoadFunctionPointer(module.Module, functionName);
            if (funcptr == IntPtr.Zero)
            {
                return new FunctionLoadResult<T>(FunctionLoadResultKind.FunctionNotFound);
            }
            else
            {
                return new FunctionLoadResult<T>(Marshal.GetDelegateForFunctionPointer<T>(funcptr));
            }
        }
    }

    ///<summary>Creates a wrapper for the given function of the given module.</summary>
    ///<param name="moduleName">The name of the module containing the function.</param>
    ///<param name="functionName">The name of the function to search for.</param>
    public FunctionWrapper(string moduleName, string functionName)
        : this(new NativeModule(moduleName), functionName)
    {
    }

    ///<summary>Creates a wrapper for the given function of the given module.</summary>
    ///<param name="module">The module wrapper containing the function.</param>
    ///<param name="functionName">The name of the function to search for.</param>
    public FunctionWrapper(NativeModule module, string functionName)
    {
        this.module = module;
        loadResult = new Lazy<FunctionLoadResult<T>>
            (() =>
            {
                return LazyInitialization(module, functionName);
            });
    }

    ///<summary>Retrieves the result of function load.</summary>
    ///<returns>The load result.</returns>
    public FunctionLoadResult<T> Value
    {
        get
        {
            return loadResult.Value;
        }
    }
}

///<summary>The outcome of the function load process.</summary>
public enum FunctionLoadResultKind
{
    ///<summary>Function was loaded successfully.</summary>
    Success,
    ///<summary>Library was not found.</summary>
    LibraryNotFound,
    ///<summary>Function symbol was not found in the given module.</summary>
    FunctionNotFound
}

///<summary>Represents the result of loading a function pointer.</summary>
public class FunctionLoadResult<T>
{
    ///<summary>The status of the load.</summary>
    public FunctionLoadResultKind Kind;
    private T _Delegate;

    ///<summary>The delegate wrapping the loaded function pointer.
    ///
    ///Throws InvalidOperationException if trying to access while not loaded.</summary>
    public T Delegate
    {
        get
        {
            if (_Delegate == null)
            {
                throw new InvalidOperationException($"Trying to get Delegate of type {typeof(T).FullName} while not loaded. Load result: {Kind}");
            }

            return _Delegate;
        }
    }

    ///<summary>Creates a new load result of the given kind.</summary>
    ///<param name="kind">The outcome of the load process.</param>
    public FunctionLoadResult(FunctionLoadResultKind kind)
    {
        this.Kind = kind;
    }

    ///<summary>Creates a new load result with the given delegate.</summary>
    ///<param name="Delegate">The delegate wrapping the native function.</param>
    public FunctionLoadResult(T Delegate)
    {
        this._Delegate = Delegate;
        this.Kind = FunctionLoadResultKind.Success;
    }
}

}

}