예제 #1
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="targetPlatform">The current game platform.</param>
        /// <param name="framework">The game framework running the game.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="paranoidMode">Whether to detect paranoid mode issues.</param>
        /// <param name="rewriteMods">Whether to rewrite mods for compatibility.</param>
        public AssemblyLoader(Platform targetPlatform, GameFramework framework, IMonitor monitor, bool paranoidMode, bool rewriteMods)
        {
            this.Monitor      = monitor;
            this.ParanoidMode = paranoidMode;
            this.RewriteMods  = rewriteMods;
            this.AssemblyMap  = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform, framework));

            // init resolver
            this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver());
            this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.ExecutionPath);
            this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.InternalFilesPath);

            // generate type => assembly lookup for types which should be rewritten
            this.TypeAssemblies = new Dictionary <string, Assembly>();
            foreach (Assembly assembly in this.AssemblyMap.Targets)
            {
                ModuleDefinition module = this.AssemblyMap.TargetModules[assembly];
                foreach (TypeDefinition type in module.GetTypes())
                {
                    if (!type.IsPublic)
                    {
                        continue; // no need to rewrite
                    }
                    if (type.Namespace.Contains("<"))
                    {
                        continue; // ignore assembly metadata
                    }
                    this.TypeAssemblies[type.FullName] = assembly;
                }
            }
        }
예제 #2
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current <see cref="Platform"/>.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        public AssemblyTypeRewriter(PlatformAssemblyMap assemblyMap, IMonitor monitor)
        {
            // save config
            this.AssemblyMap = assemblyMap;
            this.Monitor     = monitor;

            // collect type => assembly lookup
            this.TypeAssemblies = new Dictionary <string, Assembly>();
            foreach (Assembly assembly in assemblyMap.Targets)
            {
                ModuleDefinition module = this.AssemblyMap.TargetModules[assembly];
                foreach (TypeDefinition type in module.GetTypes())
                {
                    if (!type.IsPublic)
                    {
                        continue; // no need to rewrite
                    }
                    if (type.Namespace.Contains("<"))
                    {
                        continue; // ignore assembly metadata
                    }
                    this.TypeAssemblies[type.FullName] = assembly;
                }
            }
        }
예제 #3
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="cacheDirName">The name of the directory containing a mod's cached data.</param>
 /// <param name="targetPlatform">The current game platform.</param>
 /// <param name="monitor">Encapsulates monitoring and logging.</param>
 public ModAssemblyLoader(string cacheDirName, Platform targetPlatform, IMonitor monitor)
 {
     this.CacheDirName         = cacheDirName;
     this.Monitor              = monitor;
     this.AssemblyMap          = Constants.GetAssemblyMap(targetPlatform);
     this.AssemblyTypeRewriter = new AssemblyTypeRewriter(this.AssemblyMap, monitor);
 }
예제 #4
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="targetPlatform">The current game platform.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        public AssemblyLoader(Platform targetPlatform, IMonitor monitor)
        {
            this.Monitor     = monitor;
            this.AssemblyMap = Constants.GetAssemblyMap(targetPlatform);

            // generate type => assembly lookup for types which should be rewritten
            this.TypeAssemblies = new Dictionary <string, Assembly>();
            foreach (Assembly assembly in this.AssemblyMap.Targets)
            {
                ModuleDefinition module = this.AssemblyMap.TargetModules[assembly];
                foreach (TypeDefinition type in module.GetTypes())
                {
                    if (!type.IsPublic)
                    {
                        continue; // no need to rewrite
                    }
                    if (type.Namespace.Contains("<"))
                    {
                        continue; // ignore assembly metadata
                    }
                    this.TypeAssemblies[type.FullName] = assembly;
                }
            }
        }
예제 #5
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            // field reference
            FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);

            if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType))
            {
                FieldDefinition target = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
                if (target == null)
                {
                    this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (no such field)";
                    return(InstructionHandleResult.NotCompatible);
                }
            }

            // method reference
            MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);

            if (methodRef != null && this.ShouldValidate(methodRef.DeclaringType) && !this.IsUnsupported(methodRef))
            {
                MethodDefinition target = methodRef.Resolve();
                if (target == null)
                {
                    if (this.IsProperty(methodRef))
                    {
                        this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name.Substring(4)} (no such property)";
                    }
                    else if (methodRef.Name == ".ctor")
                    {
                        this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no matching constructor)";
                    }
                    else
                    {
                        this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no such method)";
                    }
                    return(InstructionHandleResult.NotCompatible);
                }
            }

            return(InstructionHandleResult.None);
        }
