Exemplo n.º 1
0
        private protected void RegisterInstructionMetadata(InstructionMetadata meta)
        {
            //generate delegate
            Action del = GenerateDelegate(meta);

            //register delegate
            Instruction inst = new Instruction(
                meta.CPUAttribute.OpCode,
                meta.CPUAttribute.Cycles,
                del
                );

            RegisterInstruction(inst);
        }
Exemplo n.º 2
0
        private protected void FindParameters(InstructionMetadata meta)
        {
            if (meta.Parameters == null)
            {
                meta.Parameters = new List <MemoryAddressAttributeBase>();
            }

            ParameterInfo[] parameters = meta.Method.GetParameters();
            foreach (ParameterInfo parameter in parameters)
            {
                MemoryAddressAttributeBase attribute = (MemoryAddressAttributeBase)parameter.GetCustomAttribute(typeof(MemoryAddressAttributeBase), false);
                if (attribute != null)
                {
                    if (parameter.ParameterType != typeof(int))
                    {
                        ThrowInvalidParameterType(parameter.Name, "int", meta);
                    }

                    // add parameter to meta
                    MemoryAddressAttributeBase param = attribute;
                    meta.Parameters.Add(param);
                }
                else
                {
                    if (parameter.ParameterType != typeof(byte))
                    {
                        ThrowInvalidParameterType(parameter.Name, "byte", meta);
                    }

                    // add parameter to meta
                    MemoryAddressAttributeBase param = null;
                    meta.Parameters.Add(param);
                }
            }

            RegisterInstructionMetadata(meta);
        }
Exemplo n.º 3
0
        private protected Action GenerateDelegate(InstructionMetadata meta)
        {
            Action del = () =>
            {
                object[] instParams = new object[meta.Parameters.Count];

                for (int i = 0; i < instParams.Length; i++)
                {
                    MemoryAddressAttributeBase attr = meta.Parameters[i];
                    if (attr == null)
                    {
                        instParams[i] = FetchNext();
                    }
                    else
                    {
                        byte[] rawAddr = FetchMultiple(attr.RequiredBytes);
                        instParams[i] = attr.Resolve(this, ref rawAddr);
                    }
                }
                meta.Method.Invoke(meta.ClassInstance, instParams);
            };

            return(del);
        }
Exemplo n.º 4
0
        /****
        ** Assembly rewriting
        ****/
        /// <summary>Rewrite the types referenced by an assembly.</summary>
        /// <param name="mod">The mod for which the assembly is being loaded.</param>
        /// <param name="assembly">The assembly to rewrite.</param>
        /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
        /// <param name="loggedMessages">The messages that have already been logged for this mod.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <returns>Returns whether the assembly was modified.</returns>
        /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
        private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, bool assumeCompatible, HashSet <string> loggedMessages, string logPrefix)
        {
            ModuleDefinition module   = assembly.MainModule;
            string           filename = $"{assembly.Name.Name}.dll";

            // swap assembly references if needed (e.g. XNA => MonoGame)
            bool platformChanged = false;

            for (int i = 0; i < module.AssemblyReferences.Count; i++)
            {
                // remove old assembly reference
                if (this.AssemblyMap.RemoveNames.Any(name => module.AssemblyReferences[i].Name == name))
                {
                    this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewriting {filename} for OS...");
                    platformChanged = true;
                    module.AssemblyReferences.RemoveAt(i);
                    i--;
                }
            }
            if (platformChanged)
            {
                // add target assembly references
                foreach (AssemblyNameReference target in this.AssemblyMap.TargetReferences.Values)
                {
                    module.AssemblyReferences.Add(target);
                }

                // rewrite type scopes to use target assemblies
                IEnumerable <TypeReference> typeReferences = module.GetTypeReferences().OrderBy(p => p.FullName);
                foreach (TypeReference type in typeReferences)
                {
                    this.ChangeTypeScope(type);
                }
            }

            // find (and optionally rewrite) incompatible instructions
            bool anyRewritten = false;

            IInstructionHandler[] handlers = new InstructionMetadata().GetHandlers(this.ParanoidMode).ToArray();
            foreach (MethodDefinition method in this.GetMethods(module))
            {
                // check method definition
                foreach (IInstructionHandler handler in handlers)
                {
                    InstructionHandleResult result = handler.Handle(module, method, this.AssemblyMap, platformChanged);
                    this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename);
                    if (result == InstructionHandleResult.Rewritten)
                    {
                        anyRewritten = true;
                    }
                }

                // check CIL instructions
                ILProcessor cil          = method.Body.GetILProcessor();
                var         instructions = cil.Body.Instructions;
                // ReSharper disable once ForCanBeConvertedToForeach -- deliberate access by index so each handler sees replacements from previous handlers
                for (int offset = 0; offset < instructions.Count; offset++)
                {
                    foreach (IInstructionHandler handler in handlers)
                    {
                        Instruction             instruction = instructions[offset];
                        InstructionHandleResult result      = handler.Handle(module, cil, instruction, this.AssemblyMap, platformChanged);
                        this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename);
                        if (result == InstructionHandleResult.Rewritten)
                        {
                            anyRewritten = true;
                        }
                    }
                }
            }

            return(platformChanged || anyRewritten);
        }
