Example #1
0
        /*
         * As a rule, all the Fix methods will call FixType to fix their types, while there is also one case where FixType
         * will call FixMethod, which is when MVars are involved. However, to avoid infinite recursion, FixType⇒FixMethod calls
         * only return partially fixed methods (Some of the types may not be fixed).
         *
         * Also, most Fix methods are recursive, especially FixType.
         */

        /// <summary>
        ///     Fixes a type reference, possibly replacing a reference to a patching type from your assembly with a reference to
        ///     the type being patched.
        /// </summary>
        /// <param name="yourTypeRef">The type reference.</param>
        /// <returns></returns>
        /// <exception cref="NotSupportedException">
        ///     This method can only fix a reference to a patching type, or a reference to a
        ///     nested type in a patching type.
        /// </exception>
        private TypeReference FixTypeReference(TypeReference yourTypeRef)
        {
            //Fixes references to YourAssembly::PatchingType to TargetAssembly::PatchedType
            //or YourAssembly::PatchingType::YourType to TargetAssembly::PatchedType::OrigType
            //also imports the type to the module.

            /*
             * A type reference may be to the following "special" forms of a type:
             * 0. A regular type.
             * 1. A generic instantiation of the type, e.g. List<int> is an instantiation of List<_>
             * 2. A reference (basically, 'ref int', which is displayed int&)
             * 3. An array of the type (in the IL, an array type T[] is a special form of T, rather than an instantiation of Array<_>)
             * x. Pinned, pointer, and a few other uncommon things I don't want to mess with right now
             *
             * Each is a kind of modified type that modifies a base type, exposed via ElementType. The ElementType could also be one of the above. For example we might have:
             *		List<int>[], List<List<int>>, List<int>&, etc
             *
             * We need to go down the "stack" and fix every modification, using recursion and calling ElementType. This is because we might have something like:
             *		List<UserIntroducedType>[] ⇒ we need to recurse twice to fix it
             *		UserIntroducedType[][][] ⇒ we need to recurse 3 times to fix it
             *
             * Note that GetElementType() should NOT be called because it returns the bottom "pure" type, not 1 level below the current type.
             *
             * Plus, we have references to type parameters ofc.
             */

            if (yourTypeRef == null)
            {
                Log_called_to_fix_null("type");
                return(null);
            }
            Log_fixing_reference("type", yourTypeRef);

            TypeReference targetTypeRef;

            var yourTypeDef = yourTypeRef.Resolve();

            if (yourTypeDef != null && yourTypeDef.Module.Assembly.IsPatchingAssembly() && yourTypeDef.IsDisablePatching())
            {
                Log_trying_to_fix_disabled_reference("type", yourTypeRef);
            }
            TypeReference targetInnerTypeRef;
            int           index;

            switch (yourTypeRef.MetadataType)
            {
            case MetadataType.Array:
                //array of the type, e.g. int[]
                //kind of amusing arrays aren't generic instantiations of Array<T>, but rather
                //a special form of T itself (though this is implified by the syntax T[]).
                var yourArrayType = (ArrayType)yourTypeRef;
                targetInnerTypeRef = FixTypeReference(yourArrayType.ElementType);
                targetTypeRef      = targetInnerTypeRef.MakeArrayType(yourArrayType.Rank);
                break;

            case MetadataType.ByReference:
                //ByRef type, e.g. int& or in C# "ref int".
                //Note that IL allows for variables and fields to have a reference type (C# does not)
                //These sometimes appear in IL, as they are introduced by the compiler.
                var yourByRefType = (ByReferenceType)yourTypeRef;
                targetInnerTypeRef = FixTypeReference(yourByRefType.ElementType);
                targetTypeRef      = targetInnerTypeRef.MakeByReferenceType();
                break;

            case MetadataType.GenericInstance:
                //fully instantiated generic type, like List<int>
                var asGenericInstanceType = (GenericInstanceType)yourTypeRef;
                var targetGenArguments    =
                    asGenericInstanceType.GenericArguments.Select <TypeReference, TypeReference>(FixTypeReference);
                targetInnerTypeRef = FixTypeReference(asGenericInstanceType.ElementType);
                targetTypeRef      = targetInnerTypeRef.MakeGenericInstanceType(targetGenArguments.ToArray());
                break;

            case MetadataType.MVar:
                //method's generic type parameter. We find the DeclaringMethod, and find its version in the target assembly.
                var asGenParam = (GenericParameter)yourTypeRef;

                index = asGenParam.DeclaringMethod.GenericParameters.IndexOf(x => x.Name == asGenParam.Name);
                //the following is dangeorus because it brings about mutual recursion FixMethod ⇔ FixType...
                //the 'false' argument makes sure the recursion doesn't become infinite, as it allows for FixMethod
                //not to fix all the types in the signature. After all, we just need the generic parameters.
                var methodReference = FixMethodReference(asGenParam.DeclaringMethod, false);
                //we need to Resolve it in order to get a sort-of more "up to date" reference to the method.
                var targetDeclaringMethod = methodReference.Resolve();
                var imported = TargetAssembly.MainModule.Import(targetDeclaringMethod);
                targetTypeRef = imported.GenericParameters[index];
                break;

            case MetadataType.Var:
                //type's generic type parameter
                var declaringType = yourTypeRef.DeclaringType;
                index = declaringType.GenericParameters.IndexOf(x => x.Name == yourTypeRef.Name);
                var targetDeclaringType = FixTypeReference(declaringType);
                targetTypeRef = targetDeclaringType.GenericParameters[index];
                break;

            case MetadataType.Sentinel:                   //interop: related to the varargs calling convention, "__arglist" in C#.
            case MetadataType.RequiredModifier:           //interop: related to marshaling
            case MetadataType.OptionalModifier:           //interop: related to marshaling
            case MetadataType.Pinned:                     //interop: created using the 'fixed' keyword
            case MetadataType.FunctionPointer:            //interop: naked function pointer
            case MetadataType.TypedByReference:           //interop: related to the varargs calling convention, __typeref
            case MetadataType.Pointer:                    //interop: pointer
                throw Errors.Feature_not_supported($"MetadataType not supported: {yourTypeRef.MetadataType}");

            default:
                //this is the stopping condition for the recursion, which is dealing with a normal type.
                if (!yourTypeDef.Module.Assembly.IsPatchingAssembly())
                {
                    //we assume any types that aren't from the patching assembly are safe to import directly.
                    targetTypeRef = TargetAssembly.MainModule.Import(yourTypeDef);
                }
                else
                {
                    //If the type is from a patching assembly.
                    targetTypeRef = CurrentMemberCache.Types[yourTypeDef].TargetType;
                }
                if (targetTypeRef == null)
                {
                    throw Errors.Could_not_resolve_reference("type", yourTypeRef);
                }
                break;
            }
            Log_fixed_reference("type", yourTypeRef, targetTypeRef);
            targetTypeRef.Module.Assembly.AssertEqual(TargetAssembly);

            return(targetTypeRef);
        }