예제 #6
0
        /// <summary>Rewrite a CIL instruction for compatibility.</summary>
        /// <param name="module">The module being rewritten.</param>
        /// <param name="cil">The CIL rewriter.</param>
        /// <param name="instruction">The instruction to rewrite.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        /// <returns>Returns whether the instruction was rewritten.</returns>
        /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
        public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction, platformChanged))
            {
                return(false);
            }

            MethodReference methodRef = (MethodReference)instruction.Operand;

            methodRef.DeclaringType = module.Import(this.ToType);
            return(true);
        }
예제 #7
0
        /// <summary>Perform the predefined logic for a method if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="method">The method definition containing the instruction.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public override InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            bool rewritten = false;

            // return type
            if (this.IsMatch(method.ReturnType))
            {
                this.RewriteIfNeeded(module, method.ReturnType, newType => method.ReturnType = newType);
                rewritten = true;
            }

            // parameters
            foreach (ParameterDefinition parameter in method.Parameters)
            {
                if (this.IsMatch(parameter.ParameterType))
                {
                    this.RewriteIfNeeded(module, parameter.ParameterType, newType => parameter.ParameterType = newType);
                    rewritten = true;
                }
            }

            // generic parameters
            for (int i = 0; i < method.GenericParameters.Count; i++)
            {
                var parameter = method.GenericParameters[i];
                if (this.IsMatch(parameter))
                {
                    this.RewriteIfNeeded(module, parameter, newType => method.GenericParameters[i] = new GenericParameter(parameter.Name, newType));
                    rewritten = true;
                }
            }

            // local variables
            foreach (VariableDefinition variable in method.Body.Variables)
            {
                if (this.IsMatch(variable.VariableType))
                {
                    this.RewriteIfNeeded(module, variable.VariableType, newType => variable.VariableType = newType);
                    rewritten = true;
                }
            }

            return(rewritten
                ? InstructionHandleResult.Rewritten
                : InstructionHandleResult.None);
        }
예제 #8
0
 /// <summary>Perform the predefined logic for a method if applicable.</summary>
 /// <param name="module">The assembly module containing the instruction.</param>
 /// <param name="method">The method definition containing the instruction.</param>
 /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
 /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
 public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
 {
     return(InstructionHandleResult.None);
 }
예제 #9
0
 /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
 /// <param name="module">The assembly module containing the instruction.</param>
 /// <param name="cil">The CIL processor.</param>
 /// <param name="instruction">The instruction to handle.</param>
 /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
 /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
 public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
 {
     return(this.IsMatch(instruction)
         ? this.Result
         : InstructionHandleResult.None);
 }
예제 #10
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction))
            {
                return(InstructionHandleResult.None);
            }

            string          methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set";
            MethodReference propertyRef  = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.PropertyName}"));

            cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef));

            return(InstructionHandleResult.Rewritten);
        }
예제 #11
0
        /// <summary>Rewrite a CIL instruction for compatibility.</summary>
        /// <param name="module">The module being rewritten.</param>
        /// <param name="cil">The CIL rewriter.</param>
        /// <param name="instruction">The instruction to rewrite.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        /// <returns>Returns whether the instruction was rewritten.</returns>
        /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
        public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction) && !instruction.ToString().Contains(this.FromTypeName))
            {
                return(false);
            }

            // field reference
            FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);

            if (fieldRef != null)
            {
                fieldRef.DeclaringType = this.RewriteIfNeeded(module, fieldRef.DeclaringType);
                fieldRef.FieldType     = this.RewriteIfNeeded(module, fieldRef.FieldType);
            }

            // method reference
            MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);

            if (methodRef != null)
            {
                methodRef.DeclaringType = this.RewriteIfNeeded(module, methodRef.DeclaringType);
                methodRef.ReturnType    = this.RewriteIfNeeded(module, methodRef.ReturnType);
                foreach (var parameter in methodRef.Parameters)
                {
                    parameter.ParameterType = this.RewriteIfNeeded(module, parameter.ParameterType);
                }
            }

            // type reference
            if (instruction.Operand is TypeReference typeRef)
            {
                TypeReference newRef = this.RewriteIfNeeded(module, typeRef);
                if (typeRef != newRef)
                {
                    cil.Replace(instruction, cil.Create(instruction.OpCode, newRef));
                }
            }

            return(true);
        }
