Пример #1
0
        /// <summary>
        /// Attempts to extract contracts from the specified assembly file. Used to verify that
        /// Foxtrot can still extract contracts from a rewritten assembly.
        /// </summary>
        private static void ExtractContracts(string assemblyFile, Options options)
        {
            // use assembly resolver from Foxtrot.Extractor
            var resolver = new AssemblyResolver(
                resolvedPaths: new string[0],
                libpaths: options.LibPaths,
                usePDB: false,
                preserveShortBranches: true,
                trace: false,
                postLoad: (r, assemblyNode) => {
                ContractNodes contractNodes = null;
                Extractor.ExtractContracts(
                    assemblyNode, null, null, null, null, out contractNodes,
                    e => Assert.True(false, e.ToString()), false);
            });

            var assembly = resolver.ProbeForAssembly(
                assemblyName: Path.GetFileNameWithoutExtension(assemblyFile),
                referencingModuleDirectory: Path.GetDirectoryName(assemblyFile),
                exts: new string[] { Path.GetExtension(assemblyFile) });

            // the assembly must be resolved and have no metadata import errors
            Assert.NotNull(assembly);
            Assert.True(assembly.MetadataImportErrors == null || assembly.MetadataImportErrors.Count == 0,
                        "Parsing back the rewritten assembly produced metadata import errors");
        }
Пример #2
0
        private bool TryLoadContractNodes(ref AssemblyNode assembly)
        {
            ContractNodes nodes = null;

            foreach (Module module in assembly.Modules)
            {
                IAssemblyResolver assemblyResolver = module.Definition.AssemblyResolver;
                foreach (AssemblyNameReference reference in module.Definition.AssemblyReferences)
                {
                    AssemblyDefinition def = assemblyResolver.Resolve(reference);
                    nodes = ContractNodes.GetContractNodes(new AssemblyNode(def), (s) => { });
                    if (nodes != null)
                    {
                        break;
                    }
                }
            }

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

            var extractor = new ContractExtractor(nodes, assembly, true);

            assembly = (AssemblyNode)extractor.Visit(assembly);
            return(true);
        }
