/// <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); }
/// <summary> /// Write load arguments /// </summary> /// <param name="il">LazyILGenerator instance</param> /// <param name="index">Argument index</param> /// <param name="isStatic">Define if we need to take into account the instance argument</param> internal static void WriteLoadArgument(this LazyILGenerator il, int index, bool isStatic) { if (!isStatic) { index += 1; } switch (index) { case 0: il.Emit(OpCodes.Ldarg_0); break; case 1: il.Emit(OpCodes.Ldarg_1); break; case 2: il.Emit(OpCodes.Ldarg_2); break; case 3: il.Emit(OpCodes.Ldarg_3); break; default: il.Emit(OpCodes.Ldarg_S, index); break; } }
/// <summary> /// Write store local /// </summary> /// <param name="il">LazyILGenerator instance</param> /// <param name="index">Local index</param> internal static void WriteStoreLocal(this LazyILGenerator il, int index) { switch (index) { case 0: il.Emit(OpCodes.Stloc_0); break; case 1: il.Emit(OpCodes.Stloc_1); break; case 2: il.Emit(OpCodes.Stloc_2); break; case 3: il.Emit(OpCodes.Stloc_3); break; default: il.Emit(OpCodes.Stloc_S, index); break; } }
/// <summary> /// Write constant int value /// </summary> /// <param name="il">LazyILGenerator instance</param> /// <param name="value">int value</param> internal static void WriteInt(this LazyILGenerator il, int value) { if (value >= -1 && value <= 8) { switch (value) { case -1: il.Emit(OpCodes.Ldc_I4_M1); break; case 0: il.Emit(OpCodes.Ldc_I4_0); break; case 1: il.Emit(OpCodes.Ldc_I4_1); break; case 2: il.Emit(OpCodes.Ldc_I4_2); break; case 3: il.Emit(OpCodes.Ldc_I4_3); break; case 4: il.Emit(OpCodes.Ldc_I4_4); break; case 5: il.Emit(OpCodes.Ldc_I4_5); break; case 6: il.Emit(OpCodes.Ldc_I4_6); break; case 7: il.Emit(OpCodes.Ldc_I4_7); break; default: il.Emit(OpCodes.Ldc_I4_8); break; } } else if (value >= -128 && value <= 127) { il.Emit(OpCodes.Ldc_I4_S, value); } else { il.Emit(OpCodes.Ldc_I4, value); } }
/// <summary> /// Write a Call to a method using Calli /// </summary> /// <param name="il">LazyILGenerator instance</param> /// <param name="method">Method to get called</param> internal static void WriteMethodCalli(this LazyILGenerator il, MethodInfo method) { il.Emit(OpCodes.Ldc_I8, (long)method.MethodHandle.GetFunctionPointer()); il.Emit(OpCodes.Conv_I); il.EmitCalli( OpCodes.Calli, method.CallingConvention, method.ReturnType, method.GetParameters().Select(p => p.ParameterType).ToArray(), null); }
/// <summary> /// Load instance argument /// </summary> /// <param name="il">LazyILGenerator instance</param> /// <param name="actualType">Actual type</param> /// <param name="expectedType">Expected type</param> internal static void LoadInstanceArgument(this LazyILGenerator il, Type actualType, Type expectedType) { il.Emit(OpCodes.Ldarg_0); if (actualType == expectedType) { return; } if (expectedType.IsValueType) { il.DeclareLocal(expectedType); il.Emit(OpCodes.Unbox_Any, expectedType); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloca_S, 0); } else { il.Emit(OpCodes.Castclass, expectedType); } }
private static MethodBuilder GetPropertySetMethod(TypeBuilder proxyTypeBuilder, Type targetType, MemberInfo proxyMember, PropertyInfo targetProperty, FieldInfo instanceField) { string proxyMemberName = null; Type[] proxyParameterTypes = Type.EmptyTypes; Type[] targetParametersTypes = GetPropertySetParametersTypes(proxyTypeBuilder, targetProperty, true).ToArray(); if (proxyMember is PropertyInfo proxyProperty) { proxyMemberName = proxyProperty.Name; proxyParameterTypes = GetPropertySetParametersTypes(proxyTypeBuilder, proxyProperty, true).ToArray(); if (proxyParameterTypes.Length != targetParametersTypes.Length) { DuckTypePropertyArgumentsLengthException.Throw(proxyProperty); } } else if (proxyMember is FieldInfo proxyField) { proxyMemberName = proxyField.Name; proxyParameterTypes = new Type[] { proxyField.FieldType }; if (proxyParameterTypes.Length != targetParametersTypes.Length) { DuckTypePropertyArgumentsLengthException.Throw(targetProperty); } } MethodBuilder proxyMethod = proxyTypeBuilder.DefineMethod( "set_" + proxyMemberName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(void), proxyParameterTypes); LazyILGenerator il = new LazyILGenerator(proxyMethod.GetILGenerator()); MethodInfo targetMethod = targetProperty.SetMethod; // 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 and set value 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 setter method if (UseDirectAccessTo(proxyTypeBuilder, targetType)) { // If the instance is public we can emit directly without any dynamic method if (targetMethod.IsPublic) { // We can emit a normal call if we have a public instance with a public property method. il.EmitCall(targetMethod.IsStatic ? 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 = $"_setNonPublicProperty+{targetProperty.DeclaringType.Name}.{targetProperty.Name}"; // We create the dynamic method Type[] targetParameters = GetPropertySetParametersTypes(proxyTypeBuilder, targetProperty, false, !targetMethod.IsStatic).ToArray(); Type[] dynParameters = targetMethod.IsStatic ? targetParametersTypes : (new[] { typeof(object) }).Concat(targetParametersTypes).ToArray(); DynamicMethod dynMethod = new DynamicMethod(dynMethodName, typeof(void), 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 ? OpCodes.Call : OpCodes.Callvirt, targetMethod, null); dynIL.Emit(OpCodes.Ret); dynIL.Flush(); // Create and load delegate for the DynamicMethod il.WriteDynamicMethodCall(dynMethod, proxyTypeBuilder); } il.Emit(OpCodes.Ret); il.Flush(); _methodBuilderGetToken.Invoke(proxyMethod, null); return(proxyMethod); }
private static MethodBuilder GetFieldGetMethod(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 proxyMethod = proxyTypeBuilder.DefineMethod( "get_" + proxyMemberName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, proxyMemberReturnType, Type.EmptyTypes); LazyILGenerator il = new LazyILGenerator(proxyMethod.GetILGenerator()); Type 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). string dynMethodName = $"_getNonPublicField_{targetField.DeclaringType.Name}_{targetField.Name}"; returnType = UseDirectAccessTo(proxyTypeBuilder, targetField.FieldType) ? targetField.FieldType : typeof(object); // We create the dynamic method Type[] dynParameters = targetField.IsStatic ? Type.EmptyTypes : new[] { typeof(object) }; DynamicMethod dynMethod = new DynamicMethod(dynMethodName, returnType, dynParameters, proxyTypeBuilder.Module, true); // Emit the dynamic method body LazyILGenerator 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 (NeedsDuckChaining(targetField.FieldType, proxyMemberReturnType)) { if (UseDirectAccessTo(proxyTypeBuilder, targetField.FieldType) && targetField.FieldType.IsValueType) { il.Emit(OpCodes.Box, targetField.FieldType); } // 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); }
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); }
/// <summary> /// Convert a current type to an expected type /// </summary> /// <param name="il">LazyILGenerator instance</param> /// <param name="actualType">Actual type</param> /// <param name="expectedType">Expected type</param> internal static void WriteTypeConversion(this LazyILGenerator il, Type actualType, Type expectedType) { var actualUnderlyingType = actualType.IsEnum ? Enum.GetUnderlyingType(actualType) : actualType; var expectedUnderlyingType = expectedType.IsEnum ? Enum.GetUnderlyingType(expectedType) : expectedType; if (actualUnderlyingType == expectedUnderlyingType) { return; } if (actualUnderlyingType.IsValueType) { if (expectedUnderlyingType.IsValueType) { // If both underlying types are value types then both must be of the same type. DuckTypeInvalidTypeConversionException.Throw(actualType, expectedType); } else { // An underlying type can be boxed and converted to an object or interface type if the actual type support this // if not we should throw. if (expectedUnderlyingType == typeof(object)) { // If the expected type is object we just need to box the value il.Emit(OpCodes.Box, actualType); } else if (expectedUnderlyingType.IsAssignableFrom(actualUnderlyingType)) { // If the expected type can be assigned from the value type (ex: struct implementing an interface) il.Emit(OpCodes.Box, actualType); il.Emit(OpCodes.Castclass, expectedType); } else { // If the expected type can't be assigned from the actual value type. // Means if the expected type is an interface the actual type doesn't implement it. // So no possible conversion or casting can be made here. DuckTypeInvalidTypeConversionException.Throw(actualType, expectedType); } } } else { if (expectedUnderlyingType.IsValueType) { // We only allow conversions from objects or interface type if the actual type support this // if not we should throw. if (actualUnderlyingType == typeof(object) || actualUnderlyingType.IsAssignableFrom(expectedUnderlyingType)) { // WARNING: The actual type instance can't be detected at this point, we have to check it at runtime. /* * In this case we emit something like: * { * if (!(value is [expectedType])) { * throw new InvalidCastException(); * } * * return ([expectedType])value; * } */ Label lblIsExpected = il.DefineLabel(); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, expectedType); il.Emit(OpCodes.Brtrue_S, lblIsExpected); il.Emit(OpCodes.Pop); il.ThrowException(typeof(InvalidCastException)); il.MarkLabel(lblIsExpected); il.Emit(OpCodes.Unbox_Any, expectedType); } else { DuckTypeInvalidTypeConversionException.Throw(actualType, expectedType); } } else if (expectedUnderlyingType != typeof(object)) { il.Emit(OpCodes.Castclass, expectedUnderlyingType); } } }