File: Compilation\BuildManager.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="BuildManager.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
/************************************************************************************************************/
 
 
 
namespace System.Web.Compilation {
 
    using System;
    using System.CodeDom.Compiler;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Versioning;
    using System.Security;
    using System.Security.Permissions;
    using System.Text;
    using System.Threading;
    using System.Web.Configuration;
    using System.Web.Hosting;
    using System.Web.Profile;
    using System.Web.UI;
    using System.Web.Util;
    using System.Xml;
 
 
    /// <devdoc>
    ///    <para>
    ///       IProvider compilation related services
    ///    </para>
    /// </devdoc>
    public sealed class BuildManager {
 
        /// Contants relating to generated assembly names
 
        // All generated assemblies start with this prefix
        internal const string AssemblyNamePrefix = "App_";
 
        // Web assemblies are the assemblies generated from web files (aspx, ascx, ...)
        internal const string WebAssemblyNamePrefix = AssemblyNamePrefix + "Web_";
 
        internal const string AppThemeAssemblyNamePrefix = AssemblyNamePrefix + "Theme_";
        internal const string GlobalThemeAssemblyNamePrefix = AssemblyNamePrefix + "GlobalTheme_";
        internal const string AppBrowserCapAssemblyNamePrefix = AssemblyNamePrefix + "Browsers";
 
        private const string CodeDirectoryAssemblyName = AssemblyNamePrefix + "Code";
        internal const string SubCodeDirectoryAssemblyNamePrefix = AssemblyNamePrefix + "SubCode_";
        private const string ResourcesDirectoryAssemblyName = AssemblyNamePrefix + "GlobalResources";
        private const string LocalResourcesDirectoryAssemblyName = AssemblyNamePrefix + "LocalResources";
        private const string WebRefDirectoryAssemblyName = AssemblyNamePrefix + "WebReferences";
        internal const string GlobalAsaxAssemblyName = AssemblyNamePrefix + HttpApplicationFactory.applicationFileName;
 
        private const string LicensesAssemblyName = AssemblyNamePrefix + "Licenses";
 
        internal const string UpdatableInheritReplacementToken = "__ASPNET_INHERITS";
 
        // Name of the temporary subdirectory under the codegen folder for buildproviders to generate embedded resource files.
        private const string CodegenResourceDirectoryName = "ResX";
 
        private static System.Security.Cryptography.RNGCryptoServiceProvider _rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
        private static bool _theBuildManagerInitialized;
        private static Exception _initializeException;
        private static BuildManager _theBuildManager = new BuildManager();  // single instance of the class
        private static long s_topLevelHash;
        private readonly HashCodeCombiner _preAppStartHashCodeCombiner = new HashCodeCombiner();
        internal static BuildManager TheBuildManager { get { return _theBuildManager; } }
 
        // Precompilation related fields
        private const string precompMarkerFileName = "PrecompiledApp.config";
        private string _precompTargetPhysicalDir;
        private PrecompilationFlags _precompilationFlags;
        private bool _isPrecompiledApp;
        private bool _isPrecompiledAppComputed;
        private bool _isUpdatablePrecompiledApp;
        private bool _precompilingApp;  // we're in the process of precompiling an app
 
        private string _strongNameKeyFile;
        private string _strongNameKeyContainer;
 
        private string _codegenResourceDir;
 
        private bool _optimizeCompilations;
        internal static bool OptimizeCompilations {
            get { return _theBuildManager._optimizeCompilations; }
        }
 
        // filepath to the generated web.hash file, This file should only be re-created when
        // the appdomain is restarted and the top-level generated assemblies need to be recompiled.
        private string _webHashFilePath;
        internal static String WebHashFilePath {
            get { return _theBuildManager._webHashFilePath; }
        }
 
        private BuildResultCache[] _caches;
        private StandardDiskBuildResultCache _codeGenCache;
        private MemoryBuildResultCache _memoryCache;
 
        private bool _topLevelFilesCompiledStarted;
        private bool _topLevelFilesCompiledCompleted;
        private Exception _topLevelFileCompilationException;
 
        private BuildResultCompiledGlobalAsaxType _globalAsaxBuildResult;
        private Type _profileType;
 
        // Special top level directories that are treated differently from regular web directories
        // during precompilation (e.g. App_Code)
        private StringSet _excludedTopLevelDirectories;
 
        // Directories that are not requestable
        private StringSet _forbiddenTopLevelDirectories;
 
        private StringSet _excludedCodeSubdirectories;
 
        private List<VirtualPath> _excludedCompilationPaths;
 
        private CompilationStage _compilationStage = CompilationStage.PreTopLevelFiles;
        internal static CompilationStage CompilationStage { get { return _theBuildManager._compilationStage; } }
 
        private VirtualPath _scriptVirtualDir;
        private VirtualPath _globalAsaxVirtualPath;
        internal static VirtualPath ScriptVirtualDir { get { return _theBuildManager._scriptVirtualDir; } }
        internal static VirtualPath GlobalAsaxVirtualPath { get { return _theBuildManager._globalAsaxVirtualPath; } }
 
        private BuildManager() { }
 
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        internal static bool InitializeBuildManager() {
 
            // If we already tried and got an exception, just rethrow it
            if (_initializeException != null) {
                // We need to wrap it in a new exception, otherwise we lose the original stack.
                throw new HttpException(_initializeException.Message, _initializeException);
            }
 
            if (!_theBuildManagerInitialized) {
 
                // If Fusion was not yet initialized, skip the init.
                // This can happen when there is a very early failure (e.g. see VSWhidbey 137366)
                Debug.Trace("BuildManager", "InitializeBuildManager " + HttpRuntime.FusionInited);
                if (!HttpRuntime.FusionInited)
                    return false;
 
                // Likewise, if the trust level has not yet been determined, skip the init (VSWhidbey 422311)
                if (HttpRuntime.TrustLevel == null)
                    return false;
 
                _theBuildManagerInitialized = true;
                try {
                    _theBuildManager.Initialize();
                }
                catch (Exception e) {
                    _theBuildManagerInitialized = false;
                    _initializeException = e;
                    throw;
                }
            }
 
            return true;
        }
 
        private ClientBuildManagerCallback _cbmCallback;
        internal static ClientBuildManagerCallback CBMCallback { get { return _theBuildManager._cbmCallback; } }
 
        private static bool _parseErrorReported;
        internal static void ReportParseError(ParserError parseError) {
            // If there is a CBM callback, inform it of the error
            if (BuildManager.CBMCallback != null) {
                _parseErrorReported = true;
                BuildManager.CBMCallback.ReportParseError(parseError);
            }
        }
 
        private void ReportTopLevelCompilationException() {
            Debug.Assert(_topLevelFileCompilationException != null);
 
            // Try to report the cached error to the CBM callback
            ReportErrorsFromException(_topLevelFileCompilationException);
 
            // We need to wrap it in a new exception, otherwise we lose the original stack.
            throw new HttpException(_topLevelFileCompilationException.Message,
                _topLevelFileCompilationException);
        }
 
        // Given an exception, attempt to turn it into calls to the CBM callback
        private void ReportErrorsFromException(Exception e) {
            // If there is no CBM callback, nothing to do
            if (BuildManager.CBMCallback == null)
                return;
 
            // Call the CBM callback as appropriate, based on the type of exception
 
            if (e is HttpCompileException) {
                CompilerResults results = ((HttpCompileException)e).Results;
                foreach (CompilerError error in results.Errors) {
                    BuildManager.CBMCallback.ReportCompilerError(error);
                }
            }
            else if (e is HttpParseException) {
                foreach (ParserError parseError in ((HttpParseException)e).ParserErrors) {
                    ReportParseError(parseError);
                }
            }
        }
 
        // The assemblies produced from the code directories and global.asax, which
        // every other compilation will linked with.
        private List<Assembly> _topLevelReferencedAssemblies = new List<Assembly>() {
            typeof(HttpRuntime).Assembly,
            typeof(System.ComponentModel.Component).Assembly,
        };
 
        private List<Assembly> TopLevelReferencedAssemblies { get { return _topLevelReferencedAssemblies; } }
 
        private Dictionary<String, AssemblyReferenceInfo> _topLevelAssembliesIndexTable;
        private IDictionary<String, AssemblyReferenceInfo> TopLevelAssembliesIndexTable { get { return _topLevelAssembliesIndexTable; } }
 
        private Dictionary<String, String> _generatedFileTable;
        internal static Dictionary<String, String> GenerateFileTable {
            get {
                if (_theBuildManager._generatedFileTable == null) {
                    _theBuildManager._generatedFileTable = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
                }
 
                return _theBuildManager._generatedFileTable;
            }
        }
 
        private ArrayList _codeAssemblies;
        public static IList CodeAssemblies {
            get {
                _theBuildManager.EnsureTopLevelFilesCompiled();
                return _theBuildManager._codeAssemblies;
            }
        }
 
        private IDictionary _assemblyResolveMapping;
 
        private Assembly _appResourcesAssembly;
        internal static Assembly AppResourcesAssembly { get { return _theBuildManager._appResourcesAssembly; } }
 
        // Indicates whether the parsers should coninue processing for more errors.
        // This is used in both CBM precompile-web, precompile-page and aspnet_compiler tool.
        private bool _throwOnFirstParseError = true;
        internal static bool ThrowOnFirstParseError {
            get { return _theBuildManager._throwOnFirstParseError; }
            set { _theBuildManager._throwOnFirstParseError = value; }
        }
 
        // Marks whether we are in the middle of performing precompilation, which affects how
        // we deal with error handling and batching
        private bool _performingPrecompilation = false;
        internal static bool PerformingPrecompilation {
            get { return _theBuildManager._performingPrecompilation; }
            set { _theBuildManager._performingPrecompilation = value; }
        }
 
        private bool _skipTopLevelCompilationExceptions;
        internal static bool SkipTopLevelCompilationExceptions {
            get { return _theBuildManager._skipTopLevelCompilationExceptions; }
            set { _theBuildManager._skipTopLevelCompilationExceptions = value; }
        }
 
        private static HashSet<Assembly> s_dynamicallyAddedReferencedAssembly = new HashSet<Assembly>();
 
        public static void AddReferencedAssembly(Assembly assembly) {
            if (assembly == null) {
                throw new ArgumentNullException("assembly");
            }
            ThrowIfPreAppStartNotRunning();
 
            s_dynamicallyAddedReferencedAssembly.Add(assembly);
        }
 
        /*
         * Return the list of assemblies that a compilation needs to reference for a given
         * config minus the top-level assemblies indexed later than removeIndex
         */
        internal static ICollection GetReferencedAssemblies(CompilationSection compConfig, int removeIndex) {
            AssemblySet referencedAssemblies = new AssemblySet();
 
            // Add all the config assemblies to the list
            foreach (AssemblyInfo a in compConfig.Assemblies) {
                Assembly[] assemblies = a.AssemblyInternal;
                if (assemblies == null) {
                    lock (compConfig) {
                        assemblies = a.AssemblyInternal;
                        if (assemblies == null)
                            // 
                            assemblies = a.AssemblyInternal = compConfig.LoadAssembly(a);
                    }
                }
 
                for (int i = 0; i < assemblies.Length; i++) {
                    if (assemblies[i] != null) {
                        referencedAssemblies.Add(assemblies[i]);
                    }
                }
            }
 
            // Clone the top level referenced assemblies (code + global.asax + etc...), up to the removeIndex
            for (int i = 0; i < removeIndex; i++) {
                referencedAssemblies.Add(TheBuildManager.TopLevelReferencedAssemblies[i]);
            }
 
            // 
 
            foreach (Assembly assembly in s_dynamicallyAddedReferencedAssembly) {
                referencedAssemblies.Add(assembly);
            }
 
            return referencedAssemblies;
        }
 
        internal static ICollection GetReferencedAssemblies(CompilationSection compConfig) {
 
            // Start by cloning the top level referenced assemblies (code + global.asax + etc...)
            AssemblySet referencedAssemblies = AssemblySet.Create(
                TheBuildManager.TopLevelReferencedAssemblies);
 
            // Add all the config assemblies to the list
            foreach (AssemblyInfo a in compConfig.Assemblies) {
                Assembly[] assemblies = a.AssemblyInternal;
                if (assemblies == null) {
                    lock (compConfig) {
                        assemblies = a.AssemblyInternal;
                        if (assemblies == null)
                            // 
                            assemblies = a.AssemblyInternal = compConfig.LoadAssembly(a);
                    }
                }
 
                for (int i = 0; i < assemblies.Length; i++) {
                    if (assemblies[i] != null) {
                        referencedAssemblies.Add(assemblies[i]);
                    }
                }
            }
 
            // 
 
            foreach (Assembly assembly in s_dynamicallyAddedReferencedAssembly) {
                referencedAssemblies.Add(assembly);
            }
 
            return referencedAssemblies;
        }
 
 
        /*
         * Return the list of assemblies that all page compilations need to reference. This includes
         * config assemblies (<assemblies> section), bin assemblies and assemblies built from the
         * app App_Code and other top level folders.
         */
 
        /// <devdoc>
        /// Returns the assemblies referenced at the root application level of the current appF
        /// </devdoc>
        public static ICollection GetReferencedAssemblies() {
            CompilationSection compConfig = MTConfigUtil.GetCompilationAppConfig();
 
            _theBuildManager.EnsureTopLevelFilesCompiled();
 
            return GetReferencedAssemblies(compConfig);
        }
 