예제 #12
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction))
            {
                return(InstructionHandleResult.None);
            }

            //Instruction: IL_0025: ldsfld StardewValley.GameLocation StardewValley.Game1::currentLocation
            string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set";

            try
            {
                //MethodReference propertyRef = module.ImportReference(this.ToType.GetMethod($"{methodPrefix}_{this.PropertyName}"));

                MethodReference method = module.ImportReference(this.ToType.GetMethod($"{methodPrefix}_{this.PropertyName}"));
                this.Monitor.Log("Method Ref: " + method.ToString());

                cil.Replace(instruction, cil.Create(OpCodes.Call, method));
            }
            catch (Exception e)
            {
                this.Monitor.Log(e.Message);
            }


            return(InstructionHandleResult.Rewritten);
        }
예제 #13
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction))
            {
                return(InstructionHandleResult.None);
            }

            cil.Replace(instruction, this.CreateConstantInstruction(cil, this.Value));
            return(InstructionHandleResult.Rewritten);
        }
예제 #14
0
        /// <summary>Rewrite a method definition for compatibility.</summary>
        /// <param name="module">The module being rewritten.</param>
        /// <param name="method">The method definition to rewrite.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        /// <returns>Returns whether the instruction was rewritten.</returns>
        /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
        public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(method))
            {
                return(false);
            }

            throw new IncompatibleInstructionException(this.NounPhrase);
        }
예제 #15
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction))
            {
                return(InstructionHandleResult.None);
            }

            // field reference
            FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);

            if (fieldRef != null)
            {
                this.RewriteIfNeeded(module, fieldRef.DeclaringType, newType => fieldRef.DeclaringType = newType);
                this.RewriteIfNeeded(module, fieldRef.FieldType, newType => fieldRef.FieldType         = newType);
            }

            // method reference
            MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);

            if (methodRef != null)
            {
                this.RewriteIfNeeded(module, methodRef.DeclaringType, newType => methodRef.DeclaringType = newType);
                this.RewriteIfNeeded(module, methodRef.ReturnType, newType => methodRef.ReturnType       = newType);
                foreach (var parameter in methodRef.Parameters)
                {
                    this.RewriteIfNeeded(module, parameter.ParameterType, newType => parameter.ParameterType = newType);
                }
            }

            // type reference
            if (instruction.Operand is TypeReference typeRef)
            {
                this.RewriteIfNeeded(module, typeRef, newType => cil.Replace(instruction, cil.Create(instruction.OpCode, newType)));
            }

            return(InstructionHandleResult.Rewritten);
        }
예제 #16
0
        /// <summary>Rewrite a method definition for compatibility.</summary>
        /// <param name="module">The module being rewritten.</param>
        /// <param name="method">The method definition to rewrite.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        /// <returns>Returns whether the instruction was rewritten.</returns>
        /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
        public override bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            bool rewritten = false;

            // return type
            if (this.IsMatch(method.ReturnType))
            {
                method.ReturnType = this.RewriteIfNeeded(module, method.ReturnType);
                rewritten         = true;
            }

            // parameters
            foreach (ParameterDefinition parameter in method.Parameters)
            {
                if (this.IsMatch(parameter.ParameterType))
                {
                    parameter.ParameterType = this.RewriteIfNeeded(module, parameter.ParameterType);
                    rewritten = true;
                }
            }

            // generic parameters
            for (int i = 0; i < method.GenericParameters.Count; i++)
            {
                var parameter = method.GenericParameters[i];
                if (this.IsMatch(parameter))
                {
                    TypeReference newType = this.RewriteIfNeeded(module, parameter);
                    if (newType != parameter)
                    {
                        method.GenericParameters[i] = new GenericParameter(parameter.Name, newType);
                    }
                    rewritten = true;
                }
            }

            // local variables
            foreach (VariableDefinition variable in method.Body.Variables)
            {
                if (this.IsMatch(variable.VariableType))
                {
                    variable.VariableType = this.RewriteIfNeeded(module, variable.VariableType);
                    rewritten             = true;
                }
            }

            return(rewritten);
        }
