Skip to content

Commit 9949f5b

Browse files
authored
Merge pull request #2 from MCpiroman/param-attribs
Support for parameter attributes
2 parents efe8606 + 5561c06 commit 9949f5b

3 files changed

Lines changed: 209 additions & 62 deletions

File tree

src/DllManipulator.cs

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ public class DllManipulator : MonoBehaviour
1616
public const string DLL_PATH_PATTERN_NAME_MACRO = "{name}";
1717
public const string DLL_PATH_PATTERN_ASSETS_MACRO = "{assets}";
1818
public const string DLL_PATH_PATTERN_PROJECT_MACRO = "{proj}";
19-
private static readonly Type[] DELEGATE_CTOR_PARAMETERS = new[] { typeof(object), typeof(IntPtr) };
20-
private static readonly Type[] UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS = new[] { typeof(CallingConvention) };
19+
public static readonly Type[] SUPPORTED_PARAMATER_ATTRIBUTES = { typeof(MarshalAsAttribute), typeof(InAttribute), typeof(OutAttribute) };
20+
private static readonly Type[] DELEGATE_CTOR_PARAMETERS = { typeof(object), typeof(IntPtr) };
21+
private static readonly Type[] UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS = { typeof(CallingConvention) };
22+
private static readonly Type[] MARSHAL_AS_ATTRIBUTE_CTOR_PARAMETERS = { typeof(UnmanagedType) };
2123

2224
public DllManipulatorOptions Options = new DllManipulatorOptions()
2325
{
@@ -262,10 +264,10 @@ private static DynamicMethod CreateNewNativeFunctionMock(MethodInfo nativeMethod
262264
var targetDelegateInvokeMethod = nativeFunction.delegateType.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public);
263265

264266
var mockedDynamicMethod = new DynamicMethod(dllName + ":::" + nativeFunctionSymbol, nativeMethod.ReturnType, parametersTypes, typeof(DllManipulator));
265-
mockedDynamicMethod.DefineParameter(0, nativeMethod.ReturnParameter.Attributes, nativeMethod.ReturnParameter.Name);
267+
mockedDynamicMethod.DefineParameter(0, nativeMethod.ReturnParameter.Attributes, null);
266268
for (int i = 0; i < parameters.Length; i++)
267269
{
268-
mockedDynamicMethod.DefineParameter(parameters[i].Position, parameters[i].Attributes, parameters[i].Name);
270+
mockedDynamicMethod.DefineParameter(i + 1, parameters[i].Attributes, null);
269271
}
270272

271273
if (_nativeFunctionsField == null)
@@ -333,7 +335,7 @@ private static void AddNativeFunction(NativeFunction nativeFunction)
333335
_nativeFunctions[_nativeFunctionsCount++] = nativeFunction;
334336
}
335337

336-
private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionSignature funcionSignature)
338+
private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionSignature functionSignature)
337339
{
338340
if (_customDelegateTypesModule == null)
339341
{
@@ -347,31 +349,81 @@ private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionS
347349

348350
//ufp = UnmanagedFunctionPointer
349351
var ufpAttrType = typeof(UnmanagedFunctionPointerAttribute);
350-
var ufpAttrCtor = ufpAttrType.GetConstructor(UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS);
351-
var ufpAttrCtorArgValues = new object[] { funcionSignature.callingConvention };
352-
var ufpAttrNamedFields = new [] {
352+
var ufpAttrCtor = ufpAttrType.GetConstructor(UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS);
353+
object[] ufpAttrCtorArgValues = { functionSignature.callingConvention };
354+
FieldInfo[] ufpAttrNamedFields = {
353355
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.BestFitMapping), BindingFlags.Public | BindingFlags.Instance),
354356
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.CharSet), BindingFlags.Public | BindingFlags.Instance),
355357
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.SetLastError), BindingFlags.Public | BindingFlags.Instance),
356358
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.ThrowOnUnmappableChar), BindingFlags.Public | BindingFlags.Instance),
357359
};
358-
var ufpAttrFieldValues = new object[] { funcionSignature.bestFitMapping, funcionSignature.charSet, funcionSignature.setLastError, funcionSignature.throwOnUnmappableChar };
360+
object[] ufpAttrFieldValues = { functionSignature.bestFitMapping, functionSignature.charSet, functionSignature.setLastError, functionSignature.throwOnUnmappableChar };
359361
var ufpAttrBuilder = new CustomAttributeBuilder(ufpAttrCtor, ufpAttrCtorArgValues, ufpAttrNamedFields, ufpAttrFieldValues);
360362
delBuilder.SetCustomAttribute(ufpAttrBuilder);
361363