        /// <summary>
        /// Specifies a string representing a dependency that the BuildManager factors when determining if a clean build is required.
        /// </summary>
        /// <param name="dependency">String representation of a dependency.</param>
        public static void AddCompilationDependency(string dependency) {
            if (String.IsNullOrEmpty(dependency)) {
                throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty), "dependency");
            }
            BuildManager.ThrowIfPreAppStartNotRunning();
            _theBuildManager._preAppStartHashCodeCombiner.AddObject(dependency);
        }
 
        /*
         * Perform initialization work that should only be done once (per app domain).
         */
        private void Initialize() {
 
            Debug.Assert(_caches == null);
 
            // Register an AssemblyResolve event
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.ResolveAssembly);
 
            _globalAsaxVirtualPath = HttpRuntime.AppDomainAppVirtualPathObject.SimpleCombine(
                HttpApplicationFactory.applicationFileName);
 
            _webHashFilePath = Path.Combine(HttpRuntime.CodegenDirInternal, "hash\\hash.web");
 
            // Indicate whether we should ignore the top level compilation exceptions.
            // In CBM case, we want to continue processing the page and return partial info even
            // if the code files fail to compile.
            _skipTopLevelCompilationExceptions = BuildManagerHost.InClientBuildManager;
 
            // Deal with precompilation if we're in that mode
            SetPrecompilationInfo(HostingEnvironment.HostingParameters);
 
            MultiTargetingUtil.EnsureFrameworkNamesInitialized();
 
            // The init code depends on whether we're precompiling or running an app
            if (_precompTargetPhysicalDir != null) {
 
                // If the app is already precompiled, fail
                FailIfPrecompiledApp();
 
                PrecompilationModeInitialize();
            }
            else {
                // Check if this application has been precompiled by aspnet_compiler.exe
                if (IsPrecompiledApp) {
                    PrecompiledAppRuntimeModeInitialize();
                }
                else {
                    RegularAppRuntimeModeInitialize();
                }
            }
 
            _scriptVirtualDir = Util.GetScriptLocation();
 
            // Top level directories that have a special semantic
            _excludedTopLevelDirectories = new CaseInsensitiveStringSet();
            _excludedTopLevelDirectories.Add(HttpRuntime.BinDirectoryName);
            _excludedTopLevelDirectories.Add(HttpRuntime.CodeDirectoryName);
            _excludedTopLevelDirectories.Add(HttpRuntime.ResourcesDirectoryName);
            _excludedTopLevelDirectories.Add(HttpRuntime.LocalResourcesDirectoryName);
            _excludedTopLevelDirectories.Add(HttpRuntime.WebRefDirectoryName);
            _excludedTopLevelDirectories.Add(HttpRuntime.ThemesDirectoryName);
 
            // Top level directories that are not requestable
            // It's the same as _excludedTopLevelDirectories, except that we allow
            // the bin directory to avoid a v1 breaking change (VSWhidbey 465018)
            _forbiddenTopLevelDirectories = new CaseInsensitiveStringSet();
            _forbiddenTopLevelDirectories.Add(HttpRuntime.CodeDirectoryName);
            _forbiddenTopLevelDirectories.Add(HttpRuntime.ResourcesDirectoryName);
            _forbiddenTopLevelDirectories.Add(HttpRuntime.LocalResourcesDirectoryName);
            _forbiddenTopLevelDirectories.Add(HttpRuntime.WebRefDirectoryName);
            _forbiddenTopLevelDirectories.Add(HttpRuntime.ThemesDirectoryName);
 
            LoadLicensesAssemblyIfExists();
        }
 
        /*
         * Init code used when we are running a non-precompiled app
         */
        private void RegularAppRuntimeModeInitialize() {
 
            //
            // Initialize the caches
            //
 
            // Always try the memory cache first
            _memoryCache = new MemoryBuildResultCache();
 
            // Use the standard disk cache for regular apps
            _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
 
            _caches = new BuildResultCache[] { _memoryCache, _codeGenCache };
        }
 
        /*
         * Init code used when we are running a precompiled app
         */
        private void PrecompiledAppRuntimeModeInitialize() {
 
            //
            // Initialize the caches
            //
 
            // Always try the memory cache first
            _memoryCache = new MemoryBuildResultCache();
 
            // Used the precomp cache for precompiled apps
            BuildResultCache preCompCache = new PrecompiledSiteDiskBuildResultCache(
                HttpRuntime.BinDirectoryInternal);
 
            // Also create a regular disk cache so that we can compile and cache additional things.
            // This is useful even in non-updatable precomp, to cache DefaultWsdlHelpGenerator.aspx.
 
            _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
 
            _caches = new BuildResultCache[] { _memoryCache, preCompCache, _codeGenCache };
        }
 
        /*
         * Init code used when we are precompiling an app
         */
        private void PrecompilationModeInitialize() {
 
            // We are precompiling an app
 
            // Always try the memory cache first
            _memoryCache = new MemoryBuildResultCache();
 
            // Create a regular disk cache, to take advantage of the fact that the app
            // may already have been compiled (and to cause it to be if it wasn't)
            _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
 
            // Create a special disk cache in the target's bin directory.  Use a slightly different
            // implementation for the updatable case.
            string targetBinDir = Path.Combine(_precompTargetPhysicalDir, HttpRuntime.BinDirectoryName);
            BuildResultCache preCompilationCache;
            if (PrecompilingForUpdatableDeployment) {
                preCompilationCache = new UpdatablePrecompilerDiskBuildResultCache(targetBinDir);
            }
            else {
                preCompilationCache = new PrecompilerDiskBuildResultCache(targetBinDir);
            }
 
            _caches = new BuildResultCache[] { _memoryCache, preCompilationCache, _codeGenCache };
        }
 
        // Load the licenses assembly from the bin dir if it exists (DevDiv 42149)
        private void LoadLicensesAssemblyIfExists() {
            string licAssemblyPath = Path.Combine(HttpRuntime.BinDirectoryInternal, LicensesAssemblyName + ".dll");
            if (File.Exists(licAssemblyPath)) {
                Assembly.Load(LicensesAssemblyName);
            }
        }
 
        // DevDiv #520869: Signal the PortableCompilationOutputSnapshotType to
        // restore a snapshot of portable compilation output
        private static void RestorePortableCompilationOutputSnapshot() {
            if (BuildManagerHost.InClientBuildManager || 
                !AppSettings.PortableCompilationOutput || 
                String.IsNullOrEmpty(AppSettings.PortableCompilationOutputSnapshotType)) {
                return;
            }
 
            // If a PortableCompilationOutputSnapshotsType has been configured but failed to be loaded, let it throw
            Type t = Type.GetType(AppSettings.PortableCompilationOutputSnapshotType, true);
            object[] args = new Object[] { AppSettings.PortableCompilationOutputSnapshotTypeOptions };
            t.InvokeMember("RestoreSnapshot", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, t, args, CultureInfo.InvariantCulture);
        }
 
        private long CheckTopLevelFilesUpToDate(long cachedHash) {
            bool gotLock = false;
            try {
                // Grab the compilation mutex, since this method accesses the codegen files
                CompilationLock.GetLock(ref gotLock);
 
                return CheckTopLevelFilesUpToDateInternal(cachedHash);
            }
            finally {
                // Always release the mutex if we had taken it
                if (gotLock) {
                    CompilationLock.ReleaseLock();
                }
            }
        }
 
        /*
         * Check if the top level files are up to date, and cleanup the codegendir
         * if they are not.
         */
        private long CheckTopLevelFilesUpToDateInternal(long cachedHash) {
            Debug.Trace("BuildManager", "specialFilesCombinedHash=" + cachedHash);
            var specialFilesHashCodeCombiner = new HashCodeCombiner();
 
            // Delete all the non essential files left over in the codegen dir, unless
            // specialFilesCombinedHash is 0, in which case we delete *everything* further down
            if (cachedHash != 0) {
                _codeGenCache.RemoveOldTempFiles();
            }
 
            // Use a HashCodeCombiner object to handle the time stamps of all the 'special'
            // files and directories that all compilations depend on:
            // - System.Web.dll (in case there is a newer version of ASP.NET)
            // - ~\Bin directory
            // - ~\Resource directory
            // - ~\WebReferences directory
            // - ~\Code directory
            // - global.asax
 
            // Add a check for the app's physical path, in case it changes (ASURT 12975)
            specialFilesHashCodeCombiner.AddObject(HttpRuntime.AppDomainAppPathInternal);
 
            // Process System.Web.dll
            string aspBinaryFileName = typeof(HttpRuntime).Module.FullyQualifiedName;
            if (!AppSettings.PortableCompilationOutput) {
                specialFilesHashCodeCombiner.AddFile(aspBinaryFileName);
            }
            else {
                specialFilesHashCodeCombiner.AddExistingFileVersion(aspBinaryFileName);
            }
 
            // Process machine.config
            string machineConfigFileName = HttpConfigurationSystem.MachineConfigurationFilePath;
            if (!AppSettings.PortableCompilationOutput) {
                specialFilesHashCodeCombiner.AddFile(machineConfigFileName);
            }
            else {
                specialFilesHashCodeCombiner.AddFileContentHash(machineConfigFileName);
            }
 
            // Process root web.config
            string rootWebConfigFileName = HttpConfigurationSystem.RootWebConfigurationFilePath;
            if (!AppSettings.PortableCompilationOutput) {
                specialFilesHashCodeCombiner.AddFile(rootWebConfigFileName);
            }
            else {
                specialFilesHashCodeCombiner.AddFileContentHash(rootWebConfigFileName);
            }
 
            RuntimeConfig appConfig = RuntimeConfig.GetAppConfig();
            CompilationSection compConfig = appConfig.Compilation;
 
            // Ignore the OptimizeCompilations flag in ClientBuildManager mode
            if (!BuildManagerHost.InClientBuildManager) {
                _optimizeCompilations = compConfig.OptimizeCompilations;
            }
 
            // In optimized compilation mode, we don't clean out all the compilations just because a top level
            // file changes.  Instead, we let already compiled pages run against the newer top level binaries.
            // In can be incorrect in some cases (e.g. return type of method changes from int to short), which is
            // why the optimization is optional
            if (!OptimizeCompilations) {
                // Add a dependency of the bin, resources, webresources and code directories
                string binPhysicalDir = HttpRuntime.BinDirectoryInternal;
                specialFilesHashCodeCombiner.AddDirectory(binPhysicalDir);
 
                // Note that we call AddResourcesDirectory instead of AddDirectory, since we only want
                // culture neutral files to be taken into account (VSWhidbey 359029)
                specialFilesHashCodeCombiner.AddResourcesDirectory(HttpRuntime.ResourcesDirectoryVirtualPath.MapPathInternal());
 
                specialFilesHashCodeCombiner.AddDirectory(HttpRuntime.WebRefDirectoryVirtualPath.MapPathInternal());
 
                specialFilesHashCodeCombiner.AddDirectory(HttpRuntime.CodeDirectoryVirtualPath.MapPathInternal());
 
                // Add a dependency on the global asax file.
                specialFilesHashCodeCombiner.AddFile(GlobalAsaxVirtualPath.MapPathInternal());
            }
 
            // Add a dependency on the hash of the app level <compilation> section, since it
            // affects all compilations, including the code directory.  It it changes,
            // we may as well, start all over.
            specialFilesHashCodeCombiner.AddObject(compConfig.RecompilationHash);
 
            ProfileSection profileSection = appConfig.Profile;
            specialFilesHashCodeCombiner.AddObject(profileSection.RecompilationHash);
 
            // Add a dependency on file encoding (DevDiv 4560)
            specialFilesHashCodeCombiner.AddObject(appConfig.Globalization.FileEncoding);
 
            // Also add a dependency on the <trust> config section
            TrustSection casConfig = appConfig.Trust;
            specialFilesHashCodeCombiner.AddObject(casConfig.Level);
            specialFilesHashCodeCombiner.AddObject(casConfig.OriginUrl);
 
            // Add a dependency on whether profile is enabled
            specialFilesHashCodeCombiner.AddObject(ProfileManager.Enabled);
 
            // Add a dependency to the force debug flag.
            specialFilesHashCodeCombiner.AddObject(PrecompilingWithDebugInfo);
 
            CheckCodeGenFiles(specialFilesHashCodeCombiner.CombinedHash, cachedHash);
            return specialFilesHashCodeCombiner.CombinedHash;
        }
 
        private void AfterPreAppStartExecute(Tuple<long, long> currentHash, Tuple<long, long> cachedTopLevelFilesHash) {
            bool gotLock = false;
            try {
                // Grab the compilation mutex, since this method accesses the codegen files
                CompilationLock.GetLock(ref gotLock);
 
                // After pre app start methods have executed, the second hash value should match the current value in the hash code combiner.
                CheckCodeGenFiles(currentHash.Item2, cachedTopLevelFilesHash.Item2);
 
                if (!cachedTopLevelFilesHash.Equals(currentHash)) {
                    // Hash has changed. Persist it to disk
                    _codeGenCache.SavePreservedSpecialFilesCombinedHash(currentHash);
                }
 
                // VSWhidbey 537929 : Setup a filechange monitor for the web.hash file. If this file is modified,
                // we will need to shutdown the appdomain so we don't use the obsolete assemblies. The new appdomain
                // will use the up-to-date assemblies.
                HttpRuntime.FileChangesMonitor.StartMonitoringFile(_webHashFilePath,
                    new FileChangeEventHandler(this.OnWebHashFileChange));
                Debug.Assert(File.Exists(_webHashFilePath), _webHashFilePath);
            }
            finally {
                // Always release the mutex if we had taken it
                if (gotLock) {
                    CompilationLock.ReleaseLock();
                }
            }
        }
 
        private void CheckCodeGenFiles(long currentHash, long cachedTopLevelFilesHash) {
            // Store the top level hash
            s_topLevelHash = currentHash;
 
            if (PrecompilingForCleanBuild || currentHash != cachedTopLevelFilesHash) {
                if (PrecompilingForCleanBuild) {
                    Debug.Trace("BuildManager", "Precompiling for clean build.");
                }
                else {
                    Debug.Trace("BuildManager", "EnsureFirstTimeInit: hash codes don't match.  Old=" +
                        cachedTopLevelFilesHash + " New=" + currentHash);
                }
 
                _codeGenCache.RemoveAllCodegenFiles();
            }
            else {
                Debug.Trace("BuildManager", "BuildManager: the special files are up to date");
            }
        }
 
        private void OnWebHashFileChange(Object sender, FileChangeEvent e) {
            // Shutdown the app domain
            Debug.Trace("BuildManager", _webHashFilePath + " changed - shutting down the app domain");
            Debug.Trace("AppDomainFactory", "Shutting down appdomain because " + _webHashFilePath + " file changed");
            string message = FileChangesMonitor.GenerateErrorMessage(e.Action, _webHashFilePath);
            if (message == null) {
                message = "Change in " + _webHashFilePath;
            }
            HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.BuildManagerChange, message);
        }
 
        /*
         * Check if an assembly name is reserved for a special purpose
         */
        internal static bool IsReservedAssemblyName(string assemblyName) {
 
            if (String.Compare(assemblyName, CodeDirectoryAssemblyName,
                    StringComparison.OrdinalIgnoreCase) == 0 ||
                String.Compare(assemblyName, ResourcesDirectoryAssemblyName,
                    StringComparison.OrdinalIgnoreCase) == 0 ||
                String.Compare(assemblyName, WebRefDirectoryAssemblyName,
                    StringComparison.OrdinalIgnoreCase) == 0 ||
                String.Compare(assemblyName, GlobalAsaxAssemblyName,
                    StringComparison.OrdinalIgnoreCase) == 0) {
 
                return true;
            }
 
            return false;
        }
 
        internal static void ThrowIfPreAppStartNotRunning() {
            if (PreStartInitStage != PreStartInitStage.DuringPreStartInit) {
                throw new InvalidOperationException(SR.GetString(SR.Method_can_only_be_called_during_pre_start_init));
            }
        }
 
        internal static PreStartInitStage PreStartInitStage { get; private set; }
 
        internal static void ExecutePreAppStart() {
            // Restore a snapshot of compilation output when the AppDomain just starts and 
            // before any web site code runs
            BuildManager.RestorePortableCompilationOutputSnapshot();
        
            string preStartInitListPath = Path.Combine(HttpRuntime.CodegenDirInternal, "preStartInitList.web");
            Tuple<long, long> specialFilesCombinedHash = _theBuildManager._codeGenCache.GetPreservedSpecialFilesCombinedHash();
            // Check top level files have changed
            long topLevelFilesHash = _theBuildManager.CheckTopLevelFilesUpToDate(specialFilesCombinedHash.Item1);
 
            bool hasUpdated = false; 
            ISet<string> preApplicationStartAssemblyNames = CallPreStartInitMethods(preStartInitListPath, out hasUpdated);
 
            // Check if pre application start code hashes have changed since.
            var currentHash = Tuple.Create(topLevelFilesHash, _theBuildManager._preAppStartHashCodeCombiner.CombinedHash);
            _theBuildManager.AfterPreAppStartExecute(currentHash, specialFilesCombinedHash);
 
            // Save the cache file only if needed
            if (hasUpdated) {
                SavePreStartInitAssembliesToFile(preStartInitListPath, preApplicationStartAssemblyNames);
            }
        }
 
        // this method requires global lock as the part of the fix of DevDiv bug 501777
        private static ISet<string> CallPreStartInitMethods(string preStartInitListPath, out bool isRefAssemblyLoaded) {
            Debug.Assert(PreStartInitStage == Compilation.PreStartInitStage.BeforePreStartInit);
            isRefAssemblyLoaded = false;
            using (new ApplicationImpersonationContext()) {
                ICollection<MethodInfo> methods = null;
                ICollection<Assembly> cachedPreStartAssemblies = LoadCachedPreAppStartAssemblies(preStartInitListPath);
                if (cachedPreStartAssemblies != null) {
                    methods = GetPreStartInitMethodsFromAssemblyCollection(cachedPreStartAssemblies, buildingFromCache: true);
                }
 
                if (methods == null) {
                    // In case of ctlr-f5 scenario, two processes (VS and IisExpress) will start compilation simultaneously.
                    // GetPreStartInitMethodsFromReferencedAssemblies() will load all referenced assemblies
                    // If shallow copy is enabled, one process may fail due race condition in copying assemblies (DevDiv bug 501777) 
                    // to fix it, put GetPreStartInitMethodsFromReferencedAssemblies() under the global lock 
                    bool gotLock = false;
                    try {
                        CompilationLock.GetLock(ref gotLock);
                        methods = GetPreStartInitMethodsFromReferencedAssemblies();
                        isRefAssemblyLoaded = true;
                    }
                    finally {
                        if (gotLock) {
                            CompilationLock.ReleaseLock();
                        }
                    }
                }
 
                InvokePreStartInitMethods(methods);
 
                Debug.Assert(PreStartInitStage == Compilation.PreStartInitStage.AfterPreStartInit);
 
                return new HashSet<string>(methods.Select(m => m.DeclaringType.Assembly.FullName), StringComparer.OrdinalIgnoreCase);
            }
        }
 
        internal static ISet<string> GetPreStartInitAssembliesFromFile(string path) {
            if (FileUtil.FileExists(path)) {
                try {
                    return new HashSet<string>(File.ReadAllLines(path), StringComparer.OrdinalIgnoreCase);
                }
                catch {
                    // If there are issues delete the bad file. The list will be created from scratch.
                    try {
                        File.Delete(path);
                    }
                    catch { }
                }
            }
            return null;
        }
 
        // this method requires global lock as the part of the fix of DevDiv bug 501777
        internal static void SavePreStartInitAssembliesToFile(string path, ISet<string> assemblies) {
            Debug.Assert(assemblies != null);
            Debug.Assert(!String.IsNullOrEmpty(path));
            Debug.Assert(!assemblies.Any(String.IsNullOrEmpty));
            bool gotLock = false;
            try {
                //put write under the global lock to avoid race condition
                CompilationLock.GetLock(ref gotLock);
                File.WriteAllLines(path, assemblies);
            }
            catch {
                try {
                    File.Delete(path);
                }
                catch { }
            }
            finally {
                if (gotLock) {
                    CompilationLock.ReleaseLock();
                }
            }
        }
 
        /// <summary>
        /// Load the cached list of assemblies containing pre app start methods. Since this is a cache we never throw from it.
        /// </summary>
        internal static ICollection<Assembly> LoadCachedPreAppStartAssemblies(string preStartInitListPath) {
            try {
                // Force the enumerable to be saved to a list so that any issues with loading assemblies get caught here.
                ISet<string> assemblyList = GetPreStartInitAssembliesFromFile(preStartInitListPath);
                if (assemblyList == null) {
                    return null;
                }
                return assemblyList.Select(Assembly.Load)
                                   .Distinct()
                                   .ToList();
            }
            catch {
                return null;
            }
        }
 
        private static void InvokePreStartInitMethods(ICollection<MethodInfo> methods) {
            PreStartInitStage = Compilation.PreStartInitStage.DuringPreStartInit;
 
            try {
                InvokePreStartInitMethodsCore(methods, HostingEnvironment.SetCultures);
                PreStartInitStage = Compilation.PreStartInitStage.AfterPreStartInit;
            }
            catch {
                PreStartInitStage = Compilation.PreStartInitStage.BeforePreStartInit;
                throw;
            }
        }
 
        internal static void InvokePreStartInitMethodsCore(ICollection<MethodInfo> methods, Func<IDisposable> setHostingEnvironmentCultures) {
            // Remove dupes 
            var methodsToExecute = methods.Distinct();
            // We want to execute PreApplicationStartmethods in a deterministic order. We'll use a sorted sequence of fully qualified type names and method names. 
            methodsToExecute = methodsToExecute.OrderBy(m => m.DeclaringType.AssemblyQualifiedName, StringComparer.OrdinalIgnoreCase)
                                               .ThenBy(m => m.Name, StringComparer.OrdinalIgnoreCase);
            foreach (var method in methodsToExecute) {
                try {
                    // 
                    using (setHostingEnvironmentCultures()) {
                        method.Invoke(null, null);
                    }
                }
                catch (TargetInvocationException e) {
                    string message = (e.InnerException != null ? e.InnerException.Message : String.Empty);
                    throw new InvalidOperationException(
                        SR.GetString(SR.Pre_application_start_init_method_threw_exception,
                            method.Name,
                            method.DeclaringType.FullName,
                            message),
                        e.InnerException);
                }
            }
        }
 
        private static ICollection<MethodInfo> GetPreStartInitMethodsFromReferencedAssemblies() {
            CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(HttpRuntime.AppDomainAppVirtualPath);
            var referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig).Cast<Assembly>();
            return GetPreStartInitMethodsFromAssemblyCollection(referencedAssemblies, buildingFromCache: false);
        }
 
        /// <summary>
        /// Resolves pre application start methods from the assemblies specified.
        /// </summary>
        /// <param name="assemblies">The list of assemblies to look for methods in.</param>
        /// <param name="buildingFromCache">Flag that determines if we are rebuilding methods from cache.</param>
        internal static ICollection<MethodInfo> GetPreStartInitMethodsFromAssemblyCollection(IEnumerable<Assembly> assemblies, bool buildingFromCache) {
            List<MethodInfo> methods = new List<MethodInfo>();
            foreach (Assembly assembly in assemblies) {
                PreApplicationStartMethodAttribute[] attributes = null;
                try {
                    attributes = (PreApplicationStartMethodAttribute[])assembly.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), inherit: true);
                }
                catch {
                    // GetCustomAttributes invokes the constructors of the attributes, so it is possible that they might throw unexpected exceptions.
                    // (Dev10 bug 831981)
                }
 
                if (attributes == null || !attributes.Any()) {
                    // When rebuilding methods from cache every assembly specified must have one or more PreApplicationStartMethod attributes. 
                    // If one of them doesn't, the cache might be stale. We'll force it to retry it with the list of assemblies currently loaded into the AppDomain.
                     if (buildingFromCache) {
                         return null;
                     }
                }
                else {
                    foreach (PreApplicationStartMethodAttribute attribute in attributes) {
                        MethodInfo method = null;
                        // Ensure the Type on the attribute is in the same assembly as the attribute itself
                        if (attribute.Type != null && !String.IsNullOrEmpty(attribute.MethodName) && attribute.Type.Assembly == assembly) {
                            method = FindPreStartInitMethod(attribute.Type, attribute.MethodName);
                        }
 
                        if (method != null) {
                            methods.Add(method);
                        }
                        else {
                            throw new HttpException(SR.GetString(SR.Invalid_PreApplicationStartMethodAttribute_value,
                                assembly.FullName,
                                (attribute.Type != null ? attribute.Type.FullName : String.Empty),
                                attribute.MethodName));
                        }
                    }
                }
            }
            return methods;
        }
 
        internal static MethodInfo FindPreStartInitMethod(Type type, string methodName) {
            Debug.Assert(type != null);
            Debug.Assert(!String.IsNullOrEmpty(methodName));
            MethodInfo method = null;
            if (type.IsPublic) {
                // Verify that type is public to avoid allowing internal code execution. This implementation will not match
                // nested public types.
                method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
                                binder: null,
                                types: Type.EmptyTypes,
                                modifiers: null);
            }
            return method;
        }
 
        // excludedSubdirectories contains a list of subdirectory names that should not be
        // recursively included in the compilation (they'll instead be compiled into their
        // own assemblies).
        private Assembly CompileCodeDirectory(VirtualPath virtualDir, CodeDirectoryType dirType,
            string assemblyName, StringSet excludedSubdirectories) {
 
            Debug.Trace("BuildManager", "CompileCodeDirectory(" + virtualDir.VirtualPathString + ")");
 
            bool isDirectoryAllowed = true;
            if (IsPrecompiledApp) {
                // Most special dirs are not allowed in precompiled apps.  App_LocalResources is
                // an exception, as it is allowed in updatable precompiled apps.
                if (IsUpdatablePrecompiledAppInternal && dirType == CodeDirectoryType.LocalResources)
                    isDirectoryAllowed = true;
                else
                    isDirectoryAllowed = false;
            }
 
            // Remember the referenced assemblies based on the current count.
            AssemblyReferenceInfo info = new AssemblyReferenceInfo(_topLevelReferencedAssemblies.Count);
            _topLevelAssembliesIndexTable[virtualDir.VirtualPathString] = info;
 
            Assembly codeAssembly = CodeDirectoryCompiler.GetCodeDirectoryAssembly(
                    virtualDir, dirType, assemblyName, excludedSubdirectories,
                    isDirectoryAllowed);
 
            if (codeAssembly != null) {
 
                // Remember the generated assembly
                info.Assembly = codeAssembly;
 
                // Page resource assemblies are not added to the top level list
                if (dirType != CodeDirectoryType.LocalResources) {
                    _topLevelReferencedAssemblies.Add(codeAssembly);
 
                    if (dirType == CodeDirectoryType.MainCode || dirType == CodeDirectoryType.SubCode) {
                        if (_codeAssemblies == null) {
                            _codeAssemblies = new ArrayList();
                        }
 
                        _codeAssemblies.Add(codeAssembly);
                    }
 
                    // Add it to the list of assembly name that we resolve, so that users can
                    // refer to the assemblies by their fixed name (even though they
                    // random names).  (VSWhidbey 276776)
                    if (_assemblyResolveMapping == null) {
                        _assemblyResolveMapping = new Hashtable(StringComparer.OrdinalIgnoreCase);
                    }
                    _assemblyResolveMapping[assemblyName] = codeAssembly;
 
                    if (dirType == CodeDirectoryType.MainCode) {
                        // Profile gets built in the same assembly as the main code dir, so
                        // see whether we can get its type from the assembly.
                        _profileType = ProfileBuildProvider.GetProfileTypeFromAssembly(
                            codeAssembly, IsPrecompiledApp);
 
                        // To avoid breaking earlier Whidbey apps, allows the name "__code"
                        // to be used for the main code assembly.
                        // 
                        _assemblyResolveMapping["__code"] = codeAssembly;
                    }
                }
            }
 
            Debug.Trace("BuildManager", "CompileCodeDirectory generated assembly: " +
                (codeAssembly == null ? "None" : codeAssembly.ToString()));
 
            return codeAssembly;
        }
 
 
        private void CompileResourcesDirectory() {
 
            VirtualPath virtualDir = HttpRuntime.ResourcesDirectoryVirtualPath;
 
            Debug.Assert(_appResourcesAssembly == null);
            _appResourcesAssembly = CompileCodeDirectory(virtualDir, CodeDirectoryType.AppResources,
                ResourcesDirectoryAssemblyName, null /*excludedSubdirectories*/);
        }
 
        private void CompileWebRefDirectory() {
 
            CompileCodeDirectory(HttpRuntime.WebRefDirectoryVirtualPath, CodeDirectoryType.WebReferences,
                WebRefDirectoryAssemblyName, null /*excludedSubdirectories*/);
        }
 
        // Compute the list of subdirectories that should not be compiled with
        // the top level Code
        private void EnsureExcludedCodeSubDirectoriesComputed() {
 
            if (_excludedCodeSubdirectories != null)
                return;
 
            _excludedCodeSubdirectories = new CaseInsensitiveStringSet();
 
            // Get the list of sub directories that will be compiled separately
            CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
 
            // Add them to the exclusion list of the top level code directory
            if (codeSubDirectories != null) {
                foreach (CodeSubDirectory entry in codeSubDirectories) {
                    _excludedCodeSubdirectories.Add(entry.DirectoryName);
                }
            }
        }
 
        private void CompileCodeDirectories() {
 
            VirtualPath virtualDir = HttpRuntime.CodeDirectoryVirtualPath;
 
            // Get the list of sub directories that will be compiled separately
            CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
 
            if (codeSubDirectories != null) {
 
                // Compile all the subdirectory that are listed in config.
 
                foreach (CodeSubDirectory entry in codeSubDirectories) {
 
                    // 
 
 
 
                    VirtualPath virtualSubDir = virtualDir.SimpleCombineWithDir(entry.DirectoryName);
 
                    string assemblyName = SubCodeDirectoryAssemblyNamePrefix + entry.AssemblyName;
 
                    // Compile the subdirectory tree (no exclusions)
                    CompileCodeDirectory(virtualSubDir, CodeDirectoryType.SubCode, assemblyName,
                        null /*excludedSubdirectories*/);
                }
            }
 
            EnsureExcludedCodeSubDirectoriesComputed();
 
            // Compile the top level Code directory tree, minus the excluded subdirectories
            CompileCodeDirectory(virtualDir, CodeDirectoryType.MainCode,
                CodeDirectoryAssemblyName, _excludedCodeSubdirectories);
        }
 
        private void CompileGlobalAsax() {
            _globalAsaxBuildResult = ApplicationBuildProvider.GetGlobalAsaxBuildResult(IsPrecompiledApp);
 
            // Make sure that global.asax notifications are set up (VSWhidbey 267245)
            HttpApplicationFactory.SetupFileChangeNotifications();
 
            if (_globalAsaxBuildResult != null) {
 
                // We need to add not only the global.asax type, but also its parent types to
                // the top level assembly list.  This can happen when global.asax has a 'src'
                // attribute pointing to a source file containing its base type.
                Type type = _globalAsaxBuildResult.ResultType;
                while (type.Assembly != typeof(HttpRuntime).Assembly) {
                    _topLevelReferencedAssemblies.Add(type.Assembly);
                    type = type.BaseType;
                }
            }
        }
 
        // Call the AppInitialize method in the Code assembly if there is one
        internal static void CallAppInitializeMethod() {
 
            // Make sure the code directory has been processed
            _theBuildManager.EnsureTopLevelFilesCompiled();
 
            CodeDirectoryCompiler.CallAppInitializeMethod();
        }
 
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        internal void EnsureTopLevelFilesCompiled() {
            if (PreStartInitStage != Compilation.PreStartInitStage.AfterPreStartInit) {
                throw new InvalidOperationException(SR.GetString(SR.Method_cannot_be_called_during_pre_start_init));
            }
 
            // This should never get executed in non-hosted appdomains
            Debug.Assert(HostingEnvironment.IsHosted);
 
            // If we already tried and got an exception, just rethrow it
            if (_topLevelFileCompilationException != null && !SkipTopLevelCompilationExceptions) {
                ReportTopLevelCompilationException();
            }
 
            if (_topLevelFilesCompiledStarted)
                return;
 
            // Set impersonation to hosting identity (process or UNC)
            using (new ApplicationImpersonationContext()) {
                bool gotLock = false;
                _parseErrorReported = false;
 
                try {
                    // Grab the compilation mutex, since this method accesses the codegen files
                    CompilationLock.GetLock(ref gotLock);
 
                    // Check again if there is an exception
                    if (_topLevelFileCompilationException != null && !SkipTopLevelCompilationExceptions) {
                        ReportTopLevelCompilationException();
                    }
 
                    // Check again if we're done
                    if (_topLevelFilesCompiledStarted)
                        return;
 
                    _topLevelFilesCompiledStarted = true;
                    _topLevelAssembliesIndexTable =
                        new Dictionary<String, AssemblyReferenceInfo>(StringComparer.OrdinalIgnoreCase);
 
                    _compilationStage = CompilationStage.TopLevelFiles;
 
                    CompileResourcesDirectory();
                    CompileWebRefDirectory();
                    CompileCodeDirectories();
 
                    _compilationStage = CompilationStage.GlobalAsax;
 
                    CompileGlobalAsax();
 
                    _compilationStage = CompilationStage.BrowserCapabilities;
 
                    // Call GetBrowserCapabilitiesType() to make sure browserCap directory is compiled
                    // early on.  This avoids getting into potential deadlock situations later (VSWhidbey 530732).
                    // For the same reason, get the EmptyHttpCapabilitiesBase.
                    BrowserCapabilitiesCompiler.GetBrowserCapabilitiesType();
                    IFilterResolutionService dummy = HttpCapabilitiesBase.EmptyHttpCapabilitiesBase;
 
                    _compilationStage = CompilationStage.AfterTopLevelFiles;
                }
                catch (Exception e) {
                    // Remember the exception, and rethrow it
                    _topLevelFileCompilationException = e;
 
                    // Do not rethrow the exception since so CBM can still provide partial support
                    if (!SkipTopLevelCompilationExceptions) {
 
                        if (!_parseErrorReported) {
                            // Report the error if this is not a CompileException. CompileExceptions are handled
                            // directly by the AssemblyBuilder already.
                            if (!(e is HttpCompileException)) {
                                ReportTopLevelCompilationException();
                            }
                        }
 
                        throw;
                    }
                }
                finally {
                    _topLevelFilesCompiledCompleted = true;
 
                    // Always release the mutex if we had taken it
                    if (gotLock) {
                        CompilationLock.ReleaseLock();
                    }
                }
            }
        }
 
        // Generate a random file name with 8 characters
        private static string GenerateRandomFileName() {
            // Generate random bytes
            byte[] data = new byte[6];
 
            lock (_rng) {
                _rng.GetBytes(data);
            }
 
            // Turn them into a string containing only characters valid in file names/url
            string s = Convert.ToBase64String(data).ToLower(CultureInfo.InvariantCulture);
            s = s.Replace('/', '-');
            s = s.Replace('+', '_');
 
            return s;
        }
 
        internal static string GenerateRandomAssemblyName(string baseName) {
            return GenerateRandomAssemblyName(baseName, true /*topLevel*/);
        }
 
        // Generate a random name for an assembly, starting with the passed in prefix
        internal static string GenerateRandomAssemblyName(string baseName, bool topLevel) {
 
            // Start with the passed in base name
            string assemblyName = baseName;
 
            // Append a random token to it.
 
            // However, don't do this when precompiling for deployment since, we want the name to be more predictable (DevDiv 36625)
            if (PrecompilingForDeployment)
                return baseName;
 
            // Also, don't use random names for top level files in OptimizeCompilations mode so that pages
            // can more easily bind against rebuilt top level assemblies
            if (OptimizeCompilations && topLevel)
                return baseName;
 
            return baseName += "." + GenerateRandomFileName();
        }
 
        private static string GetGeneratedAssemblyBaseName(VirtualPath virtualPath) {
 
            // Name the assembly using the same scheme as cache keys
            return GetCacheKeyFromVirtualPath(virtualPath);
        }
 
        /*
         * Look for a type by name in the top level and config assemblies
         */
        public static Type GetType(string typeName, bool throwOnError) {
            return GetType(typeName, throwOnError, false);
        }
 
        /*
         * Look for a type by name in the top level and config assemblies
         */
        public static Type GetType(string typeName, bool throwOnError, bool ignoreCase) {
            // If it contains an assembly name, just call Type.GetType().  Do this before even trying
            // to initialize the BuildManager, so that if InitializeBuildManager has errors, it doesn't
            // affect us when the type string can be resolved via Type.GetType().
            Type type = null;
            if (Util.TypeNameContainsAssembly(typeName)) {
                type = Type.GetType(typeName, throwOnError, ignoreCase);
 
                if (type != null) {
                    return type;
                }
            }
 
            // Make sure the build manager is initialized.  If it fails to initialize for any reason,
            // don't attempt to use the fancy GetType logic.  Just call Type.GetType instead (VSWhidbey 284498)
            if (!InitializeBuildManager()) {
                return Type.GetType(typeName, throwOnError, ignoreCase);
            }
 
            // First, always try System.Web.dll
            try {
                type = typeof(BuildManager).Assembly.GetType(typeName,
                    false /*throwOnError*/, ignoreCase);
            }
            catch (ArgumentException e) {
                // Even though we pass false to throwOnError, GetType can throw if the
                // assembly name is malformed.  In that case, throw our own error instead
                // of the cryptic ArgumentException (VSWhidbey 275586)
                throw new HttpException(
                    SR.GetString(SR.Invalid_type, typeName), e);
            }
 
            if (type != null) return type;
 
            _theBuildManager.EnsureTopLevelFilesCompiled();
 
            // Otherwise, look for the type in the top level assemblies
            type = Util.GetTypeFromAssemblies(TheBuildManager.TopLevelReferencedAssemblies,
                typeName, ignoreCase);
            if (type != null) return type;
 
            // Otherwise, look for the type in the config assemblies
            IEnumerable<Assembly> configAssemblies = GetAssembliesForAppLevel();
            type = Util.GetTypeFromAssemblies(configAssemblies, typeName, ignoreCase);
 
            if (type == null && throwOnError) {
                throw new HttpException(
                    SR.GetString(SR.Invalid_type, typeName));
            }
 
            return type;
        }
 
        /*
        * Simple wrapper to get the Assemblies
        */
        private static IEnumerable<Assembly> GetAssembliesForAppLevel() {
            CompilationSection compilationConfiguration = MTConfigUtil.GetCompilationAppConfig();
            AssemblyCollection assemblyInfoCollection = compilationConfiguration.Assemblies;
 
            Debug.Assert(s_dynamicallyAddedReferencedAssembly != null);
 
            if (assemblyInfoCollection == null) {
                return s_dynamicallyAddedReferencedAssembly.OfType<Assembly>();
            }
 
            return assemblyInfoCollection.Cast<AssemblyInfo>()
                .SelectMany(ai => ai.AssemblyInternal)
                .Union(s_dynamicallyAddedReferencedAssembly)
                .Distinct();
        }
 
 
        /*
         * Gets a type from one of the code assemblies
         */
        internal static Type GetTypeFromCodeAssembly(string typeName, bool ignoreCase) {
 
            // No code assembly: return
            if (CodeAssemblies == null)
                return null;
 
            return Util.GetTypeFromAssemblies(CodeAssemblies, typeName, ignoreCase);
        }
 
        internal static BuildProvider CreateBuildProvider(VirtualPath virtualPath,
            CompilationSection compConfig, ICollection referencedAssemblies,
            bool failIfUnknown) {
 
            return CreateBuildProvider(virtualPath, BuildProviderAppliesTo.Web,
                compConfig, referencedAssemblies, failIfUnknown);
        }
 
        internal static BuildProvider CreateBuildProvider(VirtualPath virtualPath,
            BuildProviderAppliesTo neededFor,
            CompilationSection compConfig, ICollection referencedAssemblies,
            bool failIfUnknown) {
 
            string extension = virtualPath.Extension;
 
            Type buildProviderType = CompilationUtil.GetBuildProviderTypeFromExtension(compConfig,
                extension, neededFor, failIfUnknown);
            if (buildProviderType == null)
                return null;
 
            object o = HttpRuntime.CreatePublicInstanceByWebObjectActivator(buildProviderType);
 
            BuildProvider buildProvider = (BuildProvider)o;
 
            buildProvider.SetVirtualPath(virtualPath);
            buildProvider.SetReferencedAssemblies(referencedAssemblies);
 
            return buildProvider;
        }
 
        internal static void AddFolderLevelBuildProviders(BuildProviderSet buildProviders, VirtualPath virtualPath,
            FolderLevelBuildProviderAppliesTo appliesTo, CompilationSection compConfig, ICollection referencedAssemblies) {
 
            if (buildProviders == null) {
                return;
            }
 
            List<Type> buildProviderTypes = CompilationUtil.GetFolderLevelBuildProviderTypes(compConfig, appliesTo);
            if (buildProviderTypes != null) {
                foreach (Type buildProviderType in buildProviderTypes) {
                    object o = HttpRuntime.CreatePublicInstanceByWebObjectActivator(buildProviderType);
 
                    BuildProvider buildProvider = (BuildProvider)o;
 
                    buildProvider.SetVirtualPath(virtualPath);
                    buildProvider.SetReferencedAssemblies(referencedAssemblies);
 
                    buildProviders.Add(buildProvider);
 
                }
            }
        }
 
        internal static void ValidateCodeFileVirtualPath(VirtualPath virtualPath) {
            _theBuildManager.ValidateVirtualPathInternal(virtualPath, false /*allowCrossApp*/, true /*codeFile*/);
        }
 
        private void ValidateVirtualPathInternal(VirtualPath virtualPath, bool allowCrossApp, bool codeFile) {
 
            if (!allowCrossApp) {
                virtualPath.FailIfNotWithinAppRoot();
            }
            else {
                // If cross app is allowed, and the path is in a different app, nothing more to check
                if (!virtualPath.IsWithinAppRoot)
                    return;
            }
 
            //
            // Now, detect if it's under a special directory (e.g. 'code', 'resources', 'themes')
            //
 
            // If it's exactly the app root, it's fine
            if (HttpRuntime.AppDomainAppVirtualPathObject == virtualPath)
                return;
 
            int appPathLen = HttpRuntime.AppDomainAppVirtualPathString.Length;
 
            string virtualPathString = virtualPath.VirtualPathString;
 
            // This could happen if the vpath is "/app" (while the app vpath is "/app/")
            if (virtualPathString.Length < appPathLen)
                return;
 
            // If no slash after the approot (e.g. "/app/foo.aspx"), it's valid
            int slashIndex = virtualPathString.IndexOf('/', appPathLen);
            if (slashIndex < 0)
                return;
 
            // Get the name of the first directory under the app root (e.g. "/app/aaa/bbb/foo.aspx" -> "aaa")
            string dir = virtualPathString.Substring(appPathLen, slashIndex - appPathLen);
 
            // If it's a forbidden directory, fail
            if (_forbiddenTopLevelDirectories.Contains(dir)) {
                throw new HttpException(SR.GetString(SR.Illegal_special_dir, virtualPathString, dir));
            }
        }
 
        /*
         * Returns a single hash code that represents the state of the built object for
         * the passed in virtualPath.  If it isn't already built, don't build it, but just
         * return 0.  This can be used to determine the validity of output cache that
         * has been persisted to disk.
         */
        internal static long GetBuildResultHashCodeIfCached(
            HttpContext context, string virtualPath) {
 
            BuildResult result = GetVPathBuildResult(context, VirtualPath.Create(virtualPath),
                true /*noBuild*/, false /*allowCrossApp*/);
 
            // If it's not cached, return 0
            if (result == null)
                return 0;
 
            // Return a single hash code based on both of the BuildResult's hash codes
            string dependenciesHash = result.VirtualPathDependenciesHash;
            Debug.Assert(result.DependenciesHashComputed);
            return result.ComputeHashCode(s_topLevelHash, StringUtil.GetStringHashCode(dependenciesHash));
        }
 
        internal static BuildResult GetVPathBuildResult(VirtualPath virtualPath) {
 
            return GetVPathBuildResult(null /*context*/, virtualPath,
                false /*noBuild*/, false /*allowCrossApp*/, false /*allowBuiltInPrecompile*/);
        }
 
        internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath) {
 
            return GetVPathBuildResult(context, virtualPath, false /*noBuild*/, false /*allowCrossApp*/, false /*allowBuiltInPrecompile*/);
        }
 
        internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath,
            bool noBuild, bool allowCrossApp) {
 
            return GetVPathBuildResult(context, virtualPath, noBuild, allowCrossApp, false /*allowBuiltInPrecompile*/);
        }
 
        /*
         * Calls either GetVPathBuildResultWithNoAssert or GetVPathBuildResultWithAssert,
         * depending on whether there is any point in asserting.
         */
        internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath,
            bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool ensureIsUpToDate = true) {
 
            // Could be called with user code on the stack, so need to assert here (VSWhidbey 85026)
            // e.g. This can happen during a Server.Transfer, or a LoadControl.
            // But if we're running in full trust, skip the assert for perf reasons (VSWhidbey 146871)
            if (HttpRuntime.IsFullTrust) {
                return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound: true, ensureIsUpToDate: ensureIsUpToDate);
            }
            else {
                return GetVPathBuildResultWithAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound: true, ensureIsUpToDate: ensureIsUpToDate);
            }
        }
 
 
        internal static BuildResult GetVPathBuildResultWithAssert(
            HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile) {
            return GetVPathBuildResultWithAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, true/*throwIfNotFound*/);
        }
 
        /*
         * Same as GetVPathBuildResultWithNoAssert, but with an Unrestricted Assert.
         */
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        internal static BuildResult GetVPathBuildResultWithAssert(
            HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
 
            return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound, ensureIsUpToDate);
        }
 
        internal static BuildResult GetVPathBuildResultWithNoAssert(
            HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile) {
            return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, true/*throwIfNotFound*/);
        }
 
        internal static BuildResult GetVPathBuildResultWithNoAssert(
            HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
 
            using (new ApplicationImpersonationContext()) {
                return _theBuildManager.GetVPathBuildResultInternal(virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound, ensureIsUpToDate);
            }
        }
 
        // name of the slot in call context
        private const String CircularReferenceCheckerSlotName = "CircRefChk";
 
        private BuildResult GetVPathBuildResultInternal(VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
 
            Debug.Trace("BuildManager", "GetBuildResult(" + virtualPath + ")");
 
            // This should never be called while building top level files (VSWhidbey 480256)
            if (_compilationStage == CompilationStage.TopLevelFiles) {
                throw new HttpException(SR.GetString(SR.Too_early_for_webfile, virtualPath));
            }
 
            // Make sure the path is not relative
            Debug.Assert(!virtualPath.IsRelative);
 
            // Try the cache first before getting the mutex
            BuildResult result = GetVPathBuildResultFromCacheInternal(virtualPath, ensureIsUpToDate);
            if (result != null)
                return result;
 
            // If we were only checking the cache and it wasn't there, return null.
            if (noBuild)
                return null;
 
            // Check if it's trying to go cross app, or points to a special directory.
            // It's important to do this before checkin existence, to avoid revealing information
            // about other apps (VSWhidbey 442632)
            ValidateVirtualPathInternal(virtualPath, allowCrossApp, false /*codeFile*/);
 
            if (throwIfNotFound) {
                // Before grabbing the lock, make sure the file at least exists (ASURT 46465)
                Util.CheckVirtualFileExists(virtualPath);
            }
            else if (!virtualPath.FileExists()) {
                return null;
            }
 
            // If this is a precompiled app, complain if we couldn't find it in the cache
            if (IsNonUpdatablePrecompiledApp && !allowBuildInPrecompile) {
                throw new HttpException(
                    SR.GetString(SR.Cant_update_precompiled_app, virtualPath));
            }
 
            bool gotLock = false;
 
            try {
                // Grab the compilation mutex
                CompilationLock.GetLock(ref gotLock);
 
                // Check the cache a second time after getting the mutex
                result = GetVPathBuildResultFromCacheInternal(virtualPath, ensureIsUpToDate);
                if (result != null)
                    return result;
 
                // Get the circular reference checker (create it if needed)
                VirtualPathSet circularReferenceChecker;
                circularReferenceChecker = CallContext.GetData(CircularReferenceCheckerSlotName)
                    as VirtualPathSet;
                if (circularReferenceChecker == null) {
                    circularReferenceChecker = new VirtualPathSet();
 
                    // Create it and save it in the CallContext
                    CallContext.SetData(CircularReferenceCheckerSlotName, circularReferenceChecker);
                }
 
                // If a circular reference is detected, throw an error
                if (circularReferenceChecker.Contains(virtualPath)) {
                    throw new HttpException(
                        SR.GetString(SR.Circular_include));
                }
 
                // Add the current virtualPath to the circular reference checker
                circularReferenceChecker.Add(virtualPath);
 
                try {
                    // 
                    EnsureTopLevelFilesCompiled();
                    result = CompileWebFile(virtualPath);
                }
                finally {
                    // Remove the current virtualPath from the circular reference checker
                    Debug.Assert(circularReferenceChecker.Contains(virtualPath));
                    circularReferenceChecker.Remove(virtualPath);
                }
            }
            finally {
                // Always release the mutex if we had taken it
                if (gotLock) {
                    CompilationLock.ReleaseLock();
                }
            }
 
            return result;
        }
 
        private BuildResult CompileWebFile(VirtualPath virtualPath) {
 
            BuildResult result = null;
            string cacheKey = null;
 
            if (_topLevelFilesCompiledCompleted) {
 
                VirtualPath parentPath = virtualPath.Parent;
 
                // First, try to batch the directory if enabled
                if (IsBatchEnabledForDirectory(parentPath)) {
                    BatchCompileWebDirectory(null, parentPath, true /*ignoreErrors*/);
 
                    // If successful, it would have been cached to memory
                    cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
                    result = _memoryCache.GetBuildResult(cacheKey);
 
                    if (result == null && DelayLoadType.Enabled) {
                        // We might not have cached the result in the memory cache
                        // if we are trying to delay loading the assembly.
                        result = GetBuildResultFromCache(cacheKey);
                    }
 
                    if (result != null) {
                        // If what we found in the cache is a CompileError, rethrow the exception
                        if (result is BuildResultCompileError) {
                            throw ((BuildResultCompileError)result).CompileException;
                        }
 
                        return result;
                    }
                }
            }
 
 
            DateTime utcStart = DateTime.UtcNow;
 
            // Name the assembly based on the virtual path, in order to get a recognizable name
            string outputAssemblyName = BuildManager.WebAssemblyNamePrefix +
                BuildManager.GenerateRandomAssemblyName(
                GetGeneratedAssemblyBaseName(virtualPath), false /*topLevel*/);
 
 
            BuildProvidersCompiler bpc = new BuildProvidersCompiler(virtualPath /*configPath*/, outputAssemblyName);
 
            // Create a BuildProvider based on the virtual path
            BuildProvider buildProvider = CreateBuildProvider(virtualPath, bpc.CompConfig,
                bpc.ReferencedAssemblies, true /*failIfUnknown*/);
 
            // Set the BuildProvider using a single item collection
            bpc.SetBuildProviders(new SingleObjectCollection(buildProvider));
 
            // Compile it
            CompilerResults results;
 
            try {
                results = bpc.PerformBuild();
                result = buildProvider.GetBuildResult(results);
            }
            catch (HttpCompileException e) {
 
                // If we're not supposed to cache the exception, just rethrow it
                if (e.DontCache)
                    throw;
 
                result = new BuildResultCompileError(virtualPath, e);
 
                // Add the dependencies to the compile error build provider, so that
                // we will retry compilation when a dependency changes
                buildProvider.SetBuildResultDependencies(result);
 
                // Remember the virtualpath dependencies, so that we will correctly
                // invalidate buildresult when depdency changes.
                e.VirtualPathDependencies = buildProvider.VirtualPathDependencies;
 
                // Cache it for next time
                CacheVPathBuildResultInternal(virtualPath, result, utcStart);
 
                // Set the DontCache flag, so that the exception will not be incorrectly
                // cached again lower down the stack (VSWhidbey 128234)
                e.DontCache = true;
 
                throw;
            }
 
            if (result == null)
                return null;
 
            // Cache it for next time
            CacheVPathBuildResultInternal(virtualPath, result, utcStart);
 
            if (!_precompilingApp && BuildResultCompiledType.UsesDelayLoadType(result)) {
                // The result uses DelayLoadType, which should not get exposed.
                // If we are not performing precompilation, then we should
                // get the actual result from cache and return that instead.
                if (cacheKey == null) {
                    cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
                }
                result = BuildManager.GetBuildResultFromCache(cacheKey);
            }
 
            return result;
        }
 
        // Hashtbale to remember the local resources assembly for each directory (or null
        // if there isn't one). Hashtable<VirtualPath,Assembly>
        private Hashtable _localResourcesAssemblies = new Hashtable();
 
        private void EnsureFirstTimeDirectoryInit(VirtualPath virtualDir) {
 
            // Don't process local resources when precompiling for updatable deployment.
            // Instead, we deploy the App_LocalResources folder as is.
            if (PrecompilingForUpdatableDeployment)
                return;
 
            if (virtualDir == null)
                return;
 
            // Only do this once per directory
            if (_localResourcesAssemblies.Contains(virtualDir))
                return;
 
            // Don't do anything if it's outside the app root
            if (!virtualDir.IsWithinAppRoot)
                return;
 
            Debug.Trace("BuildManager", "EnsureFirstTimeDirectoryInit(" + virtualDir + ")");
 
            // Get the virtual path to the LocalResources subdirectory for this directory
            VirtualPath localResDir = virtualDir.SimpleCombineWithDir(HttpRuntime.LocalResourcesDirectoryName);
 
            bool dirExists;
            try {
                dirExists = localResDir.DirectoryExists();
            }
            catch {
                // If an exception happens, the directory may be outside the application,
                // in which case we should skip this logic, and act is if there are no
                // local resources (VSWhidbey 258776);
 
                _localResourcesAssemblies[virtualDir] = null;
                return;
            }
 
            Debug.Trace("BuildManager", "EnsureFirstTimeDirectoryInit: dirExists=" + dirExists);
 
            try {
                // Monitor changes to it so the appdomain can shut down when it changes
                HttpRuntime.StartListeningToLocalResourcesDirectory(localResDir);
            }
            catch {
                // could fail for long directory names
                if (dirExists) {
                    throw;
                }
            }
 
            Assembly resourceAssembly = null;
 
            // If it exists, build it
            if (dirExists) {
 
                string localResAssemblyName = GetLocalResourcesAssemblyName(virtualDir);
 
                bool gotLock = false;
 
                try {
                    // Grab the compilation mutex, since this method accesses the codegen files
                    CompilationLock.GetLock(ref gotLock);
 
                    resourceAssembly = CompileCodeDirectory(localResDir, CodeDirectoryType.LocalResources,
                        localResAssemblyName, null /*excludedSubdirectories*/);
                }
                finally {
                    // Always release the mutex if we had taken it
                    if (gotLock) {
                        CompilationLock.ReleaseLock();
                    }
                }
            }
 
            // Cache it whether it's null or not
            _localResourcesAssemblies[virtualDir] = resourceAssembly;
        }
 
        // VSWhidbey Bug 560521
        private void EnsureFirstTimeDirectoryInitForDependencies(ICollection dependencies) {
            foreach (String dependency in dependencies) {
                VirtualPath dependencyPath = VirtualPath.Create(dependency);
                VirtualPath dir = dependencyPath.Parent;
                EnsureFirstTimeDirectoryInit(dir);
            }
        }
 
 
        // Retrieve a cached local resources assembly (could be null)
        internal static Assembly GetLocalResourcesAssembly(VirtualPath virtualDir) {
            return (Assembly)_theBuildManager._localResourcesAssemblies[virtualDir];
        }
 
        internal static string GetLocalResourcesAssemblyName(VirtualPath virtualDir) {
            return LocalResourcesDirectoryAssemblyName + "." + GetGeneratedAssemblyBaseName(virtualDir);
        }
 
        // name of the slot in call context
        private const String BatchCompilationSlotName = "BatchCompileChk";
 
        // The semantics are
        //   true - always batch-compile
        //   false - never batch-compile
        //   null - determine from config
        private static bool? s_batchCompilationEnabled;
 
        public static Nullable<bool> BatchCompilationEnabled {
            get {
                return s_batchCompilationEnabled;
            }
            set {
                ThrowIfPreAppStartNotRunning();
                s_batchCompilationEnabled = value;
            }
        }
 
        // Check if batching is enabled for directory specified by virtualDir
        private bool IsBatchEnabledForDirectory(VirtualPath virtualDir) {
            // False if compile for fixed name
            if (CompileWithFixedAssemblyNames) {
                return false;
            }
 
            // Always enable batching for deployement
            if (PrecompilingForDeployment) {
                return true;
            }
 
            // If it's called by other non-precompile CBM methods, always disable batching
            if (BuildManagerHost.InClientBuildManager && !PerformingPrecompilation) {
                return false;
            }
 
            // If batch compilation was set through code use that setting
            if (BatchCompilationEnabled.HasValue) {
                return BatchCompilationEnabled.Value;
            }
 
            // Check the config
            return CompilationUtil.IsBatchingEnabled(virtualDir.VirtualPathString);
        }
 
        private bool BatchCompileWebDirectory(VirtualDirectory vdir, VirtualPath virtualDir, bool ignoreErrors) {
 
            // Exactly one of vdir and virtualDir should be non-null.  The idea is to avoid calling
            // VirtualPathProvider.GetDirectory if batching is disabled (VSWhidbey 437549).
 
            if (virtualDir == null)
                virtualDir = vdir.VirtualPathObject;
 
            if (vdir == null)
                vdir = HostingEnvironment.VirtualPathProvider.GetDirectory(virtualDir);
 
            // Then, check if we're already tried batch compiling this directory on this same request
 
            CaseInsensitiveStringSet directoryBatchCompilerChecker;
            directoryBatchCompilerChecker = CallContext.GetData(BatchCompilationSlotName)
                as CaseInsensitiveStringSet;
 
            if (directoryBatchCompilerChecker == null) {
                directoryBatchCompilerChecker = new CaseInsensitiveStringSet();
 
                // Create it and save it in the CallContext
                CallContext.SetData(BatchCompilationSlotName, directoryBatchCompilerChecker);
            }
 
            // If we've already tried batch compiling this directory, don't do anything
            if (directoryBatchCompilerChecker.Contains(vdir.VirtualPath))
                return false;
 
            // Add the current virtualDir to the batch compiler checker
            directoryBatchCompilerChecker.Add(vdir.VirtualPath);
 
            // If we're in the process of precompiling an app, never ignore errors.
            if (_precompilingApp)
                ignoreErrors = false;
 
            return BatchCompileWebDirectoryInternal(vdir, ignoreErrors);
        }
 
        private bool BatchCompileWebDirectoryInternal(VirtualDirectory vdir, bool ignoreErrors) {
 
            WebDirectoryBatchCompiler sdc = new WebDirectoryBatchCompiler(vdir);
 
            // Just ignore build providers that have errors
            if (ignoreErrors) {
                sdc.SetIgnoreErrors();
 
                // Don't propagate errors that happen during batch compilation
                try {
                    sdc.Process();
                }
                catch {
                    return false;
                }
            }
            else {
                sdc.Process();
            }
 
            return true;
        }
 
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly",
            Justification = "Global Asax is a well-known concept")]
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
            Justification = "This might cick off top-level compilation so it's too big for a property")]
        public static Type GetGlobalAsaxType() {
            return _theBuildManager.GetGlobalAsaxTypeInternal();
        }
 
        private Type GetGlobalAsaxTypeInternal() {
            EnsureTopLevelFilesCompiled();
 
            if (_globalAsaxBuildResult == null)
                return PageParser.DefaultApplicationBaseType ?? typeof(HttpApplication);
 
            return _globalAsaxBuildResult.ResultType;
        }
 
        internal static BuildResultCompiledGlobalAsaxType GetGlobalAsaxBuildResult() {
            return _theBuildManager.GetGlobalAsaxBuildResultInternal();
        }
 
        private BuildResultCompiledGlobalAsaxType GetGlobalAsaxBuildResultInternal() {
            EnsureTopLevelFilesCompiled();
 
            return _globalAsaxBuildResult;
        }
 
        internal string[] GetCodeDirectories() {
 
            VirtualPath virtualDir = HttpRuntime.CodeDirectoryVirtualPath;
 
            // If there is no Code directory, return an empty array
            if (!virtualDir.DirectoryExists())
                return new string[0];
 
            // Get the list of code sub directories that will be compiled separately
            CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
 
            // Compute the number of code dirs, including the root one
            int numOfCodeDirs = 1;
            if (codeSubDirectories != null)
                numOfCodeDirs += codeSubDirectories.Count;
 
            string[] codeDirs = new string[numOfCodeDirs];
            int current = 0;
 
            if (codeSubDirectories != null) {
                foreach (CodeSubDirectory entry in codeSubDirectories) {
 
                    VirtualPath virtualSubDir = virtualDir.SimpleCombineWithDir(entry.DirectoryName);
                    codeDirs[current++] = virtualSubDir.VirtualPathString;
                }
            }
 
            // Add the root code dir at the end of the list (since it's compiled last)
            codeDirs[current++] = virtualDir.VirtualPathString;
 
            return codeDirs;
        }
 
        internal void GetCodeDirectoryInformation(VirtualPath virtualCodeDir,
            out Type codeDomProviderType, out CompilerParameters compilerParameters,
            out string generatedFilesDir) {
 
            // Backup the compilation stage, since the call will modify it
            CompilationStage savedCompilationStage = _compilationStage;
 
            try {
                GetCodeDirectoryInformationInternal(virtualCodeDir, out codeDomProviderType,
                    out compilerParameters, out generatedFilesDir);
            }
            finally {
                // Restore the compilation stage
                _compilationStage = savedCompilationStage;
            }
        }
 
        private void GetCodeDirectoryInformationInternal(VirtualPath virtualCodeDir,
            out Type codeDomProviderType, out CompilerParameters compilerParameters,
            out string generatedFilesDir) {
 
            StringSet excludedSubdirectories = null;
 
            CodeDirectoryType dirType;
 
            // Get the DirectoryType based on the path
            if (virtualCodeDir == HttpRuntime.CodeDirectoryVirtualPath) {
 
                // If it's the top level code directory, make sure we exclude any
                // subdirectories that are compiled separately
                EnsureExcludedCodeSubDirectoriesComputed();
 
                excludedSubdirectories = _excludedCodeSubdirectories;
 
                dirType = CodeDirectoryType.MainCode;
 
                _compilationStage = CompilationStage.TopLevelFiles;
            }
            else if (virtualCodeDir == HttpRuntime.ResourcesDirectoryVirtualPath) {
 
                dirType = CodeDirectoryType.AppResources;
 
                _compilationStage = CompilationStage.TopLevelFiles;
            }
            // If virtualCodeDir is a subdir of WebReference virtual path.
            else if (String.Compare(virtualCodeDir.VirtualPathString, 0,
                HttpRuntime.WebRefDirectoryVirtualPath.VirtualPathString, 0, HttpRuntime.WebRefDirectoryVirtualPath.VirtualPathString.Length,
                StringComparison.OrdinalIgnoreCase) == 0) {
 
                // Use the top WebReference directory info for its sub directories.
                virtualCodeDir = HttpRuntime.WebRefDirectoryVirtualPath;
                dirType = CodeDirectoryType.WebReferences;
 
                _compilationStage = CompilationStage.TopLevelFiles;
            }
            else if (String.Compare(virtualCodeDir.FileName, HttpRuntime.LocalResourcesDirectoryName,
                StringComparison.OrdinalIgnoreCase) == 0) {
 
                dirType = CodeDirectoryType.LocalResources;
 
                // LocalResources are compiled *after* top level files
                _compilationStage = CompilationStage.AfterTopLevelFiles;
            }
            else {
                // If all else fails, treat it as a sub directory
                // 
                dirType = CodeDirectoryType.SubCode;
 
                // Sub-code dirs are compiled *before* the main code dir
                _compilationStage = CompilationStage.TopLevelFiles;
            }
 
            Debug.Assert(virtualCodeDir.HasTrailingSlash);
            AssemblyReferenceInfo info = TheBuildManager.TopLevelAssembliesIndexTable[virtualCodeDir.VirtualPathString];
            if (info == null) {
                throw new InvalidOperationException(
                    SR.GetString(SR.Invalid_CodeSubDirectory_Not_Exist, virtualCodeDir));
            }
 
            // Get the info we need for this code directory
            CodeDirectoryCompiler.GetCodeDirectoryInformation(
                virtualCodeDir, dirType, excludedSubdirectories, info.ReferenceIndex,
                out codeDomProviderType, out compilerParameters,
                out generatedFilesDir);
 
            Assembly resultAssembly = info.Assembly;
 
            if (resultAssembly != null) {
                // Use the runtime generated assembly location. VSWhidbey 400335
                compilerParameters.OutputAssembly = resultAssembly.Location;
            }
        }
 
        internal static Type GetProfileType() {
            return _theBuildManager.GetProfileTypeInternal();
        }
 
        private Type GetProfileTypeInternal() {
            EnsureTopLevelFilesCompiled();
            return _profileType;
        }
 
 
        //
        // Caching related code
        //
 
 
        public static ICollection GetVirtualPathDependencies(string virtualPath) {
 
            CompilationSection compConfig = RuntimeConfig.GetRootWebConfig().Compilation;
 
            // Create a BuildProvider based on the virtual path
            BuildProvider buildProvider = CreateBuildProvider(VirtualPath.Create(virtualPath), compConfig,
                null, false /*failIfUnknown*/);
 
            if (buildProvider == null)
                return null;
 
            // Get its dependencies
            // 
            return buildProvider.GetBuildResultVirtualPathDependencies();
        }
 
