1 //------------------------------------------------------------------------------
2 // <copyright file="BuildManager.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 /************************************************************************************************************/
11 namespace System.Web.Compilation {
14 using System.CodeDom.Compiler;
15 using System.Collections;
16 using System.Collections.Generic;
17 using System.Diagnostics.CodeAnalysis;
18 using System.Globalization;
21 using System.Reflection;
22 using System.Runtime.Remoting.Messaging;
23 using System.Runtime.Versioning;
24 using System.Security;
25 using System.Security.Permissions;
27 using System.Threading;
28 using System.Web.Configuration;
29 using System.Web.Hosting;
30 using System.Web.Profile;
32 using System.Web.Util;
38 /// IProvider compilation related services
41 public sealed class BuildManager {
43 /// Contants relating to generated assembly names
45 // All generated assemblies start with this prefix
46 internal const string AssemblyNamePrefix = "App_";
48 // Web assemblies are the assemblies generated from web files (aspx, ascx, ...)
49 internal const string WebAssemblyNamePrefix = AssemblyNamePrefix + "Web_";
51 internal const string AppThemeAssemblyNamePrefix = AssemblyNamePrefix + "Theme_";
52 internal const string GlobalThemeAssemblyNamePrefix = AssemblyNamePrefix + "GlobalTheme_";
53 internal const string AppBrowserCapAssemblyNamePrefix = AssemblyNamePrefix + "Browsers";
55 private const string CodeDirectoryAssemblyName = AssemblyNamePrefix + "Code";
56 internal const string SubCodeDirectoryAssemblyNamePrefix = AssemblyNamePrefix + "SubCode_";
57 private const string ResourcesDirectoryAssemblyName = AssemblyNamePrefix + "GlobalResources";
58 private const string LocalResourcesDirectoryAssemblyName = AssemblyNamePrefix + "LocalResources";
59 private const string WebRefDirectoryAssemblyName = AssemblyNamePrefix + "WebReferences";
60 internal const string GlobalAsaxAssemblyName = AssemblyNamePrefix + HttpApplicationFactory.applicationFileName;
62 private const string LicensesAssemblyName = AssemblyNamePrefix + "Licenses";
64 internal const string UpdatableInheritReplacementToken = "__ASPNET_INHERITS";
66 // Name of the temporary subdirectory under the codegen folder for buildproviders to generate embedded resource files.
67 private const string CodegenResourceDirectoryName = "ResX";
69 private static System.Security.Cryptography.RNGCryptoServiceProvider _rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
70 private static bool _theBuildManagerInitialized;
71 private static Exception _initializeException;
72 private static BuildManager _theBuildManager = new BuildManager(); // single instance of the class
73 private static long s_topLevelHash;
74 private readonly HashCodeCombiner _preAppStartHashCodeCombiner = new HashCodeCombiner();
75 internal static BuildManager TheBuildManager { get { return _theBuildManager; } }
77 // Precompilation related fields
78 private const string precompMarkerFileName = "PrecompiledApp.config";
79 private string _precompTargetPhysicalDir;
80 private PrecompilationFlags _precompilationFlags;
81 private bool _isPrecompiledApp;
82 private bool _isPrecompiledAppComputed;
83 private bool _isUpdatablePrecompiledApp;
84 private bool _precompilingApp; // we're in the process of precompiling an app
86 private string _strongNameKeyFile;
87 private string _strongNameKeyContainer;
89 private string _codegenResourceDir;
91 private bool _optimizeCompilations;
92 internal static bool OptimizeCompilations {
93 get { return _theBuildManager._optimizeCompilations; }
96 // filepath to the generated web.hash file, This file should only be re-created when
97 // the appdomain is restarted and the top-level generated assemblies need to be recompiled.
98 private string _webHashFilePath;
99 internal static String WebHashFilePath {
100 get { return _theBuildManager._webHashFilePath; }
103 private BuildResultCache[] _caches;
104 private StandardDiskBuildResultCache _codeGenCache;
105 private MemoryBuildResultCache _memoryCache;
107 private bool _topLevelFilesCompiledStarted;
108 private bool _topLevelFilesCompiledCompleted;
109 private Exception _topLevelFileCompilationException;
111 private BuildResultCompiledGlobalAsaxType _globalAsaxBuildResult;
112 private Type _profileType;
114 // Special top level directories that are treated differently from regular web directories
115 // during precompilation (e.g. App_Code)
116 private StringSet _excludedTopLevelDirectories;
118 // Directories that are not requestable
119 private StringSet _forbiddenTopLevelDirectories;
121 private StringSet _excludedCodeSubdirectories;
123 private List<VirtualPath> _excludedCompilationPaths;
125 private CompilationStage _compilationStage = CompilationStage.PreTopLevelFiles;
126 internal static CompilationStage CompilationStage { get { return _theBuildManager._compilationStage; } }
128 private VirtualPath _scriptVirtualDir;
129 private VirtualPath _globalAsaxVirtualPath;
130 internal static VirtualPath ScriptVirtualDir { get { return _theBuildManager._scriptVirtualDir; } }
131 internal static VirtualPath GlobalAsaxVirtualPath { get { return _theBuildManager._globalAsaxVirtualPath; } }
133 private BuildManager() { }
135 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
136 internal static bool InitializeBuildManager() {
138 // If we already tried and got an exception, just rethrow it
139 if (_initializeException != null) {
140 // We need to wrap it in a new exception, otherwise we lose the original stack.
141 throw new HttpException(_initializeException.Message, _initializeException);
144 if (!_theBuildManagerInitialized) {
146 // If Fusion was not yet initialized, skip the init.
147 // This can happen when there is a very early failure (e.g. see VSWhidbey 137366)
148 Debug.Trace("BuildManager", "InitializeBuildManager " + HttpRuntime.FusionInited);
149 if (!HttpRuntime.FusionInited)
152 // Likewise, if the trust level has not yet been determined, skip the init (VSWhidbey 422311)
153 if (HttpRuntime.TrustLevel == null)
156 _theBuildManagerInitialized = true;
158 _theBuildManager.Initialize();
160 catch (Exception e) {
161 _theBuildManagerInitialized = false;
162 _initializeException = e;
170 private ClientBuildManagerCallback _cbmCallback;
171 internal static ClientBuildManagerCallback CBMCallback { get { return _theBuildManager._cbmCallback; } }
173 private static bool _parseErrorReported;
174 internal static void ReportParseError(ParserError parseError) {
175 // If there is a CBM callback, inform it of the error
176 if (BuildManager.CBMCallback != null) {
177 _parseErrorReported = true;
178 BuildManager.CBMCallback.ReportParseError(parseError);
182 private void ReportTopLevelCompilationException() {
183 Debug.Assert(_topLevelFileCompilationException != null);
185 // Try to report the cached error to the CBM callback
186 ReportErrorsFromException(_topLevelFileCompilationException);
188 // We need to wrap it in a new exception, otherwise we lose the original stack.
189 throw new HttpException(_topLevelFileCompilationException.Message,
190 _topLevelFileCompilationException);
193 // Given an exception, attempt to turn it into calls to the CBM callback
194 private void ReportErrorsFromException(Exception e) {
195 // If there is no CBM callback, nothing to do
196 if (BuildManager.CBMCallback == null)
199 // Call the CBM callback as appropriate, based on the type of exception
201 if (e is HttpCompileException) {
202 CompilerResults results = ((HttpCompileException)e).Results;
203 foreach (CompilerError error in results.Errors) {
204 BuildManager.CBMCallback.ReportCompilerError(error);
207 else if (e is HttpParseException) {
208 foreach (ParserError parseError in ((HttpParseException)e).ParserErrors) {
209 ReportParseError(parseError);
214 // The assemblies produced from the code directories and global.asax, which
215 // every other compilation will linked with.
216 private List<Assembly> _topLevelReferencedAssemblies = new List<Assembly>() {
217 typeof(HttpRuntime).Assembly,
218 typeof(System.ComponentModel.Component).Assembly,
221 private List<Assembly> TopLevelReferencedAssemblies { get { return _topLevelReferencedAssemblies; } }
223 private Dictionary<String, AssemblyReferenceInfo> _topLevelAssembliesIndexTable;
224 private IDictionary<String, AssemblyReferenceInfo> TopLevelAssembliesIndexTable { get { return _topLevelAssembliesIndexTable; } }
226 private Dictionary<String, String> _generatedFileTable;
227 internal static Dictionary<String, String> GenerateFileTable {
229 if (_theBuildManager._generatedFileTable == null) {
230 _theBuildManager._generatedFileTable = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
233 return _theBuildManager._generatedFileTable;
237 private ArrayList _codeAssemblies;
238 public static IList CodeAssemblies {
240 _theBuildManager.EnsureTopLevelFilesCompiled();
241 return _theBuildManager._codeAssemblies;
245 private IDictionary _assemblyResolveMapping;
247 private Assembly _appResourcesAssembly;
248 internal static Assembly AppResourcesAssembly { get { return _theBuildManager._appResourcesAssembly; } }
250 // Indicates whether the parsers should coninue processing for more errors.
251 // This is used in both CBM precompile-web, precompile-page and aspnet_compiler tool.
252 private bool _throwOnFirstParseError = true;
253 internal static bool ThrowOnFirstParseError {
254 get { return _theBuildManager._throwOnFirstParseError; }
255 set { _theBuildManager._throwOnFirstParseError = value; }
258 // Marks whether we are in the middle of performing precompilation, which affects how
259 // we deal with error handling and batching
260 private bool _performingPrecompilation = false;
261 internal static bool PerformingPrecompilation {
262 get { return _theBuildManager._performingPrecompilation; }
263 set { _theBuildManager._performingPrecompilation = value; }
266 private bool _skipTopLevelCompilationExceptions;
267 internal static bool SkipTopLevelCompilationExceptions {
268 get { return _theBuildManager._skipTopLevelCompilationExceptions; }
269 set { _theBuildManager._skipTopLevelCompilationExceptions = value; }
272 private static HashSet<Assembly> s_dynamicallyAddedReferencedAssembly = new HashSet<Assembly>();
274 public static void AddReferencedAssembly(Assembly assembly) {
275 if (assembly == null) {
276 throw new ArgumentNullException("assembly");
278 ThrowIfPreAppStartNotRunning();
280 s_dynamicallyAddedReferencedAssembly.Add(assembly);
284 * Return the list of assemblies that a compilation needs to reference for a given
285 * config minus the top-level assemblies indexed later than removeIndex
287 internal static ICollection GetReferencedAssemblies(CompilationSection compConfig, int removeIndex) {
288 AssemblySet referencedAssemblies = new AssemblySet();
290 // Add all the config assemblies to the list
291 foreach (AssemblyInfo a in compConfig.Assemblies) {
292 Assembly[] assemblies = a.AssemblyInternal;
293 if (assemblies == null) {
295 assemblies = a.AssemblyInternal;
296 if (assemblies == null)
298 assemblies = a.AssemblyInternal = compConfig.LoadAssembly(a);
302 for (int i = 0; i < assemblies.Length; i++) {
303 if (assemblies[i] != null) {
304 referencedAssemblies.Add(assemblies[i]);
309 // Clone the top level referenced assemblies (code + global.asax + etc...), up to the removeIndex
310 for (int i = 0; i < removeIndex; i++) {
311 referencedAssemblies.Add(TheBuildManager.TopLevelReferencedAssemblies[i]);
316 foreach (Assembly assembly in s_dynamicallyAddedReferencedAssembly) {
317 referencedAssemblies.Add(assembly);
320 return referencedAssemblies;
323 internal static ICollection GetReferencedAssemblies(CompilationSection compConfig) {
325 // Start by cloning the top level referenced assemblies (code + global.asax + etc...)
326 AssemblySet referencedAssemblies = AssemblySet.Create(
327 TheBuildManager.TopLevelReferencedAssemblies);
329 // Add all the config assemblies to the list
330 foreach (AssemblyInfo a in compConfig.Assemblies) {
331 Assembly[] assemblies = a.AssemblyInternal;
332 if (assemblies == null) {
334 assemblies = a.AssemblyInternal;
335 if (assemblies == null)
337 assemblies = a.AssemblyInternal = compConfig.LoadAssembly(a);
341 for (int i = 0; i < assemblies.Length; i++) {
342 if (assemblies[i] != null) {
343 referencedAssemblies.Add(assemblies[i]);
350 foreach (Assembly assembly in s_dynamicallyAddedReferencedAssembly) {
351 referencedAssemblies.Add(assembly);
354 return referencedAssemblies;
359 * Return the list of assemblies that all page compilations need to reference. This includes
360 * config assemblies (<assemblies> section), bin assemblies and assemblies built from the
361 * app App_Code and other top level folders.
365 /// Returns the assemblies referenced at the root application level of the current appF
367 public static ICollection GetReferencedAssemblies() {
368 CompilationSection compConfig = MTConfigUtil.GetCompilationAppConfig();
370 _theBuildManager.EnsureTopLevelFilesCompiled();
372 return GetReferencedAssemblies(compConfig);
376 /// Specifies a string representing a dependency that the BuildManager factors when determining if a clean build is required.
378 /// <param name="dependency">String representation of a dependency.</param>
379 public static void AddCompilationDependency(string dependency) {
380 if (String.IsNullOrEmpty(dependency)) {
381 throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty), "dependency");
383 BuildManager.ThrowIfPreAppStartNotRunning();
384 _theBuildManager._preAppStartHashCodeCombiner.AddObject(dependency);
388 * Perform initialization work that should only be done once (per app domain).
390 private void Initialize() {
392 Debug.Assert(_caches == null);
394 // Register an AssemblyResolve event
395 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.ResolveAssembly);
397 _globalAsaxVirtualPath = HttpRuntime.AppDomainAppVirtualPathObject.SimpleCombine(
398 HttpApplicationFactory.applicationFileName);
400 _webHashFilePath = Path.Combine(HttpRuntime.CodegenDirInternal, "hash\\hash.web");
402 // Indicate whether we should ignore the top level compilation exceptions.
403 // In CBM case, we want to continue processing the page and return partial info even
404 // if the code files fail to compile.
405 _skipTopLevelCompilationExceptions = BuildManagerHost.InClientBuildManager;
407 // Deal with precompilation if we're in that mode
408 SetPrecompilationInfo(HostingEnvironment.HostingParameters);
410 MultiTargetingUtil.EnsureFrameworkNamesInitialized();
412 // The init code depends on whether we're precompiling or running an app
413 if (_precompTargetPhysicalDir != null) {
415 // If the app is already precompiled, fail
416 FailIfPrecompiledApp();
418 PrecompilationModeInitialize();
421 // Check if this application has been precompiled by aspnet_compiler.exe
422 if (IsPrecompiledApp) {
423 PrecompiledAppRuntimeModeInitialize();
426 RegularAppRuntimeModeInitialize();
430 _scriptVirtualDir = Util.GetScriptLocation();
432 // Top level directories that have a special semantic
433 _excludedTopLevelDirectories = new CaseInsensitiveStringSet();
434 _excludedTopLevelDirectories.Add(HttpRuntime.BinDirectoryName);
435 _excludedTopLevelDirectories.Add(HttpRuntime.CodeDirectoryName);
436 _excludedTopLevelDirectories.Add(HttpRuntime.ResourcesDirectoryName);
437 _excludedTopLevelDirectories.Add(HttpRuntime.LocalResourcesDirectoryName);
438 _excludedTopLevelDirectories.Add(HttpRuntime.WebRefDirectoryName);
439 _excludedTopLevelDirectories.Add(HttpRuntime.ThemesDirectoryName);
441 // Top level directories that are not requestable
442 // It's the same as _excludedTopLevelDirectories, except that we allow
443 // the bin directory to avoid a v1 breaking change (VSWhidbey 465018)
444 _forbiddenTopLevelDirectories = new CaseInsensitiveStringSet();
445 _forbiddenTopLevelDirectories.Add(HttpRuntime.CodeDirectoryName);
446 _forbiddenTopLevelDirectories.Add(HttpRuntime.ResourcesDirectoryName);
447 _forbiddenTopLevelDirectories.Add(HttpRuntime.LocalResourcesDirectoryName);
448 _forbiddenTopLevelDirectories.Add(HttpRuntime.WebRefDirectoryName);
449 _forbiddenTopLevelDirectories.Add(HttpRuntime.ThemesDirectoryName);
451 LoadLicensesAssemblyIfExists();
455 * Init code used when we are running a non-precompiled app
457 private void RegularAppRuntimeModeInitialize() {
460 // Initialize the caches
463 // Always try the memory cache first
464 _memoryCache = new MemoryBuildResultCache(HttpRuntime.CacheInternal);
466 // Use the standard disk cache for regular apps
467 _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
469 _caches = new BuildResultCache[] { _memoryCache, _codeGenCache };
473 * Init code used when we are running a precompiled app
475 private void PrecompiledAppRuntimeModeInitialize() {
478 // Initialize the caches
481 // Always try the memory cache first
482 _memoryCache = new MemoryBuildResultCache(HttpRuntime.CacheInternal);
484 // Used the precomp cache for precompiled apps
485 BuildResultCache preCompCache = new PrecompiledSiteDiskBuildResultCache(
486 HttpRuntime.BinDirectoryInternal);
488 // Also create a regular disk cache so that we can compile and cache additional things.
489 // This is useful even in non-updatable precomp, to cache DefaultWsdlHelpGenerator.aspx.
491 _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
493 _caches = new BuildResultCache[] { _memoryCache, preCompCache, _codeGenCache };
497 * Init code used when we are precompiling an app
499 private void PrecompilationModeInitialize() {
501 // We are precompiling an app
503 // Always try the memory cache first
504 _memoryCache = new MemoryBuildResultCache(HttpRuntime.CacheInternal);
506 // Create a regular disk cache, to take advantage of the fact that the app
507 // may already have been compiled (and to cause it to be if it wasn't)
508 _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
510 // Create a special disk cache in the target's bin directory. Use a slightly different
511 // implementation for the updatable case.
512 string targetBinDir = Path.Combine(_precompTargetPhysicalDir, HttpRuntime.BinDirectoryName);
513 BuildResultCache preCompilationCache;
514 if (PrecompilingForUpdatableDeployment) {
515 preCompilationCache = new UpdatablePrecompilerDiskBuildResultCache(targetBinDir);
518 preCompilationCache = new PrecompilerDiskBuildResultCache(targetBinDir);
521 _caches = new BuildResultCache[] { _memoryCache, preCompilationCache, _codeGenCache };
524 // Load the licenses assembly from the bin dir if it exists (DevDiv 42149)
525 private void LoadLicensesAssemblyIfExists() {
526 string licAssemblyPath = Path.Combine(HttpRuntime.BinDirectoryInternal, LicensesAssemblyName + ".dll");
527 if (File.Exists(licAssemblyPath)) {
528 Assembly.Load(LicensesAssemblyName);
532 // DevDiv #520869: Signal the PortableCompilationOutputSnapshotType to
533 // restore a snapshot of portable compilation output
534 private static void RestorePortableCompilationOutputSnapshot() {
535 if (BuildManagerHost.InClientBuildManager ||
536 !AppSettings.PortableCompilationOutput ||
537 String.IsNullOrEmpty(AppSettings.PortableCompilationOutputSnapshotType)) {
541 // If a PortableCompilationOutputSnapshotsType has been configured but failed to be loaded, let it throw
542 Type t = Type.GetType(AppSettings.PortableCompilationOutputSnapshotType, true);
543 object[] args = new Object[] { AppSettings.PortableCompilationOutputSnapshotTypeOptions };
544 t.InvokeMember("RestoreSnapshot", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, t, args, CultureInfo.InvariantCulture);
547 private long CheckTopLevelFilesUpToDate(long cachedHash) {
548 bool gotLock = false;
550 // Grab the compilation mutex, since this method accesses the codegen files
551 CompilationLock.GetLock(ref gotLock);
553 return CheckTopLevelFilesUpToDateInternal(cachedHash);
556 // Always release the mutex if we had taken it
558 CompilationLock.ReleaseLock();
564 * Check if the top level files are up to date, and cleanup the codegendir
567 private long CheckTopLevelFilesUpToDateInternal(long cachedHash) {
568 Debug.Trace("BuildManager", "specialFilesCombinedHash=" + cachedHash);
569 var specialFilesHashCodeCombiner = new HashCodeCombiner();
571 // Delete all the non essential files left over in the codegen dir, unless
572 // specialFilesCombinedHash is 0, in which case we delete *everything* further down
573 if (cachedHash != 0) {
574 _codeGenCache.RemoveOldTempFiles();
577 // Use a HashCodeCombiner object to handle the time stamps of all the 'special'
578 // files and directories that all compilations depend on:
579 // - System.Web.dll (in case there is a newer version of ASP.NET)
581 // - ~\Resource directory
582 // - ~\WebReferences directory
583 // - ~\Code directory
586 // Add a check for the app's physical path, in case it changes (ASURT 12975)
587 specialFilesHashCodeCombiner.AddObject(HttpRuntime.AppDomainAppPathInternal);
589 // Process System.Web.dll
590 string aspBinaryFileName = typeof(HttpRuntime).Module.FullyQualifiedName;
591 if (!AppSettings.PortableCompilationOutput) {
592 specialFilesHashCodeCombiner.AddFile(aspBinaryFileName);
595 specialFilesHashCodeCombiner.AddExistingFileVersion(aspBinaryFileName);
598 // Process machine.config
599 string machineConfigFileName = HttpConfigurationSystem.MachineConfigurationFilePath;
600 if (!AppSettings.PortableCompilationOutput) {
601 specialFilesHashCodeCombiner.AddFile(machineConfigFileName);
604 specialFilesHashCodeCombiner.AddFileContentHash(machineConfigFileName);
607 // Process root web.config
608 string rootWebConfigFileName = HttpConfigurationSystem.RootWebConfigurationFilePath;
609 if (!AppSettings.PortableCompilationOutput) {
610 specialFilesHashCodeCombiner.AddFile(rootWebConfigFileName);
613 specialFilesHashCodeCombiner.AddFileContentHash(rootWebConfigFileName);
616 RuntimeConfig appConfig = RuntimeConfig.GetAppConfig();
617 CompilationSection compConfig = appConfig.Compilation;
619 // Ignore the OptimizeCompilations flag in ClientBuildManager mode
620 if (!BuildManagerHost.InClientBuildManager) {
621 _optimizeCompilations = compConfig.OptimizeCompilations;
624 // In optimized compilation mode, we don't clean out all the compilations just because a top level
625 // file changes. Instead, we let already compiled pages run against the newer top level binaries.
626 // In can be incorrect in some cases (e.g. return type of method changes from int to short), which is
627 // why the optimization is optional
628 if (!OptimizeCompilations) {
629 // Add a dependency of the bin, resources, webresources and code directories
630 string binPhysicalDir = HttpRuntime.BinDirectoryInternal;
631 specialFilesHashCodeCombiner.AddDirectory(binPhysicalDir);
633 // Note that we call AddResourcesDirectory instead of AddDirectory, since we only want
634 // culture neutral files to be taken into account (VSWhidbey 359029)
635 specialFilesHashCodeCombiner.AddResourcesDirectory(HttpRuntime.ResourcesDirectoryVirtualPath.MapPathInternal());
637 specialFilesHashCodeCombiner.AddDirectory(HttpRuntime.WebRefDirectoryVirtualPath.MapPathInternal());
639 specialFilesHashCodeCombiner.AddDirectory(HttpRuntime.CodeDirectoryVirtualPath.MapPathInternal());
641 // Add a dependency on the global asax file.
642 specialFilesHashCodeCombiner.AddFile(GlobalAsaxVirtualPath.MapPathInternal());
645 // Add a dependency on the hash of the app level <compilation> section, since it
646 // affects all compilations, including the code directory. It it changes,
647 // we may as well, start all over.
648 specialFilesHashCodeCombiner.AddObject(compConfig.RecompilationHash);
650 ProfileSection profileSection = appConfig.Profile;
651 specialFilesHashCodeCombiner.AddObject(profileSection.RecompilationHash);
653 // Add a dependency on file encoding (DevDiv 4560)
654 specialFilesHashCodeCombiner.AddObject(appConfig.Globalization.FileEncoding);
656 // Also add a dependency on the <trust> config section
657 TrustSection casConfig = appConfig.Trust;
658 specialFilesHashCodeCombiner.AddObject(casConfig.Level);
659 specialFilesHashCodeCombiner.AddObject(casConfig.OriginUrl);
661 // Add a dependency on whether profile is enabled
662 specialFilesHashCodeCombiner.AddObject(ProfileManager.Enabled);
664 // Add a dependency to the force debug flag.
665 specialFilesHashCodeCombiner.AddObject(PrecompilingWithDebugInfo);
667 CheckCodeGenFiles(specialFilesHashCodeCombiner.CombinedHash, cachedHash);
668 return specialFilesHashCodeCombiner.CombinedHash;
671 private void AfterPreAppStartExecute(Tuple<long, long> currentHash, Tuple<long, long> cachedTopLevelFilesHash) {
672 bool gotLock = false;
674 // Grab the compilation mutex, since this method accesses the codegen files
675 CompilationLock.GetLock(ref gotLock);
677 // After pre app start methods have executed, the second hash value should match the current value in the hash code combiner.
678 CheckCodeGenFiles(currentHash.Item2, cachedTopLevelFilesHash.Item2);
680 if (!cachedTopLevelFilesHash.Equals(currentHash)) {
681 // Hash has changed. Persist it to disk
682 _codeGenCache.SavePreservedSpecialFilesCombinedHash(currentHash);
685 // VSWhidbey 537929 : Setup a filechange monitor for the web.hash file. If this file is modified,
686 // we will need to shutdown the appdomain so we don't use the obsolete assemblies. The new appdomain
687 // will use the up-to-date assemblies.
688 HttpRuntime.FileChangesMonitor.StartMonitoringFile(_webHashFilePath,
689 new FileChangeEventHandler(this.OnWebHashFileChange));
690 Debug.Assert(File.Exists(_webHashFilePath), _webHashFilePath);
693 // Always release the mutex if we had taken it
695 CompilationLock.ReleaseLock();
700 private void CheckCodeGenFiles(long currentHash, long cachedTopLevelFilesHash) {
701 // Store the top level hash
702 s_topLevelHash = currentHash;
704 if (PrecompilingForCleanBuild || currentHash != cachedTopLevelFilesHash) {
705 if (PrecompilingForCleanBuild) {
706 Debug.Trace("BuildManager", "Precompiling for clean build.");
709 Debug.Trace("BuildManager", "EnsureFirstTimeInit: hash codes don't match. Old=" +
710 cachedTopLevelFilesHash + " New=" + currentHash);
713 _codeGenCache.RemoveAllCodegenFiles();
716 Debug.Trace("BuildManager", "BuildManager: the special files are up to date");
720 private void OnWebHashFileChange(Object sender, FileChangeEvent e) {
721 // Shutdown the app domain
722 Debug.Trace("BuildManager", _webHashFilePath + " changed - shutting down the app domain");
723 Debug.Trace("AppDomainFactory", "Shutting down appdomain because " + _webHashFilePath + " file changed");
724 string message = FileChangesMonitor.GenerateErrorMessage(e.Action, _webHashFilePath);
725 if (message == null) {
726 message = "Change in " + _webHashFilePath;
728 HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.BuildManagerChange, message);
732 * Check if an assembly name is reserved for a special purpose
734 internal static bool IsReservedAssemblyName(string assemblyName) {
736 if (String.Compare(assemblyName, CodeDirectoryAssemblyName,
737 StringComparison.OrdinalIgnoreCase) == 0 ||
738 String.Compare(assemblyName, ResourcesDirectoryAssemblyName,
739 StringComparison.OrdinalIgnoreCase) == 0 ||
740 String.Compare(assemblyName, WebRefDirectoryAssemblyName,
741 StringComparison.OrdinalIgnoreCase) == 0 ||
742 String.Compare(assemblyName, GlobalAsaxAssemblyName,
743 StringComparison.OrdinalIgnoreCase) == 0) {
751 internal static void ThrowIfPreAppStartNotRunning() {
752 if (PreStartInitStage != PreStartInitStage.DuringPreStartInit) {
753 throw new InvalidOperationException(SR.GetString(SR.Method_can_only_be_called_during_pre_start_init));
757 internal static PreStartInitStage PreStartInitStage { get; private set; }
759 internal static void ExecutePreAppStart() {
760 // Restore a snapshot of compilation output when the AppDomain just starts and
761 // before any web site code runs
762 BuildManager.RestorePortableCompilationOutputSnapshot();
764 string preStartInitListPath = Path.Combine(HttpRuntime.CodegenDirInternal, "preStartInitList.web");
765 Tuple<long, long> specialFilesCombinedHash = _theBuildManager._codeGenCache.GetPreservedSpecialFilesCombinedHash();
766 // Check top level files have changed
767 long topLevelFilesHash = _theBuildManager.CheckTopLevelFilesUpToDate(specialFilesCombinedHash.Item1);
769 bool hasUpdated = false;
770 ISet<string> preApplicationStartAssemblyNames = CallPreStartInitMethods(preStartInitListPath, out hasUpdated);
772 // Check if pre application start code hashes have changed since.
773 var currentHash = Tuple.Create(topLevelFilesHash, _theBuildManager._preAppStartHashCodeCombiner.CombinedHash);
774 _theBuildManager.AfterPreAppStartExecute(currentHash, specialFilesCombinedHash);
776 // Save the cache file only if needed
778 SavePreStartInitAssembliesToFile(preStartInitListPath, preApplicationStartAssemblyNames);
782 // this method requires global lock as the part of the fix of DevDiv bug 501777
783 private static ISet<string> CallPreStartInitMethods(string preStartInitListPath, out bool isRefAssemblyLoaded) {
784 Debug.Assert(PreStartInitStage == Compilation.PreStartInitStage.BeforePreStartInit);
785 isRefAssemblyLoaded = false;
786 using (new ApplicationImpersonationContext()) {
787 ICollection<MethodInfo> methods = null;
788 ICollection<Assembly> cachedPreStartAssemblies = LoadCachedPreAppStartAssemblies(preStartInitListPath);
789 if (cachedPreStartAssemblies != null) {
790 methods = GetPreStartInitMethodsFromAssemblyCollection(cachedPreStartAssemblies, buildingFromCache: true);
793 if (methods == null) {
794 // In case of ctlr-f5 scenario, two processes (VS and IisExpress) will start compilation simultaneously.
795 // GetPreStartInitMethodsFromReferencedAssemblies() will load all referenced assemblies
796 // If shallow copy is enabled, one process may fail due race condition in copying assemblies (DevDiv bug 501777)
797 // to fix it, put GetPreStartInitMethodsFromReferencedAssemblies() under the global lock
798 bool gotLock = false;
800 CompilationLock.GetLock(ref gotLock);
801 methods = GetPreStartInitMethodsFromReferencedAssemblies();
802 isRefAssemblyLoaded = true;
806 CompilationLock.ReleaseLock();
811 InvokePreStartInitMethods(methods);
813 Debug.Assert(PreStartInitStage == Compilation.PreStartInitStage.AfterPreStartInit);
815 return new HashSet<string>(methods.Select(m => m.DeclaringType.Assembly.FullName), StringComparer.OrdinalIgnoreCase);
819 internal static ISet<string> GetPreStartInitAssembliesFromFile(string path) {
820 if (FileUtil.FileExists(path)) {
822 return new HashSet<string>(File.ReadAllLines(path), StringComparer.OrdinalIgnoreCase);
825 // If there are issues delete the bad file. The list will be created from scratch.
835 // this method requires global lock as the part of the fix of DevDiv bug 501777
836 internal static void SavePreStartInitAssembliesToFile(string path, ISet<string> assemblies) {
837 Debug.Assert(assemblies != null);
838 Debug.Assert(!String.IsNullOrEmpty(path));
839 Debug.Assert(!assemblies.Any(String.IsNullOrEmpty));
840 bool gotLock = false;
842 //put write under the global lock to avoid race condition
843 CompilationLock.GetLock(ref gotLock);
844 File.WriteAllLines(path, assemblies);
854 CompilationLock.ReleaseLock();
860 /// Load the cached list of assemblies containing pre app start methods. Since this is a cache we never throw from it.
862 internal static ICollection<Assembly> LoadCachedPreAppStartAssemblies(string preStartInitListPath) {
864 // Force the enumerable to be saved to a list so that any issues with loading assemblies get caught here.
865 ISet<string> assemblyList = GetPreStartInitAssembliesFromFile(preStartInitListPath);
866 if (assemblyList == null) {
869 return assemblyList.Select(Assembly.Load)
878 private static void InvokePreStartInitMethods(ICollection<MethodInfo> methods) {
879 PreStartInitStage = Compilation.PreStartInitStage.DuringPreStartInit;
882 InvokePreStartInitMethodsCore(methods, HostingEnvironment.SetCultures);
883 PreStartInitStage = Compilation.PreStartInitStage.AfterPreStartInit;
886 PreStartInitStage = Compilation.PreStartInitStage.BeforePreStartInit;
891 internal static void InvokePreStartInitMethodsCore(ICollection<MethodInfo> methods, Func<IDisposable> setHostingEnvironmentCultures) {
893 var methodsToExecute = methods.Distinct();
894 // We want to execute PreApplicationStartmethods in a deterministic order. We'll use a sorted sequence of fully qualified type names and method names.
895 methodsToExecute = methodsToExecute.OrderBy(m => m.DeclaringType.AssemblyQualifiedName, StringComparer.OrdinalIgnoreCase)
896 .ThenBy(m => m.Name, StringComparer.OrdinalIgnoreCase);
897 foreach (var method in methodsToExecute) {
900 using (setHostingEnvironmentCultures()) {
901 method.Invoke(null, null);
904 catch (TargetInvocationException e) {
905 string message = (e.InnerException != null ? e.InnerException.Message : String.Empty);
906 throw new InvalidOperationException(
907 SR.GetString(SR.Pre_application_start_init_method_threw_exception,
909 method.DeclaringType.FullName,
916 private static ICollection<MethodInfo> GetPreStartInitMethodsFromReferencedAssemblies() {
917 CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(HttpRuntime.AppDomainAppVirtualPath);
918 var referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig).Cast<Assembly>();
919 return GetPreStartInitMethodsFromAssemblyCollection(referencedAssemblies, buildingFromCache: false);
923 /// Resolves pre application start methods from the assemblies specified.
925 /// <param name="assemblies">The list of assemblies to look for methods in.</param>
926 /// <param name="buildingFromCache">Flag that determines if we are rebuilding methods from cache.</param>
927 internal static ICollection<MethodInfo> GetPreStartInitMethodsFromAssemblyCollection(IEnumerable<Assembly> assemblies, bool buildingFromCache) {
928 List<MethodInfo> methods = new List<MethodInfo>();
929 foreach (Assembly assembly in assemblies) {
930 PreApplicationStartMethodAttribute[] attributes = null;
932 attributes = (PreApplicationStartMethodAttribute[])assembly.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), inherit: true);
935 // GetCustomAttributes invokes the constructors of the attributes, so it is possible that they might throw unexpected exceptions.
936 // (Dev10 bug 831981)
939 if (attributes == null || !attributes.Any()) {
940 // When rebuilding methods from cache every assembly specified must have one or more PreApplicationStartMethod attributes.
941 // 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.
942 if (buildingFromCache) {
947 foreach (PreApplicationStartMethodAttribute attribute in attributes) {
948 MethodInfo method = null;
949 // Ensure the Type on the attribute is in the same assembly as the attribute itself
950 if (attribute.Type != null && !String.IsNullOrEmpty(attribute.MethodName) && attribute.Type.Assembly == assembly) {
951 method = FindPreStartInitMethod(attribute.Type, attribute.MethodName);
954 if (method != null) {
958 throw new HttpException(SR.GetString(SR.Invalid_PreApplicationStartMethodAttribute_value,
960 (attribute.Type != null ? attribute.Type.FullName : String.Empty),
961 attribute.MethodName));
969 internal static MethodInfo FindPreStartInitMethod(Type type, string methodName) {
970 Debug.Assert(type != null);
971 Debug.Assert(!String.IsNullOrEmpty(methodName));
972 MethodInfo method = null;
974 // Verify that type is public to avoid allowing internal code execution. This implementation will not match
975 // nested public types.
976 method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
978 types: Type.EmptyTypes,
984 // excludedSubdirectories contains a list of subdirectory names that should not be
985 // recursively included in the compilation (they'll instead be compiled into their
987 private Assembly CompileCodeDirectory(VirtualPath virtualDir, CodeDirectoryType dirType,
988 string assemblyName, StringSet excludedSubdirectories) {
990 Debug.Trace("BuildManager", "CompileCodeDirectory(" + virtualDir.VirtualPathString + ")");
992 bool isDirectoryAllowed = true;
993 if (IsPrecompiledApp) {
994 // Most special dirs are not allowed in precompiled apps. App_LocalResources is
995 // an exception, as it is allowed in updatable precompiled apps.
996 if (IsUpdatablePrecompiledAppInternal && dirType == CodeDirectoryType.LocalResources)
997 isDirectoryAllowed = true;
999 isDirectoryAllowed = false;
1002 // Remember the referenced assemblies based on the current count.
1003 AssemblyReferenceInfo info = new AssemblyReferenceInfo(_topLevelReferencedAssemblies.Count);
1004 _topLevelAssembliesIndexTable[virtualDir.VirtualPathString] = info;
1006 Assembly codeAssembly = CodeDirectoryCompiler.GetCodeDirectoryAssembly(
1007 virtualDir, dirType, assemblyName, excludedSubdirectories,
1008 isDirectoryAllowed);
1010 if (codeAssembly != null) {
1012 // Remember the generated assembly
1013 info.Assembly = codeAssembly;
1015 // Page resource assemblies are not added to the top level list
1016 if (dirType != CodeDirectoryType.LocalResources) {
1017 _topLevelReferencedAssemblies.Add(codeAssembly);
1019 if (dirType == CodeDirectoryType.MainCode || dirType == CodeDirectoryType.SubCode) {
1020 if (_codeAssemblies == null) {
1021 _codeAssemblies = new ArrayList();
1024 _codeAssemblies.Add(codeAssembly);
1027 // Add it to the list of assembly name that we resolve, so that users can
1028 // refer to the assemblies by their fixed name (even though they
1029 // random names). (VSWhidbey 276776)
1030 if (_assemblyResolveMapping == null) {
1031 _assemblyResolveMapping = new Hashtable(StringComparer.OrdinalIgnoreCase);
1033 _assemblyResolveMapping[assemblyName] = codeAssembly;
1035 if (dirType == CodeDirectoryType.MainCode) {
1036 // Profile gets built in the same assembly as the main code dir, so
1037 // see whether we can get its type from the assembly.
1038 _profileType = ProfileBuildProvider.GetProfileTypeFromAssembly(
1039 codeAssembly, IsPrecompiledApp);
1041 // To avoid breaking earlier Whidbey apps, allows the name "__code"
1042 // to be used for the main code assembly.
1044 _assemblyResolveMapping["__code"] = codeAssembly;
1049 Debug.Trace("BuildManager", "CompileCodeDirectory generated assembly: " +
1050 (codeAssembly == null ? "None" : codeAssembly.ToString()));
1052 return codeAssembly;
1056 private void CompileResourcesDirectory() {
1058 VirtualPath virtualDir = HttpRuntime.ResourcesDirectoryVirtualPath;
1060 Debug.Assert(_appResourcesAssembly == null);
1061 _appResourcesAssembly = CompileCodeDirectory(virtualDir, CodeDirectoryType.AppResources,
1062 ResourcesDirectoryAssemblyName, null /*excludedSubdirectories*/);
1065 private void CompileWebRefDirectory() {
1067 CompileCodeDirectory(HttpRuntime.WebRefDirectoryVirtualPath, CodeDirectoryType.WebReferences,
1068 WebRefDirectoryAssemblyName, null /*excludedSubdirectories*/);
1071 // Compute the list of subdirectories that should not be compiled with
1072 // the top level Code
1073 private void EnsureExcludedCodeSubDirectoriesComputed() {
1075 if (_excludedCodeSubdirectories != null)
1078 _excludedCodeSubdirectories = new CaseInsensitiveStringSet();
1080 // Get the list of sub directories that will be compiled separately
1081 CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
1083 // Add them to the exclusion list of the top level code directory
1084 if (codeSubDirectories != null) {
1085 foreach (CodeSubDirectory entry in codeSubDirectories) {
1086 _excludedCodeSubdirectories.Add(entry.DirectoryName);
1091 private void CompileCodeDirectories() {
1093 VirtualPath virtualDir = HttpRuntime.CodeDirectoryVirtualPath;
1095 // Get the list of sub directories that will be compiled separately
1096 CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
1098 if (codeSubDirectories != null) {
1100 // Compile all the subdirectory that are listed in config.
1102 foreach (CodeSubDirectory entry in codeSubDirectories) {
1108 VirtualPath virtualSubDir = virtualDir.SimpleCombineWithDir(entry.DirectoryName);
1110 string assemblyName = SubCodeDirectoryAssemblyNamePrefix + entry.AssemblyName;
1112 // Compile the subdirectory tree (no exclusions)
1113 CompileCodeDirectory(virtualSubDir, CodeDirectoryType.SubCode, assemblyName,
1114 null /*excludedSubdirectories*/);
1118 EnsureExcludedCodeSubDirectoriesComputed();
1120 // Compile the top level Code directory tree, minus the excluded subdirectories
1121 CompileCodeDirectory(virtualDir, CodeDirectoryType.MainCode,
1122 CodeDirectoryAssemblyName, _excludedCodeSubdirectories);
1125 private void CompileGlobalAsax() {
1126 _globalAsaxBuildResult = ApplicationBuildProvider.GetGlobalAsaxBuildResult(IsPrecompiledApp);
1128 // Make sure that global.asax notifications are set up (VSWhidbey 267245)
1129 HttpApplicationFactory.SetupFileChangeNotifications();
1131 if (_globalAsaxBuildResult != null) {
1133 // We need to add not only the global.asax type, but also its parent types to
1134 // the top level assembly list. This can happen when global.asax has a 'src'
1135 // attribute pointing to a source file containing its base type.
1136 Type type = _globalAsaxBuildResult.ResultType;
1137 while (type.Assembly != typeof(HttpRuntime).Assembly) {
1138 _topLevelReferencedAssemblies.Add(type.Assembly);
1139 type = type.BaseType;
1144 // Call the AppInitialize method in the Code assembly if there is one
1145 internal static void CallAppInitializeMethod() {
1147 // Make sure the code directory has been processed
1148 _theBuildManager.EnsureTopLevelFilesCompiled();
1150 CodeDirectoryCompiler.CallAppInitializeMethod();
1153 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
1154 internal void EnsureTopLevelFilesCompiled() {
1155 if (PreStartInitStage != Compilation.PreStartInitStage.AfterPreStartInit) {
1156 throw new InvalidOperationException(SR.GetString(SR.Method_cannot_be_called_during_pre_start_init));
1159 // This should never get executed in non-hosted appdomains
1160 Debug.Assert(HostingEnvironment.IsHosted);
1162 // If we already tried and got an exception, just rethrow it
1163 if (_topLevelFileCompilationException != null && !SkipTopLevelCompilationExceptions) {
1164 ReportTopLevelCompilationException();
1167 if (_topLevelFilesCompiledStarted)
1170 // Set impersonation to hosting identity (process or UNC)
1171 using (new ApplicationImpersonationContext()) {
1172 bool gotLock = false;
1173 _parseErrorReported = false;
1176 // Grab the compilation mutex, since this method accesses the codegen files
1177 CompilationLock.GetLock(ref gotLock);
1179 // Check again if there is an exception
1180 if (_topLevelFileCompilationException != null && !SkipTopLevelCompilationExceptions) {
1181 ReportTopLevelCompilationException();
1184 // Check again if we're done
1185 if (_topLevelFilesCompiledStarted)
1188 _topLevelFilesCompiledStarted = true;
1189 _topLevelAssembliesIndexTable =
1190 new Dictionary<String, AssemblyReferenceInfo>(StringComparer.OrdinalIgnoreCase);
1192 _compilationStage = CompilationStage.TopLevelFiles;
1194 CompileResourcesDirectory();
1195 CompileWebRefDirectory();
1196 CompileCodeDirectories();
1198 _compilationStage = CompilationStage.GlobalAsax;
1200 CompileGlobalAsax();
1202 _compilationStage = CompilationStage.BrowserCapabilities;
1204 // Call GetBrowserCapabilitiesType() to make sure browserCap directory is compiled
1205 // early on. This avoids getting into potential deadlock situations later (VSWhidbey 530732).
1206 // For the same reason, get the EmptyHttpCapabilitiesBase.
1207 BrowserCapabilitiesCompiler.GetBrowserCapabilitiesType();
1208 IFilterResolutionService dummy = HttpCapabilitiesBase.EmptyHttpCapabilitiesBase;
1210 _compilationStage = CompilationStage.AfterTopLevelFiles;
1212 catch (Exception e) {
1213 // Remember the exception, and rethrow it
1214 _topLevelFileCompilationException = e;
1216 // Do not rethrow the exception since so CBM can still provide partial support
1217 if (!SkipTopLevelCompilationExceptions) {
1219 if (!_parseErrorReported) {
1220 // Report the error if this is not a CompileException. CompileExceptions are handled
1221 // directly by the AssemblyBuilder already.
1222 if (!(e is HttpCompileException)) {
1223 ReportTopLevelCompilationException();
1231 _topLevelFilesCompiledCompleted = true;
1233 // Always release the mutex if we had taken it
1235 CompilationLock.ReleaseLock();
1241 // Generate a random file name with 8 characters
1242 private static string GenerateRandomFileName() {
1243 // Generate random bytes
1244 byte[] data = new byte[6];
1247 _rng.GetBytes(data);
1250 // Turn them into a string containing only characters valid in file names/url
1251 string s = Convert.ToBase64String(data).ToLower(CultureInfo.InvariantCulture);
1252 s = s.Replace('/', '-');
1253 s = s.Replace('+', '_');
1258 internal static string GenerateRandomAssemblyName(string baseName) {
1259 return GenerateRandomAssemblyName(baseName, true /*topLevel*/);
1262 // Generate a random name for an assembly, starting with the passed in prefix
1263 internal static string GenerateRandomAssemblyName(string baseName, bool topLevel) {
1265 // Start with the passed in base name
1266 string assemblyName = baseName;
1268 // Append a random token to it.
1270 // However, don't do this when precompiling for deployment since, we want the name to be more predictable (DevDiv 36625)
1271 if (PrecompilingForDeployment)
1274 // Also, don't use random names for top level files in OptimizeCompilations mode so that pages
1275 // can more easily bind against rebuilt top level assemblies
1276 if (OptimizeCompilations && topLevel)
1279 return baseName += "." + GenerateRandomFileName();
1282 private static string GetGeneratedAssemblyBaseName(VirtualPath virtualPath) {
1284 // Name the assembly using the same scheme as cache keys
1285 return GetCacheKeyFromVirtualPath(virtualPath);
1289 * Look for a type by name in the top level and config assemblies
1291 public static Type GetType(string typeName, bool throwOnError) {
1292 return GetType(typeName, throwOnError, false);
1296 * Look for a type by name in the top level and config assemblies
1298 public static Type GetType(string typeName, bool throwOnError, bool ignoreCase) {
1299 // If it contains an assembly name, just call Type.GetType(). Do this before even trying
1300 // to initialize the BuildManager, so that if InitializeBuildManager has errors, it doesn't
1301 // affect us when the type string can be resolved via Type.GetType().
1303 if (Util.TypeNameContainsAssembly(typeName)) {
1304 type = Type.GetType(typeName, throwOnError, ignoreCase);
1311 // Make sure the build manager is initialized. If it fails to initialize for any reason,
1312 // don't attempt to use the fancy GetType logic. Just call Type.GetType instead (VSWhidbey 284498)
1313 if (!InitializeBuildManager()) {
1314 return Type.GetType(typeName, throwOnError, ignoreCase);
1317 // First, always try System.Web.dll
1319 type = typeof(BuildManager).Assembly.GetType(typeName,
1320 false /*throwOnError*/, ignoreCase);
1322 catch (ArgumentException e) {
1323 // Even though we pass false to throwOnError, GetType can throw if the
1324 // assembly name is malformed. In that case, throw our own error instead
1325 // of the cryptic ArgumentException (VSWhidbey 275586)
1326 throw new HttpException(
1327 SR.GetString(SR.Invalid_type, typeName), e);
1330 if (type != null) return type;
1332 _theBuildManager.EnsureTopLevelFilesCompiled();
1334 // Otherwise, look for the type in the top level assemblies
1335 type = Util.GetTypeFromAssemblies(TheBuildManager.TopLevelReferencedAssemblies,
1336 typeName, ignoreCase);
1337 if (type != null) return type;
1339 // Otherwise, look for the type in the config assemblies
1340 IEnumerable<Assembly> configAssemblies = GetAssembliesForAppLevel();
1341 type = Util.GetTypeFromAssemblies(configAssemblies, typeName, ignoreCase);
1343 if (type == null && throwOnError) {
1344 throw new HttpException(
1345 SR.GetString(SR.Invalid_type, typeName));
1352 * Simple wrapper to get the Assemblies
1354 private static IEnumerable<Assembly> GetAssembliesForAppLevel() {
1355 CompilationSection compilationConfiguration = MTConfigUtil.GetCompilationAppConfig();
1356 AssemblyCollection assemblyInfoCollection = compilationConfiguration.Assemblies;
1358 Debug.Assert(s_dynamicallyAddedReferencedAssembly != null);
1360 if (assemblyInfoCollection == null) {
1361 return s_dynamicallyAddedReferencedAssembly.OfType<Assembly>();
1364 return assemblyInfoCollection.Cast<AssemblyInfo>()
1365 .SelectMany(ai => ai.AssemblyInternal)
1366 .Union(s_dynamicallyAddedReferencedAssembly)
1372 * Gets a type from one of the code assemblies
1374 internal static Type GetTypeFromCodeAssembly(string typeName, bool ignoreCase) {
1376 // No code assembly: return
1377 if (CodeAssemblies == null)
1380 return Util.GetTypeFromAssemblies(CodeAssemblies, typeName, ignoreCase);
1383 internal static BuildProvider CreateBuildProvider(VirtualPath virtualPath,
1384 CompilationSection compConfig, ICollection referencedAssemblies,
1385 bool failIfUnknown) {
1387 return CreateBuildProvider(virtualPath, BuildProviderAppliesTo.Web,
1388 compConfig, referencedAssemblies, failIfUnknown);
1391 internal static BuildProvider CreateBuildProvider(VirtualPath virtualPath,
1392 BuildProviderAppliesTo neededFor,
1393 CompilationSection compConfig, ICollection referencedAssemblies,
1394 bool failIfUnknown) {
1396 string extension = virtualPath.Extension;
1398 Type buildProviderType = CompilationUtil.GetBuildProviderTypeFromExtension(compConfig,
1399 extension, neededFor, failIfUnknown);
1400 if (buildProviderType == null)
1403 object o = HttpRuntime.CreatePublicInstance(buildProviderType);
1405 BuildProvider buildProvider = (BuildProvider)o;
1407 buildProvider.SetVirtualPath(virtualPath);
1408 buildProvider.SetReferencedAssemblies(referencedAssemblies);
1410 return buildProvider;
1413 internal static void AddFolderLevelBuildProviders(BuildProviderSet buildProviders, VirtualPath virtualPath,
1414 FolderLevelBuildProviderAppliesTo appliesTo, CompilationSection compConfig, ICollection referencedAssemblies) {
1416 if (buildProviders == null) {
1420 List<Type> buildProviderTypes = CompilationUtil.GetFolderLevelBuildProviderTypes(compConfig, appliesTo);
1421 if (buildProviderTypes != null) {
1422 foreach (Type buildProviderType in buildProviderTypes) {
1423 object o = HttpRuntime.CreatePublicInstance(buildProviderType);
1425 BuildProvider buildProvider = (BuildProvider)o;
1427 buildProvider.SetVirtualPath(virtualPath);
1428 buildProvider.SetReferencedAssemblies(referencedAssemblies);
1430 buildProviders.Add(buildProvider);
1436 internal static void ValidateCodeFileVirtualPath(VirtualPath virtualPath) {
1437 _theBuildManager.ValidateVirtualPathInternal(virtualPath, false /*allowCrossApp*/, true /*codeFile*/);
1440 private void ValidateVirtualPathInternal(VirtualPath virtualPath, bool allowCrossApp, bool codeFile) {
1442 if (!allowCrossApp) {
1443 virtualPath.FailIfNotWithinAppRoot();
1446 // If cross app is allowed, and the path is in a different app, nothing more to check
1447 if (!virtualPath.IsWithinAppRoot)
1452 // Now, detect if it's under a special directory (e.g. 'code', 'resources', 'themes')
1455 // If it's exactly the app root, it's fine
1456 if (HttpRuntime.AppDomainAppVirtualPathObject == virtualPath)
1459 int appPathLen = HttpRuntime.AppDomainAppVirtualPathString.Length;
1461 string virtualPathString = virtualPath.VirtualPathString;
1463 // This could happen if the vpath is "/app" (while the app vpath is "/app/")
1464 if (virtualPathString.Length < appPathLen)
1467 // If no slash after the approot (e.g. "/app/foo.aspx"), it's valid
1468 int slashIndex = virtualPathString.IndexOf('/', appPathLen);
1472 // Get the name of the first directory under the app root (e.g. "/app/aaa/bbb/foo.aspx" -> "aaa")
1473 string dir = virtualPathString.Substring(appPathLen, slashIndex - appPathLen);
1475 // If it's a forbidden directory, fail
1476 if (_forbiddenTopLevelDirectories.Contains(dir)) {
1477 throw new HttpException(SR.GetString(SR.Illegal_special_dir, virtualPathString, dir));
1482 * Returns a single hash code that represents the state of the built object for
1483 * the passed in virtualPath. If it isn't already built, don't build it, but just
1484 * return 0. This can be used to determine the validity of output cache that
1485 * has been persisted to disk.
1487 internal static long GetBuildResultHashCodeIfCached(
1488 HttpContext context, string virtualPath) {
1490 BuildResult result = GetVPathBuildResult(context, VirtualPath.Create(virtualPath),
1491 true /*noBuild*/, false /*allowCrossApp*/);
1493 // If it's not cached, return 0
1497 // Return a single hash code based on both of the BuildResult's hash codes
1498 string dependenciesHash = result.VirtualPathDependenciesHash;
1499 Debug.Assert(result.DependenciesHashComputed);
1500 return result.ComputeHashCode(s_topLevelHash, StringUtil.GetStringHashCode(dependenciesHash));
1503 internal static BuildResult GetVPathBuildResult(VirtualPath virtualPath) {
1505 return GetVPathBuildResult(null /*context*/, virtualPath,
1506 false /*noBuild*/, false /*allowCrossApp*/, false /*allowBuiltInPrecompile*/);
1509 internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath) {
1511 return GetVPathBuildResult(context, virtualPath, false /*noBuild*/, false /*allowCrossApp*/, false /*allowBuiltInPrecompile*/);
1514 internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath,
1515 bool noBuild, bool allowCrossApp) {
1517 return GetVPathBuildResult(context, virtualPath, noBuild, allowCrossApp, false /*allowBuiltInPrecompile*/);
1521 * Calls either GetVPathBuildResultWithNoAssert or GetVPathBuildResultWithAssert,
1522 * depending on whether there is any point in asserting.
1524 internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath,
1525 bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool ensureIsUpToDate = true) {
1527 // Could be called with user code on the stack, so need to assert here (VSWhidbey 85026)
1528 // e.g. This can happen during a Server.Transfer, or a LoadControl.
1529 // But if we're running in full trust, skip the assert for perf reasons (VSWhidbey 146871)
1530 if (HttpRuntime.IsFullTrust) {
1531 return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound: true, ensureIsUpToDate: ensureIsUpToDate);
1534 return GetVPathBuildResultWithAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound: true, ensureIsUpToDate: ensureIsUpToDate);
1539 internal static BuildResult GetVPathBuildResultWithAssert(
1540 HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile) {
1541 return GetVPathBuildResultWithAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, true/*throwIfNotFound*/);
1545 * Same as GetVPathBuildResultWithNoAssert, but with an Unrestricted Assert.
1547 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
1548 internal static BuildResult GetVPathBuildResultWithAssert(
1549 HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
1551 return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound, ensureIsUpToDate);
1554 internal static BuildResult GetVPathBuildResultWithNoAssert(
1555 HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile) {
1556 return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, true/*throwIfNotFound*/);
1559 internal static BuildResult GetVPathBuildResultWithNoAssert(
1560 HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
1562 using (new ApplicationImpersonationContext()) {
1563 return _theBuildManager.GetVPathBuildResultInternal(virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound, ensureIsUpToDate);
1567 // name of the slot in call context
1568 private const String CircularReferenceCheckerSlotName = "CircRefChk";
1570 private BuildResult GetVPathBuildResultInternal(VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
1572 Debug.Trace("BuildManager", "GetBuildResult(" + virtualPath + ")");
1574 // This should never be called while building top level files (VSWhidbey 480256)
1575 if (_compilationStage == CompilationStage.TopLevelFiles) {
1576 throw new HttpException(SR.GetString(SR.Too_early_for_webfile, virtualPath));
1579 // Make sure the path is not relative
1580 Debug.Assert(!virtualPath.IsRelative);
1582 // Try the cache first before getting the mutex
1583 BuildResult result = GetVPathBuildResultFromCacheInternal(virtualPath, ensureIsUpToDate);
1587 // If we were only checking the cache and it wasn't there, return null.
1591 // Check if it's trying to go cross app, or points to a special directory.
1592 // It's important to do this before checkin existence, to avoid revealing information
1593 // about other apps (VSWhidbey 442632)
1594 ValidateVirtualPathInternal(virtualPath, allowCrossApp, false /*codeFile*/);
1596 if (throwIfNotFound) {
1597 // Before grabbing the lock, make sure the file at least exists (ASURT 46465)
1598 Util.CheckVirtualFileExists(virtualPath);
1600 else if (!virtualPath.FileExists()) {
1604 // If this is a precompiled app, complain if we couldn't find it in the cache
1605 if (IsNonUpdatablePrecompiledApp && !allowBuildInPrecompile) {
1606 throw new HttpException(
1607 SR.GetString(SR.Cant_update_precompiled_app, virtualPath));
1610 bool gotLock = false;
1613 // Grab the compilation mutex
1614 CompilationLock.GetLock(ref gotLock);
1616 // Check the cache a second time after getting the mutex
1617 result = GetVPathBuildResultFromCacheInternal(virtualPath, ensureIsUpToDate);
1621 // Get the circular reference checker (create it if needed)
1622 VirtualPathSet circularReferenceChecker;
1623 circularReferenceChecker = CallContext.GetData(CircularReferenceCheckerSlotName)
1625 if (circularReferenceChecker == null) {
1626 circularReferenceChecker = new VirtualPathSet();
1628 // Create it and save it in the CallContext
1629 CallContext.SetData(CircularReferenceCheckerSlotName, circularReferenceChecker);
1632 // If a circular reference is detected, throw an error
1633 if (circularReferenceChecker.Contains(virtualPath)) {
1634 throw new HttpException(
1635 SR.GetString(SR.Circular_include));
1638 // Add the current virtualPath to the circular reference checker
1639 circularReferenceChecker.Add(virtualPath);
1643 EnsureTopLevelFilesCompiled();
1644 result = CompileWebFile(virtualPath);
1647 // Remove the current virtualPath from the circular reference checker
1648 Debug.Assert(circularReferenceChecker.Contains(virtualPath));
1649 circularReferenceChecker.Remove(virtualPath);
1653 // Always release the mutex if we had taken it
1655 CompilationLock.ReleaseLock();
1662 private BuildResult CompileWebFile(VirtualPath virtualPath) {
1664 BuildResult result = null;
1665 string cacheKey = null;
1667 if (_topLevelFilesCompiledCompleted) {
1669 VirtualPath parentPath = virtualPath.Parent;
1671 // First, try to batch the directory if enabled
1672 if (IsBatchEnabledForDirectory(parentPath)) {
1673 BatchCompileWebDirectory(null, parentPath, true /*ignoreErrors*/);
1675 // If successful, it would have been cached to memory
1676 cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
1677 result = _memoryCache.GetBuildResult(cacheKey);
1679 if (result == null && DelayLoadType.Enabled) {
1680 // We might not have cached the result in the memory cache
1681 // if we are trying to delay loading the assembly.
1682 result = GetBuildResultFromCache(cacheKey);
1685 if (result != null) {
1686 // If what we found in the cache is a CompileError, rethrow the exception
1687 if (result is BuildResultCompileError) {
1688 throw ((BuildResultCompileError)result).CompileException;
1697 DateTime utcStart = DateTime.UtcNow;
1699 // Name the assembly based on the virtual path, in order to get a recognizable name
1700 string outputAssemblyName = BuildManager.WebAssemblyNamePrefix +
1701 BuildManager.GenerateRandomAssemblyName(
1702 GetGeneratedAssemblyBaseName(virtualPath), false /*topLevel*/);
1705 BuildProvidersCompiler bpc = new BuildProvidersCompiler(virtualPath /*configPath*/, outputAssemblyName);
1707 // Create a BuildProvider based on the virtual path
1708 BuildProvider buildProvider = CreateBuildProvider(virtualPath, bpc.CompConfig,
1709 bpc.ReferencedAssemblies, true /*failIfUnknown*/);
1711 // Set the BuildProvider using a single item collection
1712 bpc.SetBuildProviders(new SingleObjectCollection(buildProvider));
1715 CompilerResults results;
1718 results = bpc.PerformBuild();
1719 result = buildProvider.GetBuildResult(results);
1721 catch (HttpCompileException e) {
1723 // If we're not supposed to cache the exception, just rethrow it
1727 result = new BuildResultCompileError(virtualPath, e);
1729 // Add the dependencies to the compile error build provider, so that
1730 // we will retry compilation when a dependency changes
1731 buildProvider.SetBuildResultDependencies(result);
1733 // Remember the virtualpath dependencies, so that we will correctly
1734 // invalidate buildresult when depdency changes.
1735 e.VirtualPathDependencies = buildProvider.VirtualPathDependencies;
1737 // Cache it for next time
1738 CacheVPathBuildResultInternal(virtualPath, result, utcStart);
1740 // Set the DontCache flag, so that the exception will not be incorrectly
1741 // cached again lower down the stack (VSWhidbey 128234)
1750 // Cache it for next time
1751 CacheVPathBuildResultInternal(virtualPath, result, utcStart);
1753 if (!_precompilingApp && BuildResultCompiledType.UsesDelayLoadType(result)) {
1754 // The result uses DelayLoadType, which should not get exposed.
1755 // If we are not performing precompilation, then we should
1756 // get the actual result from cache and return that instead.
1757 if (cacheKey == null) {
1758 cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
1760 result = BuildManager.GetBuildResultFromCache(cacheKey);
1766 // Hashtbale to remember the local resources assembly for each directory (or null
1767 // if there isn't one). Hashtable<VirtualPath,Assembly>
1768 private Hashtable _localResourcesAssemblies = new Hashtable();
1770 private void EnsureFirstTimeDirectoryInit(VirtualPath virtualDir) {
1772 // Don't process local resources when precompiling for updatable deployment.
1773 // Instead, we deploy the App_LocalResources folder as is.
1774 if (PrecompilingForUpdatableDeployment)
1777 if (virtualDir == null)
1780 // Only do this once per directory
1781 if (_localResourcesAssemblies.Contains(virtualDir))
1784 // Don't do anything if it's outside the app root
1785 if (!virtualDir.IsWithinAppRoot)
1788 Debug.Trace("BuildManager", "EnsureFirstTimeDirectoryInit(" + virtualDir + ")");
1790 // Get the virtual path to the LocalResources subdirectory for this directory
1791 VirtualPath localResDir = virtualDir.SimpleCombineWithDir(HttpRuntime.LocalResourcesDirectoryName);
1795 dirExists = localResDir.DirectoryExists();
1798 // If an exception happens, the directory may be outside the application,
1799 // in which case we should skip this logic, and act is if there are no
1800 // local resources (VSWhidbey 258776);
1802 _localResourcesAssemblies[virtualDir] = null;
1806 Debug.Trace("BuildManager", "EnsureFirstTimeDirectoryInit: dirExists=" + dirExists);
1809 // Monitor changes to it so the appdomain can shut down when it changes
1810 HttpRuntime.StartListeningToLocalResourcesDirectory(localResDir);
1813 // could fail for long directory names
1819 Assembly resourceAssembly = null;
1821 // If it exists, build it
1824 string localResAssemblyName = GetLocalResourcesAssemblyName(virtualDir);
1826 bool gotLock = false;
1829 // Grab the compilation mutex, since this method accesses the codegen files
1830 CompilationLock.GetLock(ref gotLock);
1832 resourceAssembly = CompileCodeDirectory(localResDir, CodeDirectoryType.LocalResources,
1833 localResAssemblyName, null /*excludedSubdirectories*/);
1836 // Always release the mutex if we had taken it
1838 CompilationLock.ReleaseLock();
1843 // Cache it whether it's null or not
1844 _localResourcesAssemblies[virtualDir] = resourceAssembly;
1847 // VSWhidbey Bug 560521
1848 private void EnsureFirstTimeDirectoryInitForDependencies(ICollection dependencies) {
1849 foreach (String dependency in dependencies) {
1850 VirtualPath dependencyPath = VirtualPath.Create(dependency);
1851 VirtualPath dir = dependencyPath.Parent;
1852 EnsureFirstTimeDirectoryInit(dir);
1857 // Retrieve a cached local resources assembly (could be null)
1858 internal static Assembly GetLocalResourcesAssembly(VirtualPath virtualDir) {
1859 return (Assembly)_theBuildManager._localResourcesAssemblies[virtualDir];
1862 internal static string GetLocalResourcesAssemblyName(VirtualPath virtualDir) {
1863 return LocalResourcesDirectoryAssemblyName + "." + GetGeneratedAssemblyBaseName(virtualDir);
1866 // name of the slot in call context
1867 private const String BatchCompilationSlotName = "BatchCompileChk";
1869 // The semantics are
1870 // true - always batch-compile
1871 // false - never batch-compile
1872 // null - determine from config
1873 private static bool? s_batchCompilationEnabled;
1875 public static Nullable<bool> BatchCompilationEnabled {
1877 return s_batchCompilationEnabled;
1880 ThrowIfPreAppStartNotRunning();
1881 s_batchCompilationEnabled = value;
1885 // Check if batching is enabled for directory specified by virtualDir
1886 private bool IsBatchEnabledForDirectory(VirtualPath virtualDir) {
1887 // False if compile for fixed name
1888 if (CompileWithFixedAssemblyNames) {
1892 // Always enable batching for deployement
1893 if (PrecompilingForDeployment) {
1897 // If it's called by other non-precompile CBM methods, always disable batching
1898 if (BuildManagerHost.InClientBuildManager && !PerformingPrecompilation) {
1902 // If batch compilation was set through code use that setting
1903 if (BatchCompilationEnabled.HasValue) {
1904 return BatchCompilationEnabled.Value;
1908 return CompilationUtil.IsBatchingEnabled(virtualDir.VirtualPathString);
1911 private bool BatchCompileWebDirectory(VirtualDirectory vdir, VirtualPath virtualDir, bool ignoreErrors) {
1913 // Exactly one of vdir and virtualDir should be non-null. The idea is to avoid calling
1914 // VirtualPathProvider.GetDirectory if batching is disabled (VSWhidbey 437549).
1916 if (virtualDir == null)
1917 virtualDir = vdir.VirtualPathObject;
1920 vdir = HostingEnvironment.VirtualPathProvider.GetDirectory(virtualDir);
1922 // Then, check if we're already tried batch compiling this directory on this same request
1924 CaseInsensitiveStringSet directoryBatchCompilerChecker;
1925 directoryBatchCompilerChecker = CallContext.GetData(BatchCompilationSlotName)
1926 as CaseInsensitiveStringSet;
1928 if (directoryBatchCompilerChecker == null) {
1929 directoryBatchCompilerChecker = new CaseInsensitiveStringSet();
1931 // Create it and save it in the CallContext
1932 CallContext.SetData(BatchCompilationSlotName, directoryBatchCompilerChecker);
1935 // If we've already tried batch compiling this directory, don't do anything
1936 if (directoryBatchCompilerChecker.Contains(vdir.VirtualPath))
1939 // Add the current virtualDir to the batch compiler checker
1940 directoryBatchCompilerChecker.Add(vdir.VirtualPath);
1942 // If we're in the process of precompiling an app, never ignore errors.
1943 if (_precompilingApp)
1944 ignoreErrors = false;
1946 return BatchCompileWebDirectoryInternal(vdir, ignoreErrors);
1949 private bool BatchCompileWebDirectoryInternal(VirtualDirectory vdir, bool ignoreErrors) {
1951 WebDirectoryBatchCompiler sdc = new WebDirectoryBatchCompiler(vdir);
1953 // Just ignore build providers that have errors
1955 sdc.SetIgnoreErrors();
1957 // Don't propagate errors that happen during batch compilation
1972 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly",
1973 Justification = "Global Asax is a well-known concept")]
1974 [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
1975 Justification = "This might cick off top-level compilation so it's too big for a property")]
1976 public static Type GetGlobalAsaxType() {
1977 return _theBuildManager.GetGlobalAsaxTypeInternal();
1980 private Type GetGlobalAsaxTypeInternal() {
1981 EnsureTopLevelFilesCompiled();
1983 if (_globalAsaxBuildResult == null)
1984 return PageParser.DefaultApplicationBaseType ?? typeof(HttpApplication);
1986 return _globalAsaxBuildResult.ResultType;
1989 internal static BuildResultCompiledGlobalAsaxType GetGlobalAsaxBuildResult() {
1990 return _theBuildManager.GetGlobalAsaxBuildResultInternal();
1993 private BuildResultCompiledGlobalAsaxType GetGlobalAsaxBuildResultInternal() {
1994 EnsureTopLevelFilesCompiled();
1996 return _globalAsaxBuildResult;
1999 internal string[] GetCodeDirectories() {
2001 VirtualPath virtualDir = HttpRuntime.CodeDirectoryVirtualPath;
2003 // If there is no Code directory, return an empty array
2004 if (!virtualDir.DirectoryExists())
2005 return new string[0];
2007 // Get the list of code sub directories that will be compiled separately
2008 CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
2010 // Compute the number of code dirs, including the root one
2011 int numOfCodeDirs = 1;
2012 if (codeSubDirectories != null)
2013 numOfCodeDirs += codeSubDirectories.Count;
2015 string[] codeDirs = new string[numOfCodeDirs];
2018 if (codeSubDirectories != null) {
2019 foreach (CodeSubDirectory entry in codeSubDirectories) {
2021 VirtualPath virtualSubDir = virtualDir.SimpleCombineWithDir(entry.DirectoryName);
2022 codeDirs[current++] = virtualSubDir.VirtualPathString;
2026 // Add the root code dir at the end of the list (since it's compiled last)
2027 codeDirs[current++] = virtualDir.VirtualPathString;
2032 internal void GetCodeDirectoryInformation(VirtualPath virtualCodeDir,
2033 out Type codeDomProviderType, out CompilerParameters compilerParameters,
2034 out string generatedFilesDir) {
2036 // Backup the compilation stage, since the call will modify it
2037 CompilationStage savedCompilationStage = _compilationStage;
2040 GetCodeDirectoryInformationInternal(virtualCodeDir, out codeDomProviderType,
2041 out compilerParameters, out generatedFilesDir);
2044 // Restore the compilation stage
2045 _compilationStage = savedCompilationStage;
2049 private void GetCodeDirectoryInformationInternal(VirtualPath virtualCodeDir,
2050 out Type codeDomProviderType, out CompilerParameters compilerParameters,
2051 out string generatedFilesDir) {
2053 StringSet excludedSubdirectories = null;
2055 CodeDirectoryType dirType;
2057 // Get the DirectoryType based on the path
2058 if (virtualCodeDir == HttpRuntime.CodeDirectoryVirtualPath) {
2060 // If it's the top level code directory, make sure we exclude any
2061 // subdirectories that are compiled separately
2062 EnsureExcludedCodeSubDirectoriesComputed();
2064 excludedSubdirectories = _excludedCodeSubdirectories;
2066 dirType = CodeDirectoryType.MainCode;
2068 _compilationStage = CompilationStage.TopLevelFiles;
2070 else if (virtualCodeDir == HttpRuntime.ResourcesDirectoryVirtualPath) {
2072 dirType = CodeDirectoryType.AppResources;
2074 _compilationStage = CompilationStage.TopLevelFiles;
2076 // If virtualCodeDir is a subdir of WebReference virtual path.
2077 else if (String.Compare(virtualCodeDir.VirtualPathString, 0,
2078 HttpRuntime.WebRefDirectoryVirtualPath.VirtualPathString, 0, HttpRuntime.WebRefDirectoryVirtualPath.VirtualPathString.Length,
2079 StringComparison.OrdinalIgnoreCase) == 0) {
2081 // Use the top WebReference directory info for its sub directories.
2082 virtualCodeDir = HttpRuntime.WebRefDirectoryVirtualPath;
2083 dirType = CodeDirectoryType.WebReferences;
2085 _compilationStage = CompilationStage.TopLevelFiles;
2087 else if (String.Compare(virtualCodeDir.FileName, HttpRuntime.LocalResourcesDirectoryName,
2088 StringComparison.OrdinalIgnoreCase) == 0) {
2090 dirType = CodeDirectoryType.LocalResources;
2092 // LocalResources are compiled *after* top level files
2093 _compilationStage = CompilationStage.AfterTopLevelFiles;
2096 // If all else fails, treat it as a sub directory
2098 dirType = CodeDirectoryType.SubCode;
2100 // Sub-code dirs are compiled *before* the main code dir
2101 _compilationStage = CompilationStage.TopLevelFiles;
2104 Debug.Assert(virtualCodeDir.HasTrailingSlash);
2105 AssemblyReferenceInfo info = TheBuildManager.TopLevelAssembliesIndexTable[virtualCodeDir.VirtualPathString];
2107 throw new InvalidOperationException(
2108 SR.GetString(SR.Invalid_CodeSubDirectory_Not_Exist, virtualCodeDir));
2111 // Get the info we need for this code directory
2112 CodeDirectoryCompiler.GetCodeDirectoryInformation(
2113 virtualCodeDir, dirType, excludedSubdirectories, info.ReferenceIndex,
2114 out codeDomProviderType, out compilerParameters,
2115 out generatedFilesDir);
2117 Assembly resultAssembly = info.Assembly;
2119 if (resultAssembly != null) {
2120 // Use the runtime generated assembly location. VSWhidbey 400335
2121 compilerParameters.OutputAssembly = resultAssembly.Location;
2125 internal static Type GetProfileType() {
2126 return _theBuildManager.GetProfileTypeInternal();
2129 private Type GetProfileTypeInternal() {
2130 EnsureTopLevelFilesCompiled();
2131 return _profileType;
2136 // Caching related code
2140 public static ICollection GetVirtualPathDependencies(string virtualPath) {
2142 CompilationSection compConfig = RuntimeConfig.GetRootWebConfig().Compilation;
2144 // Create a BuildProvider based on the virtual path
2145 BuildProvider buildProvider = CreateBuildProvider(VirtualPath.Create(virtualPath), compConfig,
2146 null, false /*failIfUnknown*/);
2148 if (buildProvider == null)
2151 // Get its dependencies
2153 return buildProvider.GetBuildResultVirtualPathDependencies();
2158 * Rewrite the virtualPath if appropriate, in order to support ghosting
2160 private static void GetGhostedVirtualPath(ref string virtualPath) {
2162 VirtualPathProvider virtualPathProvider = HostingEnvironment.VirtualPathProvider;
2164 string ghostedVirtualPath = virtualPathProvider.GetGhostedVirtualPath(virtualPath);
2166 // If the file is not ghosted, don't change the path
2167 if (ghostedVirtualPath == null)
2173 // Get the list of virtual paths that it depends on (e.g. user controls)
2174 ICollection virtualPathDependencies = GetVirtualPathDependencies(virtualPath);
2176 // If there aren't any, return the ghosted path
2177 if (virtualPathDependencies == null) {
2178 virtualPath = ghostedVirtualPath;
2182 // Go through all the dependencies, and if we find any that is *not* ghosted
2183 // (i.e. for which GetGhostedVirtualPath returns null), we treat the whole request
2184 // as unghosted (and hence we return without modifying the virtualPath).
2186 foreach (string virtualDependency in virtualPathDependencies) {
2187 string ghostedVirtualDependencyPath = virtualPathProvider.GetGhostedVirtualPath(
2189 if (ghostedVirtualDependencyPath == null)
2193 // All the dependencies are ghosted, so we can safely use the ghosted path,
2194 // which can then be shared for all fully ghosted requests.
2195 virtualPath = ghostedVirtualPath;
2199 internal static string GetCacheKeyFromVirtualPath(VirtualPath virtualPath) {
2201 return GetCacheKeyFromVirtualPath(virtualPath, out keyFromVPP);
2205 * Same as GetCacheKeyFromVirtualPathInternal, but caches the cache keys
2206 * for performance, since creating them is expensive (VSWhidbey 146540)
2208 static SimpleRecyclingCache _keyCache = new SimpleRecyclingCache();
2209 private static string GetCacheKeyFromVirtualPath(VirtualPath virtualPath, out bool keyFromVPP) {
2211 // Check if the VirtualPathProvider needs to use a non-default cache key
2212 string key = virtualPath.GetCacheKey();
2214 // If so, just return it
2217 return key.ToLowerInvariant();
2220 // The VPP didn't return a key, so use our standard key algorithm
2223 // Check if the key for this virtual path is already cached
2224 key = _keyCache[virtualPath.VirtualPathString] as string;
2225 if (key != null) return key;
2228 key = GetCacheKeyFromVirtualPathInternal(virtualPath);
2230 // The key should always be lower case
2231 Debug.Assert(key == key.ToLowerInvariant());
2233 // Cache it for next time
2234 _keyCache[virtualPath.VirtualPathString] = key;
2240 * Generate a unique cache key from a virtual path. e.g. for "/approot/sub1/sub2/foo.aspx"
2241 * the key could be "foo.aspx.ccdf220e", where ccdf220e is a hash code from
2242 * the dir "sub1/sub2".
2244 private static string GetCacheKeyFromVirtualPathInternal(VirtualPath virtualPath) {
2246 // We want the key to be app independent (for precompilation), so we
2247 // change the virtual path to be app relative
2249 /* Disable assertion since global theme needs to compile theme files in different vroot.
2250 Debug.Assert(StringUtil.VirtualPathStartsWithAppPath(virtualPath),
2251 String.Format("VPath {0} is outside the application: {1}", virtualPath, HttpRuntime.AppDomainAppVirtualPath));
2253 string virtualPathString = virtualPath.AppRelativeVirtualPathString.ToLowerInvariant();
2254 virtualPathString = UrlPath.RemoveSlashFromPathIfNeeded(virtualPathString);
2256 // Split the path into the directory and the name
2257 int slashIndex = virtualPathString.LastIndexOf('/');
2258 Debug.Assert(slashIndex >= 0 || virtualPathString == "~");
2260 if (virtualPathString == "~")
2263 Debug.Assert(slashIndex != virtualPathString.Length - 1);
2264 string name = virtualPathString.Substring(slashIndex + 1);
2266 if (slashIndex <= 0)
2269 dir = virtualPathString.Substring(0, slashIndex);
2272 return name + "." + StringUtil.GetStringHashCode(dir).ToString("x", CultureInfo.InvariantCulture);
2275 internal static BuildResult GetVPathBuildResultFromCache(VirtualPath virtualPath) {
2277 return TheBuildManager.GetVPathBuildResultFromCacheInternal(virtualPath);
2280 private BuildResult GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath, bool ensureIsUpToDate = true) {
2282 string cacheKey = GetCacheKeyFromVirtualPath(virtualPath, out keyFromVPP);
2283 return GetBuildResultFromCacheInternal(cacheKey, keyFromVPP, virtualPath, 0 /*hashCode*/, ensureIsUpToDate);
2286 internal static BuildResult GetBuildResultFromCache(string cacheKey) {
2287 return _theBuildManager.GetBuildResultFromCacheInternal(cacheKey, false /*keyFromVPP*/, null /*virtualPath*/,
2291 internal static BuildResult GetBuildResultFromCache(string cacheKey, VirtualPath virtualPath) {
2292 return _theBuildManager.GetBuildResultFromCacheInternal(cacheKey, false /*keyFromVPP*/, virtualPath,
2296 private BuildResult GetBuildResultFromCacheInternal(string cacheKey, bool keyFromVPP,
2297 VirtualPath virtualPath, long hashCode, bool ensureIsUpToDate = true) {
2299 BuildResult result = null;
2301 // Allow the possibility that BuildManager was not initialized due to
2302 // a very early failure (e.g. see VSWhidbey 137366)
2303 //Debug.Trace("BuildManager", "GetBuildResultFromCacheInternal " + _theBuildManagerInitialized);
2304 if (!_theBuildManagerInitialized)
2307 // The first cache should always be memory
2308 Debug.Assert(_caches[0] == _memoryCache);
2310 // Try to get it from the memeory cache before taking any locks (for perf reasons)
2311 result = _memoryCache.GetBuildResult(cacheKey, virtualPath, hashCode, ensureIsUpToDate);
2312 if (result != null) {
2313 return PostProcessFoundBuildResult(result, keyFromVPP, virtualPath);
2316 Debug.Trace("BuildManager", "Didn't find '" + virtualPath + "' in memory cache before lock");
2319 // Try to get the BuildResult from the cheapest to most expensive cache
2321 for (i = 0; i < _caches.Length; i++) {
2322 result = _caches[i].GetBuildResult(cacheKey, virtualPath, hashCode, ensureIsUpToDate);
2324 // There might be changes in local resources for dependencies,
2325 // so we need to make sure EnsureFirstTimeDirectoryInit gets called
2326 // for them even when we already have a cache result.
2327 // VSWhidbey Bug 560521
2329 if (result != null) {
2330 // We should only process the local resources folder after the top level files have been compiled,
2331 // so that any custom VPP can be registered first. (Dev10 bug 890796)
2332 if (_compilationStage == CompilationStage.AfterTopLevelFiles && result.VirtualPathDependencies != null) {
2333 EnsureFirstTimeDirectoryInitForDependencies(result.VirtualPathDependencies);
2339 // If we didn't find it in the memory cache, perform the per directory
2340 // initialization. This is a good place to do this, because we don't
2341 // affect the memory cache code path, but we do the init as soon as
2342 // something is not found in the memory cache.
2343 if (i == 0 && virtualPath != null) {
2344 VirtualPath virtualDir = virtualPath.Parent;
2345 EnsureFirstTimeDirectoryInit(virtualDir);
2353 result = PostProcessFoundBuildResult(result, keyFromVPP, virtualPath);
2357 Debug.Assert(_memoryCache != null);
2359 // If we found it in a cache, cache it in all the caches that come before
2360 // the one where we found it. If we found it in the memory cache, this is a no op.
2361 for (int j = 0; j < i; j++)
2362 _caches[j].CacheBuildResult(cacheKey, result, DateTime.UtcNow);
2364 Debug.Trace("BuildManager", "Found '" + virtualPath + "' in " + _caches[i]);
2370 private BuildResult PostProcessFoundBuildResult(BuildResult result, bool keyFromVPP, VirtualPath virtualPath) {
2372 // Check that the virtual path in the result matches the passed in
2373 // virtualPath (VSWhidbey 516641). But skip this check in case the key came from
2374 // calling VirtualPathProvider.GetCacheKey, as it may legitimately not match.
2376 if (virtualPath != null && virtualPath != result.VirtualPath) {
2377 Debug.Assert(false);
2382 // If what we found in the cache is a CompileError, rethrow the exception
2383 if (result is BuildResultCompileError) {
2384 // Report the cached error from Callback interface.
2385 HttpCompileException compileException = ((BuildResultCompileError)result).CompileException;
2387 // But don't report it if we're doing precompilation, as that would cause it to be
2388 // reported twice because we always try to compile everything that has failed
2389 // before (VSWhidbey 525414)
2390 if (!PerformingPrecompilation) {
2391 ReportErrorsFromException(compileException);
2394 throw compileException;
2400 internal static bool CacheVPathBuildResult(VirtualPath virtualPath,
2401 BuildResult result, DateTime utcStart) {
2403 return _theBuildManager.CacheVPathBuildResultInternal(virtualPath, result, utcStart);
2406 private bool CacheVPathBuildResultInternal(VirtualPath virtualPath,
2407 BuildResult result, DateTime utcStart) {
2409 string cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
2410 return CacheBuildResult(cacheKey, result, utcStart);
2413 internal static bool CacheBuildResult(string cacheKey, BuildResult result, DateTime utcStart) {
2414 return _theBuildManager.CacheBuildResultInternal(cacheKey, result, 0 /*hashCode*/, utcStart);
2417 private bool CacheBuildResultInternal(string cacheKey, BuildResult result,
2418 long hashCode, DateTime utcStart) {
2420 // Before caching it, make sure the hash has been computed
2421 result.EnsureVirtualPathDependenciesHashComputed();
2423 for (int i = 0; i < _caches.Length; i++) {
2424 _caches[i].CacheBuildResult(cacheKey, result, hashCode, utcStart);
2427 // If we find that it's no longer valid after caching it, remove it from the cache (VSWhidbey 578372)
2428 if (!TimeStampChecker.CheckFilesStillValid(cacheKey, result.VirtualPathDependencies)) {
2429 _memoryCache.RemoveAssemblyAndCleanupDependencies(result as BuildResultCompiledAssemblyBase);
2438 // Precompilation related code
2441 internal void SetPrecompilationInfo(HostingEnvironmentParameters hostingParameters) {
2443 if (hostingParameters == null || hostingParameters.ClientBuildManagerParameter == null)
2446 _precompilationFlags = hostingParameters.ClientBuildManagerParameter.PrecompilationFlags;
2448 _strongNameKeyFile = hostingParameters.ClientBuildManagerParameter.StrongNameKeyFile;
2449 _strongNameKeyContainer = hostingParameters.ClientBuildManagerParameter.StrongNameKeyContainer;
2451 // Check if we're precompiling to a target directory
2452 _precompTargetPhysicalDir = hostingParameters.PrecompilationTargetPhysicalDirectory;
2453 if (_precompTargetPhysicalDir == null)
2456 // Check if the target dir already exists and is not empty
2457 if (Util.IsNonEmptyDirectory(_precompTargetPhysicalDir)) {
2459 // If it's not empty and OverwriteTarget is off, fail
2460 if ((_precompilationFlags & PrecompilationFlags.OverwriteTarget) == 0) {
2461 throw new HttpException(SR.GetString(SR.Dir_not_empty));
2464 // Does it contain the precomp marker file
2466 bool precompiled = ReadPrecompMarkerFile(_precompTargetPhysicalDir, out updatable);
2468 // If not, refuse to delete the directory, even if OverwriteTarget is on (VSWhidbey 425095)
2470 throw new HttpException(SR.GetString(SR.Dir_not_empty_not_precomp));
2473 // The OverwriteTarget flag was specified, so delete the directory
2474 if (!DeletePrecompTargetDirectory()) {
2475 // If we failed to delete it, sleep 250 ms and try again, in case there is
2476 // an appdomain in the process of shutting down (the shut down would
2477 // have been triggered by the first delete attempt)
2478 Debug.Trace("BuildManager", "Failed to delete " + _precompTargetPhysicalDir + ". Sleeping and trying once more...");
2481 if (!DeletePrecompTargetDirectory()) {
2482 Debug.Trace("BuildManager", "Failed to delete " + _precompTargetPhysicalDir + ". Sleeping and trying once more...");
2483 // Try again after 1 second.
2486 // If we still couldn't delete it, fail
2487 if (!DeletePrecompTargetDirectory()) {
2488 throw new HttpException(SR.GetString(SR.Cant_delete_dir));
2494 // Create a marker file to mark the fact that this is a precompiled app
2495 CreatePrecompMarkerFile();
2498 private bool DeletePrecompTargetDirectory() {
2500 if (_precompTargetPhysicalDir != null) {
2501 // Go through all the files in the directory and delete them.
2502 foreach (FileData fileData in FileEnumerator.Create(_precompTargetPhysicalDir)) {
2504 if (fileData.IsDirectory) {
2505 Directory.Delete(fileData.FullName, true /*recursive*/);
2508 Util.DeleteFileNoException(fileData.FullName);
2514 catch (Exception e) {
2515 Debug.Trace("BuildManager", "DeletePrecompTargetDirectory failed: " + e.Message);
2520 return !Util.IsNonEmptyDirectory(_precompTargetPhysicalDir);
2523 private void FailIfPrecompiledApp() {
2525 if (IsPrecompiledApp) {
2526 throw new HttpException(SR.GetString(SR.Already_precomp));
2530 internal void PrecompileApp(ClientBuildManagerCallback callback, IEnumerable<string> excludedVirtualPaths) {
2532 // Remember the original setting
2533 bool skipTopLevelExceptions = SkipTopLevelCompilationExceptions;
2536 _cbmCallback = callback;
2538 // Don't stop on the first parse errors, process as many errors as possible.
2539 ThrowOnFirstParseError = false;
2541 // Don't skip top level compilation exceptions even called by CBM.
2542 SkipTopLevelCompilationExceptions = false;
2544 PrecompileApp(HttpRuntime.AppDomainAppVirtualPathObject, excludedVirtualPaths);
2547 // Revert to original setting
2548 SkipTopLevelCompilationExceptions = skipTopLevelExceptions;
2549 ThrowOnFirstParseError = true;
2551 _cbmCallback = null;
2555 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
2556 private void PrecompileApp(VirtualPath startingVirtualDir, IEnumerable<string> excludedVirtualPaths) {
2557 using (new ApplicationImpersonationContext()) {
2559 PerformingPrecompilation = true;
2561 PrecompileAppInternal(startingVirtualDir, excludedVirtualPaths);
2564 // If anything fails during precompilation, wipe out the target to avoid
2565 // leaving it in a random state (VSWhidbey 447338)
2566 DeletePrecompTargetDirectory();
2571 PerformingPrecompilation = false;
2576 private void PrecompileAppInternal(VirtualPath startingVirtualDir, IEnumerable<string> excludedVirtualPaths) {
2578 // If the app is already precompiled, fail
2579 FailIfPrecompiledApp();
2581 excludedVirtualPaths = excludedVirtualPaths ?? Enumerable.Empty<string>();
2582 _excludedCompilationPaths = excludedVirtualPaths.Select(path => VirtualPath.Create(UrlPath.Combine("~", path))).ToList();
2584 VirtualDirectory appVdir = startingVirtualDir.GetDirectory();
2586 EnsureTopLevelFilesCompiled();
2589 // Clear the parseError flag first
2590 _parseErrorReported = false;
2592 PrecompileWebDirectoriesRecursive(appVdir, topLevel: true);
2593 PrecompileThemeDirectories();
2595 catch (HttpParseException parseException) {
2596 // if nothing calls callback.reportparseerror yet, report the parse error.
2597 if (!_parseErrorReported) {
2598 ReportErrorsFromException(parseException);
2604 // Copy all the DLL's we compiled into the destination's bin directory (if any)
2605 if (_precompTargetPhysicalDir != null) {
2606 string targetBinDir = Path.Combine(_precompTargetPhysicalDir, HttpRuntime.BinDirectoryName);
2607 CopyCompiledAssembliesToDestinationBin(HttpRuntime.CodegenDirInternal, targetBinDir);
2610 // Copy all the static files to the destination directory (if any). We treat anything we
2611 // don't compile as a static file. It's better to do this at the end of the precompilation,
2612 // this way if any pages has errors (parse or compile), we never get to this step.
2613 if (_precompTargetPhysicalDir != null) {
2614 CopyStaticFilesRecursive(appVdir, _precompTargetPhysicalDir, topLevel: true);
2618 // Create a small file that marks that app as being precompiled
2619 private void CreatePrecompMarkerFile() {
2621 Debug.Assert(PrecompilingForDeployment);
2623 Directory.CreateDirectory(_precompTargetPhysicalDir);
2624 string precompMarkerFile = Path.Combine(_precompTargetPhysicalDir, precompMarkerFileName);
2626 using (StreamWriter writer = new StreamWriter(precompMarkerFile, false /*append*/, Encoding.UTF8)) {
2627 writer.Write("<precompiledApp version=\"2\" updatable=\"");
2629 // Write out a flag that determines if the precompiled app is updatable
2630 if (PrecompilingForUpdatableDeployment)
2631 writer.Write("true");
2633 writer.Write("false");
2634 writer.Write("\"/>");
2638 [SuppressMessage("Microsoft.Security", "MSEC1207:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")]
2639 private static bool ReadPrecompMarkerFile(string appRoot, out bool updatable) {
2643 // Get the full physical path to the precompilation market file
2644 string precompMarkerFile = Path.Combine(appRoot, precompMarkerFileName);
2646 // If the file doesn't exist at all, it's not a precompiled app
2647 if (!File.Exists(precompMarkerFile))
2650 XmlDocument doc = new XmlDocument();
2652 doc.Load(precompMarkerFile);
2655 // If we fail to read it for any reason, ignore it.
2659 // Get the root element, and make sure it's what we expect
2660 XmlNode root = doc.DocumentElement;
2661 Debug.Assert(root != null && root.Name == "precompiledApp");
2662 if (root == null || root.Name != "precompiledApp")
2665 // Check the updatable flag
2666 HandlerBase.GetAndRemoveBooleanAttribute(root, "updatable", ref updatable);
2672 * Are we precompiling the app for deployment (as opposed to in-place)
2674 internal static bool PrecompilingForDeployment {
2676 return (_theBuildManager._precompTargetPhysicalDir != null);
2680 internal static bool PrecompilingForUpdatableDeployment {
2682 // The updatebale mode only applies in deployment precompilation mode
2683 if (!PrecompilingForDeployment)
2686 return (_theBuildManager._precompilationFlags & PrecompilationFlags.Updatable) != 0;
2690 private static bool PrecompilingForCleanBuild {
2692 return (_theBuildManager._precompilationFlags & PrecompilationFlags.Clean) != 0;
2696 internal static bool PrecompilingWithDebugInfo {
2698 // The ForceDebug flag only applies in deployment precompilation mode
2699 if (!PrecompilingForDeployment)
2702 return (_theBuildManager._precompilationFlags & PrecompilationFlags.ForceDebug) != 0;
2706 internal static bool PrecompilingWithCodeAnalysisSymbol {
2708 return (_theBuildManager._precompilationFlags & PrecompilationFlags.CodeAnalysis) != 0;
2712 private static bool CompileWithFixedAssemblyNames {
2714 return (_theBuildManager._precompilationFlags & PrecompilationFlags.FixedNames) != 0;
2718 internal static bool CompileWithAllowPartiallyTrustedCallersAttribute {
2720 return (_theBuildManager._precompilationFlags & PrecompilationFlags.AllowPartiallyTrustedCallers) != 0;
2724 internal static bool CompileWithDelaySignAttribute {
2726 return (_theBuildManager._precompilationFlags & PrecompilationFlags.DelaySign) != 0;
2730 internal static bool IgnoreBadImageFormatException {
2732 return (_theBuildManager._precompilationFlags & PrecompilationFlags.IgnoreBadImageFormatException) != 0;
2736 internal static string StrongNameKeyFile {
2738 return _theBuildManager._strongNameKeyFile;
2742 internal static string StrongNameKeyContainer {
2744 return _theBuildManager._strongNameKeyContainer;
2748 // If we're in the process of precompiling for updatable deployment, this returns
2749 // a writer to the target file specified by the virtual path. This is used when the
2750 // deployed file needs to be different from the original (as is the case for aspx files).
2751 internal static TextWriter GetUpdatableDeploymentTargetWriter(VirtualPath virtualPath, Encoding fileEncoding) {
2753 Debug.Assert(fileEncoding != null);
2755 if (!PrecompilingForUpdatableDeployment)
2758 Debug.Assert(!virtualPath.IsRelative);
2760 string path = virtualPath.AppRelativeVirtualPathString;
2762 // Skip the "~/" to be left with the relative path
2763 path = path.Substring(2);
2765 // Combine it with the precomp target dir to get the full path
2766 string physicalPath = Path.Combine(_theBuildManager._precompTargetPhysicalDir, path);
2768 // Before trying to create the file, make sure the directory exists
2769 string physicalDir = Path.GetDirectoryName(physicalPath);
2770 Directory.CreateDirectory(physicalDir);
2772 return new StreamWriter(physicalPath, false /*append*/, fileEncoding);
2775 private bool IsPrecompiledAppInternal {
2777 if (!_isPrecompiledAppComputed) {
2778 _isPrecompiledApp = ReadPrecompMarkerFile(HttpRuntime.AppDomainAppPathInternal,
2779 out _isUpdatablePrecompiledApp);
2781 _isPrecompiledAppComputed = true;
2784 return _isPrecompiledApp;
2788 public static bool IsPrecompiledApp {
2790 return _theBuildManager.IsPrecompiledAppInternal;
2794 private bool IsUpdatablePrecompiledAppInternal {
2796 return IsPrecompiledApp && _isUpdatablePrecompiledApp;
2800 public static bool IsUpdatablePrecompiledApp {
2802 return _theBuildManager.IsUpdatablePrecompiledAppInternal;
2806 private bool IsNonUpdatablePrecompiledApp {
2808 return IsPrecompiledApp && !_isUpdatablePrecompiledApp;
2812 private bool IsExcludedFromPrecompilation(VirtualDirectory dir) {
2813 Debug.Assert(dir != null);
2814 return _excludedCompilationPaths.Any(path => UrlPath.IsEqualOrSubpath(path.VirtualPathString, dir.VirtualPath));
2817 private void PrecompileWebDirectoriesRecursive(VirtualDirectory vdir, bool topLevel) {
2819 // Precompile the children directory
2821 foreach (VirtualDirectory childVdir in vdir.Directories) {
2823 if (topLevel && _excludedTopLevelDirectories.Contains(childVdir.Name))
2826 // Exclude the special FrontPage directory (VSWhidbey 116727, 518602)
2827 if (childVdir.Name == "_vti_cnf")
2830 // Exclude target directory in precompilation scenarios
2831 if (SourceDirectoryIsInPrecompilationDestination(childVdir)) {
2835 if (IsExcludedFromPrecompilation(childVdir)) {
2839 PrecompileWebDirectoriesRecursive(childVdir, topLevel: false);
2842 // Precompile this directory
2844 // Set a flag to remember that we're in the process of precompiling. This
2845 // way, if BatchCompileWebDirectory ends up getting called again recursively
2846 // via CompileWebFile, we know that we cannot ignore errors.
2847 _precompilingApp = true;
2849 if (IsBatchEnabledForDirectory(vdir.VirtualPathObject)) {
2850 // batch everything if enabled
2851 BatchCompileWebDirectory(vdir, virtualDir: null, ignoreErrors: false);
2854 // if batching is disabled, compile each web file individually.
2855 NonBatchDirectoryCompiler dirCompiler = new NonBatchDirectoryCompiler(vdir);
2856 dirCompiler.Process();
2860 // Always restore the flag to false when we're done.
2861 _precompilingApp = false;
2865 private void PrecompileThemeDirectories() {
2866 string appPhysicalDir = Path.Combine(HttpRuntime.AppDomainAppPathInternal, HttpRuntime.ThemesDirectoryName);
2868 if (Directory.Exists(appPhysicalDir)) {
2869 string[] themeDirs = Directory.GetDirectories(appPhysicalDir);
2871 foreach (string themeDirPath in themeDirs) {
2872 string themeDirName = Path.GetFileName(themeDirPath);
2873 ThemeDirectoryCompiler.GetThemeBuildResultType(null /*context*/, themeDirName);
2879 * Recursively copy all the static files from the source directory to the
2880 * target directory of the precompilation
2882 private void CopyStaticFilesRecursive(VirtualDirectory sourceVdir, string destPhysicalDir,
2885 // Make sure the target physical dir has no relation with the source. It's important to
2886 // check at every new directory, because IIS apps can have disconnected virtual sub dirs,
2887 // making an app root check insufficient (VSWhidbey 426251)
2888 if (SourceDirectoryIsInPrecompilationDestination(sourceVdir)) {
2892 if (IsExcludedFromPrecompilation(sourceVdir)) {
2896 bool directoryCreationAttempted = false;
2898 foreach (VirtualFileBase child in sourceVdir.Children) {
2900 string destPhysicalSubDir = Path.Combine(destPhysicalDir, child.Name);
2902 if (child.IsDirectory) {
2904 // Skip the special top level directories, since they never contain relevant
2905 // static files. Note that we don't skip Themes, which does contain static files.
2907 (StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.CodeDirectoryName) ||
2908 StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.ResourcesDirectoryName) ||
2909 StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.WebRefDirectoryName))) {
2914 // Also, skip the LocalResources directory at any level, except when precompiling
2915 // for updatable deployment (in which case, we deploy the local resources file)
2916 if (!PrecompilingForUpdatableDeployment && StringUtil.EqualsIgnoreCase(child.Name,
2917 HttpRuntime.LocalResourcesDirectoryName)) {
2921 CopyStaticFilesRecursive(child as VirtualDirectory, destPhysicalSubDir, topLevel: false);
2925 // Create the destination directory if needed
2926 if (!directoryCreationAttempted) {
2927 directoryCreationAttempted = true;
2928 Directory.CreateDirectory(destPhysicalDir);
2931 // Copy the file as appropriate based on its extension
2932 CopyPrecompiledFile(child as VirtualFile, destPhysicalSubDir);
2937 * Copy all the assemblies from the codegen dir into the bin directory of the
2938 * target precompiled app.
2940 private void CopyCompiledAssembliesToDestinationBin(string fromDir, string toDir) {
2942 bool createdDirectory = false;
2944 foreach (FileData fileData in FileEnumerator.Create(fromDir)) {
2945 // Windows OS Bug 1981578
2946 // Create a new directory only if there is something in the directory.
2947 if (!createdDirectory)
2948 Directory.CreateDirectory(toDir);
2949 createdDirectory = true;
2951 // Recurse on subdirectories.if they contain culture files
2952 if (fileData.IsDirectory) {
2954 if (Util.IsCultureName(fileData.Name)) {
2955 string fromSubDir = Path.Combine(fromDir, fileData.Name);
2956 string toSubDir = Path.Combine(toDir, fileData.Name);
2957 CopyCompiledAssembliesToDestinationBin(fromSubDir, toSubDir);
2963 // Only process DLL's and PDB's
2964 string extension = Path.GetExtension(fileData.Name);
2965 if (extension != ".dll" && extension != ".pdb")
2968 // Do not copy the file to the target folder if it has been already
2969 // marked for deletion - Dev10 bug 676794
2970 if (DiskBuildResultCache.HasDotDeleteFile(fileData.FullName)) {
2974 string sourcePhysicalPath = Path.Combine(fromDir, fileData.Name);
2975 string destPhysicalPath = Path.Combine(toDir, fileData.Name);
2977 // Copy the file to the destination
2979 File.Copy(sourcePhysicalPath, destPhysicalPath, true /*overwrite*/);
2983 // Copy one file from the source app to the precompiled app
2984 private void CopyPrecompiledFile(VirtualFile vfile, string destPhysicalPath) {
2988 if (CompilationUtil.NeedToCopyFile(vfile.VirtualPathObject, PrecompilingForUpdatableDeployment,
2992 string sourcePhysicalPath = HostingEnvironment.MapPathInternal(vfile.VirtualPath);
2994 // The file could already exist with updatable precompilation, since we would create the modified file
2995 // earlier during processing of a code beside page.
2996 if (File.Exists(destPhysicalPath)) {
2998 // In that case, we still need to fix it up to insert the correct type string in the
2999 // inherits attribute (VSWhidbey 467936)
3001 // First, get the just-compiled BuildResult. It should always exist
3002 BuildResultCompiledType result = GetVPathBuildResult(null, vfile.VirtualPathObject,
3003 true /*noBuild*/, false /*allowCrossApp*/) as BuildResultCompiledType;
3004 Debug.Assert(result != null);
3006 // VSWhidbey 527299. Need to use the same encoding of the original file to
3007 // read and write to the new file.
3008 Encoding encoding = Util.GetEncodingFromConfigPath(vfile.VirtualPathObject);
3011 string newAspxFile = Util.StringFromFile(destPhysicalPath, ref encoding);
3013 // Replace the placeholder token by the true type with the assembly
3014 newAspxFile = newAspxFile.Replace(UpdatableInheritReplacementToken,
3015 Util.GetAssemblyQualifiedTypeName(result.ResultType));
3017 // Write the modified file back with the correct inherits type string
3018 StreamWriter writer = new StreamWriter(destPhysicalPath, false /* append */, encoding);
3019 writer.Write(newAspxFile);
3023 // Just copy the file to the destination
3024 File.Copy(sourcePhysicalPath, destPhysicalPath, false /*overwrite*/);
3027 // If it has a readonly attribute, clear it on the destination (VSWhidbey 122359)
3028 Util.ClearReadOnlyAttribute(destPhysicalPath);
3032 // Create the stub file, with a helpful static message
3033 StreamWriter writer = new StreamWriter(destPhysicalPath);
3034 writer.Write(SR.GetString(SR.Precomp_stub_file));
3040 // Make sure the target physical dir has no relation with the source. Return true if it does.
3041 private bool SourceDirectoryIsInPrecompilationDestination(VirtualDirectory sourceDir) {
3042 // Alwasy return false for in-place precompilations or non-precompilation scenarios.
3043 if (_precompTargetPhysicalDir == null) {
3047 string sourcePhysicalDir = HostingEnvironment.MapPathInternal(sourceDir.VirtualPath);
3049 // Make sure they're normalized and end with a '\' before comparing (VSWhidbey 452554)
3050 sourcePhysicalDir = FileUtil.FixUpPhysicalDirectory(sourcePhysicalDir);
3051 string destPhysicalDir = FileUtil.FixUpPhysicalDirectory(_precompTargetPhysicalDir);
3053 return StringUtil.StringStartsWithIgnoreCase(sourcePhysicalDir, destPhysicalDir);
3056 internal static void ReportDirectoryCompilationProgress(VirtualPath virtualDir) {
3058 // Nothing to do if there is no CBM callback
3059 if (CBMCallback == null)
3062 // Don't report anything if the directory doesn't exist
3063 if (!virtualDir.DirectoryExists())
3066 string message = SR.GetString(SR.Directory_progress, virtualDir.VirtualPathString);
3067 CBMCallback.ReportProgress(message);
3077 /// Compiles a file given its virtual path, using the appropriate BuildProvider (based
3078 /// on the file's extension). The compiled type is returned.
3079 /// This methods performs both memory and disk caching of the compiled Type.
3081 public static Type GetCompiledType(string virtualPath) {
3082 if (virtualPath == null) {
3083 throw new ArgumentNullException("virtualPath");
3086 return GetCompiledType(VirtualPath.Create(virtualPath));
3089 // This method is called by BuildManagerHost thru CBM
3090 internal static Type GetCompiledType(VirtualPath virtualPath, ClientBuildManagerCallback callback) {
3091 // Remember the original setting
3092 bool skipTopLevelExceptions = SkipTopLevelCompilationExceptions;
3093 bool throwOnFirstParseError = ThrowOnFirstParseError;
3096 // Don't skip top level compilation exceptions even called by CBM.
3097 SkipTopLevelCompilationExceptions = false;
3099 // Don't stop on the first parse error, process as many errors as possible.
3100 ThrowOnFirstParseError = false;
3102 _theBuildManager._cbmCallback = callback;
3103 return GetCompiledType(virtualPath);
3106 _theBuildManager._cbmCallback = null;
3108 // Revert to original setting
3109 SkipTopLevelCompilationExceptions = skipTopLevelExceptions;
3111 ThrowOnFirstParseError = throwOnFirstParseError;
3115 internal static Type GetCompiledType(VirtualPath virtualPath) {
3116 ITypedWebObjectFactory factory = GetVirtualPathObjectFactory(virtualPath,
3117 null /*context*/, false /*allowCrossApp*/);
3119 BuildResultCompiledType resultType = factory as BuildResultCompiledType;
3120 if (resultType == null) return null;
3122 return resultType.ResultType;
3125 /// Process a file based on its virtual path, and instantiate the result. This API works for both
3126 /// compiled and no compile pages. requiredBaseType specifies a type from which the resulting
3127 /// object must derive. If it doesn't, the API fails without instantiating the object.
3128 public static object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType) {
3129 VirtualPath virtualPathObject = VirtualPath.CreateNonRelative(virtualPath);
3130 return CreateInstanceFromVirtualPath(virtualPathObject, requiredBaseType,
3131 null /*context*/, false /*allowCrossApp*/);
3135 /// Process a file given its virtual path, using the appropriate BuildProvider (based
3136 /// on the file's extension). The result is then instantiated and returned.
3138 internal static object CreateInstanceFromVirtualPath(VirtualPath virtualPath,
3139 Type requiredBaseType, HttpContext context, bool allowCrossApp) {
3141 ITypedWebObjectFactory objectFactory = GetVirtualPathObjectFactory(virtualPath, context, allowCrossApp);
3142 if (objectFactory == null) return null;
3144 // Make sure it has the required base type (VSWhidbey 516771)
3145 Util.CheckAssignableType(requiredBaseType, objectFactory.InstantiatedType);
3147 // impersonate client while executing page ctor (see ASURT 89712)
3148 // (compilation is done while not impersonating client)
3151 using (new ClientImpersonationContext(context)) {
3152 instance = objectFactory.CreateInstance();
3158 public static IWebObjectFactory GetObjectFactory(string virtualPath, bool throwIfNotFound) {
3159 ITypedWebObjectFactory factory = GetVirtualPathObjectFactory(VirtualPath.Create(virtualPath),
3160 null /*context*/, false /*allowCrossApp*/, throwIfNotFound);
3164 private static ITypedWebObjectFactory GetVirtualPathObjectFactory(VirtualPath virtualPath,
3165 HttpContext context, bool allowCrossApp) {
3166 return GetVirtualPathObjectFactory(virtualPath, context, allowCrossApp, true /*throwIfNotFound*/);
3170 /// Process a file given its virtual path, using the appropriate BuildProvider (based
3171 /// on the file's extension). The ITypedWebObjectFactory is returned.
3172 /// This methods performs both memory and disk caching of the compiled Type.
3174 private static ITypedWebObjectFactory GetVirtualPathObjectFactory(VirtualPath virtualPath,
3175 HttpContext context, bool allowCrossApp, bool throwIfNotFound) {
3177 if (virtualPath == null)
3178 throw new ArgumentNullException("virtualPath");
3180 // Throw here immediately if top level exception exists.
3181 // This is because EnsureTopLevelFilesCompiled (where the exception is thrown)
3182 // might not be called.
3183 if (_theBuildManager._topLevelFileCompilationException != null) {
3184 _theBuildManager.ReportTopLevelCompilationException();
3187 ITypedWebObjectFactory objectFactory;
3188 BuildResult buildResult;
3190 // We need to assert here since there may be user code on the stack,
3191 // and code may demand UnmanagedCode permission. But if we're in full trust,
3192 // or noAssert is true, skip the assert for perf reasons (VSWhidbey 146871, 500699)
3194 // In regard to previous comment, in v2/3.5 we only needed to assert when we were
3195 // running in partial trust and user code was on the stack. In v4, we need to assert
3196 // whenever we are running in partial turst, because the AppDomain is homogenous.
3197 if (HttpRuntime.IsFullTrust) {
3198 buildResult = GetVPathBuildResultWithNoAssert(
3199 context, virtualPath, false /*noBuild*/, allowCrossApp, false /*allowBuildInPrecompile*/, throwIfNotFound);
3202 buildResult = GetVPathBuildResultWithAssert(
3203 context, virtualPath, false /*noBuild*/, allowCrossApp, false /*allowBuildInPrecompile*/, throwIfNotFound);
3207 // The returned build result may not always be castable to ITypedWebObjectFactory.
3208 objectFactory = buildResult as ITypedWebObjectFactory;
3210 return objectFactory;
3214 /// Compiles a file given its virtual path, using the appropriate BuildProvider (based
3215 /// on the file's extension). The compiled assembly is returned.
3216 /// This methods performs both memory and disk caching of the compiled assembly.
3218 public static Assembly GetCompiledAssembly(string virtualPath) {
3220 BuildResult result = GetVPathBuildResult(VirtualPath.Create(virtualPath));
3221 if (result == null) return null;
3223 BuildResultCompiledAssemblyBase resultAssembly = result as BuildResultCompiledAssemblyBase;
3224 if (resultAssembly == null) return null;
3226 return resultAssembly.ResultAssembly;
3231 /// Compiles a file given its virtual path, using the appropriate BuildProvider (based
3232 /// on the file's extension). If the BuildProvider chose to persist a custom
3233 /// string, the string is returned.
3234 /// This methods performs both memory and disk caching.
3236 public static string GetCompiledCustomString(string virtualPath) {
3238 BuildResult result = GetVPathBuildResult(VirtualPath.Create(virtualPath));
3239 if (result == null) return null;
3241 BuildResultCustomString resultCustomString = result as BuildResultCustomString;
3242 if (resultCustomString == null) return null;
3244 return resultCustomString.CustomString;
3248 /// Returns the BuildDependencySet for the passed in virtualPath, assuming
3249 /// that information is cached. Otherwise, return null.
3251 public static BuildDependencySet GetCachedBuildDependencySet(
3252 HttpContext context, string virtualPath) {
3253 return GetCachedBuildDependencySet(context, virtualPath, ensureIsUpToDate: true);
3256 public static BuildDependencySet GetCachedBuildDependencySet(
3257 HttpContext context, string virtualPath, bool ensureIsUpToDate) {
3259 BuildResult result = GetVPathBuildResult(context, VirtualPath.Create(virtualPath),
3260 true /*noBuild*/, false /*allowCrossApp*/, allowBuildInPrecompile: false, ensureIsUpToDate: ensureIsUpToDate);
3262 // If it's not cached, return null
3266 // We found it in the cache. Wrap it with a BuildDependencySet object.
3267 return new BuildDependencySet(result);
3271 /// Returns the target framework moniker for the current web site. For framework versions less than
3272 /// 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
3273 /// web sites to reference 3.0 assemblies.
3275 public static FrameworkName TargetFramework {
3277 return MultiTargetingUtil.TargetFrameworkName;
3281 private Assembly ResolveAssembly(object sender, ResolveEventArgs e) {
3283 if (_assemblyResolveMapping == null)
3286 string name = e.Name;
3287 Assembly assembly = (Assembly)_assemblyResolveMapping[name];
3289 // Return the assembly if we have it in our mapping (VSWhidbey 276776)
3290 if (assembly != null) {
3294 // Get the normalized assembly name from random name (VSWhidbey 380793)
3295 String normalizedName = GetNormalizedCodeAssemblyName(name);
3296 if (normalizedName != null) {
3297 return (Assembly)_assemblyResolveMapping[normalizedName];
3303 internal static string GetNormalizedCodeAssemblyName(string assemblyName) {
3304 // Return the main code assembly.
3305 if (assemblyName.StartsWith(CodeDirectoryAssemblyName, StringComparison.Ordinal)) {
3306 return CodeDirectoryAssemblyName;
3309 // Check the sub code directories.
3310 CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
3311 foreach (CodeSubDirectory directory in codeSubDirectories) {
3312 if (assemblyName.StartsWith(SubCodeDirectoryAssemblyNamePrefix + directory.AssemblyName + ".", StringComparison.Ordinal)) {
3313 return directory.AssemblyName;
3320 internal static string GetNormalizedTypeName(Type t) {
3321 string assemblyFullName = t.Assembly.FullName;
3322 string normalizedCodeAssemblyName = GetNormalizedCodeAssemblyName(assemblyFullName);
3323 if (normalizedCodeAssemblyName == null) {
3324 return t.AssemblyQualifiedName;
3327 string normalizedTypeName = t.FullName + ", " + normalizedCodeAssemblyName;
3328 return normalizedTypeName;
3332 /// Temporary subdirectory under the codegen folder for buildproviders to generate embedded resource files.
3334 internal static string CodegenResourceDir {
3336 string resxDir = _theBuildManager._codegenResourceDir;
3337 if (resxDir == null) {
3338 resxDir = Path.Combine(HttpRuntime.CodegenDirInternal, CodegenResourceDirectoryName);
3339 _theBuildManager._codegenResourceDir = resxDir;
3345 // The Use Cache lives under the codegen folder
3346 private static string _userCachePath;
3347 private static string UserCachePath {
3349 if (_userCachePath == null) {
3350 // Build the full path to the User Cache folder
3351 string userCachePath = Path.Combine(HttpRuntime.CodegenDirInternal, "UserCache");
3353 // Create it if it doesn't exist
3354 if (!Directory.Exists(userCachePath)) {
3355 Directory.CreateDirectory(userCachePath);
3358 _userCachePath = userCachePath;
3361 return _userCachePath;
3365 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
3366 Justification = "Too late in the loc process to add an exception message.")]
3367 private static string GetUserCacheFilePath(string fileName) {
3368 string path = Path.Combine(UserCachePath, fileName);
3370 // Make sure that the full path's directory is exactly the User Cache folder. This prevents creating files in any other folders
3371 if (Path.GetDirectoryName(path) != UserCachePath) {
3372 throw new ArgumentException();
3378 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity",
3379 Justification = "This is the correct Assert for the situation.")]
3380 public static Stream CreateCachedFile(string fileName) {
3381 new FileIOPermission(FileIOPermissionAccess.AllAccess, HttpRuntime.CodegenDirInternal).Assert();
3383 // Get the path to the file in the User Cache folder
3384 string path = GetUserCacheFilePath(fileName);
3386 return File.Create(path);
3389 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity",
3390 Justification = "This is the correct Assert for the situation.")]
3391 public static Stream ReadCachedFile(string fileName) {
3392 new FileIOPermission(FileIOPermissionAccess.AllAccess, HttpRuntime.CodegenDirInternal).Assert();
3394 // Get the path to the file in the User Cache folder
3395 string path = GetUserCacheFilePath(fileName);
3397 // If the file doesn't exist, just return null, to convey a cache miss
3398 if (!File.Exists(path))
3401 return File.OpenRead(path);
3405 internal enum CompilationStage {
3406 PreTopLevelFiles = 0, // Before EnsureTopLevelFilesCompiled() is called
3407 TopLevelFiles = 1, // In EnsureTopLevelFilesCompiled() but before building global.asax
3408 GlobalAsax = 2, // While building global.asax
3409 BrowserCapabilities = 3, // While building browserCap
3410 AfterTopLevelFiles = 4 // After EnsureTopLevelFilesCompiled() is called
3413 internal enum PreStartInitStage {
3419 internal class AssemblyReferenceInfo {
3420 internal Assembly Assembly;
3421 internal int ReferenceIndex;
3423 internal AssemblyReferenceInfo(int referenceIndex) {
3424 ReferenceIndex = referenceIndex;