362-
363364
var ctorBuilder = delBuilder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
364365
CallingConventions.Standard, DELEGATE_CTOR_PARAMETERS);
365366
ctorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
366367

367368
var invokeBuilder = delBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot,
368-
CallingConventions.Standard | CallingConventions.HasThis, funcionSignature.returnParameterType, funcionSignature.parameterTypes);
369+
CallingConventions.Standard | CallingConventions.HasThis, functionSignature.returnParameter.type, functionSignature.parameters.Select(p => p.type).ToArray());
369370
invokeBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
371+
var invokeReturnParam = invokeBuilder.DefineParameter(0, functionSignature.returnParameter.parameterAttributes, null);
372+
foreach (var attr in functionSignature.returnParameter.customAttributes)
373+
{
374+
invokeReturnParam.SetCustomAttribute(GetAttributeBuilderFromAttributeInstance(attr));
375+
}
376+
for (int i = 0; i < functionSignature.parameters.Length; i++)
377+
{
378+
var param = functionSignature.parameters[i];
379+
var paramBuilder = invokeBuilder.DefineParameter(i + 1, param.parameterAttributes, null);
380+
foreach(var attr in param.customAttributes)
381+
{
382+
paramBuilder.SetCustomAttribute(GetAttributeBuilderFromAttributeInstance(attr));
383+
}
384+
}
370385

371386
_createdDelegateTypes++;
372387
return delBuilder.CreateType();
373388
}
374389

390+
private static CustomAttributeBuilder GetAttributeBuilderFromAttributeInstance(Attribute attribute)
391+
{
392+
var attrType = attribute.GetType();
393+
switch (attribute)
394+
{
395+
case MarshalAsAttribute marshalAsAttribute:
396+
{
397+
var ctor = attrType.GetConstructor(MARSHAL_AS_ATTRIBUTE_CTOR_PARAMETERS);
398+
object[] ctorArgs = { marshalAsAttribute.Value };
399+
var fields = attrType.GetFields(BindingFlags.Public | BindingFlags.Instance)
400+
.Where(f => f.FieldType.IsValueType).ToArray(); //XXX: Used to bypass Mono bug, see https://github.com/mono/mono/issues/12747
401+
var fieldArgumentValues = new object[fields.Length];
402+
for(int i = 0; i < fields.Length; i++)
403+
{
404+
fieldArgumentValues[i] = fields[i].GetValue(attribute);
405+
}
406+
407+
//MarshalAsAttribute has no properties other than Value, which is passed in constructor, hence empty properties array
408+
return new CustomAttributeBuilder(ctor, ctorArgs, Array.Empty<PropertyInfo>(), Array.Empty<object>(),
409+
fields, fieldArgumentValues);
410+
}
411+
case InAttribute _:
412+
{
413+
var ctor = attrType.GetConstructor(Type.EmptyTypes);
414+
return new CustomAttributeBuilder(ctor, Array.Empty<object>(), Array.Empty<PropertyInfo>(), Array.Empty<object>(),
415+
Array.Empty<FieldInfo>(), Array.Empty<object>());
416+
}
417+
case OutAttribute _:
418+
{
419+
var ctor = attrType.GetConstructor(Type.EmptyTypes);
420+
return new CustomAttributeBuilder(ctor, Array.Empty<object>(), Array.Empty<PropertyInfo>(), Array.Empty<object>(),
421+
Array.Empty<FieldInfo>(), Array.Empty<object>());
422+
}
423+
default:
424+
throw new NotImplementedException($"Attribute {attrType} is not supported");
425+
}
426+
}
375427