#if OLD
    /*
     * Rewrite the virtualPath if appropriate, in order to support ghosting
     */
    private static void GetGhostedVirtualPath(ref string virtualPath) {
 
        VirtualPathProvider virtualPathProvider = HostingEnvironment.VirtualPathProvider;
 
        string ghostedVirtualPath = virtualPathProvider.GetGhostedVirtualPath(virtualPath);
 
        // If the file is not ghosted, don't change the path
        if (ghostedVirtualPath == null)
            return;
 
        // 
 
 
        // Get the list of virtual paths that it depends on (e.g. user controls)
        ICollection virtualPathDependencies = GetVirtualPathDependencies(virtualPath);
 
        // If there aren't any, return the ghosted path
        if (virtualPathDependencies == null) {
            virtualPath = ghostedVirtualPath;
            return;
        }
 
        // Go through all the dependencies, and if we find any that is *not* ghosted
        // (i.e. for which GetGhostedVirtualPath returns null), we treat the whole request
        // as unghosted (and hence we return without modifying the virtualPath).
 
        foreach (string virtualDependency in virtualPathDependencies) {
            string ghostedVirtualDependencyPath = virtualPathProvider.GetGhostedVirtualPath(
                virtualDependency);
            if (ghostedVirtualDependencyPath == null)
                return;
        }
 
        // All the dependencies are ghosted, so we can safely use the ghosted path,
        // which can then be shared for all fully ghosted requests.
        virtualPath = ghostedVirtualPath;
    }