예제 #17
0
 /// <summary>Rewrite a method for compatibility.</summary>
 /// <param name="module">The module being rewritten.</param>
 /// <param name="cil">The CIL rewriter.</param>
 /// <param name="callOp">The instruction which calls the method.</param>
 /// <param name="methodRef">The method reference invoked by the <paramref name="callOp"/>.</param>
 /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
 public abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction callOp, MethodReference methodRef, PlatformAssemblyMap assemblyMap);
예제 #18
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction))
            {
                return(InstructionHandleResult.None);
            }

            FieldReference newRef = module.ImportReference(this.ToField);

            cil.Replace(instruction, cil.Create(instruction.OpCode, newRef));
            return(InstructionHandleResult.Rewritten);
        }
예제 #19
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction, platformChanged))
            {
                return(InstructionHandleResult.None);
            }

            MethodReference methodRef = (MethodReference)instruction.Operand;

            methodRef.DeclaringType = module.ImportReference(this.ToType);
            return(InstructionHandleResult.Rewritten);
        }
예제 #20
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            // field reference
            FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);

            if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType))
            {
                // get target field
                FieldDefinition targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
                if (targetField == null)
                {
                    return(InstructionHandleResult.None);
                }

                // validate return type
                if (!RewriteHelper.LooksLikeSameType(fieldRef.FieldType, targetField.FieldType))
                {
                    this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType)}, not {this.GetFriendlyTypeName(fieldRef.FieldType)})";
                    return(InstructionHandleResult.NotCompatible);
                }
            }

            // method reference
            MethodReference methodReference = RewriteHelper.AsMethodReference(instruction);

            if (methodReference != null && !this.IsUnsupported(methodReference) && this.ShouldValidate(methodReference.DeclaringType))
            {
                // get potential targets
                MethodDefinition[] candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray();
                if (candidateMethods == null || !candidateMethods.Any())
                {
                    return(InstructionHandleResult.None);
                }

                // compare return types
                MethodDefinition methodDef = methodReference.Resolve();
                if (methodDef == null)
                {
                    this.NounPhrase = $"reference to {methodReference.DeclaringType.FullName}.{methodReference.Name} (no such method)";
                    return(InstructionHandleResult.NotCompatible);
                }

                if (candidateMethods.All(method => !RewriteHelper.LooksLikeSameType(method.ReturnType, methodDef.ReturnType)))
                {
                    this.NounPhrase = $"reference to {methodDef.DeclaringType.FullName}.{methodDef.Name} (no such method returns {this.GetFriendlyTypeName(methodDef.ReturnType)})";
                    return(InstructionHandleResult.NotCompatible);
                }
            }

            return(InstructionHandleResult.None);
        }
예제 #21
0
        /// <summary>Rewrite a CIL instruction for compatibility.</summary>
        /// <param name="module">The module being rewritten.</param>
        /// <param name="cil">The CIL rewriter.</param>
        /// <param name="instruction">The instruction to rewrite.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        /// <returns>Returns whether the instruction was rewritten.</returns>
        /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
        public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction))
            {
                return(false);
            }

            throw new IncompatibleInstructionException(this.NounPhrase);
        }
예제 #22
0
 /// <summary>Rewrite a method for compatibility.</summary>
 /// <param name="module">The module being rewritten.</param>
 /// <param name="cil">The CIL rewriter.</param>
 /// <param name="callOp">The instruction which calls the method.</param>
 /// <param name="methodRef">The method reference invoked by the <paramref name="callOp"/>.</param>
 /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
 public override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction callOp, MethodReference methodRef, PlatformAssemblyMap assemblyMap)
 {
     methodRef.DeclaringType = module.Import(typeof(CompatibleSpriteBatch));
 }