Пример #3
0
        private static int InternalMain(string[] args)
        {
            options = new FoxtrotOptions();
            options.Parse(args);

            if (!options.nologo)
            {
                var version = typeof (FoxtrotOptions).Assembly.GetName().Version;

                Console.WriteLine("Microsoft (R) .NET Contract Rewriter Version {0}", version);
                Console.WriteLine("Copyright (C) Microsoft Corporation. All rights reserved.");
                Console.WriteLine("");
            }

            if (options.HasErrors)
            {
                options.PrintErrorsAndExit(Console.Out);
                return -1;
            }
            if (options.HelpRequested)
            {
                options.PrintOptions("", Console.Out);
                return 0;
            }

            if (options.breakIntoDebugger)
            {
                Debugger.Launch();
            }

#if DEBUG
            if (options.nobox)
            {
                Debug.Listeners.Clear();

                // listen for failed assertions
                Debug.Listeners.Add(new ExitTraceListener());
            }
#else
      Debug.Listeners.Clear();
#endif

            if (options.repro)
            {
                WriteReproFile(args);
            }

            var resolver = new AssemblyResolver(
                options.resolvedPaths, options.libpaths, options.debug,
                options.shortBranches, options.verbose > 2,
                PostLoadExtractionHook);

            GlobalAssemblyCache.probeGAC = options.useGAC;

            // Connect to LeaderBoard

            SendLeaderBoardRewriterFeature(options);

            int errorReturnValue = -1;

            IDictionary assemblyCache = new Hashtable();

            // Trigger static initializer of SystemTypes
            var savedGACFlag = GlobalAssemblyCache.probeGAC;
            
            GlobalAssemblyCache.probeGAC = false;
            
            TypeNode dummy = SystemTypes.Object;

            TargetPlatform.Clear();
            TargetPlatform.AssemblyReferenceFor = null;
            GlobalAssemblyCache.probeGAC = savedGACFlag;

            try
            {
                // Validate the command-line arguments.

                if (options.output != "same")
                {
                    if (!Path.IsPathRooted(options.output))
                    {
                        string s = Directory.GetCurrentDirectory();
                        options.output = Path.Combine(s, options.output);
                    }
                }

                if (options.assembly == null && options.GeneralArguments.Count == 1)
                {
                    options.assembly = options.GeneralArguments[0];
                }

                if (!File.Exists(options.assembly))
                {
                    throw new FileNotFoundException(String.Format("The given assembly '{0}' does not exist.", options.assembly));
                }

                InitializePlatform(resolver, assemblyCache);

                if (options.passthrough)
                {
                    options.rewrite = false;
                }
                if (!(options.rewrite || options.passthrough))
                {
                    Console.WriteLine("Error: Need to specify at least one of: /rewrite, /pass");
                    options.PrintOptions("", Console.Out);

                    return errorReturnValue;
                }

                if (options.extractSourceText && !options.debug)
                {
                    Console.WriteLine("Error: Cannot specify /sourceText without also specifying /debug");
                    options.PrintOptions("", Console.Out);

                    return errorReturnValue;
                }

                if (!(0 <= options.level && options.level <= 4))
                {
                    Console.WriteLine("Error: incorrect /level: {0}. /level must be between 0 and 4 (inclusive)", options.level);
                    return errorReturnValue;
                }

                if (options.automaticallyLookForOOBs && options.contracts != null && 0 < options.contracts.Count)
                {
                    Console.WriteLine("Error: Out of band contracts are being automatically applied, all files specified using the contracts option are ignored.");
                    return errorReturnValue;
                }

                // Sanity check: just make sure that all files specified for out-of-band contracts actually exist
                bool atLeastOneOobNotFound = false;
                if (options.contracts != null)
                {
                    foreach (string oob in options.contracts)
                    {
                        bool found = false;
                        if (File.Exists(oob)) found = true;

                        if (!found)
                        {
                            if (options.libpaths != null)
                            {
                                foreach (string dir in options.libpaths)
                                {
                                    if (File.Exists(Path.Combine(dir, oob)))
                                    {
                                        found = true;
                                        break;
                                    }
                                }
                            }

                            if (!found)
                            {
                                Console.WriteLine("Error: Contract file '" + oob + "' could not be found");
                                atLeastOneOobNotFound = true;
                            }
                        }
                    }
                }

                if (atLeastOneOobNotFound)
                {
                    return errorReturnValue;
                }

                // Load the assembly to be rewritten

                originalAssemblyName = Path.GetFileNameWithoutExtension(options.assembly);
                AssemblyNode assemblyNode = AssemblyNode.GetAssembly(options.assembly,
                    TargetPlatform.StaticAssemblyCache, true, options.debug, true, options.shortBranches
                    , delegate(AssemblyNode a)
                    {
                        //Console.WriteLine("Loaded '" + a.Name + "' from '" + a.Location.ToString() + "'");
                        PossiblyLoadOOB(resolver, a, originalAssemblyName);
                    });

                if (assemblyNode == null)
                    throw new FileLoadException("The given assembly could not be loaded.", options.assembly);

                // Check to see if any metadata errors were reported

                if (assemblyNode.MetadataImportWarnings != null && assemblyNode.MetadataImportWarnings.Count > 0)
                {
                    string msg = "\tThere were warnings reported in " + assemblyNode.Name + "'s metadata.\n";
                    foreach (Exception e in assemblyNode.MetadataImportWarnings)
                    {
                        msg += "\t" + e.Message;
                    }

                    Console.WriteLine(msg);
                }

                if (assemblyNode.MetadataImportErrors != null && assemblyNode.MetadataImportErrors.Count > 0)
                {
                    string msg = "\tThere were errors reported in " + assemblyNode.Name + "'s metadata.\n";
                    foreach (Exception e in assemblyNode.MetadataImportErrors)
                    {
                        msg += "\t" + e.Message;
                    }

                    Console.WriteLine(msg);

                    throw new InvalidOperationException("Foxtrot: " + msg);
                }
                else
                {
                    //Console.WriteLine("\tThere were no errors reported in {0}'s metadata.", assemblyNode.Name);
                }

                // Load the rewriter assembly if any

                AssemblyNode rewriterMethodAssembly = null;
                if (options.rewriterMethods != null && 0 < options.rewriterMethods.Length)
                {
                    string[] pieces = options.rewriterMethods.Split(',');
                    if (!(pieces.Length == 2 || pieces.Length == 3))
                    {
                        Console.WriteLine("Error: Need to provide two or three comma separated arguments to /rewriterMethods");
                        options.PrintOptions("", Console.Out);

                        return errorReturnValue;
                    }

                    string assemName = pieces[0];
                    rewriterMethodAssembly = resolver.ProbeForAssembly(assemName, null, resolver.AllExt);
                    if (rewriterMethodAssembly == null)
                    {
                        Console.WriteLine("Error: Could not open assembly '" + assemName + "'");
                        return errorReturnValue;
                    }

                    string nameSpaceName = null;
                    string bareClassName = null;

                    if (pieces.Length == 2)
                    {
                        // interpret A.B.C as namespace A.B and class C
                        // no nested classes allowed.
                        string namespaceAndClassName = pieces[1];

                        int lastDot = namespaceAndClassName.LastIndexOf('.');

                        nameSpaceName = lastDot == -1 ? "" : namespaceAndClassName.Substring(0, lastDot);
                        bareClassName = namespaceAndClassName.Substring(lastDot + 1);

                        userSpecifiedContractType = rewriterMethodAssembly.GetType(Identifier.For(nameSpaceName),
                            Identifier.For(bareClassName));
                    }
                    else
                    {
                        // pieces.Length == 3
                        // namespace can be A.B and class can be C.D
                        nameSpaceName = pieces[1];
                        bareClassName = pieces[2];
                        userSpecifiedContractType = GetPossiblyNestedType(rewriterMethodAssembly, nameSpaceName, bareClassName);
                    }

                    if (userSpecifiedContractType == null)
                    {
                        Console.WriteLine("Error: Could not find type '" + bareClassName + "' in the namespace '" +
                                          nameSpaceName + "' in the assembly '" + assemName + "'");

                        return errorReturnValue;
                    }
                }

                // Load the ASTs for all of the contract methods

                AssemblyNode contractAssembly = null;
                if (!options.passthrough)
                {
                    // The contract assembly should be determined in the following order:
                    // 0. if the option contractLibrary was specified, use that.
                    // 1. the assembly being rewritten
                    // 2. the system assembly
                    // 3. the microsoft.contracts library

                    if (options.contractLibrary != null)
                    {
                        contractAssembly = resolver.ProbeForAssembly(options.contractLibrary,
                            Path.GetDirectoryName(options.assembly), resolver.EmptyAndDllExt);

                        if (contractAssembly != null)
                            DefaultContractLibrary = ContractNodes.GetContractNodes(contractAssembly, options.EmitError);

                        if (contractAssembly == null || DefaultContractLibrary == null)
                        {
                            Console.WriteLine("Error: could not load Contracts API from assembly '{0}'", options.contractLibrary);
                            return -1;
                        }
                    }
                    else
                    {
                        if (DefaultContractLibrary == null)
                        {
                            // See if contracts are in the assembly we're rewriting
                            DefaultContractLibrary = ContractNodes.GetContractNodes(assemblyNode, options.EmitError);
                        }

                        if (DefaultContractLibrary == null)
                        {
                            // See if contracts are in Mscorlib
                            DefaultContractLibrary = ContractNodes.GetContractNodes(SystemTypes.SystemAssembly,
                                options.EmitError);
                        }

                        // try to load Microsoft.Contracts.dll
                        var microsoftContractsLibrary = resolver.ProbeForAssembly("Microsoft.Contracts",
                            Path.GetDirectoryName(options.assembly), resolver.DllExt);

                        if (microsoftContractsLibrary != null)
                        {
                            BackupContractLibrary = ContractNodes.GetContractNodes(microsoftContractsLibrary, options.EmitError);
                        }

                        if (DefaultContractLibrary == null && BackupContractLibrary != null)
                        {
                            DefaultContractLibrary = BackupContractLibrary;
                        }
                    }
                }

                if (DefaultContractLibrary == null)
                {
                    if (options.output == "same")
                    {
                        Console.WriteLine(
                            "Warning: Runtime Contract Checking requested, but contract class could not be found. Did you forget to reference the contracts assembly?");

                        return 0; // Returning success so that it doesn't break any build processes.
                    }
                    else
                    {
                        // Then an output file was specified (assume it is not the same as the input assembly, but that could be checked here).
                        // In that case, consider it an error to not have found a contract class.
                        // No prinicpled reason, but this is a common pitfall that several users have run into and the rewriter just 
                        // didn't do anything without giving any reason.
                        Console.WriteLine(
                            "Error: Runtime Contract Checking requested, but contract class could not be found. Did you forget to reference the contracts assembly?");

                        return errorReturnValue;
                    }
                }

                if (0 < options.verbose)
                {
                    Console.WriteLine("Trace: Using '" + DefaultContractLibrary.ContractClass.DeclaringModule.Location +
                                      "' for the definition of the contract class");
                }

                // Make sure we extract contracts from the system assembly and the system.dll assemblies.
                // As they are already loaded, they will never trigger the post assembly load event.
                // But even if we are rewriting one of these assemblies (and so *not* trying to extract
                // the contracts at this point), still need to hook up our resolver to them. If this
                // isn't done, then any references chased down from them might not get resolved.

                bool isPreloadedAssembly = false;

                CheckIfPreloaded(resolver, assemblyNode, SystemTypes.SystemAssembly, ref isPreloadedAssembly);
                CheckIfPreloaded(resolver, assemblyNode, SystemTypes.SystemDllAssembly, ref isPreloadedAssembly);

                //CheckIfPreloaded(resolver, assemblyNode, SystemTypes.SystemRuntimeWindowsRuntimeAssembly, ref isPreloadedAssembly);
                //CheckIfPreloaded(resolver, assemblyNode, SystemTypes.SystemRuntimeWindowsRuntimeUIXamlAssembly, ref isPreloadedAssembly);

                if (!isPreloadedAssembly)
                {
                    assemblyNode.AssemblyReferenceResolution += resolver.ResolveAssemblyReference;
                }

                MikesArchitecture(resolver, assemblyNode, DefaultContractLibrary, BackupContractLibrary);

                return options.GetErrorCount();
            }
            catch (Exception exception)
            {
                SendLeaderBoardFailure();

                // Redirect the exception message to the console and quit.
                Console.Error.WriteLine(new System.CodeDom.Compiler.CompilerError(exception.Source, 0, 0, null, exception.Message));
                return errorReturnValue;
            }
            finally
            {
                // Reset statics

                userSpecifiedContractType = null;
                DefaultContractLibrary = null;
                BackupContractLibrary = null;
                originalAssemblyName = null;
                options = null;

                // eagerly close all assemblies due to pdb file locking issues
                DisposeAssemblies(assemblyCache.Values);

                // copy needed since Dispose actually removes things from the StaticAssemblyCache
                object[] assemblies = new object[TargetPlatform.StaticAssemblyCache.Values.Count];

                TargetPlatform.StaticAssemblyCache.Values.CopyTo(assemblies, 0);
                DisposeAssemblies(assemblies);
            }
        }