376428
private static string GetDllPath(string dllName)
377429
{

src/NativeFunction.cs

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Reflection;
5-
using System.Runtime.InteropServices;
62

73
namespace DllManipulator.Internal
84
{
@@ -50,51 +46,4 @@ public NativeFunction(NativeFunctionIdentity identity, NativeDll containingDll)
5046
this.containingDll = containingDll;
5147
}
5248
}
53-
54-
internal class NativeFunctionSignature
55-
{
56-
public readonly Type returnParameterType;
57-
public readonly Type[] parameterTypes;
58-
public readonly CallingConvention callingConvention;
59-
public readonly bool bestFitMapping;
60-
public readonly CharSet charSet;
61-
public readonly bool setLastError;
62-
public readonly bool throwOnUnmappableChar;
63-
64-
public NativeFunctionSignature(MethodInfo methodInfo, CallingConvention callingConvention, bool bestFitMapping, CharSet charSet, bool setLastError, bool throwOnUnmappableChar)
65-
{
66-
this.returnParameterType = methodInfo.ReturnType;
67-
this.parameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
68-
this.callingConvention = callingConvention;
69-
this.bestFitMapping = bestFitMapping;
70-
this.charSet = charSet;
71-
this.setLastError = setLastError;
72-
this.throwOnUnmappableChar = throwOnUnmappableChar;
73-
}
74-
75-
public override bool Equals(object obj)
76-
{
77-
return obj is NativeFunctionSignature other &&
78-
EqualityComparer<Type>.Default.Equals(returnParameterType, other.returnParameterType) &&
79-
EqualityComparer<Type[]>.Default.Equals(parameterTypes, other.parameterTypes) &&
80-
callingConvention == other.callingConvention &&
81-
bestFitMapping == other.bestFitMapping &&
82-
charSet == other.charSet &&
83-
setLastError == other.setLastError &&
84-
throwOnUnmappableChar == other.throwOnUnmappableChar;
85-
}
86-
87-
public override int GetHashCode()
88-
{
89-
var hashCode = 763644728;
90-
hashCode = hashCode * -1521134295 + EqualityComparer<Type>.Default.GetHashCode(returnParameterType);
91-
hashCode = hashCode * -1521134295 + EqualityComparer<Type[]>.Default.GetHashCode(parameterTypes);
92-
hashCode = hashCode * -1521134295 + callingConvention.GetHashCode();
93-
hashCode = hashCode * -1521134295 + bestFitMapping.GetHashCode();
94-
hashCode = hashCode * -1521134295 + charSet.GetHashCode();
95-
hashCode = hashCode * -1521134295 + setLastError.GetHashCode();
96-
hashCode = hashCode * -1521134295 + throwOnUnmappableChar.GetHashCode();
97-
return hashCode;
98-
}
99-
}
10049
}

