pschelpdesk/Vendor/Prise.Proxy/runtime/DispatchProxyGenerator.cs
2024-11-04 20:45:34 +01:00

935 lines
42 KiB
C#

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// Original file: https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.ExceptionServices;
using System.Threading;
namespace System.Reflection
{
// Helper class to handle the IL EMIT for the generation of proxies.
// Much of this code was taken directly from the Silverlight proxy generation.
// Differences between this and the Silverlight version are:
// 1. This version is based on DispatchProxy from NET Native and CoreCLR, not RealProxy in Silverlight ServiceModel.
// There are several notable differences between them.
// 2. Both DispatchProxy and RealProxy permit the caller to ask for a proxy specifying a pair of types:
// the interface type to implement, and a base type. But they behave slightly differently:
// - RealProxy generates a proxy type that derives from Object and *implements" all the base type's
// interfaces plus all the interface type's interfaces.
// - DispatchProxy generates a proxy type that *derives* from the base type and implements all
// the interface type's interfaces. This is true for both the CLR version in NET Native and this
// version for CoreCLR.
// 3. DispatchProxy and RealProxy use different type hierarchies for the generated proxies:
// - RealProxy type hierarchy is:
// proxyType : proxyBaseType : object
// Presumably the 'proxyBaseType' in the middle is to allow it to implement the base type's interfaces
// explicitly, preventing collision for same name methods on the base and interface types.
// - DispatchProxy hierarchy is:
// proxyType : baseType (where baseType : DispatchProxy)
// The generated DispatchProxy proxy type does not need to generate implementation methods
// for the base type's interfaces, because the base type already must have implemented them.
// 4. RealProxy required a proxy instance to hold a backpointer to the RealProxy instance to mirror
// the .NET Remoting design that required the proxy and RealProxy to be separate instances.
// But the DispatchProxy design encourages the proxy type to *be* an DispatchProxy. Therefore,
// the proxy's 'this' becomes the equivalent of RealProxy's backpointer to RealProxy, so we were
// able to remove an extraneous field and ctor arg from the DispatchProxy proxies.
//
internal static class DispatchProxyGenerator
{
// Generated proxies have a private Action field that all generated methods
// invoke. It is the first field in the class and the first ctor parameter.
private const int InvokeActionFieldAndCtorParameterIndex = 0;
// Proxies are requested for a pair of types: base type and interface type.
// The generated proxy will subclass the given base type and implement the interface type.
// We maintain a cache keyed by 'base type' containing a dictionary keyed by interface type,
// containing the generated proxy type for that pair. There are likely to be few (maybe only 1)
// base type in use for many interface types.
// Note: this differs from Silverlight's RealProxy implementation which keys strictly off the
// interface type. But this does not allow the same interface type to be used with more than a
// single base type. The implementation here permits multiple interface types to be used with
// multiple base types, and the generated proxy types will be unique.
// This cache of generated types grows unbounded, one element per unique T/ProxyT pair.
// This approach is used to prevent regenerating identical proxy types for identical T/Proxy pairs,
// which would ultimately be a more expensive leak.
// Proxy instances are not cached. Their lifetime is entirely owned by the caller of DispatchProxy.Create.
private static readonly Dictionary<Type, Dictionary<Type, Type>> s_baseTypeAndInterfaceToGeneratedProxyType = new Dictionary<Type, Dictionary<Type, Type>>();
private static readonly ProxyAssembly s_proxyAssembly = new ProxyAssembly();
private static readonly MethodInfo s_dispatchProxyInvokeMethod = typeof(DispatchProxy).GetTypeInfo().GetDeclaredMethod("Invoke")!;
// Returns a new instance of a proxy the derives from 'baseType' and implements 'interfaceType'
internal static object CreateProxyInstance(Type baseType, Type interfaceType)
{
Debug.Assert(baseType != null);
Debug.Assert(interfaceType != null);
Type proxiedType = GetProxyType(baseType!, interfaceType!);
return Activator.CreateInstance(proxiedType, (Action<object[]>)DispatchProxyGenerator.Invoke)!;
}
private static Type GetProxyType(Type baseType, Type interfaceType)
{
lock (s_baseTypeAndInterfaceToGeneratedProxyType)
{
if (!s_baseTypeAndInterfaceToGeneratedProxyType.TryGetValue(baseType, out Dictionary<Type, Type>? interfaceToProxy))
{
interfaceToProxy = new Dictionary<Type, Type>();
s_baseTypeAndInterfaceToGeneratedProxyType[baseType] = interfaceToProxy;
}
if (!interfaceToProxy.TryGetValue(interfaceType, out Type? generatedProxy))
{
generatedProxy = GenerateProxyType(baseType, interfaceType);
interfaceToProxy[interfaceType] = generatedProxy;
}
return generatedProxy;
}
}
// Unconditionally generates a new proxy type derived from 'baseType' and implements 'interfaceType'
private static Type GenerateProxyType(Type baseType, Type interfaceType)
{
// Parameter validation is deferred until the point we need to create the proxy.
// This prevents unnecessary overhead revalidating cached proxy types.
TypeInfo baseTypeInfo = baseType.GetTypeInfo();
// The interface type must be an interface, not a class
if (!interfaceType.GetTypeInfo().IsInterface)
{
// "T" is the generic parameter seen via the public contract
throw new ArgumentException("SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName)", "T");
}
// The base type cannot be sealed because the proxy needs to subclass it.
if (baseTypeInfo.IsSealed)
{
// "TProxy" is the generic parameter seen via the public contract
throw new ArgumentException("SR.Format(SR.BaseType_Cannot_Be_Sealed, baseTypeInfo.FullName)", "TProxy");
}
// The base type cannot be abstract
if (baseTypeInfo.IsAbstract)
{
throw new ArgumentException("SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName)", "TProxy");
}
// The base type must have a public default ctor
if (!baseTypeInfo.DeclaredConstructors.Any(c => c.IsPublic && c.GetParameters().Length == 0))
{
throw new ArgumentException("SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName)", "TProxy");
}
// Create a type that derives from 'baseType' provided by caller
ProxyBuilder pb = s_proxyAssembly.CreateProxy("generatedProxy", baseType);
foreach (Type t in interfaceType.GetTypeInfo().ImplementedInterfaces)
pb.AddInterfaceImpl(t);
pb.AddInterfaceImpl(interfaceType);
Type generatedProxyType = pb.CreateType();
return generatedProxyType;
}
// All generated proxy methods call this static helper method to dispatch.
// Its job is to unpack the arguments and the 'this' instance and to dispatch directly
// to the (abstract) DispatchProxy.Invoke() method.
private static void Invoke(object?[] args)
{
PackedArgs packed = new PackedArgs(args);
MethodBase method = s_proxyAssembly.ResolveMethodToken(packed.DeclaringType, packed.MethodToken);
if (method.IsGenericMethodDefinition)
method = ((MethodInfo)method).MakeGenericMethod(packed.GenericTypes!);
// Call (protected method) DispatchProxy.Invoke()
try
{
Debug.Assert(s_dispatchProxyInvokeMethod != null);
object? returnValue = s_dispatchProxyInvokeMethod!.Invoke(packed.DispatchProxy,
new object?[] { method, packed.Args });
packed.ReturnValue = returnValue;
}
catch (TargetInvocationException tie)
{
Debug.Assert(tie.InnerException != null);
ExceptionDispatchInfo.Capture(tie.InnerException).Throw();
}
}
private class PackedArgs
{
internal const int DispatchProxyPosition = 0;
internal const int DeclaringTypePosition = 1;
internal const int MethodTokenPosition = 2;
internal const int ArgsPosition = 3;
internal const int GenericTypesPosition = 4;
internal const int ReturnValuePosition = 5;
internal static readonly Type[] PackedTypes = new Type[] { typeof(object), typeof(Type), typeof(int), typeof(object[]), typeof(Type[]), typeof(object) };
private readonly object?[] _args;
internal PackedArgs() : this(new object[PackedTypes.Length]) { }
internal PackedArgs(object?[] args) { _args = args; }
internal DispatchProxy? DispatchProxy { get { return (DispatchProxy?)_args[DispatchProxyPosition]; } }
internal Type? DeclaringType { get { return (Type?)_args[DeclaringTypePosition]; } }
internal int MethodToken { get { return (int)_args[MethodTokenPosition]!; } }
internal object[]? Args { get { return (object[]?)_args[ArgsPosition]; } }
internal Type[]? GenericTypes { get { return (Type[]?)_args[GenericTypesPosition]; } }
internal object? ReturnValue { /*get { return args[ReturnValuePosition]; }*/ set { _args[ReturnValuePosition] = value; } }
}
private class ProxyAssembly
{
private readonly AssemblyBuilder _ab;
private readonly ModuleBuilder _mb;
private int _typeId;
// Maintain a MethodBase-->int, int-->MethodBase mapping to permit generated code
// to pass methods by token
private readonly Dictionary<MethodBase, int> _methodToToken = new Dictionary<MethodBase, int>();
private readonly List<MethodBase> _methodsByToken = new List<MethodBase>();
private readonly HashSet<string?> _ignoresAccessAssemblyNames = new HashSet<string?>();
private ConstructorInfo? _ignoresAccessChecksToAttributeConstructor;
public ProxyAssembly()
{
_ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("ProxyBuilder"), AssemblyBuilderAccess.Run);
_mb = _ab.DefineDynamicModule("testmod");
}
// Gets or creates the ConstructorInfo for the IgnoresAccessChecksAttribute.
// This attribute is both defined and referenced in the dynamic assembly to
// allow access to internal types in other assemblies.
internal ConstructorInfo IgnoresAccessChecksAttributeConstructor
{
get
{
if (_ignoresAccessChecksToAttributeConstructor == null)
{
_ignoresAccessChecksToAttributeConstructor = IgnoreAccessChecksToAttributeBuilder.AddToModule(_mb);
}
return null;
}
}
public ProxyBuilder CreateProxy(string name, Type proxyBaseType)
{
int nextId = Interlocked.Increment(ref _typeId);
TypeBuilder tb = _mb.DefineType(name + "_" + nextId, TypeAttributes.Public, proxyBaseType);
return new ProxyBuilder(this, tb, proxyBaseType);
}
// Generates an instance of the IgnoresAccessChecksToAttribute to
// identify the given assembly as one which contains internal types
// the dynamic assembly will need to reference.
internal void GenerateInstanceOfIgnoresAccessChecksToAttribute(string assemblyName)
{
// Add this assembly level attribute:
// [assembly: System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute(assemblyName)]
ConstructorInfo attributeConstructor = IgnoresAccessChecksAttributeConstructor;
CustomAttributeBuilder customAttributeBuilder =
new CustomAttributeBuilder(attributeConstructor, new object[] { assemblyName });
_ab.SetCustomAttribute(customAttributeBuilder);
}
// Ensures the type we will reference from the dynamic assembly
// is visible. Non-public types need to emit an attribute that
// allows access from the dynamic assembly.
internal void EnsureTypeIsVisible(Type type)
{
TypeInfo typeInfo = type.GetTypeInfo();
if (!typeInfo.IsVisible)
{
string assemblyName = typeInfo.Assembly.GetName().Name!;
if (!_ignoresAccessAssemblyNames.Contains(assemblyName))
{
GenerateInstanceOfIgnoresAccessChecksToAttribute(assemblyName);
_ignoresAccessAssemblyNames.Add(assemblyName);
}
}
}
internal void GetTokenForMethod(MethodBase method, out Type type, out int token)
{
Debug.Assert(method.DeclaringType != null);
type = method.DeclaringType!;
token = 0;
if (!_methodToToken.TryGetValue(method, out token))
{
_methodsByToken.Add(method);
token = _methodsByToken.Count - 1;
_methodToToken[method] = token;
}
}
internal MethodBase ResolveMethodToken(Type? type, int token)
{
Debug.Assert(token >= 0 && token < _methodsByToken.Count);
return _methodsByToken[token];
}
}
private class ProxyBuilder
{
private static readonly MethodInfo s_delegateInvoke = typeof(Action<object[]>).GetTypeInfo().GetDeclaredMethod("Invoke")!;
private readonly ProxyAssembly _assembly;
private readonly TypeBuilder _tb;
private readonly Type _proxyBaseType;
private readonly List<FieldBuilder> _fields;
internal ProxyBuilder(ProxyAssembly assembly, TypeBuilder tb, Type proxyBaseType)
{
_assembly = assembly;
_tb = tb;
_proxyBaseType = proxyBaseType;
_fields = new List<FieldBuilder>();
_fields.Add(tb.DefineField("invoke", typeof(Action<object[]>), FieldAttributes.Private));
}
private void Complete()
{
Type[] args = new Type[_fields.Count];
for (int i = 0; i < args.Length; i++)
{
args[i] = _fields[i].FieldType;
}
ConstructorBuilder cb = _tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, args);
ILGenerator il = cb.GetILGenerator();
// chained ctor call
ConstructorInfo? baseCtor = _proxyBaseType.GetTypeInfo().DeclaredConstructors.SingleOrDefault(c => c.IsPublic && c.GetParameters().Length == 0);
Debug.Assert(baseCtor != null);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseCtor!);
// store all the fields
for (int i = 0; i < args.Length; i++)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg, i + 1);
il.Emit(OpCodes.Stfld, _fields[i]);
}
il.Emit(OpCodes.Ret);
}
internal Type CreateType()
{
this.Complete();
return _tb.CreateTypeInfo()!.AsType();
}
internal void AddInterfaceImpl(Type iface)
{
// If necessary, generate an attribute to permit visibility
// to internal types.
_assembly.EnsureTypeIsVisible(iface);
_tb.AddInterfaceImplementation(iface);
// AccessorMethods -> Metadata mappings.
var propertyMap = new Dictionary<MethodInfo, PropertyAccessorInfo>(MethodInfoEqualityComparer.Instance);
foreach (PropertyInfo pi in iface.GetRuntimeProperties())
{
var ai = new PropertyAccessorInfo(pi.GetMethod, pi.SetMethod);
if (pi.GetMethod != null)
propertyMap[pi.GetMethod] = ai;
if (pi.SetMethod != null)
propertyMap[pi.SetMethod] = ai;
}
var eventMap = new Dictionary<MethodInfo, EventAccessorInfo>(MethodInfoEqualityComparer.Instance);
foreach (EventInfo ei in iface.GetRuntimeEvents())
{
var ai = new EventAccessorInfo(ei.AddMethod, ei.RemoveMethod, ei.RaiseMethod);
if (ei.AddMethod != null)
eventMap[ei.AddMethod] = ai;
if (ei.RemoveMethod != null)
eventMap[ei.RemoveMethod] = ai;
if (ei.RaiseMethod != null)
eventMap[ei.RaiseMethod] = ai;
}
foreach (MethodInfo mi in iface.GetRuntimeMethods())
{
// Skip regular/non-virtual instance methods, static methods, and methods that cannot be overriden
// ("methods that cannot be overriden" includes default implementation of other interface methods).
if (!mi.IsVirtual || mi.IsFinal)
continue;
MethodBuilder mdb = AddMethodImpl(mi);
if (propertyMap.TryGetValue(mi, out PropertyAccessorInfo? associatedProperty))
{
if (MethodInfoEqualityComparer.Instance.Equals(associatedProperty.InterfaceGetMethod, mi))
associatedProperty.GetMethodBuilder = mdb;
else
associatedProperty.SetMethodBuilder = mdb;
}
if (eventMap.TryGetValue(mi, out EventAccessorInfo? associatedEvent))
{
if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceAddMethod, mi))
associatedEvent.AddMethodBuilder = mdb;
else if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceRemoveMethod, mi))
associatedEvent.RemoveMethodBuilder = mdb;
else
associatedEvent.RaiseMethodBuilder = mdb;
}
}
foreach (PropertyInfo pi in iface.GetRuntimeProperties())
{
PropertyAccessorInfo ai = propertyMap[pi.GetMethod ?? pi.SetMethod!];
// If we didn't make an overriden accessor above, this was a static property, non-virtual property,
// or a default implementation of a property of a different interface. In any case, we don't need
// to redeclare it.
if (ai.GetMethodBuilder == null && ai.SetMethodBuilder == null)
continue;
PropertyBuilder pb = _tb.DefineProperty(pi.Name, pi.Attributes, pi.PropertyType, pi.GetIndexParameters().Select(p => p.ParameterType).ToArray());
if (ai.GetMethodBuilder != null)
pb.SetGetMethod(ai.GetMethodBuilder);
if (ai.SetMethodBuilder != null)
pb.SetSetMethod(ai.SetMethodBuilder);
}
foreach (EventInfo ei in iface.GetRuntimeEvents())
{
EventAccessorInfo ai = eventMap[ei.AddMethod ?? ei.RemoveMethod!];
// If we didn't make an overriden accessor above, this was a static event, non-virtual event,
// or a default implementation of an event of a different interface. In any case, we don't
// need to redeclare it.
if (ai.AddMethodBuilder == null && ai.RemoveMethodBuilder == null && ai.RaiseMethodBuilder == null)
continue;
Debug.Assert(ei.EventHandlerType != null);
EventBuilder eb = _tb.DefineEvent(ei.Name, ei.Attributes, ei.EventHandlerType!);
if (ai.AddMethodBuilder != null)
eb.SetAddOnMethod(ai.AddMethodBuilder);
if (ai.RemoveMethodBuilder != null)
eb.SetRemoveOnMethod(ai.RemoveMethodBuilder);
if (ai.RaiseMethodBuilder != null)
eb.SetRaiseMethod(ai.RaiseMethodBuilder);
}
}
private MethodBuilder AddMethodImpl(MethodInfo mi)
{
ParameterInfo[] parameters = mi.GetParameters();
Type[] paramTypes = ParamTypes(parameters, false);
MethodBuilder mdb = _tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, mi.ReturnType, paramTypes);
if (mi.ContainsGenericParameters)
{
Type[] ts = mi.GetGenericArguments();
string[] ss = new string[ts.Length];
for (int i = 0; i < ts.Length; i++)
{
ss[i] = ts[i].Name;
}
GenericTypeParameterBuilder[] genericParameters = mdb.DefineGenericParameters(ss);
for (int i = 0; i < genericParameters.Length; i++)
{
genericParameters[i].SetGenericParameterAttributes(ts[i].GetTypeInfo().GenericParameterAttributes);
}
}
ILGenerator il = mdb.GetILGenerator();
ParametersArray args = new ParametersArray(il, paramTypes);
// object[] args = new object[paramCount];
il.Emit(OpCodes.Nop);
GenericArray<object> argsArr = new GenericArray<object>(il, ParamTypes(parameters, true).Length);
for (int i = 0; i < parameters.Length; i++)
{
// args[i] = argi;
bool isOutRef = parameters[i].IsOut && parameters[i].ParameterType.IsByRef && !parameters[i].IsIn;
if (!isOutRef)
{
argsArr.BeginSet(i);
args.Get(i);
argsArr.EndSet(parameters[i].ParameterType);
}
}
// object[] packed = new object[PackedArgs.PackedTypes.Length];
GenericArray<object> packedArr = new GenericArray<object>(il, PackedArgs.PackedTypes.Length);
// packed[PackedArgs.DispatchProxyPosition] = this;
packedArr.BeginSet(PackedArgs.DispatchProxyPosition);
il.Emit(OpCodes.Ldarg_0);
packedArr.EndSet(typeof(DispatchProxy));
// packed[PackedArgs.DeclaringTypePosition] = typeof(iface);
MethodInfo Type_GetTypeFromHandle = typeof(Type).GetRuntimeMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) })!;
_assembly.GetTokenForMethod(mi, out Type declaringType, out int methodToken);
packedArr.BeginSet(PackedArgs.DeclaringTypePosition);
il.Emit(OpCodes.Ldtoken, declaringType);
il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
packedArr.EndSet(typeof(object));
// packed[PackedArgs.MethodTokenPosition] = iface method token;
packedArr.BeginSet(PackedArgs.MethodTokenPosition);
il.Emit(OpCodes.Ldc_I4, methodToken);
packedArr.EndSet(typeof(int));
// packed[PackedArgs.ArgsPosition] = args;
packedArr.BeginSet(PackedArgs.ArgsPosition);
argsArr.Load();
packedArr.EndSet(typeof(object[]));
// packed[PackedArgs.GenericTypesPosition] = mi.GetGenericArguments();
if (mi.ContainsGenericParameters)
{
packedArr.BeginSet(PackedArgs.GenericTypesPosition);
Type[] genericTypes = mi.GetGenericArguments();
GenericArray<Type> typeArr = new GenericArray<Type>(il, genericTypes.Length);
for (int i = 0; i < genericTypes.Length; ++i)
{
typeArr.BeginSet(i);
il.Emit(OpCodes.Ldtoken, genericTypes[i]);
il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
typeArr.EndSet(typeof(Type));
}
typeArr.Load();
packedArr.EndSet(typeof(Type[]));
}
// Call static DispatchProxyHelper.Invoke(object[])
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, _fields[InvokeActionFieldAndCtorParameterIndex]); // delegate
packedArr.Load();
il.Emit(OpCodes.Call, s_delegateInvoke);
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].ParameterType.IsByRef)
{
args.BeginSet(i);
argsArr.Get(i);
args.EndSet(i, typeof(object));
}
}
if (mi.ReturnType != typeof(void))
{
packedArr.Get(PackedArgs.ReturnValuePosition);
Convert(il, typeof(object), mi.ReturnType, false);
}
il.Emit(OpCodes.Ret);
_tb.DefineMethodOverride(mdb, mi);
return mdb;
}
private static Type[] ParamTypes(ParameterInfo[] parms, bool noByRef)
{
Type[] types = new Type[parms.Length];
for (int i = 0; i < parms.Length; i++)
{
types[i] = parms[i].ParameterType;
if (noByRef && types[i].IsByRef)
types[i] = types[i].GetElementType()!;
}
return types;
}
// TypeCode does not exist in ProjectK or ProjectN.
// This lookup method was copied from PortableLibraryThunks\Internal\PortableLibraryThunks\System\TypeThunks.cs
// but returns the integer value equivalent to its TypeCode enum.
private static int GetTypeCode(Type? type)
{
if (type == null)
return 0; // TypeCode.Empty;
if (type == typeof(bool))
return 3; // TypeCode.Boolean;
if (type == typeof(char))
return 4; // TypeCode.Char;
if (type == typeof(sbyte))
return 5; // TypeCode.SByte;
if (type == typeof(byte))
return 6; // TypeCode.Byte;
if (type == typeof(short))
return 7; // TypeCode.Int16;
if (type == typeof(ushort))
return 8; // TypeCode.UInt16;
if (type == typeof(int))
return 9; // TypeCode.Int32;
if (type == typeof(uint))
return 10; // TypeCode.UInt32;
if (type == typeof(long))
return 11; // TypeCode.Int64;
if (type == typeof(ulong))
return 12; // TypeCode.UInt64;
if (type == typeof(float))
return 13; // TypeCode.Single;
if (type == typeof(double))
return 14; // TypeCode.Double;
if (type == typeof(decimal))
return 15; // TypeCode.Decimal;
if (type == typeof(DateTime))
return 16; // TypeCode.DateTime;
if (type == typeof(string))
return 18; // TypeCode.String;
if (type.GetTypeInfo().IsEnum)
return GetTypeCode(Enum.GetUnderlyingType(type));
return 1; // TypeCode.Object;
}
private static readonly OpCode[] s_convOpCodes = new OpCode[] {
OpCodes.Nop, //Empty = 0,
OpCodes.Nop, //Object = 1,
OpCodes.Nop, //DBNull = 2,
OpCodes.Conv_I1, //Boolean = 3,
OpCodes.Conv_I2, //Char = 4,
OpCodes.Conv_I1, //SByte = 5,
OpCodes.Conv_U1, //Byte = 6,
OpCodes.Conv_I2, //Int16 = 7,
OpCodes.Conv_U2, //UInt16 = 8,
OpCodes.Conv_I4, //Int32 = 9,
OpCodes.Conv_U4, //UInt32 = 10,
OpCodes.Conv_I8, //Int64 = 11,
OpCodes.Conv_U8, //UInt64 = 12,
OpCodes.Conv_R4, //Single = 13,
OpCodes.Conv_R8, //Double = 14,
OpCodes.Nop, //Decimal = 15,
OpCodes.Nop, //DateTime = 16,
OpCodes.Nop, //17
OpCodes.Nop, //String = 18,
};
private static readonly OpCode[] s_ldindOpCodes = new OpCode[] {
OpCodes.Nop, //Empty = 0,
OpCodes.Nop, //Object = 1,
OpCodes.Nop, //DBNull = 2,
OpCodes.Ldind_I1, //Boolean = 3,
OpCodes.Ldind_I2, //Char = 4,
OpCodes.Ldind_I1, //SByte = 5,
OpCodes.Ldind_U1, //Byte = 6,
OpCodes.Ldind_I2, //Int16 = 7,
OpCodes.Ldind_U2, //UInt16 = 8,
OpCodes.Ldind_I4, //Int32 = 9,
OpCodes.Ldind_U4, //UInt32 = 10,
OpCodes.Ldind_I8, //Int64 = 11,
OpCodes.Ldind_I8, //UInt64 = 12,
OpCodes.Ldind_R4, //Single = 13,
OpCodes.Ldind_R8, //Double = 14,
OpCodes.Nop, //Decimal = 15,
OpCodes.Nop, //DateTime = 16,
OpCodes.Nop, //17
OpCodes.Ldind_Ref, //String = 18,
};
private static readonly OpCode[] s_stindOpCodes = new OpCode[] {
OpCodes.Nop, //Empty = 0,
OpCodes.Nop, //Object = 1,
OpCodes.Nop, //DBNull = 2,
OpCodes.Stind_I1, //Boolean = 3,
OpCodes.Stind_I2, //Char = 4,
OpCodes.Stind_I1, //SByte = 5,
OpCodes.Stind_I1, //Byte = 6,
OpCodes.Stind_I2, //Int16 = 7,
OpCodes.Stind_I2, //UInt16 = 8,
OpCodes.Stind_I4, //Int32 = 9,
OpCodes.Stind_I4, //UInt32 = 10,
OpCodes.Stind_I8, //Int64 = 11,
OpCodes.Stind_I8, //UInt64 = 12,
OpCodes.Stind_R4, //Single = 13,
OpCodes.Stind_R8, //Double = 14,
OpCodes.Nop, //Decimal = 15,
OpCodes.Nop, //DateTime = 16,
OpCodes.Nop, //17
OpCodes.Stind_Ref, //String = 18,
};
private static void Convert(ILGenerator il, Type source, Type target, bool isAddress)
{
Debug.Assert(!target.IsByRef);
if (target == source)
return;
TypeInfo sourceTypeInfo = source.GetTypeInfo();
TypeInfo targetTypeInfo = target.GetTypeInfo();
if (source.IsByRef)
{
Debug.Assert(!isAddress);
Type argType = source.GetElementType()!;
Ldind(il, argType);
Convert(il, argType, target, isAddress);
return;
}
if (targetTypeInfo.IsValueType)
{
if (sourceTypeInfo.IsValueType)
{
OpCode opCode = s_convOpCodes[GetTypeCode(target)];
Debug.Assert(!opCode.Equals(OpCodes.Nop));
il.Emit(opCode);
}
else
{
Debug.Assert(sourceTypeInfo.IsAssignableFrom(targetTypeInfo));
il.Emit(OpCodes.Unbox, target);
if (!isAddress)
Ldind(il, target);
}
}
else if (targetTypeInfo.IsAssignableFrom(sourceTypeInfo))
{
if (sourceTypeInfo.IsValueType || source.IsGenericParameter)
{
if (isAddress)
Ldind(il, source);
il.Emit(OpCodes.Box, source);
}
}
else
{
Debug.Assert(sourceTypeInfo.IsAssignableFrom(targetTypeInfo) || targetTypeInfo.IsInterface || sourceTypeInfo.IsInterface);
if (target.IsGenericParameter)
{
il.Emit(OpCodes.Unbox_Any, target);
}
else
{
il.Emit(OpCodes.Castclass, target);
}
}
}
private static void Ldind(ILGenerator il, Type type)
{
OpCode opCode = s_ldindOpCodes[GetTypeCode(type)];
if (!opCode.Equals(OpCodes.Nop))
{
il.Emit(opCode);
}
else
{
il.Emit(OpCodes.Ldobj, type);
}
}
private static void Stind(ILGenerator il, Type type)
{
OpCode opCode = s_stindOpCodes[GetTypeCode(type)];
if (!opCode.Equals(OpCodes.Nop))
{
il.Emit(opCode);
}
else
{
il.Emit(OpCodes.Stobj, type);
}
}
private class ParametersArray
{
private readonly ILGenerator _il;
private readonly Type[] _paramTypes;
internal ParametersArray(ILGenerator il, Type[] paramTypes)
{
_il = il;
_paramTypes = paramTypes;
}
internal void Get(int i)
{
_il.Emit(OpCodes.Ldarg, i + 1);
}
internal void BeginSet(int i)
{
_il.Emit(OpCodes.Ldarg, i + 1);
}
internal void EndSet(int i, Type stackType)
{
Debug.Assert(_paramTypes[i].IsByRef);
Type argType = _paramTypes[i].GetElementType()!;
Convert(_il, stackType, argType, false);
Stind(_il, argType);
}
}
private class GenericArray<T>
{
private readonly ILGenerator _il;
private readonly LocalBuilder _lb;
internal GenericArray(ILGenerator il, int len)
{
_il = il;
_lb = il.DeclareLocal(typeof(T[]));
il.Emit(OpCodes.Ldc_I4, len);
il.Emit(OpCodes.Newarr, typeof(T));
il.Emit(OpCodes.Stloc, _lb);
}
internal void Load()
{
_il.Emit(OpCodes.Ldloc, _lb);
}
internal void Get(int i)
{
_il.Emit(OpCodes.Ldloc, _lb);
_il.Emit(OpCodes.Ldc_I4, i);
_il.Emit(OpCodes.Ldelem_Ref);
}
internal void BeginSet(int i)
{
_il.Emit(OpCodes.Ldloc, _lb);
_il.Emit(OpCodes.Ldc_I4, i);
}
internal void EndSet(Type stackType)
{
Convert(_il, stackType, typeof(T), false);
_il.Emit(OpCodes.Stelem_Ref);
}
}
private sealed class PropertyAccessorInfo
{
public MethodInfo? InterfaceGetMethod { get; }
public MethodInfo? InterfaceSetMethod { get; }
public MethodBuilder? GetMethodBuilder { get; set; }
public MethodBuilder? SetMethodBuilder { get; set; }
public PropertyAccessorInfo(MethodInfo? interfaceGetMethod, MethodInfo? interfaceSetMethod)
{
InterfaceGetMethod = interfaceGetMethod;
InterfaceSetMethod = interfaceSetMethod;
}
}
private sealed class EventAccessorInfo
{
public MethodInfo? InterfaceAddMethod { get; }
public MethodInfo? InterfaceRemoveMethod { get; }
public MethodInfo? InterfaceRaiseMethod { get; }
public MethodBuilder? AddMethodBuilder { get; set; }
public MethodBuilder? RemoveMethodBuilder { get; set; }
public MethodBuilder? RaiseMethodBuilder { get; set; }
public EventAccessorInfo(MethodInfo? interfaceAddMethod, MethodInfo? interfaceRemoveMethod, MethodInfo? interfaceRaiseMethod)
{
InterfaceAddMethod = interfaceAddMethod;
InterfaceRemoveMethod = interfaceRemoveMethod;
InterfaceRaiseMethod = interfaceRaiseMethod;
}
}
private sealed class MethodInfoEqualityComparer : EqualityComparer<MethodInfo>
{
public static readonly MethodInfoEqualityComparer Instance = new MethodInfoEqualityComparer();
private MethodInfoEqualityComparer() { }
public sealed override bool Equals(MethodInfo? left, MethodInfo? right)
{
if (ReferenceEquals(left, right))
return true;
if (left == null)
return right == null;
else if (right == null)
return false;
// This assembly should work in netstandard1.3,
// so we cannot use MemberInfo.MetadataToken here.
// Therefore, it compares honestly referring ECMA-335 I.8.6.1.6 Signature Matching.
if (!Equals(left.DeclaringType, right.DeclaringType))
return false;
if (!Equals(left.ReturnType, right.ReturnType))
return false;
if (left.CallingConvention != right.CallingConvention)
return false;
if (left.IsStatic != right.IsStatic)
return false;
if (left.Name != right.Name)
return false;
Type[] leftGenericParameters = left.GetGenericArguments();
Type[] rightGenericParameters = right.GetGenericArguments();
if (leftGenericParameters.Length != rightGenericParameters.Length)
return false;
for (int i = 0; i < leftGenericParameters.Length; i++)
{
if (!Equals(leftGenericParameters[i], rightGenericParameters[i]))
return false;
}
ParameterInfo[] leftParameters = left.GetParameters();
ParameterInfo[] rightParameters = right.GetParameters();
if (leftParameters.Length != rightParameters.Length)
return false;
for (int i = 0; i < leftParameters.Length; i++)
{
if (!Equals(leftParameters[i].ParameterType, rightParameters[i].ParameterType))
return false;
}
return true;
}
public sealed override int GetHashCode(MethodInfo obj)
{
if (obj == null)
return 0;
Debug.Assert(obj.DeclaringType != null);
int hashCode = obj.DeclaringType!.GetHashCode();
hashCode ^= obj.Name.GetHashCode();
foreach (ParameterInfo parameter in obj.GetParameters())
{
hashCode ^= parameter.ParameterType.GetHashCode();
}
return hashCode;
}
}
}
}
}