private static MethodBuilder GetFieldSetMethod(TypeBuilder proxyTypeBuilder, Type targetType, MemberInfo proxyMember, FieldInfo targetField, FieldInfo instanceField ) { var proxyMemberName = proxyMember.Name; var proxyMemberReturnType = proxyMember is PropertyInfo pinfo ? pinfo.PropertyType : proxyMember is FieldInfo finfo ? finfo.FieldType : typeof(object); var method = proxyTypeBuilder.DefineMethod( "set_" + proxyMemberName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(void), new[] { proxyMemberReturnType }); var il = new LazyILGenerator(method.GetILGenerator()); var currentValueType = proxyMemberReturnType; // Load instance if (!targetField.IsStatic) { il.Emit(OpCodes.Ldarg_0); il.Emit(instanceField.FieldType.IsValueType ? OpCodes.Ldflda : OpCodes.Ldfld, instanceField); } // Check if the type can be converted of if we need to enable duck chaining if (DuckType.NeedsDuckChaining(targetField.FieldType, proxyMemberReturnType)) { // Load the argument and convert it to Duck type il.Emit(OpCodes.Ldarg_1); il.WriteTypeConversion(proxyMemberReturnType, typeof(IDuckType)); // Call IDuckType.Instance property to get the actual value il.EmitCall(OpCodes.Callvirt, DuckTypeInstancePropertyInfo.GetMethod, null); currentValueType = typeof(object); } else { // Load the value into the stack il.Emit(OpCodes.Ldarg_1); } // We set the field value if (UseDirectAccessTo(proxyTypeBuilder, targetType) && targetField.IsPublic) { // If the instance and the field are public then is easy to set. il.WriteTypeConversion(currentValueType, targetField.FieldType); il.Emit(targetField.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, targetField); } else { // If the instance or the field are non public we need to create a Dynamic method to overpass the visibility checks var dynMethodName = $"_setField_{targetField.DeclaringType.Name}_{targetField.Name}"; // Convert the field type for the dynamic method var dynValueType = UseDirectAccessTo(proxyTypeBuilder, targetField.FieldType) ? targetField.FieldType : typeof(object); il.WriteTypeConversion(currentValueType, dynValueType); // Create dynamic method var dynParameters = targetField.IsStatic ? new[] { dynValueType } : new[] { typeof(object), dynValueType }; var dynMethod = new DynamicMethod(dynMethodName, typeof(void), dynParameters, proxyTypeBuilder.Module, true); // Write the dynamic method body var dynIL = new LazyILGenerator(dynMethod.GetILGenerator()); dynIL.Emit(OpCodes.Ldarg_0); if (targetField.IsStatic) { dynIL.WriteTypeConversion(dynValueType, targetField.FieldType); dynIL.Emit(OpCodes.Stsfld, targetField); } else { if (targetField.DeclaringType != typeof(object)) { dynIL.Emit(OpCodes.Castclass, targetField.DeclaringType); } dynIL.Emit(OpCodes.Ldarg_1); dynIL.WriteTypeConversion(dynValueType, targetField.FieldType); dynIL.Emit(OpCodes.Stfld, targetField); } dynIL.Emit(OpCodes.Ret); dynIL.Flush(); // Emit the call to the dynamic method il.WriteDynamicMethodCall(dynMethod, proxyTypeBuilder); } il.Emit(OpCodes.Ret); il.Flush(); _methodBuilderGetToken.Invoke(method, null); return(method); }
private static MethodBuilder GetFieldGetMethod(TypeBuilder proxyTypeBuilder, Type targetType, MemberInfo proxyMember, FieldInfo targetField, FieldInfo instanceField ) { var proxyMemberName = proxyMember.Name; var proxyMemberReturnType = proxyMember is PropertyInfo pinfo ? pinfo.PropertyType : proxyMember is FieldInfo finfo ? finfo.FieldType : typeof(object); var proxyMethod = proxyTypeBuilder.DefineMethod( "get_" + proxyMemberName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, proxyMemberReturnType, Type.EmptyTypes); var il = new LazyILGenerator(proxyMethod.GetILGenerator()); var returnType = targetField.FieldType; // Load the instance if (!targetField.IsStatic) { il.Emit(OpCodes.Ldarg_0); il.Emit(instanceField.FieldType.IsValueType ? OpCodes.Ldflda : OpCodes.Ldfld, instanceField); } // Load the field value to the stack if (UseDirectAccessTo(proxyTypeBuilder, targetType) && targetField.IsPublic) { // In case is public is pretty simple il.Emit(targetField.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, targetField); } else { // If the instance or the field are non 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 if is needed). var dynMethodName = $"_getNonPublicField_{targetField.DeclaringType.Name}_{targetField.Name}"; returnType = UseDirectAccessTo(proxyTypeBuilder, targetField.FieldType) ? targetField.FieldType : typeof(object); // We create the dynamic method var dynParameters = targetField.IsStatic ? Type.EmptyTypes : new[] { typeof(object) }; var dynMethod = new DynamicMethod(dynMethodName, returnType, dynParameters, proxyTypeBuilder.Module, true); // Emit the dynamic method body var dynIL = new LazyILGenerator(dynMethod.GetILGenerator()); if (!targetField.IsStatic) { // Emit the instance load in the dynamic method dynIL.Emit(OpCodes.Ldarg_0); if (targetField.DeclaringType != typeof(object)) { dynIL.Emit(targetField.DeclaringType !.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, targetField.DeclaringType); } } // Emit the field and convert before returning (in case of boxing) dynIL.Emit(targetField.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, targetField); dynIL.WriteTypeConversion(targetField.FieldType, returnType); dynIL.Emit(OpCodes.Ret); dynIL.Flush(); // Emit the call to the dynamic method il.WriteDynamicMethodCall(dynMethod, proxyTypeBuilder); } // Check if the type can be converted or if we need to enable duck chaining if (DuckType.NeedsDuckChaining(targetField.FieldType, proxyMemberReturnType)) { if (UseDirectAccessTo(proxyTypeBuilder, targetField.FieldType) && targetField.FieldType.IsValueType) { il.Emit(OpCodes.Box, targetField.FieldType); } // We call DuckType.CreateCache<>.Create() var getProxyMethodInfo = typeof(DuckType.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); }
private static MethodBuilder GetPropertyGetMethod(TypeBuilder proxyTypeBuilder, Type targetType, MemberInfo proxyMember, PropertyInfo targetProperty, FieldInfo instanceField) { var proxyMemberName = proxyMember.Name; var proxyMemberReturnType = typeof(object); var proxyParameterTypes = Type.EmptyTypes; var 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); } } var proxyMethod = proxyTypeBuilder.DefineMethod( "get_" + proxyMemberName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, proxyMemberReturnType, proxyParameterTypes); var il = new LazyILGenerator(proxyMethod.GetILGenerator()); var targetMethod = targetProperty.GetMethod; var 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 (var pIndex = 0; pIndex < proxyParameterTypes.Length; pIndex++) { var proxyParamType = proxyParameterTypes[pIndex]; var 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). var dynMethodName = $"_getNonPublicProperty_{targetProperty.DeclaringType.Name}_{targetProperty.Name}"; returnType = UseDirectAccessTo(proxyTypeBuilder, targetProperty.PropertyType) ? targetProperty.PropertyType : typeof(object); // We create the dynamic method var targetParameters = GetPropertyGetParametersTypes(proxyTypeBuilder, targetProperty, false, !targetMethod.IsStatic).ToArray(); var dynParameters = targetMethod.IsStatic ? targetParametersTypes : (new[] { typeof(object) }).Concat(targetParametersTypes).ToArray(); var dynMethod = new DynamicMethod(dynMethodName, returnType, dynParameters, proxyTypeBuilder.Module, true); // Emit the dynamic method body var dynIL = new LazyILGenerator(dynMethod.GetILGenerator()); if (!targetMethod.IsStatic) { dynIL.LoadInstanceArgument(typeof(object), targetProperty.DeclaringType); } for (var 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() var 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); }