private static MethodBuilder GetPropertyGetMethod(TypeBuilder proxyTypeBuilder, Type targetType, MemberInfo proxyMember, PropertyInfo targetProperty, FieldInfo instanceField) { string proxyMemberName = proxyMember.Name; Type proxyMemberReturnType = typeof(object); Type[] proxyParameterTypes = Type.EmptyTypes; Type[] targetParametersTypes = GetPropertyGetParametersTypes(proxyTypeBuilder, targetProperty, true).ToArray(); if (proxyMember is PropertyInfo proxyProperty) { proxyMemberReturnType = proxyProperty.PropertyType; proxyParameterTypes = GetPropertyGetParametersTypes(proxyTypeBuilder, proxyProperty, true).ToArray(); if (proxyParameterTypes.Length != targetParametersTypes.Length) { DuckTypePropertyArgumentsLengthException.Throw(proxyProperty); } } else if (proxyMember is FieldInfo proxyField) { proxyMemberReturnType = proxyField.FieldType; proxyParameterTypes = Type.EmptyTypes; if (proxyParameterTypes.Length != targetParametersTypes.Length) { DuckTypePropertyArgumentsLengthException.Throw(targetProperty); } } MethodBuilder proxyMethod = proxyTypeBuilder.DefineMethod( "get_" + proxyMemberName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, proxyMemberReturnType, proxyParameterTypes); LazyILGenerator il = new LazyILGenerator(proxyMethod.GetILGenerator()); MethodInfo targetMethod = targetProperty.GetMethod; Type returnType = targetProperty.PropertyType; // Load the instance if needed if (!targetMethod.IsStatic) { il.Emit(OpCodes.Ldarg_0); il.Emit(instanceField.FieldType.IsValueType ? OpCodes.Ldflda : OpCodes.Ldfld, instanceField); } // Load the indexer keys to the stack for (int pIndex = 0; pIndex < proxyParameterTypes.Length; pIndex++) { Type proxyParamType = proxyParameterTypes[pIndex]; Type targetParamType = targetParametersTypes[pIndex]; // Check if the type can be converted of if we need to enable duck chaining if (NeedsDuckChaining(targetParamType, proxyParamType)) { // Load the argument and cast it as Duck type il.WriteLoadArgument(pIndex, false); il.Emit(OpCodes.Castclass, typeof(IDuckType)); // Call IDuckType.Instance property to get the actual value il.EmitCall(OpCodes.Callvirt, DuckTypeInstancePropertyInfo.GetMethod, null); targetParamType = typeof(object); } else { il.WriteLoadArgument(pIndex, false); } // If the target parameter type is public or if it's by ref we have to actually use the original target type. targetParamType = UseDirectAccessTo(proxyTypeBuilder, targetParamType) || targetParamType.IsByRef ? targetParamType : typeof(object); il.WriteTypeConversion(proxyParamType, targetParamType); targetParametersTypes[pIndex] = targetParamType; } // Call the getter method if (UseDirectAccessTo(proxyTypeBuilder, targetType)) { // If the instance is public we can emit directly without any dynamic method // Method call if (targetMethod.IsPublic) { // We can emit a normal call if we have a public instance with a public property method. il.EmitCall(targetMethod.IsStatic || instanceField.FieldType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, targetMethod, null); } else { // In case we have a public instance and a non public property method we can use [Calli] with the function pointer il.WriteMethodCalli(targetMethod); } } else { // If the instance is not public we need to create a Dynamic method to overpass the visibility checks // we can't access non public types so we have to cast to object type (in the instance object and the return type). string dynMethodName = $"_getNonPublicProperty_{targetProperty.DeclaringType.Name}_{targetProperty.Name}"; returnType = UseDirectAccessTo(proxyTypeBuilder, targetProperty.PropertyType) ? targetProperty.PropertyType : typeof(object); // We create the dynamic method Type[] targetParameters = GetPropertyGetParametersTypes(proxyTypeBuilder, targetProperty, false, !targetMethod.IsStatic).ToArray(); Type[] dynParameters = targetMethod.IsStatic ? targetParametersTypes : (new[] { typeof(object) }).Concat(targetParametersTypes).ToArray(); DynamicMethod dynMethod = new DynamicMethod(dynMethodName, returnType, dynParameters, proxyTypeBuilder.Module, true); // Emit the dynamic method body LazyILGenerator dynIL = new LazyILGenerator(dynMethod.GetILGenerator()); if (!targetMethod.IsStatic) { dynIL.LoadInstanceArgument(typeof(object), targetProperty.DeclaringType); } for (int idx = targetMethod.IsStatic ? 0 : 1; idx < dynParameters.Length; idx++) { dynIL.WriteLoadArgument(idx, true); dynIL.WriteTypeConversion(dynParameters[idx], targetParameters[idx]); } dynIL.EmitCall(targetMethod.IsStatic || instanceField.FieldType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, targetMethod, null); dynIL.WriteTypeConversion(targetProperty.PropertyType, returnType); dynIL.Emit(OpCodes.Ret); dynIL.Flush(); // Emit the call to the dynamic method il.WriteDynamicMethodCall(dynMethod, proxyTypeBuilder); } // Handle the return value // Check if the type can be converted or if we need to enable duck chaining if (NeedsDuckChaining(targetProperty.PropertyType, proxyMemberReturnType)) { if (UseDirectAccessTo(proxyTypeBuilder, targetProperty.PropertyType) && targetProperty.PropertyType.IsValueType) { il.Emit(OpCodes.Box, targetProperty.PropertyType); } // We call DuckType.CreateCache<>.Create() MethodInfo getProxyMethodInfo = typeof(CreateCache <>) .MakeGenericType(proxyMemberReturnType).GetMethod("Create"); il.Emit(OpCodes.Call, getProxyMethodInfo); } else if (returnType != proxyMemberReturnType) { // If the type is not the expected type we try a conversion. il.WriteTypeConversion(returnType, proxyMemberReturnType); } il.Emit(OpCodes.Ret); il.Flush(); _methodBuilderGetToken.Invoke(proxyMethod, null); return(proxyMethod); }