src/NativeFunctionSignature.cs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Runtime.InteropServices;
6+
7+
namespace DllManipulator.Internal
8+
{
9+
internal class NativeFunctionSignature
10+
{
11+
public readonly NativeFunctionParameterSignature returnParameter;
12+
public readonly NativeFunctionParameterSignature[] parameters;
13+
public readonly CallingConvention callingConvention;
14+
public readonly bool bestFitMapping;
15+
public readonly CharSet charSet;
16+
public readonly bool setLastError;
17+
public readonly bool throwOnUnmappableChar;
18+
19+
public NativeFunctionSignature(MethodInfo methodInfo, CallingConvention callingConvention, bool bestFitMapping, CharSet charSet, bool setLastError, bool throwOnUnmappableChar)
20+
{
21+
this.returnParameter = new NativeFunctionParameterSignature(methodInfo.ReturnParameter);
22+
this.parameters = methodInfo.GetParameters().Select(p => new NativeFunctionParameterSignature(p)).ToArray();
23+
this.callingConvention = callingConvention;
24+
this.bestFitMapping = bestFitMapping;
25+
this.charSet = charSet;
26+
this.setLastError = setLastError;
27+
this.throwOnUnmappableChar = throwOnUnmappableChar;
28+
}
29+
30+
public override bool Equals(object obj)
31+
{
32+
var other = obj as NativeFunctionSignature;
33+
if (other == null)
34+
{
35+
return false;
36+
}
37+
38+
if(!returnParameter.Equals(other.returnParameter))
39+
{
40+
return false;
41+
}
42+
43+
if (!parameters.SequenceEqual(other.parameters))
44+
{
45+
return false;
46+
}
47+
48+
if (callingConvention != other.callingConvention)
49+
{
50+
return false;
51+
}
52+
53+
if (bestFitMapping != other.bestFitMapping)
54+
{
55+
return false;
56+
}
57+
58+
if (charSet != other.charSet)
59+
{
60+
return false;
61+
}
62+
63+
if (setLastError != other.setLastError)
64+
{
65+
return false;
66+
}
67+
68+
if (throwOnUnmappableChar != other.throwOnUnmappableChar)
69+
{
70+
return false;
71+
}
72+
73+
return true;
74+
}
75+
76+
public override int GetHashCode()
77+
{
78+
var hashCode = 316391695;
79+
hashCode = hashCode * -1521134295 + returnParameter.GetHashCode();
80+
hashCode = hashCode * -1521134295 + callingConvention.GetHashCode();
81+
hashCode = hashCode * -1521134295 + bestFitMapping.GetHashCode();
82+
hashCode = hashCode * -1521134295 + charSet.GetHashCode();
83+
hashCode = hashCode * -1521134295 + setLastError.GetHashCode();
84+
hashCode = hashCode * -1521134295 + throwOnUnmappableChar.GetHashCode();
85+
return hashCode;
86+
}
87+
}
88+
89+
internal class NativeFunctionParameterSignature
90+
{
91+
public readonly Type type;
92+
public readonly ParameterAttributes parameterAttributes;
93+
public readonly Attribute[] customAttributes;
94+
95+
public NativeFunctionParameterSignature(ParameterInfo parameterInfo)
96+
{
97+
this.type = parameterInfo.ParameterType;
98+
this.parameterAttributes = parameterInfo.Attributes;
99+
var attrs = parameterInfo.GetCustomAttributes(false).OfType<Attribute>(); //XXX: This is required way of obtaining attributes, since both CustomAttributeExtensions.GetCustomAttributes() and Attribute.GetCustomAttributes() return at most 1 attribute (mono bug?)
100+
this.customAttributes = attrs
101+
.Where(a => DllManipulator.SUPPORTED_PARAMATER_ATTRIBUTES.Contains(a.GetType()))
102+
.ToArray();
103+
}
104+
105+
public NativeFunctionParameterSignature(Type type, ParameterAttributes parameterAttributes, Attribute[] customAttributes)
106+
{
107+
this.type = type;
108+
this.parameterAttributes = parameterAttributes;
109+
this.customAttributes = customAttributes;
110+
}
111+
112+
public override bool Equals(object obj)
113+
{
114+
var other = obj as NativeFunctionParameterSignature;
115+
if(other == null)
116+
{
117+
return false;
118+
}
119+
120+
if (type != other.type)
121+
{
122+
return false;
123+
}
124+
125+
if (parameterAttributes != other.parameterAttributes)
126+
{
127+
return false;
128+
}
129+
130+
if (customAttributes.Except(other.customAttributes).Any()) //Check if arrays have the same elements
131+
{
132+
return false;
133+
}
134+
135+
return true;
136+
}
137+
138+
public override int GetHashCode()
139+
{
140+
var hashCode = 424392846;
141+
hashCode = hashCode * -1521134295 + type.GetHashCode();
142+
hashCode = hashCode * -1521134295 + parameterAttributes.GetHashCode();
143+
return hashCode;
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)