Пример #4
0
        private static void MikesArchitecture(AssemblyResolver resolver, AssemblyNode assemblyNode,
            ContractNodes contractNodes, ContractNodes backupContracts)
        {
#if false
      var originalsourceDir = Path.GetDirectoryName(assemblyNode.Location);
      int oldPeVerifyCode = options.verify ? PEVerify(assemblyNode.Location, originalsourceDir) : -1;
#endif

            // Check to see if the assembly has already been rewritten

            if (!options.passthrough)
            {
                if (ContractNodes.IsAlreadyRewritten(assemblyNode))
                {
                    if (!options.allowRewritten)
                    {
                        Console.WriteLine("Assembly '" + assemblyNode.Name +
                                          "' has already been rewritten. I, your poor humble servant, cannot rewrite it. Instead I must give up without rewriting it. Help!");
                    }

                    return;
                }
            }

            // Extract the contracts from the code (includes checking the contracts)
            
            string contractFileName = Path.GetFileNameWithoutExtension(assemblyNode.Location) + ".Contracts";

            if (options.contracts == null || options.contracts.Count <= 0) contractFileName = null;

            if (options.contracts != null &&
                !options.contracts.Exists(name => name.Equals(assemblyNode.Name + ".Contracts.dll", StringComparison.OrdinalIgnoreCase)))
            {
                contractFileName = null;
            }

            AssemblyNode contractAssembly = null;
            if (contractFileName != null)
            {
                contractAssembly = resolver.ProbeForAssembly(contractFileName, assemblyNode.Directory,
                    resolver.DllExt);
            }

            if (!options.passthrough)
            {
                ContractNodes usedContractNodes;

                Extractor.ExtractContracts(assemblyNode, contractAssembly, contractNodes, backupContracts, contractNodes,
                    out usedContractNodes, options.EmitError, false);

                // important to extract source before we perform any more traversals due to contract instantiation. Otherwise,
                // we might get copies of contracts due to instantiation that have no source text yet.

                // Extract the text from the sources (optional)

                if (usedContractNodes != null && options.extractSourceText)
                {
                    GenerateDocumentationFromPDB gd = new GenerateDocumentationFromPDB(contractNodes);
                    gd.VisitForDoc(assemblyNode);
                }

                // After all contracts have been extracted in assembly, do some post-extractor checks

                // we run these even if no contracts were extracted due to checks having to do with overrides
                var contractNodesForChecks = usedContractNodes != null ? usedContractNodes : contractNodes;
                if (contractNodesForChecks != null)
                {
                    PostExtractorChecker pec = new PostExtractorChecker(contractNodesForChecks, options.EmitError, false,
                        options.fSharp, options.IsLegacyModeAssembly, options.addInterfaceWrappersWhenNeeded,
                        options.level);

                    if (contractAssembly != null)
                    {
                        pec.VisitForPostCheck(contractAssembly);
                    }
                    else
                    {
                        pec.VisitForPostCheck(assemblyNode);
                    }
                }

                // don't really need to test, since if they are the same, the assignment doesn't change that
                // but this is to emphasize that the nodes used in the AST by the extractor are different
                // than what we thought we were using.

                if (options.GetErrorCount() > 0)
                {
                    // we are done.
                    // But first, report any metadata errors so they are not masked by the errors
                    CheckForMetaDataErrors(assemblyNode);
                    return;
                }
            }

            // If we have metadata errors, cop out

            {
#if false
          for (int i = 0; i < assemblyNode.ModuleReferences.Count; i++)
          {
            Module m = assemblyNode.ModuleReferences[i].Module;
            Console.WriteLine("Location for referenced module '{0}' is '{1}'", m.Name, m.Location);
          }
#endif
                if (CheckForMetaDataErrors(assemblyNode))
                {
                    throw new Exception("Rewrite aborted due to metadata errors. Check output window");
                }
                for (int i = 0; i < assemblyNode.AssemblyReferences.Count; i++)
                {
                    AssemblyNode aref = assemblyNode.AssemblyReferences[i].Assembly;
                    if (CheckForMetaDataErrors(aref))
                    {
                        throw new Exception("Rewrite aborted due to metadata errors. Check output window");
                    }
                }
            }

            // Inject the contracts into the code (optional)

            if (options.rewrite && !options.passthrough)
            {
                // Rewrite the assembly in memory.
                ContractNodes cnForRuntime = null;

                // make sure to use the correct contract nodes for runtime code generation. We may have Contractnodes pointing to microsoft.Contracts even though
                // the code relies on mscorlib to provide the contracts. So make sure the code references the contract nodes first.
                if (contractNodes != null && contractNodes.ContractClass != null &&
                    contractNodes.ContractClass.DeclaringModule != SystemTypes.SystemAssembly)
                {
                    string assemblyNameContainingContracts = contractNodes.ContractClass.DeclaringModule.Name;
                    for (int i = 0; i < assemblyNode.AssemblyReferences.Count; i++)
                    {
                        if (assemblyNode.AssemblyReferences[i].Name == assemblyNameContainingContracts)
                        {
                            cnForRuntime = contractNodes;
                            break; // runtime actually references the contract library
                        }
                    }
                }

                if (cnForRuntime == null)
                {
                    // try to grab the system assembly contracts
                    cnForRuntime = ContractNodes.GetContractNodes(SystemTypes.SystemAssembly, null);
                }

                if (cnForRuntime == null)
                {
                    // Can happen if the assembly does not use contracts directly, but inherits them from some other place
                    // Use the normal contractNodes in this case (actually we should generate whatever we grab from ContractNodes)
                    cnForRuntime = contractNodes;
                }

                RuntimeContractMethods runtimeContracts =
                    new RuntimeContractMethods(userSpecifiedContractType,
                        cnForRuntime, assemblyNode, options.throwOnFailure, options.level, options.publicSurfaceOnly,
                        options.callSiteRequires,
                        options.recursionGuard, options.hideFromDebugger, options.IsLegacyModeAssembly);

                Rewriter rewriter = new Rewriter(assemblyNode, runtimeContracts, options.EmitError,
                    options.inheritInvariants, options.skipQuantifiers);

                rewriter.Verbose = 0 < options.verbose;
                rewriter.Visit(assemblyNode);

                // Perform this check only when there are no out-of-band contracts in use due to rewriter bug #336
                if (contractAssembly == null)
                {
                    PostRewriteChecker checker = new PostRewriteChecker(options.EmitError);
                    checker.Visit(assemblyNode);
                }
            }

            //Console.WriteLine(">>>Finished Rewriting<<<");

            // Set metadata version for target the same as for the source

            TargetPlatform.TargetRuntimeVersion = assemblyNode.TargetRuntimeVersion;

            // Write out the assembly (optional)

            if (options.rewrite || options.passthrough)
            {
                bool updateInPlace = options.output == "same";

                string pdbFile = Path.ChangeExtension(options.assembly, ".pdb");
                bool pdbExists = File.Exists(pdbFile);

                string backupAssembly = options.assembly + ".original";
                string backupPDB = pdbFile + ".original";


                if (updateInPlace)
                {
                    // Write the rewritten assembly in a temporary location.
                    options.output = options.assembly;

                    MoveAssemblyFileAndPDB(options.output, pdbFile, pdbExists, backupAssembly, backupPDB);
                }

                // Write the assembly.
                // Don't pass the debugInfo flag to WriteModule unless the PDB file exists.
                assemblyNode.WriteModule(options.output, options.debug && pdbExists && options.writePDBFile);

                string outputDir = updateInPlace
                    ? Path.GetDirectoryName(options.assembly)
                    : Path.GetDirectoryName(options.output);

                // Re-attach external file resources to the new output assembly.
                MoveModuleResources(assemblyNode, outputDir);

#if false
        if (oldPeVerifyCode == 0)
        {
          var newPeVerifyCode = PEVerify(assemblyNode.Location, originalsourceDir);
          if (newPeVerifyCode > 0)
          {
            if (updateInPlace)
            {
              // move original back in place
              MoveAssemblyFileAndPDB(backupAssembly, backupPDB, pdbExists, options.output, pdbFile);
            }
            throw new Exception("Rewrite failed to produce verifiable assembly");
          }
          else if (newPeVerifyCode == 0)
          {
            Console.WriteLine("rewriter output verified");
          }
        }
#endif
                if (updateInPlace)
                {
                    if (!options.keepOriginalFiles)
                    {
                        try
                        {
                            File.Delete(backupAssembly);
                        }
                        catch
                        {
                            // there are situations where the exe is still in use
                        }
                        if (options.debug && pdbExists && options.writePDBFile)
                        {
                            try
                            {
                                File.Delete(backupPDB);
                            }
                            catch
                            {
                                // I know this is stupid, but somehow on some machines we get an AccessError trying to delete the pdb.
                                // so we leave it in place.
                            }
                        }
                    }
                }
            }
        }