예제 #23
0
 /// <summary>Rewrite a method definition for compatibility.</summary>
 /// <param name="module">The module being rewritten.</param>
 /// <param name="method">The method definition to rewrite.</param>
 /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
 /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
 /// <returns>Returns whether the instruction was rewritten.</returns>
 /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
 public bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
 {
     return(false);
 }
예제 #24
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            // field reference
            FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);

            if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType))
            {
                // can't compare generic type parameters between definition and reference
                if (fieldRef.FieldType.IsGenericInstance || fieldRef.FieldType.IsGenericParameter)
                {
                    return(InstructionHandleResult.None);
                }

                // get target field
                FieldDefinition targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
                if (targetField == null)
                {
                    return(InstructionHandleResult.None);
                }

                // validate return type
                string actualReturnTypeID   = this.GetComparableTypeID(targetField.FieldType);
                string expectedReturnTypeID = this.GetComparableTypeID(fieldRef.FieldType);
                if (actualReturnTypeID != expectedReturnTypeID)
                {
                    this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType, actualReturnTypeID)}, not {this.GetFriendlyTypeName(fieldRef.FieldType, expectedReturnTypeID)})";
                    return(InstructionHandleResult.NotCompatible);
                }
            }

            // method reference
            MethodReference methodReference = RewriteHelper.AsMethodReference(instruction);

            if (methodReference != null && this.ShouldValidate(methodReference.DeclaringType))
            {
                // can't compare generic type parameters between definition and reference
                if (methodReference.ReturnType.IsGenericInstance || methodReference.ReturnType.IsGenericParameter)
                {
                    return(InstructionHandleResult.None);
                }

                // get potential targets
                MethodDefinition[] candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray();
                if (candidateMethods == null || !candidateMethods.Any())
                {
                    return(InstructionHandleResult.None);
                }

                // compare return types
                MethodDefinition methodDef = methodReference.Resolve();
                if (methodDef == null)
                {
                    this.NounPhrase = $"reference to {methodReference.DeclaringType.FullName}.{methodReference.Name} (no such method)";
                    return(InstructionHandleResult.NotCompatible);
                }

                string expectedReturnType = this.GetComparableTypeID(methodDef.ReturnType);
                if (candidateMethods.All(method => this.GetComparableTypeID(method.ReturnType) != expectedReturnType))
                {
                    this.NounPhrase = $"reference to {methodDef.DeclaringType.FullName}.{methodDef.Name} (no such method returns {this.GetFriendlyTypeName(methodDef.ReturnType, expectedReturnType)})";
                    return(InstructionHandleResult.NotCompatible);
                }
            }

            return(InstructionHandleResult.None);
        }
예제 #25
0
        /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
        /// <param name="module">The assembly module containing the instruction.</param>
        /// <param name="cil">The CIL processor.</param>
        /// <param name="instruction">The instruction to handle.</param>
        /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
        /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
        public InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
        {
            if (!this.IsMatch(instruction))
            {
                return(InstructionHandleResult.None);
            }

            // get instructions comprising method call
            int         index    = cil.Body.Instructions.IndexOf(instruction);
            Instruction loadArg0 = cil.Body.Instructions[index - 2];
            Instruction loadArg1 = cil.Body.Instructions[index - 1];

            if (loadArg0.OpCode != OpCodes.Ldarg_0)
            {
                throw new InvalidOperationException($"Unexpected instruction sequence while removing virtual {this.ToType.Name}.{this.MethodName} call: found {loadArg0.OpCode.Name} instead of {OpCodes.Ldarg_0.Name}");
            }
            if (loadArg1.OpCode != OpCodes.Ldarg_1)
            {
                throw new InvalidOperationException($"Unexpected instruction sequence while removing virtual {this.ToType.Name}.{this.MethodName} call: found {loadArg1.OpCode.Name} instead of {OpCodes.Ldarg_1.Name}");
            }

            // remove method call
            cil.Remove(loadArg0);
            cil.Remove(loadArg1);
            cil.Remove(instruction);
            return(InstructionHandleResult.Rewritten);
        }