Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
.meta
*.meta
74 changes: 63 additions & 11 deletions src/DllManipulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ public class DllManipulator : MonoBehaviour
public const string DLL_PATH_PATTERN_NAME_MACRO = "{name}";
public const string DLL_PATH_PATTERN_ASSETS_MACRO = "{assets}";
public const string DLL_PATH_PATTERN_PROJECT_MACRO = "{proj}";
private static readonly Type[] DELEGATE_CTOR_PARAMETERS = new[] { typeof(object), typeof(IntPtr) };
private static readonly Type[] UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS = new[] { typeof(CallingConvention) };
public static readonly Type[] SUPPORTED_PARAMATER_ATTRIBUTES = { typeof(MarshalAsAttribute), typeof(InAttribute), typeof(OutAttribute) };
private static readonly Type[] DELEGATE_CTOR_PARAMETERS = { typeof(object), typeof(IntPtr) };
private static readonly Type[] UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS = { typeof(CallingConvention) };
private static readonly Type[] MARSHAL_AS_ATTRIBUTE_CTOR_PARAMETERS = { typeof(UnmanagedType) };

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

var mockedDynamicMethod = new DynamicMethod(dllName + ":::" + nativeFunctionSymbol, nativeMethod.ReturnType, parametersTypes, typeof(DllManipulator));
mockedDynamicMethod.DefineParameter(0, nativeMethod.ReturnParameter.Attributes, nativeMethod.ReturnParameter.Name);
mockedDynamicMethod.DefineParameter(0, nativeMethod.ReturnParameter.Attributes, null);
for (int i = 0; i < parameters.Length; i++)
{
mockedDynamicMethod.DefineParameter(parameters[i].Position, parameters[i].Attributes, parameters[i].Name);
mockedDynamicMethod.DefineParameter(i + 1, parameters[i].Attributes, null);
}

if (_nativeFunctionsField == null)
Expand Down Expand Up @@ -333,7 +335,7 @@ private static void AddNativeFunction(NativeFunction nativeFunction)
_nativeFunctions[_nativeFunctionsCount++] = nativeFunction;
}

private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionSignature funcionSignature)
private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionSignature functionSignature)
{
if (_customDelegateTypesModule == null)
{
Expand All @@ -347,31 +349,81 @@ private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionS

//ufp = UnmanagedFunctionPointer
var ufpAttrType = typeof(UnmanagedFunctionPointerAttribute);
var ufpAttrCtor = ufpAttrType.GetConstructor(UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS);
var ufpAttrCtorArgValues = new object[] { funcionSignature.callingConvention };
var ufpAttrNamedFields = new [] {
var ufpAttrCtor = ufpAttrType.GetConstructor(UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS);
object[] ufpAttrCtorArgValues = { functionSignature.callingConvention };
FieldInfo[] ufpAttrNamedFields = {
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.BestFitMapping), BindingFlags.Public | BindingFlags.Instance),
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.CharSet), BindingFlags.Public | BindingFlags.Instance),
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.SetLastError), BindingFlags.Public | BindingFlags.Instance),
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.ThrowOnUnmappableChar), BindingFlags.Public | BindingFlags.Instance),
};
var ufpAttrFieldValues = new object[] { funcionSignature.bestFitMapping, funcionSignature.charSet, funcionSignature.setLastError, funcionSignature.throwOnUnmappableChar };
object[] ufpAttrFieldValues = { functionSignature.bestFitMapping, functionSignature.charSet, functionSignature.setLastError, functionSignature.throwOnUnmappableChar };
var ufpAttrBuilder = new CustomAttributeBuilder(ufpAttrCtor, ufpAttrCtorArgValues, ufpAttrNamedFields, ufpAttrFieldValues);
delBuilder.SetCustomAttribute(ufpAttrBuilder);


var ctorBuilder = delBuilder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
CallingConventions.Standard, DELEGATE_CTOR_PARAMETERS);
ctorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