Example #2
0
        /// <summary>
        ///     Fixes the method reference.
        /// </summary>
        /// <param name="yourMethodRef">The method reference.</param>
        /// <param name="isntFixTypeCall">This parameter is sort of a hack that lets FixType call FixMethod to fix MVars, without infinite recursion. If set to false, it avoids fixing some types.</param>
        /// <returns></returns>
        /// <exception cref="Exception">Method isn't part of a patching type in this assembly...</exception>
        private MethodReference FixMethodReference(MethodReference yourMethodRef, bool isntFixTypeCall = true)
        {
            //Fixes reference like YourAssembly::PatchingClass::Method to TargetAssembly::PatchedClass::Method
            if (yourMethodRef == null)
            {
                Log_called_to_fix_null("method");
                return(null);
            }
            Log_fixing_reference("method", yourMethodRef);
            Asserts.AssertTrue(yourMethodRef.DeclaringType != null);
            var yourMethodDef = yourMethodRef.Resolve();

            if (yourMethodDef.IsDisablePatching())
            {
                Log_trying_to_fix_disabled_reference("method", yourMethodDef);
            }

            /*
             * In comparison to types, method references to methods are pretty simple. There are only two kinds:
             * 1. A regular method reference
             * 2. A reference to an instantiated generic method, e.g. Create<int>
             *
             * 3. As a special case, any method reference (assume non-generic) that has a generic DeclaringType.
             */
            MethodReference targetMethodRef;

            if (yourMethodRef.IsGenericInstance)
            {
                var yourGeneric             = (GenericInstanceMethod)yourMethodRef;
                var targetBaseMethod        = FixMethodReference(yourGeneric.ElementMethod);
                var targetGenericInstMethod = new GenericInstanceMethod(targetBaseMethod);

                foreach (var arg in yourGeneric.GenericArguments)
                {
                    var targetGenericArg = FixTypeReference(arg);
                    targetGenericInstMethod.GenericArguments.Add(targetGenericArg);
                }
                targetMethodRef = targetGenericInstMethod;
            }
            else
            {
                TypeReference typeToFix   = yourMethodRef.DeclaringType;
                var           action      = CurrentMemberCache.Methods.TryGet(yourMethodDef);
                var           memberAlias = action?.ActionAttribute as MemberAliasAttribute;
                if (memberAlias?.AliasedMemberDeclaringType != null)
                {
                    typeToFix = (TypeReference)memberAlias.AliasedMemberDeclaringType;
                }
                var targetType          = FixTypeReference(typeToFix);
                var targetBaseMethodDef = yourMethodRef;
                if (yourMethodDef.Module.Assembly.IsPatchingAssembly())
                {
                    var targetMethodDef = action?.TargetMember;
                    if (targetMethodDef == null)
                    {
                        throw Errors.Could_not_resolve_reference("method", yourMethodRef);
                    }
                    targetBaseMethodDef = targetMethodDef;
                }
                else
                {
                    targetBaseMethodDef = yourMethodRef;
                }
                var newMethodRef = targetBaseMethodDef.CloneReference();
                newMethodRef.DeclaringType = targetType;
                targetMethodRef            = newMethodRef;
                if (isntFixTypeCall)
                {
                    targetMethodRef = ManualImportMethod(targetMethodRef);
                }
            }

            targetMethodRef.Module.Assembly.AssertEqual(TargetAssembly);
            Log_fixed_reference("method", yourMethodRef, targetMethodRef);
            return(targetMethodRef);
        }