Exemplo n.º 5
0
        /****
        ** Assembly rewriting
        ****/
        /// <summary>Rewrite the types referenced by an assembly.</summary>
        /// <param name="mod">The mod for which the assembly is being loaded.</param>
        /// <param name="assembly">The assembly to rewrite.</param>
        /// <param name="loggedMessages">The messages that have already been logged for this mod.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <returns>Returns whether the assembly was modified.</returns>
        /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
        private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, HashSet <string> loggedMessages, string logPrefix)
        {
            ModuleDefinition module   = assembly.MainModule;
            string           filename = $"{assembly.Name.Name}.dll";

            // swap assembly references if needed (e.g. XNA => MonoGame)
            bool platformChanged = false;

            for (int i = 0; i < module.AssemblyReferences.Count; i++)
            {
                // remove old assembly reference
                if (this.AssemblyMap.RemoveNames.Any(name => module.AssemblyReferences[i].Name == name))
                {
                    this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewriting {filename} for OS...");
                    platformChanged = true;
                    module.AssemblyReferences.RemoveAt(i);
                    i--;
                }
            }
            if (platformChanged)
            {
                // add target assembly references
                foreach (AssemblyNameReference target in this.AssemblyMap.TargetReferences.Values)
                {
                    module.AssemblyReferences.Add(target);
                }

                // rewrite type scopes to use target assemblies
                IEnumerable <TypeReference> typeReferences = module.GetTypeReferences().OrderBy(p => p.FullName);
                foreach (TypeReference type in typeReferences)
                {
                    this.ChangeTypeScope(type);
                }

                // rewrite types using custom attributes
                foreach (TypeDefinition type in module.GetTypes())
                {
                    foreach (var attr in type.CustomAttributes)
                    {
                        foreach (var conField in attr.ConstructorArguments)
                        {
                            if (conField.Value is TypeReference typeRef)
                            {
                                this.ChangeTypeScope(typeRef);
                            }
                        }
                    }
                }
            }

            // find or rewrite code
            IInstructionHandler[] handlers = new InstructionMetadata().GetHandlers(this.ParanoidMode, platformChanged, this.RewriteMods).ToArray();
            RecursiveRewriter     rewriter = new RecursiveRewriter(
                module: module,
                rewriteType: (type, replaceWith) =>
            {
                bool rewritten = false;
                foreach (IInstructionHandler handler in handlers)
                {
                    rewritten |= handler.Handle(module, type, replaceWith);
                }
                return(rewritten);
            },
                rewriteInstruction: (ref Instruction instruction, ILProcessor cil) =>
            {
                bool rewritten = false;
                foreach (IInstructionHandler handler in handlers)
                {
                    rewritten |= handler.Handle(module, cil, instruction);
                }
                return(rewritten);
            }
                );
            bool anyRewritten = rewriter.RewriteModule();

            // handle rewrite flags
            foreach (IInstructionHandler handler in handlers)
            {
                foreach (var flag in handler.Flags)
                {
                    this.ProcessInstructionHandleResult(mod, handler, flag, loggedMessages, logPrefix, filename);
                }
            }

            return(platformChanged || anyRewritten);
        }
