Example #1
0
        /// <summary>Rewrite custom attributes if needed.</summary>
        /// <param name="attributes">The current custom attributes.</param>
        private bool RewriteCustomAttributes(Collection <CustomAttribute> attributes)
        {
            bool rewritten = false;

            for (int attrIndex = 0; attrIndex < attributes.Count; attrIndex++)
            {
                CustomAttribute attribute  = attributes[attrIndex];
                bool            curChanged = false;

                // attribute type
                TypeReference newAttrType = null;
                rewritten |= this.RewriteTypeReference(attribute.AttributeType, newType =>
                {
                    newAttrType = newType;
                    curChanged  = true;
                });

                // constructor arguments
                TypeReference[] argTypes = new TypeReference[attribute.ConstructorArguments.Count];
                for (int i = 0; i < argTypes.Length; i++)
                {
                    var arg = attribute.ConstructorArguments[i];

                    argTypes[i] = arg.Type;
                    rewritten  |= this.RewriteTypeReference(arg.Type, newType =>
                    {
                        argTypes[i] = newType;
                        curChanged  = true;
                    });
                }

                // swap attribute
                if (curChanged)
                {
                    // get constructor
                    MethodDefinition constructor = (newAttrType ?? attribute.AttributeType)
                                                   .Resolve()
                                                   .Methods
                                                   .Where(method => method.IsConstructor)
                                                   .FirstOrDefault(ctor => RewriteHelper.HasMatchingSignature(ctor, attribute.Constructor));
                    if (constructor == null)
                    {
                        throw new InvalidOperationException($"Can't rewrite attribute type '{attribute.AttributeType.FullName}' to '{newAttrType?.FullName}', no equivalent constructor found.");
                    }

                    // create new attribute
                    var newAttr = new CustomAttribute(this.Module.ImportReference(constructor));
                    for (int i = 0; i < argTypes.Length; i++)
                    {
                        newAttr.ConstructorArguments.Add(new CustomAttributeArgument(argTypes[i], attribute.ConstructorArguments[i].Value));
                    }
                    foreach (var prop in attribute.Properties)
                    {
                        newAttr.Properties.Add(new CustomAttributeNamedArgument(prop.Name, prop.Argument));
                    }
                    foreach (var field in attribute.Fields)
                    {
                        newAttr.Fields.Add(new CustomAttributeNamedArgument(field.Name, field.Argument));
                    }

                    // swap attribute
                    attributes[attrIndex] = newAttr;
                    rewritten             = true;
                }
            }

            return(rewritten);
        }
Example #2
0
        /*********
        ** Private methods
        *********/
        /// <summary>Rewrite a loaded type definition.</summary>
        /// <param name="type">The type definition to rewrite.</param>
        /// <returns>Returns whether the type was modified.</returns>
        private bool RewriteTypeDefinition(TypeDefinition type)
        {
            bool changed = false;

            changed |= this.RewriteCustomAttributes(type.CustomAttributes);
            changed |= this.RewriteGenericParameters(type.GenericParameters);

            foreach (InterfaceImplementation @interface in type.Interfaces)
            {
                changed |= this.RewriteTypeReference(@interface.InterfaceType, newType => @interface.InterfaceType = newType);
            }

            if (type.BaseType.FullName != "System.Object")
            {
                changed |= this.RewriteTypeReference(type.BaseType, newType => type.BaseType = newType);
            }

            foreach (MethodDefinition method in type.Methods)
            {
                changed |= this.RewriteTypeReference(method.ReturnType, newType => method.ReturnType = newType);
                changed |= this.RewriteGenericParameters(method.GenericParameters);
                changed |= this.RewriteCustomAttributes(method.CustomAttributes);

                foreach (ParameterDefinition parameter in method.Parameters)
                {
                    changed |= this.RewriteTypeReference(parameter.ParameterType, newType => parameter.ParameterType = newType);
                }

                foreach (var methodOverride in method.Overrides)
                {
                    changed |= this.RewriteMethodReference(methodOverride);
                }

                if (method.HasBody)
                {
                    foreach (VariableDefinition variable in method.Body.Variables)
                    {
                        changed |= this.RewriteTypeReference(variable.VariableType, newType => variable.VariableType = newType);
                    }

                    // rewrite CIL instructions
                    ILProcessor cil = method.Body.GetILProcessor();
                    Collection <Instruction> instructions = cil.Body.Instructions;
                    bool addedInstructions = false;
                    for (int i = 0; i < instructions.Count; i++)
                    {
                        var instruction = instructions[i];
                        if (instruction.OpCode.Code == Code.Nop)
                        {
                            continue;
                        }

                        int oldCount = cil.Body.Instructions.Count;
                        changed |= this.RewriteInstruction(instruction, cil, newInstruction =>
                        {
                            changed = true;
                            cil.Replace(instruction, newInstruction);
                            instruction = newInstruction;
                        });

                        if (cil.Body.Instructions.Count > oldCount)
                        {
                            addedInstructions = true;
                        }
                    }

                    // special case: added instructions may cause an instruction to be out of range
                    // of a short jump that references it
                    if (addedInstructions)
                    {
                        foreach (var instruction in instructions)
                        {
                            var longJumpCode = RewriteHelper.GetEquivalentLongJumpCode(instruction.OpCode);
                            if (longJumpCode != null)
                            {
                                instruction.OpCode = longJumpCode.Value;
                            }
                        }
                        changed = true;
                    }
                }
            }

            return(changed);
        }
Example #3
0
        /// <inheritdoc />
        public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
        {
            // get method ref
            MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);

            if (methodRef == null || !this.ShouldValidate(methodRef.DeclaringType))
            {
                return(false);
            }

            // skip if not broken
            if (methodRef.Resolve() != null)
            {
                return(false);
            }

            // get type
            var type = methodRef.DeclaringType.Resolve();

            if (type == null)
            {
                return(false);
            }

            // get method definition
            MethodDefinition method = null;

            foreach (var match in type.Methods.Where(p => p.Name == methodRef.Name))
            {
                // reference matches initial parameters of definition
                if (methodRef.Parameters.Count >= match.Parameters.Count || !this.InitialParametersMatch(methodRef, match))
                {
                    continue;
                }

                // all remaining parameters in definition are optional
                if (!match.Parameters.Skip(methodRef.Parameters.Count).All(p => p.IsOptional))
                {
                    continue;
                }

                method = match;
                break;
            }
            if (method == null)
            {
                return(false);
            }

            // get instructions to inject parameter values
            var loadInstructions = method.Parameters.Skip(methodRef.Parameters.Count)
                                   .Select(p => RewriteHelper.GetLoadValueInstruction(p.Constant))
                                   .ToArray();

            if (loadInstructions.Any(p => p == null))
            {
                return(false); // SMAPI needs to load the value onto the stack before the method call, but the optional parameter type wasn't recognized
            }
            // rewrite method reference
            foreach (Instruction loadInstruction in loadInstructions)
            {
                cil.InsertBefore(instruction, loadInstruction);
            }
            instruction.Operand = module.ImportReference(method);

            this.Phrases.Add($"{methodRef.DeclaringType.Name}.{methodRef.Name} (added missing optional parameters)");
            return(this.MarkRewritten());
        }