#endif
 
        internal static string GetCacheKeyFromVirtualPath(VirtualPath virtualPath) {
            bool keyFromVPP;
            return GetCacheKeyFromVirtualPath(virtualPath, out keyFromVPP);
        }
 
        /*
         * Same as GetCacheKeyFromVirtualPathInternal, but caches the cache keys
         * for performance, since creating them is expensive (VSWhidbey 146540)
         */
        static SimpleRecyclingCache _keyCache = new SimpleRecyclingCache();
        private static string GetCacheKeyFromVirtualPath(VirtualPath virtualPath, out bool keyFromVPP) {
 
            // Check if the VirtualPathProvider needs to use a non-default cache key
            string key = virtualPath.GetCacheKey();
 
            // If so, just return it
            if (key != null) {
                keyFromVPP = true;
                return key.ToLowerInvariant();
            }
 
            // The VPP didn't return a key, so use our standard key algorithm
            keyFromVPP = false;
 
            // Check if the key for this virtual path is already cached
            key = _keyCache[virtualPath.VirtualPathString] as string;
            if (key != null) return key;
 
            // Compute the key
            key = GetCacheKeyFromVirtualPathInternal(virtualPath);
 
            // The key should always be lower case
            Debug.Assert(key == key.ToLowerInvariant());
 
            // Cache it for next time
            _keyCache[virtualPath.VirtualPathString] = key;
 
            return key;
        }
 
        /*
         * Generate a unique cache key from a virtual path.  e.g. for "/approot/sub1/sub2/foo.aspx"
         * the key could be "foo.aspx.ccdf220e", where ccdf220e is a hash code from
         * the dir "sub1/sub2".
         */
        private static string GetCacheKeyFromVirtualPathInternal(VirtualPath virtualPath) {
 
            // We want the key to be app independent (for precompilation), so we
            // change the virtual path to be app relative
 
            /* Disable assertion since global theme needs to compile theme files in different vroot.
            Debug.Assert(StringUtil.VirtualPathStartsWithAppPath(virtualPath),
                String.Format("VPath {0} is outside the application: {1}", virtualPath, HttpRuntime.AppDomainAppVirtualPath));
            */
            string virtualPathString = virtualPath.AppRelativeVirtualPathString.ToLowerInvariant();
            virtualPathString = UrlPath.RemoveSlashFromPathIfNeeded(virtualPathString);
 
            // Split the path into the directory and the name
            int slashIndex = virtualPathString.LastIndexOf('/');
            Debug.Assert(slashIndex >= 0 || virtualPathString == "~");
 
            if (virtualPathString == "~")
                return "root";
 
            Debug.Assert(slashIndex != virtualPathString.Length - 1);
            string name = virtualPathString.Substring(slashIndex + 1);
            string dir;
            if (slashIndex <= 0)
                dir = "/";
            else {
                dir = virtualPathString.Substring(0, slashIndex);
            }
 
            return name + "." + StringUtil.GetStringHashCode(dir).ToString("x", CultureInfo.InvariantCulture);
        }
 
        internal static BuildResult GetVPathBuildResultFromCache(VirtualPath virtualPath) {
 
            return TheBuildManager.GetVPathBuildResultFromCacheInternal(virtualPath);
        }
 
        private BuildResult GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath, bool ensureIsUpToDate = true) {
            bool keyFromVPP;
            string cacheKey = GetCacheKeyFromVirtualPath(virtualPath, out keyFromVPP);
            return GetBuildResultFromCacheInternal(cacheKey, keyFromVPP, virtualPath, 0 /*hashCode*/, ensureIsUpToDate);
        }
 
        internal static BuildResult GetBuildResultFromCache(string cacheKey) {
            return _theBuildManager.GetBuildResultFromCacheInternal(cacheKey, false /*keyFromVPP*/, null /*virtualPath*/,
                0 /*hashCode*/);
        }
 
        internal static BuildResult GetBuildResultFromCache(string cacheKey, VirtualPath virtualPath) {
            return _theBuildManager.GetBuildResultFromCacheInternal(cacheKey, false /*keyFromVPP*/, virtualPath,
                0 /*hashCode*/);
        }
 
        private BuildResult GetBuildResultFromCacheInternal(string cacheKey, bool keyFromVPP,
            VirtualPath virtualPath, long hashCode, bool ensureIsUpToDate = true) {
 
            BuildResult result = null;
 
            // Allow the possibility that BuildManager was not initialized due to
            // a very early failure (e.g. see VSWhidbey 137366)
            //Debug.Trace("BuildManager", "GetBuildResultFromCacheInternal " + _theBuildManagerInitialized);
            if (!_theBuildManagerInitialized)
                return null;
 
            // The first cache should always be memory
            Debug.Assert(_caches[0] == _memoryCache);
 
            // Try to get it from the memeory cache before taking any locks (for perf reasons)
            result = _memoryCache.GetBuildResult(cacheKey, virtualPath, hashCode, ensureIsUpToDate);
            if (result != null) {
                return PostProcessFoundBuildResult(result, keyFromVPP, virtualPath);
            }
 
            Debug.Trace("BuildManager", "Didn't find '" + virtualPath + "' in memory cache before lock");
 
            lock (this) {
                // Try to get the BuildResult from the cheapest to most expensive cache
                int i;
                for (i = 0; i < _caches.Length; i++) {
                    result = _caches[i].GetBuildResult(cacheKey, virtualPath, hashCode, ensureIsUpToDate);
 
                    // There might be changes in local resources for dependencies,
                    // so we need to make sure EnsureFirstTimeDirectoryInit gets called
                    // for them even when we already have a cache result.
                    // VSWhidbey Bug 560521
 
                    if (result != null) {
                        // We should only process the local resources folder after the top level files have been compiled,
                        // so that any custom VPP can be registered first. (Dev10 bug 890796)
                        if (_compilationStage == CompilationStage.AfterTopLevelFiles && result.VirtualPathDependencies != null) {
                            EnsureFirstTimeDirectoryInitForDependencies(result.VirtualPathDependencies);
                        }
 
                        break;
                    }
 
                    // If we didn't find it in the memory cache, perform the per directory
                    // initialization.  This is a good place to do this, because we don't
                    // affect the memory cache code path, but we do the init as soon as
                    // something is not found in the memory cache.
                    if (i == 0 && virtualPath != null) {
                        VirtualPath virtualDir = virtualPath.Parent;
                        EnsureFirstTimeDirectoryInit(virtualDir);
                    }
                }
 
 
                if (result == null)
                    return null;
 
                result = PostProcessFoundBuildResult(result, keyFromVPP, virtualPath);
                if (result == null)
                    return null;
 
                Debug.Assert(_memoryCache != null);
 
                // If we found it in a cache, cache it in all the caches that come before
                // the one where we found it.  If we found it in the memory cache, this is a no op.
                for (int j = 0; j < i; j++)
                    _caches[j].CacheBuildResult(cacheKey, result, DateTime.UtcNow);
 
                Debug.Trace("BuildManager", "Found '" + virtualPath + "' in " + _caches[i]);
 
                return result;
            }
        }
 
        private BuildResult PostProcessFoundBuildResult(BuildResult result, bool keyFromVPP, VirtualPath virtualPath) {
 
            // Check that the virtual path in the result matches the passed in
            // virtualPath (VSWhidbey 516641).  But skip this check in case the key came from
            // calling VirtualPathProvider.GetCacheKey, as it may legitimately not match.
            if (!keyFromVPP) {
                if (virtualPath != null && virtualPath != result.VirtualPath) {
                    Debug.Assert(false);
                    return null;
                }
            }
 
            // If what we found in the cache is a CompileError, rethrow the exception
            if (result is BuildResultCompileError) {
                // Report the cached error from Callback interface.
                HttpCompileException compileException = ((BuildResultCompileError)result).CompileException;
 
                // But don't report it if we're doing precompilation, as that would cause it to be
                // reported twice because we always try to compile everything that has failed
                // before (VSWhidbey 525414)
                if (!PerformingPrecompilation) {
                    ReportErrorsFromException(compileException);
                }
 
                throw compileException;
            }
 
            return result;
        }
 
        internal static bool CacheVPathBuildResult(VirtualPath virtualPath,
            BuildResult result, DateTime utcStart) {
 
            return _theBuildManager.CacheVPathBuildResultInternal(virtualPath, result, utcStart);
        }
 
        private bool CacheVPathBuildResultInternal(VirtualPath virtualPath,
            BuildResult result, DateTime utcStart) {
 
            string cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
            return CacheBuildResult(cacheKey, result, utcStart);
        }
 
        internal static bool CacheBuildResult(string cacheKey, BuildResult result, DateTime utcStart) {
            return _theBuildManager.CacheBuildResultInternal(cacheKey, result, 0 /*hashCode*/, utcStart);
        }
 
        private bool CacheBuildResultInternal(string cacheKey, BuildResult result,
            long hashCode, DateTime utcStart) {
 
            // Before caching it, make sure the hash has been computed
            result.EnsureVirtualPathDependenciesHashComputed();
 
            for (int i = 0; i < _caches.Length; i++) {
                _caches[i].CacheBuildResult(cacheKey, result, hashCode, utcStart);
            }
 
            // If we find that it's no longer valid after caching it, remove it from the cache (VSWhidbey 578372)
            if (!TimeStampChecker.CheckFilesStillValid(cacheKey, result.VirtualPathDependencies)) {
                _memoryCache.RemoveAssemblyAndCleanupDependencies(result as BuildResultCompiledAssemblyBase);
                return false;
            }
 
            return true;
        }
 
 
        //
        // Precompilation related code
        //
 
        internal void SetPrecompilationInfo(HostingEnvironmentParameters hostingParameters) {
 
            if (hostingParameters == null || hostingParameters.ClientBuildManagerParameter == null)
                return;
 
            _precompilationFlags = hostingParameters.ClientBuildManagerParameter.PrecompilationFlags;
 
            _strongNameKeyFile = hostingParameters.ClientBuildManagerParameter.StrongNameKeyFile;
            _strongNameKeyContainer = hostingParameters.ClientBuildManagerParameter.StrongNameKeyContainer;
 
            // Check if we're precompiling to a target directory
            _precompTargetPhysicalDir = hostingParameters.PrecompilationTargetPhysicalDirectory;
            if (_precompTargetPhysicalDir == null)
                return;
 
            // Check if the target dir already exists and is not empty
            if (Util.IsNonEmptyDirectory(_precompTargetPhysicalDir)) {
 
                // If it's not empty and OverwriteTarget is off, fail
                if ((_precompilationFlags & PrecompilationFlags.OverwriteTarget) == 0) {
                    throw new HttpException(SR.GetString(SR.Dir_not_empty));
                }
 
                // Does it contain the precomp marker file
                bool updatable;
                bool precompiled = ReadPrecompMarkerFile(_precompTargetPhysicalDir, out updatable);
 
                // If not, refuse to delete the directory, even if OverwriteTarget is on (VSWhidbey 425095)
                if (!precompiled) {
                    throw new HttpException(SR.GetString(SR.Dir_not_empty_not_precomp));
                }
 
                // The OverwriteTarget flag was specified, so delete the directory
                if (!DeletePrecompTargetDirectory()) {
                    // If we failed to delete it, sleep 250 ms and try again, in case there is
                    // an appdomain in the process of shutting down (the shut down would
                    // have been triggered by the first delete attempt)
                    Debug.Trace("BuildManager", "Failed to delete " + _precompTargetPhysicalDir + ".  Sleeping and trying once more...");
                    Thread.Sleep(250);
 
                    if (!DeletePrecompTargetDirectory()) {
                        Debug.Trace("BuildManager", "Failed to delete " + _precompTargetPhysicalDir + ".  Sleeping and trying once more...");
                        // Try again after 1 second.
                        Thread.Sleep(1000);
 
                        // If we still couldn't delete it, fail
                        if (!DeletePrecompTargetDirectory()) {
                            throw new HttpException(SR.GetString(SR.Cant_delete_dir));
                        }
                    }
                }
            }
 
            // Create a marker file to mark the fact that this is a precompiled app
            CreatePrecompMarkerFile();
        }
 
        private bool DeletePrecompTargetDirectory() {
            try {
                if (_precompTargetPhysicalDir != null) {
                    // Go through all the files in the directory and delete them.
                    foreach (FileData fileData in FileEnumerator.Create(_precompTargetPhysicalDir)) {
 
                        if (fileData.IsDirectory) {
                            Directory.Delete(fileData.FullName, true /*recursive*/);
                        }
                        else {
                            Util.DeleteFileNoException(fileData.FullName);
                        }
                    }
                }
            }
#if DEBUG
            catch (Exception e) {
                Debug.Trace("BuildManager", "DeletePrecompTargetDirectory failed: " + e.Message);
            }
#else
            catch { }
#endif
            return !Util.IsNonEmptyDirectory(_precompTargetPhysicalDir);
        }
 
        private void FailIfPrecompiledApp() {
 
            if (IsPrecompiledApp) {
                throw new HttpException(SR.GetString(SR.Already_precomp));
            }
        }
 
        internal void PrecompileApp(ClientBuildManagerCallback callback, IEnumerable<string> excludedVirtualPaths) {
 
            // Remember the original setting
            bool skipTopLevelExceptions = SkipTopLevelCompilationExceptions;
 
            try {
                _cbmCallback = callback;
 
                // Don't stop on the first parse errors, process as many errors as possible.
                ThrowOnFirstParseError = false;
 
                // Don't skip top level compilation exceptions even called by CBM.
                SkipTopLevelCompilationExceptions = false;
 
                PrecompileApp(HttpRuntime.AppDomainAppVirtualPathObject, excludedVirtualPaths);
            }
            finally {
                // Revert to original setting
                SkipTopLevelCompilationExceptions = skipTopLevelExceptions;
                ThrowOnFirstParseError = true;
 
                _cbmCallback = null;
            }
        }
 
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        private void PrecompileApp(VirtualPath startingVirtualDir, IEnumerable<string> excludedVirtualPaths) {
            using (new ApplicationImpersonationContext()) {
                try {
                    PerformingPrecompilation = true;
 
                    PrecompileAppInternal(startingVirtualDir, excludedVirtualPaths);
                }
                catch {
                    // If anything fails during precompilation, wipe out the target to avoid
                    // leaving it in a random state (VSWhidbey 447338)
                    DeletePrecompTargetDirectory();
 
                    throw;
                }
                finally {
                    PerformingPrecompilation = false;
                }
            }
        }
 
        private void PrecompileAppInternal(VirtualPath startingVirtualDir, IEnumerable<string> excludedVirtualPaths) {
 
            // If the app is already precompiled, fail
            FailIfPrecompiledApp();
 
            excludedVirtualPaths = excludedVirtualPaths ?? Enumerable.Empty<string>();
            _excludedCompilationPaths = excludedVirtualPaths.Select(path => VirtualPath.Create(UrlPath.Combine("~", path))).ToList();
 
            VirtualDirectory appVdir = startingVirtualDir.GetDirectory();
 
            EnsureTopLevelFilesCompiled();
 
            try {
                // Clear the parseError flag first
                _parseErrorReported = false;
 
                PrecompileWebDirectoriesRecursive(appVdir, topLevel: true);
                PrecompileThemeDirectories();
            }
            catch (HttpParseException parseException) {
                // if nothing calls callback.reportparseerror yet, report the parse error.
                if (!_parseErrorReported) {
                    ReportErrorsFromException(parseException);
                }
 
                throw;
            }
 
            // Copy all the DLL's we compiled into the destination's bin directory (if any)
            if (_precompTargetPhysicalDir != null) {
                string targetBinDir = Path.Combine(_precompTargetPhysicalDir, HttpRuntime.BinDirectoryName);
                CopyCompiledAssembliesToDestinationBin(HttpRuntime.CodegenDirInternal, targetBinDir);
            }
 
            // Copy all the static files to the destination directory (if any).  We treat anything we
            // don't compile as a static file.  It's better to do this at the end of the precompilation,
            // this way if any pages has errors (parse or compile), we never get to this step.
            if (_precompTargetPhysicalDir != null) {
                CopyStaticFilesRecursive(appVdir, _precompTargetPhysicalDir, topLevel: true);
            }
        }
 
        // Create a small file that marks that app as being precompiled
        private void CreatePrecompMarkerFile() {
 
            Debug.Assert(PrecompilingForDeployment);
 
            Directory.CreateDirectory(_precompTargetPhysicalDir);
            string precompMarkerFile = Path.Combine(_precompTargetPhysicalDir, precompMarkerFileName);
 
            using (StreamWriter writer = new StreamWriter(precompMarkerFile, false /*append*/, Encoding.UTF8)) {
                writer.Write("<precompiledApp version=\"2\" updatable=\"");
 
                // Write out a flag that determines if the precompiled app is updatable
                if (PrecompilingForUpdatableDeployment)
                    writer.Write("true");
                else
                    writer.Write("false");
                writer.Write("\"/>");
            }
        }
 
        [SuppressMessage("Microsoft.Security", "MSEC1207:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")]
        [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")]
        private static bool ReadPrecompMarkerFile(string appRoot, out bool updatable) {
 
            updatable = false;
 
            // Get the full physical path to the precompilation market file
            string precompMarkerFile = Path.Combine(appRoot, precompMarkerFileName);
 
            // If the file doesn't exist at all, it's not a precompiled app
            if (!File.Exists(precompMarkerFile))
                return false;
 
            XmlDocument doc = new XmlDocument();
            try {
                doc.Load(precompMarkerFile);
            }
            catch {
                // If we fail to read it for any reason, ignore it.
                return false;
            }
 
            // Get the root element, and make sure it's what we expect
            XmlNode root = doc.DocumentElement;
            Debug.Assert(root != null && root.Name == "precompiledApp");
            if (root == null || root.Name != "precompiledApp")
                return false;
 
            // Check the updatable flag
            HandlerBase.GetAndRemoveBooleanAttribute(root, "updatable", ref updatable);
 
            return true;
        }
 
        /*
         * Are we precompiling the app for deployment (as opposed to in-place)
         */
        internal static bool PrecompilingForDeployment {
            get {
                return (_theBuildManager._precompTargetPhysicalDir != null);
            }
        }
 
        internal static bool PrecompilingForUpdatableDeployment {
            get {
                // The updatebale mode only applies in deployment precompilation mode
                if (!PrecompilingForDeployment)
                    return false;
 
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.Updatable) != 0;
            }
        }
 
        private static bool PrecompilingForCleanBuild {
            get {
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.Clean) != 0;
            }
        }
 
        internal static bool PrecompilingWithDebugInfo {
            get {
                // The ForceDebug flag only applies in deployment precompilation mode
                if (!PrecompilingForDeployment)
                    return false;
 
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.ForceDebug) != 0;
            }
        }
 
        internal static bool PrecompilingWithCodeAnalysisSymbol {
            get {
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.CodeAnalysis) != 0;
            }
        }
 
        private static bool CompileWithFixedAssemblyNames {
            get {
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.FixedNames) != 0;
            }
        }
 
        internal static bool CompileWithAllowPartiallyTrustedCallersAttribute {
            get {
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.AllowPartiallyTrustedCallers) != 0;
            }
        }
 
        internal static bool CompileWithDelaySignAttribute {
            get {
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.DelaySign) != 0;
            }
        }
 
        internal static bool IgnoreBadImageFormatException {
            get {
                return (_theBuildManager._precompilationFlags & PrecompilationFlags.IgnoreBadImageFormatException) != 0;
            }
        }
 
        internal static string StrongNameKeyFile {
            get {
                return _theBuildManager._strongNameKeyFile;
            }
        }
 
        internal static string StrongNameKeyContainer {
            get {
                return _theBuildManager._strongNameKeyContainer;
            }
        }
 
        // If we're in the process of precompiling for updatable deployment, this returns
        // a writer to the target file specified by the virtual path.  This is used when the
        // deployed file needs to be different from the original (as is the case for aspx files).
        internal static TextWriter GetUpdatableDeploymentTargetWriter(VirtualPath virtualPath, Encoding fileEncoding) {
 
            Debug.Assert(fileEncoding != null);
 
            if (!PrecompilingForUpdatableDeployment)
                return null;
 
            Debug.Assert(!virtualPath.IsRelative);
 
            string path = virtualPath.AppRelativeVirtualPathString;
 
            // Skip the "~/" to be left with the relative path
            path = path.Substring(2);
 
            // Combine it with the precomp target dir to get the full path
            string physicalPath = Path.Combine(_theBuildManager._precompTargetPhysicalDir, path);
 
            // Before trying to create the file, make sure the directory exists
            string physicalDir = Path.GetDirectoryName(physicalPath);
            Directory.CreateDirectory(physicalDir);
 
            return new StreamWriter(physicalPath, false /*append*/, fileEncoding);
        }
 
        private bool IsPrecompiledAppInternal {
            get {
                if (!_isPrecompiledAppComputed) {
                    _isPrecompiledApp = ReadPrecompMarkerFile(HttpRuntime.AppDomainAppPathInternal,
                        out _isUpdatablePrecompiledApp);
 
                    _isPrecompiledAppComputed = true;
                }
 
                return _isPrecompiledApp;
            }
        }
 
        public static bool IsPrecompiledApp {
            get {
                return _theBuildManager.IsPrecompiledAppInternal;
            }
        }
 
        private bool IsUpdatablePrecompiledAppInternal {
            get {
                return IsPrecompiledApp && _isUpdatablePrecompiledApp;
            }
        }
 
        public static bool IsUpdatablePrecompiledApp {
            get {
                return _theBuildManager.IsUpdatablePrecompiledAppInternal;
            }
        }
 
        private bool IsNonUpdatablePrecompiledApp {
            get {
                return IsPrecompiledApp && !_isUpdatablePrecompiledApp;
            }
        }
 
        private bool IsExcludedFromPrecompilation(VirtualDirectory dir) {
            Debug.Assert(dir != null);
            return _excludedCompilationPaths.Any(path => UrlPath.IsEqualOrSubpath(path.VirtualPathString, dir.VirtualPath));
        }
 
        private void PrecompileWebDirectoriesRecursive(VirtualDirectory vdir, bool topLevel) {
 
            // Precompile the children directory
 
            foreach (VirtualDirectory childVdir in vdir.Directories) {
 
                if (topLevel && _excludedTopLevelDirectories.Contains(childVdir.Name))
                    continue;
 
                // Exclude the special FrontPage directory (VSWhidbey 116727, 518602)
                if (childVdir.Name == "_vti_cnf")
                    continue;
 
                // Exclude target directory in precompilation scenarios
                if (SourceDirectoryIsInPrecompilationDestination(childVdir)) {
                    continue;
                }
 
                if (IsExcludedFromPrecompilation(childVdir)) {
                    continue;
                }
 
                PrecompileWebDirectoriesRecursive(childVdir, topLevel: false);
            }
 
            // Precompile this directory
            try {
                // Set a flag to remember that we're in the process of precompiling.  This
                // way, if BatchCompileWebDirectory ends up getting called again recursively
                // via CompileWebFile, we know that we cannot ignore errors.
                _precompilingApp = true;
 
                if (IsBatchEnabledForDirectory(vdir.VirtualPathObject)) {
                    // batch everything if enabled
                    BatchCompileWebDirectory(vdir, virtualDir: null, ignoreErrors: false);
                }
                else {
                    // if batching is disabled, compile each web file individually.
                    NonBatchDirectoryCompiler dirCompiler = new NonBatchDirectoryCompiler(vdir);
                    dirCompiler.Process();
                }
            }
            finally {
                // Always restore the flag to false when we're done.
                _precompilingApp = false;
            }
        }
 
        private void PrecompileThemeDirectories() {
            string appPhysicalDir = Path.Combine(HttpRuntime.AppDomainAppPathInternal, HttpRuntime.ThemesDirectoryName);
 
            if (Directory.Exists(appPhysicalDir)) {
                string[] themeDirs = Directory.GetDirectories(appPhysicalDir);
 
                foreach (string themeDirPath in themeDirs) {
                    string themeDirName = Path.GetFileName(themeDirPath);
                    ThemeDirectoryCompiler.GetThemeBuildResultType(null /*context*/, themeDirName);
                }
            }
        }
 
        /*
         * Recursively copy all the static files from the source directory to the
         * target directory of the precompilation
         */
        private void CopyStaticFilesRecursive(VirtualDirectory sourceVdir, string destPhysicalDir,
            bool topLevel) {
 
            // Make sure the target physical dir has no relation with the source.  It's important to
            // check at every new directory, because IIS apps can have disconnected virtual sub dirs,
            // making an app root check insufficient (VSWhidbey 426251)
            if (SourceDirectoryIsInPrecompilationDestination(sourceVdir)) {
                return;
            }
 
            if (IsExcludedFromPrecompilation(sourceVdir)) {
                return;
            }
 
            bool directoryCreationAttempted = false;
 
            foreach (VirtualFileBase child in sourceVdir.Children) {
 
                string destPhysicalSubDir = Path.Combine(destPhysicalDir, child.Name);
 
                if (child.IsDirectory) {
 
                    // Skip the special top level directories, since they never contain relevant
                    // static files.  Note that we don't skip Themes, which does contain static files.
                    if (topLevel &&
                        (StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.CodeDirectoryName) ||
                        StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.ResourcesDirectoryName) ||
                        StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.WebRefDirectoryName))) {
 
                        continue;
                    }
 
                    // Also, skip the LocalResources directory at any level, except when precompiling
                    // for updatable deployment (in which case, we deploy the local resources file)
                    if (!PrecompilingForUpdatableDeployment && StringUtil.EqualsIgnoreCase(child.Name,
                        HttpRuntime.LocalResourcesDirectoryName)) {
                        continue;
                    }
 
                    CopyStaticFilesRecursive(child as VirtualDirectory, destPhysicalSubDir, topLevel: false);
                    continue;
                }
 
                // Create the destination directory if needed
                if (!directoryCreationAttempted) {
                    directoryCreationAttempted = true;
                    Directory.CreateDirectory(destPhysicalDir);
                }
 
                // Copy the file as appropriate based on its extension
                CopyPrecompiledFile(child as VirtualFile, destPhysicalSubDir);
            }
        }
 
        /*
         * Copy all the assemblies from the codegen dir into the bin directory of the
         * target precompiled app.
         */
        private void CopyCompiledAssembliesToDestinationBin(string fromDir, string toDir) {
 
            bool createdDirectory = false;
 
            foreach (FileData fileData in FileEnumerator.Create(fromDir)) {
                // Windows OS Bug 1981578
                // Create a new directory only if there is something in the directory.
                if (!createdDirectory)
                    Directory.CreateDirectory(toDir);
                createdDirectory = true;
 
                // Recurse on subdirectories.if they contain culture files
                if (fileData.IsDirectory) {
 
                    if (Util.IsCultureName(fileData.Name)) {
                        string fromSubDir = Path.Combine(fromDir, fileData.Name);
                        string toSubDir = Path.Combine(toDir, fileData.Name);
                        CopyCompiledAssembliesToDestinationBin(fromSubDir, toSubDir);
                    }
 
                    continue;
                }
 
                // Only process DLL's and PDB's
                string extension = Path.GetExtension(fileData.Name);
                if (extension != ".dll" && extension != ".pdb")
                    continue;
 
                // Do not copy the file to the target folder if it has been already
                // marked for deletion - Dev10 bug 676794
                if (DiskBuildResultCache.HasDotDeleteFile(fileData.FullName)) {
                    continue;
                }
 
                string sourcePhysicalPath = Path.Combine(fromDir, fileData.Name);
                string destPhysicalPath = Path.Combine(toDir, fileData.Name);
 
                // Copy the file to the destination
                // 
                File.Copy(sourcePhysicalPath, destPhysicalPath, true /*overwrite*/);
            }
        }
 
        // Copy one file from the source app to the precompiled app
        private void CopyPrecompiledFile(VirtualFile vfile, string destPhysicalPath) {
 
            bool createStub;
 
            if (CompilationUtil.NeedToCopyFile(vfile.VirtualPathObject, PrecompilingForUpdatableDeployment,
                out createStub)) {
 
                // 
                string sourcePhysicalPath = HostingEnvironment.MapPathInternal(vfile.VirtualPath);
 
                // The file could already exist with updatable precompilation, since we would create the modified file
                // earlier during processing of a code beside page.
                if (File.Exists(destPhysicalPath)) {
 
                    // In that case, we still need to fix it up to insert the correct type string in the
                    // inherits attribute (VSWhidbey 467936)
 
                    // First, get the just-compiled BuildResult.  It should always exist
                    BuildResultCompiledType result = GetVPathBuildResult(null, vfile.VirtualPathObject,
                        true /*noBuild*/, false /*allowCrossApp*/) as BuildResultCompiledType;
                    Debug.Assert(result != null);
 
                    // VSWhidbey 527299. Need to use the same encoding of the original file to
                    // read and write to the new file.
                    Encoding encoding = Util.GetEncodingFromConfigPath(vfile.VirtualPathObject);
 
                    // Read in the file
                    string newAspxFile = Util.StringFromFile(destPhysicalPath, ref encoding);
 
                    // Replace the placeholder token by the true type with the assembly
                    newAspxFile = newAspxFile.Replace(UpdatableInheritReplacementToken,
                        Util.GetAssemblyQualifiedTypeName(result.ResultType));
 
                    // Write the modified file back with the correct inherits type string
                    StreamWriter writer = new StreamWriter(destPhysicalPath, false /* append */, encoding);
                    writer.Write(newAspxFile);
                    writer.Close();
                }
                else {
                    // Just copy the file to the destination
                    File.Copy(sourcePhysicalPath, destPhysicalPath, false /*overwrite*/);
                }
 
                // If it has a readonly attribute, clear it on the destination (VSWhidbey 122359)
                Util.ClearReadOnlyAttribute(destPhysicalPath);
            }
            else {
                if (createStub) {
                    // Create the stub file, with a helpful static message
                    StreamWriter writer = new StreamWriter(destPhysicalPath);
                    writer.Write(SR.GetString(SR.Precomp_stub_file));
                    writer.Close();
                }
            }
        }
 
        // Make sure the target physical dir has no relation with the source. Return true if it does.
        private bool SourceDirectoryIsInPrecompilationDestination(VirtualDirectory sourceDir) {
            // Alwasy return false for in-place precompilations or non-precompilation scenarios.
            if (_precompTargetPhysicalDir == null) {
                return false;
            }
 
            string sourcePhysicalDir = HostingEnvironment.MapPathInternal(sourceDir.VirtualPath);
 
            // Make sure they're normalized and end with a '\' before comparing (VSWhidbey 452554)
            sourcePhysicalDir = FileUtil.FixUpPhysicalDirectory(sourcePhysicalDir);
            string destPhysicalDir = FileUtil.FixUpPhysicalDirectory(_precompTargetPhysicalDir);
 
            return StringUtil.StringStartsWithIgnoreCase(sourcePhysicalDir, destPhysicalDir);
        }
 
        internal static void ReportDirectoryCompilationProgress(VirtualPath virtualDir) {
 
            // Nothing to do if there is no CBM callback
            ClientBuildManagerCallback callback = CBMCallback;
            if (callback == null)
                return;
 
            // Don't report anything if the directory doesn't exist
            if (!virtualDir.DirectoryExists())
                return;
 
            string message = SR.GetString(SR.Directory_progress, virtualDir.VirtualPathString);
            callback.ReportProgress(message);
        }
 
 
        //
        // Public methods
        //
 
 
        /// <devdoc>
        ///     Compiles a file given its virtual path, using the appropriate BuildProvider (based
        ///     on the file's extension).  The compiled type is returned.
        ///     This methods performs both memory and disk caching of the compiled Type.
        /// </devdoc>
        public static Type GetCompiledType(string virtualPath) {
            if (virtualPath == null) {
                throw new ArgumentNullException("virtualPath");
            }
 
            return GetCompiledType(VirtualPath.Create(virtualPath));
        }
 
        // This method is called by BuildManagerHost thru CBM
        internal static Type GetCompiledType(VirtualPath virtualPath, ClientBuildManagerCallback callback) {
            // Remember the original setting
            bool skipTopLevelExceptions = SkipTopLevelCompilationExceptions;
            bool throwOnFirstParseError = ThrowOnFirstParseError;
 
            try {
                // Don't skip top level compilation exceptions even called by CBM.
                SkipTopLevelCompilationExceptions = false;
 
                // Don't stop on the first parse error, process as many errors as possible.
                ThrowOnFirstParseError = false;
 
                _theBuildManager._cbmCallback = callback;
                return GetCompiledType(virtualPath);
            }
            finally {
                _theBuildManager._cbmCallback = null;
 
                // Revert to original setting
                SkipTopLevelCompilationExceptions = skipTopLevelExceptions;
 
                ThrowOnFirstParseError = throwOnFirstParseError;
            }
        }
 
        internal static Type GetCompiledType(VirtualPath virtualPath) {
            ITypedWebObjectFactory factory = GetVirtualPathObjectFactory(virtualPath,
                null /*context*/, false /*allowCrossApp*/);
 
            BuildResultCompiledType resultType = factory as BuildResultCompiledType;
            if (resultType == null) return null;
 
            return resultType.ResultType;
        }
 
        /// Process a file based on its virtual path, and instantiate the result.  This API works for both
        /// compiled and no compile pages.  requiredBaseType specifies a type from which the resulting
        /// object must derive.  If it doesn't, the API fails without instantiating the object.
        public static object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType) {
            VirtualPath virtualPathObject = VirtualPath.CreateNonRelative(virtualPath);
            return CreateInstanceFromVirtualPath(virtualPathObject, requiredBaseType,
                null /*context*/, false /*allowCrossApp*/);
        }
 
        /// <devdoc>
        ///     Process a file given its virtual path, using the appropriate BuildProvider (based
        ///     on the file's extension).  The result is then instantiated and returned.
        /// </devdoc>
        internal static object CreateInstanceFromVirtualPath(VirtualPath virtualPath,
            Type requiredBaseType, HttpContext context, bool allowCrossApp) {
 
            ITypedWebObjectFactory objectFactory = GetVirtualPathObjectFactory(virtualPath, context, allowCrossApp);
            if (objectFactory == null) return null;
 
            // Make sure it has the required base type (VSWhidbey 516771)
            Util.CheckAssignableType(requiredBaseType, objectFactory.InstantiatedType);
 
            // impersonate client while executing page ctor (see ASURT 89712)
            // (compilation is done while not impersonating client)
 
            Object instance;
            using (new ClientImpersonationContext(context)) {
                instance = objectFactory.CreateInstance();
            }
 
            return instance;
        }
 
        public static IWebObjectFactory GetObjectFactory(string virtualPath, bool throwIfNotFound) {
            ITypedWebObjectFactory factory = GetVirtualPathObjectFactory(VirtualPath.Create(virtualPath),
                null /*context*/, false /*allowCrossApp*/, throwIfNotFound);
            return factory;
        }
 
        private static ITypedWebObjectFactory GetVirtualPathObjectFactory(VirtualPath virtualPath,
            HttpContext context, bool allowCrossApp) {
            return GetVirtualPathObjectFactory(virtualPath, context, allowCrossApp, true /*throwIfNotFound*/);
        }
 
        /// <devdoc>
        ///     Process a file given its virtual path, using the appropriate BuildProvider (based
        ///     on the file's extension).  The ITypedWebObjectFactory is returned.
        ///     This methods performs both memory and disk caching of the compiled Type.
        /// </devdoc>
        private static ITypedWebObjectFactory GetVirtualPathObjectFactory(VirtualPath virtualPath,
            HttpContext context, bool allowCrossApp, bool throwIfNotFound) {
 
            if (virtualPath == null)
                throw new ArgumentNullException("virtualPath");
 
            // Throw here immediately if top level exception exists.
            // This is because EnsureTopLevelFilesCompiled (where the exception is thrown)
            // might not be called.
            if (_theBuildManager._topLevelFileCompilationException != null) {
                _theBuildManager.ReportTopLevelCompilationException();
            }
 
            ITypedWebObjectFactory objectFactory;
            BuildResult buildResult;
 
            // We need to assert here since there may be user code on the stack,
            // and code may demand UnmanagedCode permission.  But if we're in full trust,
            // or noAssert is true, skip the assert for perf reasons (VSWhidbey 146871, 500699)
            //
            // In regard to previous comment, in v2/3.5 we only needed to assert when we were
            // running in partial trust and user code was on the stack.  In v4, we need to assert
            // whenever we are running in partial turst, because the AppDomain is homogenous.
            if (HttpRuntime.IsFullTrust) {
                buildResult = GetVPathBuildResultWithNoAssert(
                    context, virtualPath, false /*noBuild*/, allowCrossApp, false /*allowBuildInPrecompile*/, throwIfNotFound);
            }
            else {
                buildResult = GetVPathBuildResultWithAssert(
                    context, virtualPath, false /*noBuild*/, allowCrossApp, false /*allowBuildInPrecompile*/, throwIfNotFound);
            }
 
            // DevDiv 67952
            // The returned build result may not always be castable to ITypedWebObjectFactory.
            objectFactory = buildResult as ITypedWebObjectFactory;
 
            return objectFactory;
        }
 
        /// <devdoc>
        ///     Compiles a file given its virtual path, using the appropriate BuildProvider (based
        ///     on the file's extension).  The compiled assembly is returned.
        ///     This methods performs both memory and disk caching of the compiled assembly.
        /// </devdoc>
        public static Assembly GetCompiledAssembly(string virtualPath) {
 
            BuildResult result = GetVPathBuildResult(VirtualPath.Create(virtualPath));
            if (result == null) return null;
 
            BuildResultCompiledAssemblyBase resultAssembly = result as BuildResultCompiledAssemblyBase;
            if (resultAssembly == null) return null;
 
            return resultAssembly.ResultAssembly;
        }
 
 
        /// <devdoc>
        ///     Compiles a file given its virtual path, using the appropriate BuildProvider (based
        ///     on the file's extension).  If the BuildProvider chose to persist a custom
        ///     string, the string is returned.
        ///     This methods performs both memory and disk caching.
        /// </devdoc>
        public static string GetCompiledCustomString(string virtualPath) {
 
            BuildResult result = GetVPathBuildResult(VirtualPath.Create(virtualPath));
            if (result == null) return null;
 
            BuildResultCustomString resultCustomString = result as BuildResultCustomString;
            if (resultCustomString == null) return null;
 
            return resultCustomString.CustomString;
        }
 
        /// <devdoc>
        ///     Returns the BuildDependencySet for the passed in virtualPath, assuming
        ///     that information is cached.  Otherwise, return null.
        /// </devdoc>
        public static BuildDependencySet GetCachedBuildDependencySet(
            HttpContext context, string virtualPath) {
            return GetCachedBuildDependencySet(context, virtualPath, ensureIsUpToDate: true);
        }
 
        public static BuildDependencySet GetCachedBuildDependencySet(
            HttpContext context, string virtualPath, bool ensureIsUpToDate) {
 
            BuildResult result = GetVPathBuildResult(context, VirtualPath.Create(virtualPath),
                true /*noBuild*/, false /*allowCrossApp*/, allowBuildInPrecompile: false, ensureIsUpToDate: ensureIsUpToDate);
 
            // If it's not cached, return null
            if (result == null)
                return null;
 
            // We found it in the cache.  Wrap it with a BuildDependencySet object.
            return new BuildDependencySet(result);
        }
 
        /// <summary>
        /// Returns the target framework moniker for the current web site. For framework versions less than
        /// 4.0, it will either be 3.0 or 3.5. 2.0 and 3.0 have similar web.config, so we use 3.0 to allow 3.0
        /// web sites to reference 3.0 assemblies.
        /// </summary>
        public static FrameworkName TargetFramework {
            get {
                return MultiTargetingUtil.TargetFrameworkName;
            }
        }
 
        private Assembly ResolveAssembly(object sender, ResolveEventArgs e) {
 
            if (_assemblyResolveMapping == null)
                return null;
 
            string name = e.Name;
            Assembly assembly = (Assembly)_assemblyResolveMapping[name];
 
            // Return the assembly if we have it in our mapping (VSWhidbey 276776)
            if (assembly != null) {
                return assembly;
            }
 
            // Get the normalized assembly name from random name (VSWhidbey 380793)
            String normalizedName = GetNormalizedCodeAssemblyName(name);
            if (normalizedName != null) {
                return (Assembly)_assemblyResolveMapping[normalizedName];
            }
 
            return null;
        }
 
        internal static string GetNormalizedCodeAssemblyName(string assemblyName) {
            // Return the main code assembly.
            if (assemblyName.StartsWith(CodeDirectoryAssemblyName, StringComparison.Ordinal)) {
                return CodeDirectoryAssemblyName;
            }
 
            // Check the sub code directories.
            CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
            foreach (CodeSubDirectory directory in codeSubDirectories) {
                if (assemblyName.StartsWith(SubCodeDirectoryAssemblyNamePrefix + directory.AssemblyName + ".", StringComparison.Ordinal)) {
                    return directory.AssemblyName;
                }
            }
 
            return null;
        }
 
        internal static string GetNormalizedTypeName(Type t) {
            string assemblyFullName = t.Assembly.FullName;
            string normalizedCodeAssemblyName = GetNormalizedCodeAssemblyName(assemblyFullName);
            if (normalizedCodeAssemblyName == null) {
                return t.AssemblyQualifiedName;
            }
 
            string normalizedTypeName = t.FullName + ", " + normalizedCodeAssemblyName;
            return normalizedTypeName;
        }
 
        /// <summary>
        /// Temporary subdirectory under the codegen folder for buildproviders to generate embedded resource files.
        /// </summary>
        internal static string CodegenResourceDir {
            get {
                string resxDir = _theBuildManager._codegenResourceDir;
                if (resxDir == null) {
                    resxDir = Path.Combine(HttpRuntime.CodegenDirInternal, CodegenResourceDirectoryName);
                    _theBuildManager._codegenResourceDir = resxDir;
                }
                return resxDir;
            }
        }
 
        // The Use Cache lives under the codegen folder
        private static string _userCachePath;
        private static string UserCachePath {
            get {
                if (_userCachePath == null) {
                    // Build the full path to the User Cache folder
                    string userCachePath = Path.Combine(HttpRuntime.CodegenDirInternal, "UserCache");
 
                    // Create it if it doesn't exist
                    if (!Directory.Exists(userCachePath)) {
                        Directory.CreateDirectory(userCachePath);
                    }
 
                    _userCachePath = userCachePath;
                }
 
                return _userCachePath;
            }
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
            Justification = "Too late in the loc process to add an exception message.")]
        private static string GetUserCacheFilePath(string fileName) {
            string path = Path.Combine(UserCachePath, fileName);
 
            // Make sure that the full path's directory is exactly the User Cache folder. This prevents creating files in any other folders
            if (Path.GetDirectoryName(path) != UserCachePath) {
                throw new ArgumentException();
            }
 
            return path;
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity",
            Justification = "This is the correct Assert for the situation.")]
        public static Stream CreateCachedFile(string fileName) {
            new FileIOPermission(FileIOPermissionAccess.AllAccess, HttpRuntime.CodegenDirInternal).Assert();
 
            // Get the path to the file in the User Cache folder
            string path = GetUserCacheFilePath(fileName);
 
            return File.Create(path);
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity",
            Justification = "This is the correct Assert for the situation.")]
        public static Stream ReadCachedFile(string fileName) {
            new FileIOPermission(FileIOPermissionAccess.AllAccess, HttpRuntime.CodegenDirInternal).Assert();
 
            // Get the path to the file in the User Cache folder
            string path = GetUserCacheFilePath(fileName);
 
            // If the file doesn't exist, just return null, to convey a cache miss
            if (!File.Exists(path))
                return null;
 
            return File.OpenRead(path);
        }
    }
 
    internal enum CompilationStage {
        PreTopLevelFiles = 0,       // Before EnsureTopLevelFilesCompiled() is called
        TopLevelFiles = 1,          // In EnsureTopLevelFilesCompiled() but before building global.asax
        GlobalAsax = 2,             // While building global.asax
        BrowserCapabilities = 3,    // While building browserCap
        AfterTopLevelFiles = 4      // After EnsureTopLevelFilesCompiled() is called
    }
 
    internal enum PreStartInitStage {
        BeforePreStartInit,
        DuringPreStartInit,
        AfterPreStartInit,
    }
 
    internal class AssemblyReferenceInfo {
        internal Assembly Assembly;
        internal int ReferenceIndex;
 
        internal AssemblyReferenceInfo(int referenceIndex) {
            ReferenceIndex = referenceIndex;
        }
    }
 
}