Exemplo n.º 6
0
        private protected void FindMethods(Type classType)
        {
            // try to instantiate class
            object instance = null;

            try
            {
                instance = Activator.CreateInstance(classType, this);
            }
            catch (Exception e)
            {
                if (e is MissingMethodException)
                {
                    throw
                        new MissingMethodException($"No constructor with single parameter of type 'ICpu' found in class '{classType.Name}'. Define one or manually use ICpu.Register to register instruction");
                }
                else
                {
                    throw e;
                }
            }

            // Find CPU instructions
            MethodInfo[] methods = classType.GetMethods();
            foreach (MethodInfo method in methods)
            {
                CPUInstructionAttribute[] attributes = (CPUInstructionAttribute[])method.GetCustomAttributes(typeof(CPUInstructionAttribute), false);
                LimitCPUTypeAttribute     typeLimit  = (LimitCPUTypeAttribute)method.GetCustomAttribute(typeof(LimitCPUTypeAttribute), false);

                if (attributes.Length < 1)
                {
                    continue;
                }

                // ignore instruction if it's only for one type of CPU
                if (typeLimit != null && typeLimit.Type != _type)
                {
                    continue;
                }

                List <InstructionMetadata?> metadata = new List <InstructionMetadata?>();

                foreach (var attribute in attributes)
                {
                    // check if instruction has the same cpu type
                    if (!attribute.CPUType.HasFlag(_type))
                    {
                        metadata.Add(null);
                        continue;
                    }

                    // generate metadata
                    InstructionMetadata meta = new InstructionMetadata();
                    meta.ClassType     = classType;
                    meta.ClassInstance = instance;
                    meta.Method        = method;
                    meta.CPUAttribute  = attribute;

                    metadata.Add(meta);
                }

                if (metadata.Count == 1)
                {
                    if (metadata[0].HasValue)
                    {
                        FindParameters(metadata[0].Value);
                    }
                }
                else if (metadata.Count > 1)
                {
                    FindMemoryAddressParameters(method, metadata.ToArray());
                }
            }
        }
Exemplo n.º 7
0
 private void ThrowInvalidParameterType(string parameterName, string targetType, InstructionMetadata meta)
 {
     throw
         new InvalidParameterTypeException($"Parameter '{parameterName}' of CPU instruction '{meta.ClassType.Name}.{meta.Method.Name}' must be of type '{targetType}'");
 }
Exemplo n.º 8
0
        private protected void FindMemoryAddressParameters(MethodInfo method, InstructionMetadata?[] meta)
        {
            if (meta.Length < 1)
            {
                return;
            }
            // check if all attributes have memory attribute
            MemoryAddressAttributeBase[] memAttributes =
                (MemoryAddressAttributeBase[])method.GetCustomAttributes(typeof(MemoryAddressAttributeBase), false);

            if (memAttributes.Length > 0 && meta.Length != memAttributes.Length)
            {
                throw new Exception($"Method {method.Name} doesn't have the same number of CPUInstruction attributes ({meta.Length}) and MemoryResolver attributes ({memAttributes.Length})!");
            }

            // check parameters
            ParameterInfo[] parameters = method.GetParameters();
            bool[]          isIntParam = new bool[parameters.Length];

            for (int i = 0; i < parameters.Length; i++)
            {
                ParameterInfo parameter = parameters[i];
                if (parameter.ParameterType == typeof(int))
                {
                    isIntParam[i] = true;
                    continue;
                }
                else if (parameter.ParameterType == typeof(byte))
                {
                    isIntParam[i] = false;
                    continue;
                }
                else
                {
                    foreach (InstructionMetadata?metadata in meta)
                    {
                        if (metadata.HasValue)
                        {
                            ThrowInvalidParameterType(parameter.Name, "int|byte", metadata.Value);
                        }
                    }
                    // in case that all provided metadata is null
                    return;
                }
            }

            // generate delegates
            for (int i = 0; i < meta.Length; i++)
            {
                if (!meta[i].HasValue)
                {
                    continue;
                }
                InstructionMetadata metadata = meta[i].Value;

                metadata.Parameters = new List <MemoryAddressAttributeBase>();

                foreach (bool isInt in isIntParam)
                {
                    if (isInt)
                    {
                        if (i < memAttributes.Length)
                        {
                            metadata.Parameters.Add(memAttributes[i]);
                        }
                        else
                        {
                            ThrowInvalidParameterType(parameters[i].Name, "byte", metadata);
                        }
                    }
                    else
                    {
                        metadata.Parameters.Add(null);
                    }
                }

                RegisterInstructionMetadata(metadata);
            }
        }
Exemplo n.º 9
0
 public InstructionMetadata(InstructionMetadata metadata, Register register) : this(metadata.Attribute, metadata.FieldInfo)
 {
     Register = register;
 }