// 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> s_baseTypeAndInterfaceToGeneratedProxyType = new Dictionary>(); 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)DispatchProxyGenerator.Invoke)!; } private static Type GetProxyType(Type baseType, Type interfaceType) { lock (s_baseTypeAndInterfaceToGeneratedProxyType) { if (!s_baseTypeAndInterfaceToGeneratedProxyType.TryGetValue(baseType, out Dictionary? interfaceToProxy)) { interfaceToProxy = new Dictionary(); 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 _methodToToken = new Dictionary(); private readonly List _methodsByToken = new List(); private readonly HashSet _ignoresAccessAssemblyNames = new HashSet(); 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).GetTypeInfo().GetDeclaredMethod("Invoke")!; private readonly ProxyAssembly _assembly; private readonly TypeBuilder _tb; private readonly Type _proxyBaseType; private readonly List _fields; internal ProxyBuilder(ProxyAssembly assembly, TypeBuilder tb, Type proxyBaseType) { _assembly = assembly; _tb = tb; _proxyBaseType = proxyBaseType; _fields = new List(); _fields.Add(tb.DefineField("invoke", typeof(Action), 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(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(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 argsArr = new GenericArray(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 packedArr = new GenericArray(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 typeArr = new GenericArray(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 { 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 { 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; } } } } }