var invokeBuilder = delBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot,
CallingConventions.Standard | CallingConventions.HasThis, funcionSignature.returnParameterType, funcionSignature.parameterTypes);
CallingConventions.Standard | CallingConventions.HasThis, functionSignature.returnParameter.type, functionSignature.parameters.Select(p => p.type).ToArray());
invokeBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
var invokeReturnParam = invokeBuilder.DefineParameter(0, functionSignature.returnParameter.parameterAttributes, null);
foreach (var attr in functionSignature.returnParameter.customAttributes)
{
invokeReturnParam.SetCustomAttribute(GetAttributeBuilderFromAttributeInstance(attr));
}
for (int i = 0; i < functionSignature.parameters.Length; i++)
{
var param = functionSignature.parameters[i];
var paramBuilder = invokeBuilder.DefineParameter(i + 1, param.parameterAttributes, null);
foreach(var attr in param.customAttributes)
{
paramBuilder.SetCustomAttribute(GetAttributeBuilderFromAttributeInstance(attr));
}
}

_createdDelegateTypes++;
return delBuilder.CreateType();
}

private static CustomAttributeBuilder GetAttributeBuilderFromAttributeInstance(Attribute attribute)
{
var attrType = attribute.GetType();
switch (attribute)
{
case MarshalAsAttribute marshalAsAttribute:
{
var ctor = attrType.GetConstructor(MARSHAL_AS_ATTRIBUTE_CTOR_PARAMETERS);
object[] ctorArgs = { marshalAsAttribute.Value };
var fields = attrType.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => f.FieldType.IsValueType).ToArray(); //XXX: Used to bypass Mono bug, see https://github.com/mono/mono/issues/12747
var fieldArgumentValues = new object[fields.Length];
for(int i = 0; i < fields.Length; i++)
{
fieldArgumentValues[i] = fields[i].GetValue(attribute);
}

//MarshalAsAttribute has no properties other than Value, which is passed in constructor, hence empty properties array
return new CustomAttributeBuilder(ctor, ctorArgs, Array.Empty<PropertyInfo>(), Array.Empty<object>(),
fields, fieldArgumentValues);
}
case InAttribute _:
{
var ctor = attrType.GetConstructor(Type.EmptyTypes);
return new CustomAttributeBuilder(ctor, Array.Empty<object>(), Array.Empty<PropertyInfo>(), Array.Empty<object>(),
Array.Empty<FieldInfo>(), Array.Empty<object>());
}
case OutAttribute _:
{
var ctor = attrType.GetConstructor(Type.EmptyTypes);
return new CustomAttributeBuilder(ctor, Array.Empty<object>(), Array.Empty<PropertyInfo>(), Array.Empty<object>(),
Array.Empty<FieldInfo>(), Array.Empty<object>());
}
default:
throw new NotImplementedException($"Attribute {attrType} is not supported");
}
}

private static string GetDllPath(string dllName)
{
Expand Down
51 changes: 0 additions & 51 deletions src/NativeFunction.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

namespace DllManipulator.Internal
{
Expand Down Expand Up @@ -50,51 +46,4 @@ public NativeFunction(NativeFunctionIdentity identity, NativeDll containingDll)
this.containingDll = containingDll;
}
}

internal class NativeFunctionSignature
{
public readonly Type returnParameterType;
public readonly Type[] parameterTypes;
public readonly CallingConvention callingConvention;
public readonly bool bestFitMapping;
public readonly CharSet charSet;
public readonly bool setLastError;
public readonly bool throwOnUnmappableChar;

public NativeFunctionSignature(MethodInfo methodInfo, CallingConvention callingConvention, bool bestFitMapping, CharSet charSet, bool setLastError, bool throwOnUnmappableChar)
{
this.returnParameterType = methodInfo.ReturnType;
this.parameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
this.callingConvention = callingConvention;
this.bestFitMapping = bestFitMapping;
this.charSet = charSet;
this.setLastError = setLastError;
this.throwOnUnmappableChar = throwOnUnmappableChar;
}

public override bool Equals(object obj)
{
return obj is NativeFunctionSignature other &&
EqualityComparer<Type>.Default.Equals(returnParameterType, other.returnParameterType) &&
EqualityComparer<Type[]>.Default.Equals(parameterTypes, other.parameterTypes) &&
callingConvention == other.callingConvention &&
bestFitMapping == other.bestFitMapping &&
charSet == other.charSet &&
setLastError == other.setLastError &&
throwOnUnmappableChar == other.throwOnUnmappableChar;
}

public override int GetHashCode()
{
var hashCode = 763644728;
hashCode = hashCode * -1521134295 + EqualityComparer<Type>.Default.GetHashCode(returnParameterType);
hashCode = hashCode * -1521134295 + EqualityComparer<Type[]>.Default.GetHashCode(parameterTypes);
hashCode = hashCode * -1521134295 + callingConvention.GetHashCode();
hashCode = hashCode * -1521134295 + bestFitMapping.GetHashCode();
hashCode = hashCode * -1521134295 + charSet.GetHashCode();
hashCode = hashCode * -1521134295 + setLastError.GetHashCode();
hashCode = hashCode * -1521134295 + throwOnUnmappableChar.GetHashCode();
return hashCode;
}
}
}
146 changes: 146 additions & 0 deletions src/NativeFunctionSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

