/// <summary> /// Write a DynamicMethod call by creating and injecting a custom delegate in the proxyType /// </summary> /// <param name="il">LazyILGenerator instance</param> /// <param name="dynamicMethod">Dynamic method to get called</param> /// <param name="proxyType">ProxyType builder</param> internal static void WriteDynamicMethodCall(this LazyILGenerator il, DynamicMethod dynamicMethod, TypeBuilder proxyType) { // We create a custom delegate inside the module builder CreateDelegateTypeFor(proxyType, dynamicMethod, out Type delegateType, out MethodInfo invokeMethod); int index; lock (_dynamicMethods) { _dynamicMethods.Add(dynamicMethod); index = _dynamicMethods.Count - 1; } // We fill the DelegateCache<> for that custom type with the delegate instance MethodInfo fillDelegateMethodInfo = typeof(DuckType.DelegateCache <>).MakeGenericType(delegateType).GetMethod("FillDelegate", BindingFlags.NonPublic | BindingFlags.Static); fillDelegateMethodInfo.Invoke(null, new object[] { index }); // We get the delegate instance and load it in to the stack before the parameters (at the begining of the IL body) il.SetOffset(0); il.EmitCall(OpCodes.Call, typeof(DuckType.DelegateCache <>).MakeGenericType(delegateType).GetMethod("GetDelegate"), null); il.ResetOffset(); // We emit the call to the delegate invoke method. il.EmitCall(OpCodes.Callvirt, invokeMethod, null); }
private static MethodBuilder GetFieldSetMethod(TypeBuilder proxyTypeBuilder, Type targetType, MemberInfo proxyMember, FieldInfo targetField, FieldInfo instanceField) { string proxyMemberName = proxyMember.Name; Type proxyMemberReturnType = proxyMember is PropertyInfo pinfo ? pinfo.PropertyType : proxyMember is FieldInfo finfo ? finfo.FieldType : typeof(object); MethodBuilder method = proxyTypeBuilder.DefineMethod( "set_" + proxyMemberName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(void), new[] { proxyMemberReturnType }); LazyILGenerator il = new LazyILGenerator(method.GetILGenerator()); Type 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 (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 string dynMethodName = $"_setField_{targetField.DeclaringType.Name}_{targetField.Name}"; // Convert the field type for the dynamic method Type dynValueType = UseDirectAccessTo(proxyTypeBuilder, targetField.FieldType) ? targetField.FieldType : typeof(object); il.WriteTypeConversion(currentValueType, dynValueType); // Create dynamic method Type[] dynParameters = targetField.IsStatic ? new[] { dynValueType } : new[] { typeof(object), dynValueType }; DynamicMethod dynMethod = new DynamicMethod(dynMethodName, typeof(void), dynParameters, proxyTypeBuilder.Module, true); // Write the dynamic method body LazyILGenerator 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 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); }