Example #1
0
    /// <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;
        }
    }
Example #2
0
    /// <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);
    }
Example #3
0
    /// <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;
        }
    }
Example #4
0
    /// <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);
        }
    }
Example #5
0
 /// <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);
 }
Example #6
0
    /// <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 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);
    }
Example #9
0
    /// <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);
            }
        }
    }
    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);
    }