namespace DllManipulator.Internal
{
internal class NativeFunctionSignature
{
public readonly NativeFunctionParameterSignature returnParameter;
public readonly NativeFunctionParameterSignature[] parameters;
public readonly CallingConvention callingConvention;
public readonly bool bestFitMapping;
public readonly CharSet charSet;
public readonly bool setLastError;
public readonly bool throwOnUnmappableChar;

public NativeFunctionSignature(MethodInfo methodInfo, CallingConvention callingConvention, bool bestFitMapping, CharSet charSet, bool setLastError, bool throwOnUnmappableChar)
{
this.returnParameter = new NativeFunctionParameterSignature(methodInfo.ReturnParameter);
this.parameters = methodInfo.GetParameters().Select(p => new NativeFunctionParameterSignature(p)).ToArray();
this.callingConvention = callingConvention;
this.bestFitMapping = bestFitMapping;
this.charSet = charSet;
this.setLastError = setLastError;
this.throwOnUnmappableChar = throwOnUnmappableChar;
}

public override bool Equals(object obj)
{
var other = obj as NativeFunctionSignature;
if (other == null)
{
return false;
}

if(!returnParameter.Equals(other.returnParameter))
{
return false;
}

if (!parameters.SequenceEqual(other.parameters))
{
return false;
}

if (callingConvention != other.callingConvention)
{
return false;
}

if (bestFitMapping != other.bestFitMapping)
{
return false;
}

if (charSet != other.charSet)
{
return false;
}

if (setLastError != other.setLastError)
{
return false;
}

if (throwOnUnmappableChar != other.throwOnUnmappableChar)
{
return false;
}

return true;
}

public override int GetHashCode()
{
var hashCode = 316391695;
hashCode = hashCode * -1521134295 + returnParameter.GetHashCode();
hashCode = hashCode * -1521134295 + callingConvention.GetHashCode();
hashCode = hashCode * -1521134295 + bestFitMapping.GetHashCode();
hashCode = hashCode * -1521134295 + charSet.GetHashCode();
hashCode = hashCode * -1521134295 + setLastError.GetHashCode();
hashCode = hashCode * -1521134295 + throwOnUnmappableChar.GetHashCode();
return hashCode;
}
}

internal class NativeFunctionParameterSignature
{
public readonly Type type;
public readonly ParameterAttributes parameterAttributes;
public readonly Attribute[] customAttributes;

public NativeFunctionParameterSignature(ParameterInfo parameterInfo)
{
this.type = parameterInfo.ParameterType;
this.parameterAttributes = parameterInfo.Attributes;
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?)
this.customAttributes = attrs
.Where(a => DllManipulator.SUPPORTED_PARAMATER_ATTRIBUTES.Contains(a.GetType()))
.ToArray();
}

public NativeFunctionParameterSignature(Type type, ParameterAttributes parameterAttributes, Attribute[] customAttributes)
{
this.type = type;
this.parameterAttributes = parameterAttributes;
this.customAttributes = customAttributes;
}

public override bool Equals(object obj)
{
var other = obj as NativeFunctionParameterSignature;
if(other == null)
{
return false;
}

if (type != other.type)
{
return false;
}

if (parameterAttributes != other.parameterAttributes)
{
return false;
}

if (customAttributes.Except(other.customAttributes).Any()) //Check if arrays have the same elements
{
return false;
}

return true;
}

public override int GetHashCode()
{
var hashCode = 424392846;
hashCode = hashCode * -1521134295 + type.GetHashCode();
hashCode = hashCode * -1521134295 + parameterAttributes.GetHashCode();
return hashCode;
}
}
}