コード例 #1
0
        /// <summary>
        /// Checks that all abstract methods and properties of the base class have been implemented,
        /// as they exist currently in the target assembly.
        /// </summary>
        /// <returns><c>false</c>, if one of more abstract methods were not implemented in the derived class,
        /// <c>true</c> otherwise.</returns>
        /// <param name="type">A class.</param>
        /// <remarks>Resolves the base class against the reference assemblies supplied on the command line.</remarks>
        static bool CheckAbstractMethods(TypeDefinition type)
        {
            bool failure = false;
            // ensure all abstract methods in the base class are overridden
            TypeDefinition @base = null;

            // 14 Jan 2017 S. Baer
            // If the type itself is abstract, then it doesn't need to implement all
            // of the abstract members in it's base class
            if (null != type.BaseType && !type.IsAbstract)
            {
                // resolve the base class so we're checking against the version of the library that we want
                try
                {
                    @base = type.BaseType.Resolve();
                }
                catch (AssemblyResolutionException)
                {
                    logger.Warning("Couldn't resolve base class: {0}", type.BaseType.FullName);
                }

                if (null != @base)
                {
                    // skip if base class isn't defined in one of the reference assemblies
                    var scope = @base.Module.Assembly.Name; // be consistent
                    if (!cache.ContainsKey(scope.Name))
                    {
                        return(true);
                    }

                    Console.WriteLine("  Overrides ({0})", @base.FullName);

                    foreach (var method in @base.Methods)
                    {
                        if (!method.IsAbstract)
                        {
                            continue;
                        }

                        bool is_overridden = null != Utils.TryMatchMethod(type, method);

                        if (is_overridden)
                        {
                            Pretty.Instruction(ResolutionStatus.Success, scope.Name, method.FullName);
                        }
                        else
                        {
                            failure = true;
                            Pretty.Instruction(ResolutionStatus.Failure, scope.Name, method.FullName);
                        }
                    }
                }
            }

            return(!failure);
        }
コード例 #2
0
        static int Main(string[] args)
        {
            if (args.Length < 1)
            {
                Usage("Not enough arguments");
                return(ERROR_BAD_COMMAND);
            }

            // control verbosity
            if (args[0] == "--quiet" || args[0] == "-q")
            {
                quiet        = true;
                logger.Level = Logger.LogLevel.WARNING;
                args         = args.Skip(1).ToArray();
            }
            else if (args[0] == "--debug")
            {
                logger.Level = Logger.LogLevel.DEBUG;
                args         = args.Skip(1).ToArray();
            }

            // again, check if we have enough arguments
            if (args.Length < 1)
            {
                Usage("Not enough arguments");
                return(ERROR_BAD_COMMAND);
            }

            // should we return an error code if pinvokes exist?

            bool treatPInvokeAsError = false;

            if (args[0] == "--treat-pinvoke-as-error")
            {
                treatPInvokeAsError = true;
                args = args.Skip(1).ToArray();
            }

            // again, check if we have enough arguments
            if (args.Length < 1)
            {
                Usage("Not enough arguments");
                return(ERROR_BAD_COMMAND);
            }

            // first arg is the path to the main assembly being processed
            string fileName = args[0];

            if (!File.Exists(fileName))
            {
                // if the file doesn't exist, it might be a directory
                // TODO: handle directories
                if (Directory.Exists(fileName))
                {
                    logger.Error("{0} appears to be a directory; .NET assemblies only, please.", fileName);
                    return(ERROR_NOT_DOTNET);
                }
                logger.Error("Couldn't find {0}. Are you sure it exists?", fileName);
                return(ERROR_NOT_THERE);
            }

            // check that the main file is a dot net assembly
            // this gives a clearer error message than the "one or more..." error
            try
            {
                System.Reflection.AssemblyName.GetAssemblyName(fileName);
            }
            catch (System.BadImageFormatException)
            {
                logger.Error("{0} is not a .NET assembly.", fileName);
                return(ERROR_NOT_DOTNET);
            }

            // load module and assembly resolver
            ModuleDefinition       module;
            CustomAssemblyResolver customResolver;

            try
            {
                // second arg and onwards should be paths to reference assemblies
                // instantiate custom assembly resolver that loads reference assemblies into cache
                // note: ONLY these assemblies will be available to the resolver
                customResolver = new CustomAssemblyResolver(args.Skip(1));

                // load the plugin module (with the custom assembly resolver)
                // TODO: perhaps we should load the plugin assembly then iterate through all modules
                module = ModuleDefinition.ReadModule(fileName, new ReaderParameters
                {
                    AssemblyResolver = customResolver
                });
            }
            catch (BadImageFormatException)
            {
                logger.Error("One (or more) of the files specified is not a .NET assembly");
                return(ERROR_NOT_DOTNET);
            }
            catch (FileNotFoundException e)
            {
                logger.Error("Couldn't find {0}. Are you sure it exists?", e.FileName);
                return(ERROR_NOT_THERE);
            }

            if (module.Assembly.Name.Name == "")
            {
                logger.Error("Assembly has no name. This is unexpected.");
                return(ERROR_UNHANDLED_EXCEPTION);
            }

            // extract cached reference assemblies from custom assembly resolver
            // we'll query these later to make sure we only attempt to resolve a reference when the
            // definition is defined in an assembly in this list
            cache = customResolver.Cache;

            // print assembly name
            logger.Info("{0}\n", module.Assembly.FullName);

            // print assembly references (buildtime)
            if (module.AssemblyReferences.Count > 0)
            {
                logger.Info("Assembly references:", module.Assembly.Name.Name);
                foreach (AssemblyNameReference reference in module.AssemblyReferences)
                {
                    logger.Info("  {0}", reference.FullName);
                }
            }
            logger.Info("");

            // print cached assembly names (i.e. runtime references)
            if (args.Length > 1)
            {
                logger.Info("Cached assemblies:");
                foreach (var assembly in args.Skip(1))
                {
                    logger.Info("  {0}", AssemblyDefinition.ReadAssembly(assembly).FullName);
                }
            }
            else // no reference assemblies. Grab the skipping rope
            {
                logger.Warning("Empty resolution cache (no reference assemblies specified)");
            }
            logger.Info("");

            // mixed-mode?
            bool isMixed = (module.Attributes & ModuleAttributes.ILOnly) != ModuleAttributes.ILOnly;

            logger.Info("Mixed-mode? {0}\n", isMixed);

            // global failure/pinvoke trackers for setting return code
            bool failure = false;
            bool pinvoke = false;

            List <TypeDefinition> types = GetAllTypesAndNestedTypes(module.Types);

            // iterate over all the TYPES
            foreach (TypeDefinition type in types)
            {
                Pretty.Class("{0}", type.FullName);

                // iterate over all the METHODS that have a method body
                foreach (MethodDefinition method in type.Methods)
                {
                    Pretty.Method("{0}", method.FullName);
                    if (!method.HasBody) // skip if no body
                    {
                        continue;
                    }

                    // iterate over all the INSTRUCTIONS
                    foreach (var instruction in method.Body.Instructions)
                    {
                        // skip if no operand
                        if (instruction.Operand == null)
                        {
                            continue;
                        }

                        logger.Debug(
                            "Found instruction at {0} with code: {1}",
                            instruction.Offset,
                            instruction.OpCode.Code);

                        string instructionString = instruction.Operand.ToString()          // for sake of consistency
                                                   .Replace("{", "{{").Replace("}", "}}"); // escape curly brackets

                        // get the scope (the name of the assembly in which the operand is defined)
                        IMetadataScope scope = GetOperandScope(instruction.Operand);
                        if (scope != null)
                        {
                            // pinvoke?
                            ModuleReference nativeModule;
                            bool            isPInvoke = IsPInvoke(instruction.Operand, out nativeModule);
                            if (isPInvoke && nativeModule != null)
                            {
                                Pretty.Instruction(ResolutionStatus.PInvoke, nativeModule.Name, instructionString);
                                pinvoke = true;
                                continue;
                            }

                            // skip if scope is not in the list of cached reference assemblies
                            if (!cache.ContainsKey(scope.Name))
                            {
                                Pretty.Instruction(ResolutionStatus.Skipped, scope.Name, instructionString);
                                continue;
                            }
                            logger.Debug("{0} is on the list so let's try to resolve it", scope.Name);
                            logger.Debug(instruction.Operand.ToString());
                            // try to resolve operand
                            // this is the big question - does the field/method/class exist in one of
                            // the cached reference assemblies
                            bool success = TryResolve(instruction.Operand, type);
                            if (success || CheckMultidimensionalArray(instruction, method, type, scope))
                            {
                                Pretty.Instruction(ResolutionStatus.Success, scope.Name, instructionString);
                            }
                            else
                            {
                                Pretty.Instruction(ResolutionStatus.Failure, scope.Name, instructionString);
                                failure = true; // set global failure (non-zero exit code)
                            }
                        }
                    }
                }

                // check that all abstract methods in the base type (where appropriate) have been implemented
                // note: base type resolved against the referenced assemblies
                failure |= CheckAbstractMethods(type) == false;
            }

            // exit code
            if (failure)
            {
                return(ERROR_COMPAT);
            }
            if (pinvoke && treatPInvokeAsError)
            {
                return(ERROR_PINVOKE);
            }

            return(0); // a-ok
        }