Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Web / Compilation / BuildManager.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="BuildManager.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /************************************************************************************************************/
8
9
10
11 namespace System.Web.Compilation {
12
13     using System;
14     using System.CodeDom.Compiler;
15     using System.Collections;
16     using System.Collections.Generic;
17     using System.Diagnostics.CodeAnalysis;
18     using System.Globalization;
19     using System.IO;
20     using System.Linq;
21     using System.Reflection;
22     using System.Runtime.Remoting.Messaging;
23     using System.Runtime.Versioning;
24     using System.Security;
25     using System.Security.Permissions;
26     using System.Text;
27     using System.Threading;
28     using System.Web.Configuration;
29     using System.Web.Hosting;
30     using System.Web.Profile;
31     using System.Web.UI;
32     using System.Web.Util;
33     using System.Xml;
34
35
36     /// <devdoc>
37     ///    <para>
38     ///       IProvider compilation related services
39     ///    </para>
40     /// </devdoc>
41     public sealed class BuildManager {
42
43         /// Contants relating to generated assembly names
44
45         // All generated assemblies start with this prefix
46         internal const string AssemblyNamePrefix = "App_";
47
48         // Web assemblies are the assemblies generated from web files (aspx, ascx, ...)
49         internal const string WebAssemblyNamePrefix = AssemblyNamePrefix + "Web_";
50
51         internal const string AppThemeAssemblyNamePrefix = AssemblyNamePrefix + "Theme_";
52         internal const string GlobalThemeAssemblyNamePrefix = AssemblyNamePrefix + "GlobalTheme_";
53         internal const string AppBrowserCapAssemblyNamePrefix = AssemblyNamePrefix + "Browsers";
54
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;
61
62         private const string LicensesAssemblyName = AssemblyNamePrefix + "Licenses";
63
64         internal const string UpdatableInheritReplacementToken = "__ASPNET_INHERITS";
65
66         // Name of the temporary subdirectory under the codegen folder for buildproviders to generate embedded resource files.
67         private const string CodegenResourceDirectoryName = "ResX";
68
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; } }
76
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
85
86         private string _strongNameKeyFile;
87         private string _strongNameKeyContainer;
88
89         private string _codegenResourceDir;
90
91         private bool _optimizeCompilations;
92         internal static bool OptimizeCompilations {
93             get { return _theBuildManager._optimizeCompilations; }
94         }
95
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; }
101         }
102
103         private BuildResultCache[] _caches;
104         private StandardDiskBuildResultCache _codeGenCache;
105         private MemoryBuildResultCache _memoryCache;
106
107         private bool _topLevelFilesCompiledStarted;
108         private bool _topLevelFilesCompiledCompleted;
109         private Exception _topLevelFileCompilationException;
110
111         private BuildResultCompiledGlobalAsaxType _globalAsaxBuildResult;
112         private Type _profileType;
113
114         // Special top level directories that are treated differently from regular web directories
115         // during precompilation (e.g. App_Code)
116         private StringSet _excludedTopLevelDirectories;
117
118         // Directories that are not requestable
119         private StringSet _forbiddenTopLevelDirectories;
120
121         private StringSet _excludedCodeSubdirectories;
122
123         private List<VirtualPath> _excludedCompilationPaths;
124
125         private CompilationStage _compilationStage = CompilationStage.PreTopLevelFiles;
126         internal static CompilationStage CompilationStage { get { return _theBuildManager._compilationStage; } }
127
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; } }
132
133         private BuildManager() { }
134
135         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
136         internal static bool InitializeBuildManager() {
137
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);
142             }
143
144             if (!_theBuildManagerInitialized) {
145
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)
150                     return false;
151
152                 // Likewise, if the trust level has not yet been determined, skip the init (VSWhidbey 422311)
153                 if (HttpRuntime.TrustLevel == null)
154                     return false;
155
156                 _theBuildManagerInitialized = true;
157                 try {
158                     _theBuildManager.Initialize();
159                 }
160                 catch (Exception e) {
161                     _theBuildManagerInitialized = false;
162                     _initializeException = e;
163                     throw;
164                 }
165             }
166
167             return true;
168         }
169
170         private ClientBuildManagerCallback _cbmCallback;
171         internal static ClientBuildManagerCallback CBMCallback { get { return _theBuildManager._cbmCallback; } }
172
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);
179             }
180         }
181
182         private void ReportTopLevelCompilationException() {
183             Debug.Assert(_topLevelFileCompilationException != null);
184
185             // Try to report the cached error to the CBM callback
186             ReportErrorsFromException(_topLevelFileCompilationException);
187
188             // We need to wrap it in a new exception, otherwise we lose the original stack.
189             throw new HttpException(_topLevelFileCompilationException.Message,
190                 _topLevelFileCompilationException);
191         }
192
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)
197                 return;
198
199             // Call the CBM callback as appropriate, based on the type of exception
200
201             if (e is HttpCompileException) {
202                 CompilerResults results = ((HttpCompileException)e).Results;
203                 foreach (CompilerError error in results.Errors) {
204                     BuildManager.CBMCallback.ReportCompilerError(error);
205                 }
206             }
207             else if (e is HttpParseException) {
208                 foreach (ParserError parseError in ((HttpParseException)e).ParserErrors) {
209                     ReportParseError(parseError);
210                 }
211             }
212         }
213
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,
219         };
220
221         private List<Assembly> TopLevelReferencedAssemblies { get { return _topLevelReferencedAssemblies; } }
222
223         private Dictionary<String, AssemblyReferenceInfo> _topLevelAssembliesIndexTable;
224         private IDictionary<String, AssemblyReferenceInfo> TopLevelAssembliesIndexTable { get { return _topLevelAssembliesIndexTable; } }
225
226         private Dictionary<String, String> _generatedFileTable;
227         internal static Dictionary<String, String> GenerateFileTable {
228             get {
229                 if (_theBuildManager._generatedFileTable == null) {
230                     _theBuildManager._generatedFileTable = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
231                 }
232
233                 return _theBuildManager._generatedFileTable;
234             }
235         }
236
237         private ArrayList _codeAssemblies;
238         public static IList CodeAssemblies {
239             get {
240                 _theBuildManager.EnsureTopLevelFilesCompiled();
241                 return _theBuildManager._codeAssemblies;
242             }
243         }
244
245         private IDictionary _assemblyResolveMapping;
246
247         private Assembly _appResourcesAssembly;
248         internal static Assembly AppResourcesAssembly { get { return _theBuildManager._appResourcesAssembly; } }
249
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; }
256         }
257
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; }
264         }
265
266         private bool _skipTopLevelCompilationExceptions;
267         internal static bool SkipTopLevelCompilationExceptions {
268             get { return _theBuildManager._skipTopLevelCompilationExceptions; }
269             set { _theBuildManager._skipTopLevelCompilationExceptions = value; }
270         }
271
272         private static HashSet<Assembly> s_dynamicallyAddedReferencedAssembly = new HashSet<Assembly>();
273
274         public static void AddReferencedAssembly(Assembly assembly) {
275             if (assembly == null) {
276                 throw new ArgumentNullException("assembly");
277             }
278             ThrowIfPreAppStartNotRunning();
279
280             s_dynamicallyAddedReferencedAssembly.Add(assembly);
281         }
282
283         /*
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
286          */
287         internal static ICollection GetReferencedAssemblies(CompilationSection compConfig, int removeIndex) {
288             AssemblySet referencedAssemblies = new AssemblySet();
289
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) {
294                     lock (compConfig) {
295                         assemblies = a.AssemblyInternal;
296                         if (assemblies == null)
297                             // 
298                             assemblies = a.AssemblyInternal = compConfig.LoadAssembly(a);
299                     }
300                 }
301
302                 for (int i = 0; i < assemblies.Length; i++) {
303                     if (assemblies[i] != null) {
304                         referencedAssemblies.Add(assemblies[i]);
305                     }
306                 }
307             }
308
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]);
312             }
313
314             // 
315
316             foreach (Assembly assembly in s_dynamicallyAddedReferencedAssembly) {
317                 referencedAssemblies.Add(assembly);
318             }
319
320             return referencedAssemblies;
321         }
322
323         internal static ICollection GetReferencedAssemblies(CompilationSection compConfig) {
324
325             // Start by cloning the top level referenced assemblies (code + global.asax + etc...)
326             AssemblySet referencedAssemblies = AssemblySet.Create(
327                 TheBuildManager.TopLevelReferencedAssemblies);
328
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) {
333                     lock (compConfig) {
334                         assemblies = a.AssemblyInternal;
335                         if (assemblies == null)
336                             // 
337                             assemblies = a.AssemblyInternal = compConfig.LoadAssembly(a);
338                     }
339                 }
340
341                 for (int i = 0; i < assemblies.Length; i++) {
342                     if (assemblies[i] != null) {
343                         referencedAssemblies.Add(assemblies[i]);
344                     }
345                 }
346             }
347
348             // 
349
350             foreach (Assembly assembly in s_dynamicallyAddedReferencedAssembly) {
351                 referencedAssemblies.Add(assembly);
352             }
353
354             return referencedAssemblies;
355         }
356
357
358         /*
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.
362          */
363
364         /// <devdoc>
365         /// Returns the assemblies referenced at the root application level of the current appF
366         /// </devdoc>
367         public static ICollection GetReferencedAssemblies() {
368             CompilationSection compConfig = MTConfigUtil.GetCompilationAppConfig();
369
370             _theBuildManager.EnsureTopLevelFilesCompiled();
371
372             return GetReferencedAssemblies(compConfig);
373         }
374
375         /// <summary>
376         /// Specifies a string representing a dependency that the BuildManager factors when determining if a clean build is required.
377         /// </summary>
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");
382             }
383             BuildManager.ThrowIfPreAppStartNotRunning();
384             _theBuildManager._preAppStartHashCodeCombiner.AddObject(dependency);
385         }
386
387         /*
388          * Perform initialization work that should only be done once (per app domain).
389          */
390         private void Initialize() {
391
392             Debug.Assert(_caches == null);
393
394             // Register an AssemblyResolve event
395             AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.ResolveAssembly);
396
397             _globalAsaxVirtualPath = HttpRuntime.AppDomainAppVirtualPathObject.SimpleCombine(
398                 HttpApplicationFactory.applicationFileName);
399
400             _webHashFilePath = Path.Combine(HttpRuntime.CodegenDirInternal, "hash\\hash.web");
401
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;
406
407             // Deal with precompilation if we're in that mode
408             SetPrecompilationInfo(HostingEnvironment.HostingParameters);
409
410             MultiTargetingUtil.EnsureFrameworkNamesInitialized();
411
412             // The init code depends on whether we're precompiling or running an app
413             if (_precompTargetPhysicalDir != null) {
414
415                 // If the app is already precompiled, fail
416                 FailIfPrecompiledApp();
417
418                 PrecompilationModeInitialize();
419             }
420             else {
421                 // Check if this application has been precompiled by aspnet_compiler.exe
422                 if (IsPrecompiledApp) {
423                     PrecompiledAppRuntimeModeInitialize();
424                 }
425                 else {
426                     RegularAppRuntimeModeInitialize();
427                 }
428             }
429
430             _scriptVirtualDir = Util.GetScriptLocation();
431
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);
440
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);
450
451             LoadLicensesAssemblyIfExists();
452         }
453
454         /*
455          * Init code used when we are running a non-precompiled app
456          */
457         private void RegularAppRuntimeModeInitialize() {
458
459             //
460             // Initialize the caches
461             //
462
463             // Always try the memory cache first
464             _memoryCache = new MemoryBuildResultCache(HttpRuntime.CacheInternal);
465
466             // Use the standard disk cache for regular apps
467             _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
468
469             _caches = new BuildResultCache[] { _memoryCache, _codeGenCache };
470         }
471
472         /*
473          * Init code used when we are running a precompiled app
474          */
475         private void PrecompiledAppRuntimeModeInitialize() {
476
477             //
478             // Initialize the caches
479             //
480
481             // Always try the memory cache first
482             _memoryCache = new MemoryBuildResultCache(HttpRuntime.CacheInternal);
483
484             // Used the precomp cache for precompiled apps
485             BuildResultCache preCompCache = new PrecompiledSiteDiskBuildResultCache(
486                 HttpRuntime.BinDirectoryInternal);
487
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.
490
491             _codeGenCache = new StandardDiskBuildResultCache(HttpRuntime.CodegenDirInternal);
492
493             _caches = new BuildResultCache[] { _memoryCache, preCompCache, _codeGenCache };
494         }
495
496         /*
497          * Init code used when we are precompiling an app
498          */
499         private void PrecompilationModeInitialize() {
500
501             // We are precompiling an app
502
503             // Always try the memory cache first
504             _memoryCache = new MemoryBuildResultCache(HttpRuntime.CacheInternal);
505
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);
509
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);
516             }
517             else {
518                 preCompilationCache = new PrecompilerDiskBuildResultCache(targetBinDir);
519             }
520
521             _caches = new BuildResultCache[] { _memoryCache, preCompilationCache, _codeGenCache };
522         }
523
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);
529             }
530         }
531
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)) {
538                 return;
539             }
540
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);
545         }
546
547         private long CheckTopLevelFilesUpToDate(long cachedHash) {
548             bool gotLock = false;
549             try {
550                 // Grab the compilation mutex, since this method accesses the codegen files
551                 CompilationLock.GetLock(ref gotLock);
552
553                 return CheckTopLevelFilesUpToDateInternal(cachedHash);
554             }
555             finally {
556                 // Always release the mutex if we had taken it
557                 if (gotLock) {
558                     CompilationLock.ReleaseLock();
559                 }
560             }
561         }
562
563         /*
564          * Check if the top level files are up to date, and cleanup the codegendir
565          * if they are not.
566          */
567         private long CheckTopLevelFilesUpToDateInternal(long cachedHash) {
568             Debug.Trace("BuildManager", "specialFilesCombinedHash=" + cachedHash);
569             var specialFilesHashCodeCombiner = new HashCodeCombiner();
570
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();
575             }
576
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)
580             // - ~\Bin directory
581             // - ~\Resource directory
582             // - ~\WebReferences directory
583             // - ~\Code directory
584             // - global.asax
585
586             // Add a check for the app's physical path, in case it changes (ASURT 12975)
587             specialFilesHashCodeCombiner.AddObject(HttpRuntime.AppDomainAppPathInternal);
588
589             // Process System.Web.dll
590             string aspBinaryFileName = typeof(HttpRuntime).Module.FullyQualifiedName;
591             if (!AppSettings.PortableCompilationOutput) {
592                 specialFilesHashCodeCombiner.AddFile(aspBinaryFileName);
593             }
594             else {
595                 specialFilesHashCodeCombiner.AddExistingFileVersion(aspBinaryFileName);
596             }
597
598             // Process machine.config
599             string machineConfigFileName = HttpConfigurationSystem.MachineConfigurationFilePath;
600             if (!AppSettings.PortableCompilationOutput) {
601                 specialFilesHashCodeCombiner.AddFile(machineConfigFileName);
602             }
603             else {
604                 specialFilesHashCodeCombiner.AddFileContentHash(machineConfigFileName);
605             }
606
607             // Process root web.config
608             string rootWebConfigFileName = HttpConfigurationSystem.RootWebConfigurationFilePath;
609             if (!AppSettings.PortableCompilationOutput) {
610                 specialFilesHashCodeCombiner.AddFile(rootWebConfigFileName);
611             }
612             else {
613                 specialFilesHashCodeCombiner.AddFileContentHash(rootWebConfigFileName);
614             }
615
616             RuntimeConfig appConfig = RuntimeConfig.GetAppConfig();
617             CompilationSection compConfig = appConfig.Compilation;
618
619             // Ignore the OptimizeCompilations flag in ClientBuildManager mode
620             if (!BuildManagerHost.InClientBuildManager) {
621                 _optimizeCompilations = compConfig.OptimizeCompilations;
622             }
623
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);
632
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());
636
637                 specialFilesHashCodeCombiner.AddDirectory(HttpRuntime.WebRefDirectoryVirtualPath.MapPathInternal());
638
639                 specialFilesHashCodeCombiner.AddDirectory(HttpRuntime.CodeDirectoryVirtualPath.MapPathInternal());
640
641                 // Add a dependency on the global asax file.
642                 specialFilesHashCodeCombiner.AddFile(GlobalAsaxVirtualPath.MapPathInternal());
643             }
644
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);
649
650             ProfileSection profileSection = appConfig.Profile;
651             specialFilesHashCodeCombiner.AddObject(profileSection.RecompilationHash);
652
653             // Add a dependency on file encoding (DevDiv 4560)
654             specialFilesHashCodeCombiner.AddObject(appConfig.Globalization.FileEncoding);
655
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);
660
661             // Add a dependency on whether profile is enabled
662             specialFilesHashCodeCombiner.AddObject(ProfileManager.Enabled);
663
664             // Add a dependency to the force debug flag.
665             specialFilesHashCodeCombiner.AddObject(PrecompilingWithDebugInfo);
666
667             CheckCodeGenFiles(specialFilesHashCodeCombiner.CombinedHash, cachedHash);
668             return specialFilesHashCodeCombiner.CombinedHash;
669         }
670
671         private void AfterPreAppStartExecute(Tuple<long, long> currentHash, Tuple<long, long> cachedTopLevelFilesHash) {
672             bool gotLock = false;
673             try {
674                 // Grab the compilation mutex, since this method accesses the codegen files
675                 CompilationLock.GetLock(ref gotLock);
676
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);
679
680                 if (!cachedTopLevelFilesHash.Equals(currentHash)) {
681                     // Hash has changed. Persist it to disk
682                     _codeGenCache.SavePreservedSpecialFilesCombinedHash(currentHash);
683                 }
684
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);
691             }
692             finally {
693                 // Always release the mutex if we had taken it
694                 if (gotLock) {
695                     CompilationLock.ReleaseLock();
696                 }
697             }
698         }
699
700         private void CheckCodeGenFiles(long currentHash, long cachedTopLevelFilesHash) {
701             // Store the top level hash
702             s_topLevelHash = currentHash;
703
704             if (PrecompilingForCleanBuild || currentHash != cachedTopLevelFilesHash) {
705                 if (PrecompilingForCleanBuild) {
706                     Debug.Trace("BuildManager", "Precompiling for clean build.");
707                 }
708                 else {
709                     Debug.Trace("BuildManager", "EnsureFirstTimeInit: hash codes don't match.  Old=" +
710                         cachedTopLevelFilesHash + " New=" + currentHash);
711                 }
712
713                 _codeGenCache.RemoveAllCodegenFiles();
714             }
715             else {
716                 Debug.Trace("BuildManager", "BuildManager: the special files are up to date");
717             }
718         }
719
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;
727             }
728             HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.BuildManagerChange, message);
729         }
730
731         /*
732          * Check if an assembly name is reserved for a special purpose
733          */
734         internal static bool IsReservedAssemblyName(string assemblyName) {
735
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) {
744
745                 return true;
746             }
747
748             return false;
749         }
750
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));
754             }
755         }
756
757         internal static PreStartInitStage PreStartInitStage { get; private set; }
758
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();
763         
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);
768
769             bool hasUpdated = false; 
770             ISet<string> preApplicationStartAssemblyNames = CallPreStartInitMethods(preStartInitListPath, out hasUpdated);
771
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);
775
776             // Save the cache file only if needed
777             if (hasUpdated) {
778                 SavePreStartInitAssembliesToFile(preStartInitListPath, preApplicationStartAssemblyNames);
779             }
780         }
781
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);
791                 }
792
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;
799                     try {
800                         CompilationLock.GetLock(ref gotLock);
801                         methods = GetPreStartInitMethodsFromReferencedAssemblies();
802                         isRefAssemblyLoaded = true;
803                     }
804                     finally {
805                         if (gotLock) {
806                             CompilationLock.ReleaseLock();
807                         }
808                     }
809                 }
810
811                 InvokePreStartInitMethods(methods);
812
813                 Debug.Assert(PreStartInitStage == Compilation.PreStartInitStage.AfterPreStartInit);
814
815                 return new HashSet<string>(methods.Select(m => m.DeclaringType.Assembly.FullName), StringComparer.OrdinalIgnoreCase);
816             }
817         }
818
819         internal static ISet<string> GetPreStartInitAssembliesFromFile(string path) {
820             if (FileUtil.FileExists(path)) {
821                 try {
822                     return new HashSet<string>(File.ReadAllLines(path), StringComparer.OrdinalIgnoreCase);
823                 }
824                 catch {
825                     // If there are issues delete the bad file. The list will be created from scratch.
826                     try {
827                         File.Delete(path);
828                     }
829                     catch { }
830                 }
831             }
832             return null;
833         }
834
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;
841             try {
842                 //put write under the global lock to avoid race condition
843                 CompilationLock.GetLock(ref gotLock);
844                 File.WriteAllLines(path, assemblies);
845             }
846             catch {
847                 try {
848                     File.Delete(path);
849                 }
850                 catch { }
851             }
852             finally {
853                 if (gotLock) {
854                     CompilationLock.ReleaseLock();
855                 }
856             }
857         }
858
859         /// <summary>
860         /// Load the cached list of assemblies containing pre app start methods. Since this is a cache we never throw from it.
861         /// </summary>
862         internal static ICollection<Assembly> LoadCachedPreAppStartAssemblies(string preStartInitListPath) {
863             try {
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) {
867                     return null;
868                 }
869                 return assemblyList.Select(Assembly.Load)
870                                    .Distinct()
871                                    .ToList();
872             }
873             catch {
874                 return null;
875             }
876         }
877
878         private static void InvokePreStartInitMethods(ICollection<MethodInfo> methods) {
879             PreStartInitStage = Compilation.PreStartInitStage.DuringPreStartInit;
880
881             try {
882                 InvokePreStartInitMethodsCore(methods, HostingEnvironment.SetCultures);
883                 PreStartInitStage = Compilation.PreStartInitStage.AfterPreStartInit;
884             }
885             catch {
886                 PreStartInitStage = Compilation.PreStartInitStage.BeforePreStartInit;
887                 throw;
888             }
889         }
890
891         internal static void InvokePreStartInitMethodsCore(ICollection<MethodInfo> methods, Func<IDisposable> setHostingEnvironmentCultures) {
892             // Remove dupes 
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) {
898                 try {
899                     // 
900                     using (setHostingEnvironmentCultures()) {
901                         method.Invoke(null, null);
902                     }
903                 }
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,
908                             method.Name,
909                             method.DeclaringType.FullName,
910                             message),
911                         e.InnerException);
912                 }
913             }
914         }
915
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);
920         }
921
922         /// <summary>
923         /// Resolves pre application start methods from the assemblies specified.
924         /// </summary>
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;
931                 try {
932                     attributes = (PreApplicationStartMethodAttribute[])assembly.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), inherit: true);
933                 }
934                 catch {
935                     // GetCustomAttributes invokes the constructors of the attributes, so it is possible that they might throw unexpected exceptions.
936                     // (Dev10 bug 831981)
937                 }
938
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) {
943                          return null;
944                      }
945                 }
946                 else {
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);
952                         }
953
954                         if (method != null) {
955                             methods.Add(method);
956                         }
957                         else {
958                             throw new HttpException(SR.GetString(SR.Invalid_PreApplicationStartMethodAttribute_value,
959                                 assembly.FullName,
960                                 (attribute.Type != null ? attribute.Type.FullName : String.Empty),
961                                 attribute.MethodName));
962                         }
963                     }
964                 }
965             }
966             return methods;
967         }
968
969         internal static MethodInfo FindPreStartInitMethod(Type type, string methodName) {
970             Debug.Assert(type != null);
971             Debug.Assert(!String.IsNullOrEmpty(methodName));
972             MethodInfo method = null;
973             if (type.IsPublic) {
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,
977                                 binder: null,
978                                 types: Type.EmptyTypes,
979                                 modifiers: null);
980             }
981             return method;
982         }
983
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
986         // own assemblies).
987         private Assembly CompileCodeDirectory(VirtualPath virtualDir, CodeDirectoryType dirType,
988             string assemblyName, StringSet excludedSubdirectories) {
989
990             Debug.Trace("BuildManager", "CompileCodeDirectory(" + virtualDir.VirtualPathString + ")");
991
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;
998                 else
999                     isDirectoryAllowed = false;
1000             }
1001
1002             // Remember the referenced assemblies based on the current count.
1003             AssemblyReferenceInfo info = new AssemblyReferenceInfo(_topLevelReferencedAssemblies.Count);
1004             _topLevelAssembliesIndexTable[virtualDir.VirtualPathString] = info;
1005
1006             Assembly codeAssembly = CodeDirectoryCompiler.GetCodeDirectoryAssembly(
1007                     virtualDir, dirType, assemblyName, excludedSubdirectories,
1008                     isDirectoryAllowed);
1009
1010             if (codeAssembly != null) {
1011
1012                 // Remember the generated assembly
1013                 info.Assembly = codeAssembly;
1014
1015                 // Page resource assemblies are not added to the top level list
1016                 if (dirType != CodeDirectoryType.LocalResources) {
1017                     _topLevelReferencedAssemblies.Add(codeAssembly);
1018
1019                     if (dirType == CodeDirectoryType.MainCode || dirType == CodeDirectoryType.SubCode) {
1020                         if (_codeAssemblies == null) {
1021                             _codeAssemblies = new ArrayList();
1022                         }
1023
1024                         _codeAssemblies.Add(codeAssembly);
1025                     }
1026
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);
1032                     }
1033                     _assemblyResolveMapping[assemblyName] = codeAssembly;
1034
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);
1040
1041                         // To avoid breaking earlier Whidbey apps, allows the name "__code"
1042                         // to be used for the main code assembly.
1043                         // 
1044                         _assemblyResolveMapping["__code"] = codeAssembly;
1045                     }
1046                 }
1047             }
1048
1049             Debug.Trace("BuildManager", "CompileCodeDirectory generated assembly: " +
1050                 (codeAssembly == null ? "None" : codeAssembly.ToString()));
1051
1052             return codeAssembly;
1053         }
1054
1055
1056         private void CompileResourcesDirectory() {
1057
1058             VirtualPath virtualDir = HttpRuntime.ResourcesDirectoryVirtualPath;
1059
1060             Debug.Assert(_appResourcesAssembly == null);
1061             _appResourcesAssembly = CompileCodeDirectory(virtualDir, CodeDirectoryType.AppResources,
1062                 ResourcesDirectoryAssemblyName, null /*excludedSubdirectories*/);
1063         }
1064
1065         private void CompileWebRefDirectory() {
1066
1067             CompileCodeDirectory(HttpRuntime.WebRefDirectoryVirtualPath, CodeDirectoryType.WebReferences,
1068                 WebRefDirectoryAssemblyName, null /*excludedSubdirectories*/);
1069         }
1070
1071         // Compute the list of subdirectories that should not be compiled with
1072         // the top level Code
1073         private void EnsureExcludedCodeSubDirectoriesComputed() {
1074
1075             if (_excludedCodeSubdirectories != null)
1076                 return;
1077
1078             _excludedCodeSubdirectories = new CaseInsensitiveStringSet();
1079
1080             // Get the list of sub directories that will be compiled separately
1081             CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
1082
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);
1087                 }
1088             }
1089         }
1090
1091         private void CompileCodeDirectories() {
1092
1093             VirtualPath virtualDir = HttpRuntime.CodeDirectoryVirtualPath;
1094
1095             // Get the list of sub directories that will be compiled separately
1096             CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
1097
1098             if (codeSubDirectories != null) {
1099
1100                 // Compile all the subdirectory that are listed in config.
1101
1102                 foreach (CodeSubDirectory entry in codeSubDirectories) {
1103
1104                     // 
1105
1106
1107
1108                     VirtualPath virtualSubDir = virtualDir.SimpleCombineWithDir(entry.DirectoryName);
1109
1110                     string assemblyName = SubCodeDirectoryAssemblyNamePrefix + entry.AssemblyName;
1111
1112                     // Compile the subdirectory tree (no exclusions)
1113                     CompileCodeDirectory(virtualSubDir, CodeDirectoryType.SubCode, assemblyName,
1114                         null /*excludedSubdirectories*/);
1115                 }
1116             }
1117
1118             EnsureExcludedCodeSubDirectoriesComputed();
1119
1120             // Compile the top level Code directory tree, minus the excluded subdirectories
1121             CompileCodeDirectory(virtualDir, CodeDirectoryType.MainCode,
1122                 CodeDirectoryAssemblyName, _excludedCodeSubdirectories);
1123         }
1124
1125         private void CompileGlobalAsax() {
1126             _globalAsaxBuildResult = ApplicationBuildProvider.GetGlobalAsaxBuildResult(IsPrecompiledApp);
1127
1128             // Make sure that global.asax notifications are set up (VSWhidbey 267245)
1129             HttpApplicationFactory.SetupFileChangeNotifications();
1130
1131             if (_globalAsaxBuildResult != null) {
1132
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;
1140                 }
1141             }
1142         }
1143
1144         // Call the AppInitialize method in the Code assembly if there is one
1145         internal static void CallAppInitializeMethod() {
1146
1147             // Make sure the code directory has been processed
1148             _theBuildManager.EnsureTopLevelFilesCompiled();
1149
1150             CodeDirectoryCompiler.CallAppInitializeMethod();
1151         }
1152
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));
1157             }
1158
1159             // This should never get executed in non-hosted appdomains
1160             Debug.Assert(HostingEnvironment.IsHosted);
1161
1162             // If we already tried and got an exception, just rethrow it
1163             if (_topLevelFileCompilationException != null && !SkipTopLevelCompilationExceptions) {
1164                 ReportTopLevelCompilationException();
1165             }
1166
1167             if (_topLevelFilesCompiledStarted)
1168                 return;
1169
1170             // Set impersonation to hosting identity (process or UNC)
1171             using (new ApplicationImpersonationContext()) {
1172                 bool gotLock = false;
1173                 _parseErrorReported = false;
1174
1175                 try {
1176                     // Grab the compilation mutex, since this method accesses the codegen files
1177                     CompilationLock.GetLock(ref gotLock);
1178
1179                     // Check again if there is an exception
1180                     if (_topLevelFileCompilationException != null && !SkipTopLevelCompilationExceptions) {
1181                         ReportTopLevelCompilationException();
1182                     }
1183
1184                     // Check again if we're done
1185                     if (_topLevelFilesCompiledStarted)
1186                         return;
1187
1188                     _topLevelFilesCompiledStarted = true;
1189                     _topLevelAssembliesIndexTable =
1190                         new Dictionary<String, AssemblyReferenceInfo>(StringComparer.OrdinalIgnoreCase);
1191
1192                     _compilationStage = CompilationStage.TopLevelFiles;
1193
1194                     CompileResourcesDirectory();
1195                     CompileWebRefDirectory();
1196                     CompileCodeDirectories();
1197
1198                     _compilationStage = CompilationStage.GlobalAsax;
1199
1200                     CompileGlobalAsax();
1201
1202                     _compilationStage = CompilationStage.BrowserCapabilities;
1203
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;
1209
1210                     _compilationStage = CompilationStage.AfterTopLevelFiles;
1211                 }
1212                 catch (Exception e) {
1213                     // Remember the exception, and rethrow it
1214                     _topLevelFileCompilationException = e;
1215
1216                     // Do not rethrow the exception since so CBM can still provide partial support
1217                     if (!SkipTopLevelCompilationExceptions) {
1218
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();
1224                             }
1225                         }
1226
1227                         throw;
1228                     }
1229                 }
1230                 finally {
1231                     _topLevelFilesCompiledCompleted = true;
1232
1233                     // Always release the mutex if we had taken it
1234                     if (gotLock) {
1235                         CompilationLock.ReleaseLock();
1236                     }
1237                 }
1238             }
1239         }
1240
1241         // Generate a random file name with 8 characters
1242         private static string GenerateRandomFileName() {
1243             // Generate random bytes
1244             byte[] data = new byte[6];
1245
1246             lock (_rng) {
1247                 _rng.GetBytes(data);
1248             }
1249
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('+', '_');
1254
1255             return s;
1256         }
1257
1258         internal static string GenerateRandomAssemblyName(string baseName) {
1259             return GenerateRandomAssemblyName(baseName, true /*topLevel*/);
1260         }
1261
1262         // Generate a random name for an assembly, starting with the passed in prefix
1263         internal static string GenerateRandomAssemblyName(string baseName, bool topLevel) {
1264
1265             // Start with the passed in base name
1266             string assemblyName = baseName;
1267
1268             // Append a random token to it.
1269
1270             // However, don't do this when precompiling for deployment since, we want the name to be more predictable (DevDiv 36625)
1271             if (PrecompilingForDeployment)
1272                 return baseName;
1273
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)
1277                 return baseName;
1278
1279             return baseName += "." + GenerateRandomFileName();
1280         }
1281
1282         private static string GetGeneratedAssemblyBaseName(VirtualPath virtualPath) {
1283
1284             // Name the assembly using the same scheme as cache keys
1285             return GetCacheKeyFromVirtualPath(virtualPath);
1286         }
1287
1288         /*
1289          * Look for a type by name in the top level and config assemblies
1290          */
1291         public static Type GetType(string typeName, bool throwOnError) {
1292             return GetType(typeName, throwOnError, false);
1293         }
1294
1295         /*
1296          * Look for a type by name in the top level and config assemblies
1297          */
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().
1302             Type type = null;
1303             if (Util.TypeNameContainsAssembly(typeName)) {
1304                 type = Type.GetType(typeName, throwOnError, ignoreCase);
1305
1306                 if (type != null) {
1307                     return type;
1308                 }
1309             }
1310
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);
1315             }
1316
1317             // First, always try System.Web.dll
1318             try {
1319                 type = typeof(BuildManager).Assembly.GetType(typeName,
1320                     false /*throwOnError*/, ignoreCase);
1321             }
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);
1328             }
1329
1330             if (type != null) return type;
1331
1332             _theBuildManager.EnsureTopLevelFilesCompiled();
1333
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;
1338
1339             // Otherwise, look for the type in the config assemblies
1340             IEnumerable<Assembly> configAssemblies = GetAssembliesForAppLevel();
1341             type = Util.GetTypeFromAssemblies(configAssemblies, typeName, ignoreCase);
1342
1343             if (type == null && throwOnError) {
1344                 throw new HttpException(
1345                     SR.GetString(SR.Invalid_type, typeName));
1346             }
1347
1348             return type;
1349         }
1350
1351         /*
1352         * Simple wrapper to get the Assemblies
1353         */
1354         private static IEnumerable<Assembly> GetAssembliesForAppLevel() {
1355             CompilationSection compilationConfiguration = MTConfigUtil.GetCompilationAppConfig();
1356             AssemblyCollection assemblyInfoCollection = compilationConfiguration.Assemblies;
1357
1358             Debug.Assert(s_dynamicallyAddedReferencedAssembly != null);
1359
1360             if (assemblyInfoCollection == null) {
1361                 return s_dynamicallyAddedReferencedAssembly.OfType<Assembly>();
1362             }
1363
1364             return assemblyInfoCollection.Cast<AssemblyInfo>()
1365                 .SelectMany(ai => ai.AssemblyInternal)
1366                 .Union(s_dynamicallyAddedReferencedAssembly)
1367                 .Distinct();
1368         }
1369
1370
1371         /*
1372          * Gets a type from one of the code assemblies
1373          */
1374         internal static Type GetTypeFromCodeAssembly(string typeName, bool ignoreCase) {
1375
1376             // No code assembly: return
1377             if (CodeAssemblies == null)
1378                 return null;
1379
1380             return Util.GetTypeFromAssemblies(CodeAssemblies, typeName, ignoreCase);
1381         }
1382
1383         internal static BuildProvider CreateBuildProvider(VirtualPath virtualPath,
1384             CompilationSection compConfig, ICollection referencedAssemblies,
1385             bool failIfUnknown) {
1386
1387             return CreateBuildProvider(virtualPath, BuildProviderAppliesTo.Web,
1388                 compConfig, referencedAssemblies, failIfUnknown);
1389         }
1390
1391         internal static BuildProvider CreateBuildProvider(VirtualPath virtualPath,
1392             BuildProviderAppliesTo neededFor,
1393             CompilationSection compConfig, ICollection referencedAssemblies,
1394             bool failIfUnknown) {
1395
1396             string extension = virtualPath.Extension;
1397
1398             Type buildProviderType = CompilationUtil.GetBuildProviderTypeFromExtension(compConfig,
1399                 extension, neededFor, failIfUnknown);
1400             if (buildProviderType == null)
1401                 return null;
1402
1403             object o = HttpRuntime.CreatePublicInstance(buildProviderType);
1404
1405             BuildProvider buildProvider = (BuildProvider)o;
1406
1407             buildProvider.SetVirtualPath(virtualPath);
1408             buildProvider.SetReferencedAssemblies(referencedAssemblies);
1409
1410             return buildProvider;
1411         }
1412
1413         internal static void AddFolderLevelBuildProviders(BuildProviderSet buildProviders, VirtualPath virtualPath,
1414             FolderLevelBuildProviderAppliesTo appliesTo, CompilationSection compConfig, ICollection referencedAssemblies) {
1415
1416             if (buildProviders == null) {
1417                 return;
1418             }
1419
1420             List<Type> buildProviderTypes = CompilationUtil.GetFolderLevelBuildProviderTypes(compConfig, appliesTo);
1421             if (buildProviderTypes != null) {
1422                 foreach (Type buildProviderType in buildProviderTypes) {
1423                     object o = HttpRuntime.CreatePublicInstance(buildProviderType);
1424
1425                     BuildProvider buildProvider = (BuildProvider)o;
1426
1427                     buildProvider.SetVirtualPath(virtualPath);
1428                     buildProvider.SetReferencedAssemblies(referencedAssemblies);
1429
1430                     buildProviders.Add(buildProvider);
1431
1432                 }
1433             }
1434         }
1435
1436         internal static void ValidateCodeFileVirtualPath(VirtualPath virtualPath) {
1437             _theBuildManager.ValidateVirtualPathInternal(virtualPath, false /*allowCrossApp*/, true /*codeFile*/);
1438         }
1439
1440         private void ValidateVirtualPathInternal(VirtualPath virtualPath, bool allowCrossApp, bool codeFile) {
1441
1442             if (!allowCrossApp) {
1443                 virtualPath.FailIfNotWithinAppRoot();
1444             }
1445             else {
1446                 // If cross app is allowed, and the path is in a different app, nothing more to check
1447                 if (!virtualPath.IsWithinAppRoot)
1448                     return;
1449             }
1450
1451             //
1452             // Now, detect if it's under a special directory (e.g. 'code', 'resources', 'themes')
1453             //
1454
1455             // If it's exactly the app root, it's fine
1456             if (HttpRuntime.AppDomainAppVirtualPathObject == virtualPath)
1457                 return;
1458
1459             int appPathLen = HttpRuntime.AppDomainAppVirtualPathString.Length;
1460
1461             string virtualPathString = virtualPath.VirtualPathString;
1462
1463             // This could happen if the vpath is "/app" (while the app vpath is "/app/")
1464             if (virtualPathString.Length < appPathLen)
1465                 return;
1466
1467             // If no slash after the approot (e.g. "/app/foo.aspx"), it's valid
1468             int slashIndex = virtualPathString.IndexOf('/', appPathLen);
1469             if (slashIndex < 0)
1470                 return;
1471
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);
1474
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));
1478             }
1479         }
1480
1481         /*
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.
1486          */
1487         internal static long GetBuildResultHashCodeIfCached(
1488             HttpContext context, string virtualPath) {
1489
1490             BuildResult result = GetVPathBuildResult(context, VirtualPath.Create(virtualPath),
1491                 true /*noBuild*/, false /*allowCrossApp*/);
1492
1493             // If it's not cached, return 0
1494             if (result == null)
1495                 return 0;
1496
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));
1501         }
1502
1503         internal static BuildResult GetVPathBuildResult(VirtualPath virtualPath) {
1504
1505             return GetVPathBuildResult(null /*context*/, virtualPath,
1506                 false /*noBuild*/, false /*allowCrossApp*/, false /*allowBuiltInPrecompile*/);
1507         }
1508
1509         internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath) {
1510
1511             return GetVPathBuildResult(context, virtualPath, false /*noBuild*/, false /*allowCrossApp*/, false /*allowBuiltInPrecompile*/);
1512         }
1513
1514         internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath,
1515             bool noBuild, bool allowCrossApp) {
1516
1517             return GetVPathBuildResult(context, virtualPath, noBuild, allowCrossApp, false /*allowBuiltInPrecompile*/);
1518         }
1519
1520         /*
1521          * Calls either GetVPathBuildResultWithNoAssert or GetVPathBuildResultWithAssert,
1522          * depending on whether there is any point in asserting.
1523          */
1524         internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath,
1525             bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool ensureIsUpToDate = true) {
1526
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);
1532             }
1533             else {
1534                 return GetVPathBuildResultWithAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound: true, ensureIsUpToDate: ensureIsUpToDate);
1535             }
1536         }
1537
1538
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*/);
1542         }
1543
1544         /*
1545          * Same as GetVPathBuildResultWithNoAssert, but with an Unrestricted Assert.
1546          */
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) {
1550
1551             return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound, ensureIsUpToDate);
1552         }
1553
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*/);
1557         }
1558
1559         internal static BuildResult GetVPathBuildResultWithNoAssert(
1560             HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
1561
1562             using (new ApplicationImpersonationContext()) {
1563                 return _theBuildManager.GetVPathBuildResultInternal(virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, throwIfNotFound, ensureIsUpToDate);
1564             }
1565         }
1566
1567         // name of the slot in call context
1568         private const String CircularReferenceCheckerSlotName = "CircRefChk";
1569
1570         private BuildResult GetVPathBuildResultInternal(VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate = true) {
1571
1572             Debug.Trace("BuildManager", "GetBuildResult(" + virtualPath + ")");
1573
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));
1577             }
1578
1579             // Make sure the path is not relative
1580             Debug.Assert(!virtualPath.IsRelative);
1581
1582             // Try the cache first before getting the mutex
1583             BuildResult result = GetVPathBuildResultFromCacheInternal(virtualPath, ensureIsUpToDate);
1584             if (result != null)
1585                 return result;
1586
1587             // If we were only checking the cache and it wasn't there, return null.
1588             if (noBuild)
1589                 return null;
1590
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*/);
1595
1596             if (throwIfNotFound) {
1597                 // Before grabbing the lock, make sure the file at least exists (ASURT 46465)
1598                 Util.CheckVirtualFileExists(virtualPath);
1599             }
1600             else if (!virtualPath.FileExists()) {
1601                 return null;
1602             }
1603
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));
1608             }
1609
1610             bool gotLock = false;
1611
1612             try {
1613                 // Grab the compilation mutex
1614                 CompilationLock.GetLock(ref gotLock);
1615
1616                 // Check the cache a second time after getting the mutex
1617                 result = GetVPathBuildResultFromCacheInternal(virtualPath, ensureIsUpToDate);
1618                 if (result != null)
1619                     return result;
1620
1621                 // Get the circular reference checker (create it if needed)
1622                 VirtualPathSet circularReferenceChecker;
1623                 circularReferenceChecker = CallContext.GetData(CircularReferenceCheckerSlotName)
1624                     as VirtualPathSet;
1625                 if (circularReferenceChecker == null) {
1626                     circularReferenceChecker = new VirtualPathSet();
1627
1628                     // Create it and save it in the CallContext
1629                     CallContext.SetData(CircularReferenceCheckerSlotName, circularReferenceChecker);
1630                 }
1631
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));
1636                 }
1637
1638                 // Add the current virtualPath to the circular reference checker
1639                 circularReferenceChecker.Add(virtualPath);
1640
1641                 try {
1642                     // 
1643                     EnsureTopLevelFilesCompiled();
1644                     result = CompileWebFile(virtualPath);
1645                 }
1646                 finally {
1647                     // Remove the current virtualPath from the circular reference checker
1648                     Debug.Assert(circularReferenceChecker.Contains(virtualPath));
1649                     circularReferenceChecker.Remove(virtualPath);
1650                 }
1651             }
1652             finally {
1653                 // Always release the mutex if we had taken it
1654                 if (gotLock) {
1655                     CompilationLock.ReleaseLock();
1656                 }
1657             }
1658
1659             return result;
1660         }
1661
1662         private BuildResult CompileWebFile(VirtualPath virtualPath) {
1663
1664             BuildResult result = null;
1665             string cacheKey = null;
1666
1667             if (_topLevelFilesCompiledCompleted) {
1668
1669                 VirtualPath parentPath = virtualPath.Parent;
1670
1671                 // First, try to batch the directory if enabled
1672                 if (IsBatchEnabledForDirectory(parentPath)) {
1673                     BatchCompileWebDirectory(null, parentPath, true /*ignoreErrors*/);
1674
1675                     // If successful, it would have been cached to memory
1676                     cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
1677                     result = _memoryCache.GetBuildResult(cacheKey);
1678
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);
1683                     }
1684
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;
1689                         }
1690
1691                         return result;
1692                     }
1693                 }
1694             }
1695
1696
1697             DateTime utcStart = DateTime.UtcNow;
1698
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*/);
1703
1704
1705             BuildProvidersCompiler bpc = new BuildProvidersCompiler(virtualPath /*configPath*/, outputAssemblyName);
1706
1707             // Create a BuildProvider based on the virtual path
1708             BuildProvider buildProvider = CreateBuildProvider(virtualPath, bpc.CompConfig,
1709                 bpc.ReferencedAssemblies, true /*failIfUnknown*/);
1710
1711             // Set the BuildProvider using a single item collection
1712             bpc.SetBuildProviders(new SingleObjectCollection(buildProvider));
1713
1714             // Compile it
1715             CompilerResults results;
1716
1717             try {
1718                 results = bpc.PerformBuild();
1719                 result = buildProvider.GetBuildResult(results);
1720             }
1721             catch (HttpCompileException e) {
1722
1723                 // If we're not supposed to cache the exception, just rethrow it
1724                 if (e.DontCache)
1725                     throw;
1726
1727                 result = new BuildResultCompileError(virtualPath, e);
1728
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);
1732
1733                 // Remember the virtualpath dependencies, so that we will correctly
1734                 // invalidate buildresult when depdency changes.
1735                 e.VirtualPathDependencies = buildProvider.VirtualPathDependencies;
1736
1737                 // Cache it for next time
1738                 CacheVPathBuildResultInternal(virtualPath, result, utcStart);
1739
1740                 // Set the DontCache flag, so that the exception will not be incorrectly
1741                 // cached again lower down the stack (VSWhidbey 128234)
1742                 e.DontCache = true;
1743
1744                 throw;
1745             }
1746
1747             if (result == null)
1748                 return null;
1749
1750             // Cache it for next time
1751             CacheVPathBuildResultInternal(virtualPath, result, utcStart);
1752
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);
1759                 }
1760                 result = BuildManager.GetBuildResultFromCache(cacheKey);
1761             }
1762
1763             return result;
1764         }
1765
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();
1769
1770         private void EnsureFirstTimeDirectoryInit(VirtualPath virtualDir) {
1771
1772             // Don't process local resources when precompiling for updatable deployment.
1773             // Instead, we deploy the App_LocalResources folder as is.
1774             if (PrecompilingForUpdatableDeployment)
1775                 return;
1776
1777             if (virtualDir == null)
1778                 return;
1779
1780             // Only do this once per directory
1781             if (_localResourcesAssemblies.Contains(virtualDir))
1782                 return;
1783
1784             // Don't do anything if it's outside the app root
1785             if (!virtualDir.IsWithinAppRoot)
1786                 return;
1787
1788             Debug.Trace("BuildManager", "EnsureFirstTimeDirectoryInit(" + virtualDir + ")");
1789
1790             // Get the virtual path to the LocalResources subdirectory for this directory
1791             VirtualPath localResDir = virtualDir.SimpleCombineWithDir(HttpRuntime.LocalResourcesDirectoryName);
1792
1793             bool dirExists;
1794             try {
1795                 dirExists = localResDir.DirectoryExists();
1796             }
1797             catch {
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);
1801
1802                 _localResourcesAssemblies[virtualDir] = null;
1803                 return;
1804             }
1805
1806             Debug.Trace("BuildManager", "EnsureFirstTimeDirectoryInit: dirExists=" + dirExists);
1807
1808             try {
1809                 // Monitor changes to it so the appdomain can shut down when it changes
1810                 HttpRuntime.StartListeningToLocalResourcesDirectory(localResDir);
1811             }
1812             catch {
1813                 // could fail for long directory names
1814                 if (dirExists) {
1815                     throw;
1816                 }
1817             }
1818
1819             Assembly resourceAssembly = null;
1820
1821             // If it exists, build it
1822             if (dirExists) {
1823
1824                 string localResAssemblyName = GetLocalResourcesAssemblyName(virtualDir);
1825
1826                 bool gotLock = false;
1827
1828                 try {
1829                     // Grab the compilation mutex, since this method accesses the codegen files
1830                     CompilationLock.GetLock(ref gotLock);
1831
1832                     resourceAssembly = CompileCodeDirectory(localResDir, CodeDirectoryType.LocalResources,
1833                         localResAssemblyName, null /*excludedSubdirectories*/);
1834                 }
1835                 finally {
1836                     // Always release the mutex if we had taken it
1837                     if (gotLock) {
1838                         CompilationLock.ReleaseLock();
1839                     }
1840                 }
1841             }
1842
1843             // Cache it whether it's null or not
1844             _localResourcesAssemblies[virtualDir] = resourceAssembly;
1845         }
1846
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);
1853             }
1854         }
1855
1856
1857         // Retrieve a cached local resources assembly (could be null)
1858         internal static Assembly GetLocalResourcesAssembly(VirtualPath virtualDir) {
1859             return (Assembly)_theBuildManager._localResourcesAssemblies[virtualDir];
1860         }
1861
1862         internal static string GetLocalResourcesAssemblyName(VirtualPath virtualDir) {
1863             return LocalResourcesDirectoryAssemblyName + "." + GetGeneratedAssemblyBaseName(virtualDir);
1864         }
1865
1866         // name of the slot in call context
1867         private const String BatchCompilationSlotName = "BatchCompileChk";
1868
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;
1874
1875         public static Nullable<bool> BatchCompilationEnabled {
1876             get {
1877                 return s_batchCompilationEnabled;
1878             }
1879             set {
1880                 ThrowIfPreAppStartNotRunning();
1881                 s_batchCompilationEnabled = value;
1882             }
1883         }
1884
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) {
1889                 return false;
1890             }
1891
1892             // Always enable batching for deployement
1893             if (PrecompilingForDeployment) {
1894                 return true;
1895             }
1896
1897             // If it's called by other non-precompile CBM methods, always disable batching
1898             if (BuildManagerHost.InClientBuildManager && !PerformingPrecompilation) {
1899                 return false;
1900             }
1901
1902             // If batch compilation was set through code use that setting
1903             if (BatchCompilationEnabled.HasValue) {
1904                 return BatchCompilationEnabled.Value;
1905             }
1906
1907             // Check the config
1908             return CompilationUtil.IsBatchingEnabled(virtualDir.VirtualPathString);
1909         }
1910
1911         private bool BatchCompileWebDirectory(VirtualDirectory vdir, VirtualPath virtualDir, bool ignoreErrors) {
1912
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).
1915
1916             if (virtualDir == null)
1917                 virtualDir = vdir.VirtualPathObject;
1918
1919             if (vdir == null)
1920                 vdir = HostingEnvironment.VirtualPathProvider.GetDirectory(virtualDir);
1921
1922             // Then, check if we're already tried batch compiling this directory on this same request
1923
1924             CaseInsensitiveStringSet directoryBatchCompilerChecker;
1925             directoryBatchCompilerChecker = CallContext.GetData(BatchCompilationSlotName)
1926                 as CaseInsensitiveStringSet;
1927
1928             if (directoryBatchCompilerChecker == null) {
1929                 directoryBatchCompilerChecker = new CaseInsensitiveStringSet();
1930
1931                 // Create it and save it in the CallContext
1932                 CallContext.SetData(BatchCompilationSlotName, directoryBatchCompilerChecker);
1933             }
1934
1935             // If we've already tried batch compiling this directory, don't do anything
1936             if (directoryBatchCompilerChecker.Contains(vdir.VirtualPath))
1937                 return false;
1938
1939             // Add the current virtualDir to the batch compiler checker
1940             directoryBatchCompilerChecker.Add(vdir.VirtualPath);
1941
1942             // If we're in the process of precompiling an app, never ignore errors.
1943             if (_precompilingApp)
1944                 ignoreErrors = false;
1945
1946             return BatchCompileWebDirectoryInternal(vdir, ignoreErrors);
1947         }
1948
1949         private bool BatchCompileWebDirectoryInternal(VirtualDirectory vdir, bool ignoreErrors) {
1950
1951             WebDirectoryBatchCompiler sdc = new WebDirectoryBatchCompiler(vdir);
1952
1953             // Just ignore build providers that have errors
1954             if (ignoreErrors) {
1955                 sdc.SetIgnoreErrors();
1956
1957                 // Don't propagate errors that happen during batch compilation
1958                 try {
1959                     sdc.Process();
1960                 }
1961                 catch {
1962                     return false;
1963                 }
1964             }
1965             else {
1966                 sdc.Process();
1967             }
1968
1969             return true;
1970         }
1971
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();
1978         }
1979
1980         private Type GetGlobalAsaxTypeInternal() {
1981             EnsureTopLevelFilesCompiled();
1982
1983             if (_globalAsaxBuildResult == null)
1984                 return PageParser.DefaultApplicationBaseType ?? typeof(HttpApplication);
1985
1986             return _globalAsaxBuildResult.ResultType;
1987         }
1988
1989         internal static BuildResultCompiledGlobalAsaxType GetGlobalAsaxBuildResult() {
1990             return _theBuildManager.GetGlobalAsaxBuildResultInternal();
1991         }
1992
1993         private BuildResultCompiledGlobalAsaxType GetGlobalAsaxBuildResultInternal() {
1994             EnsureTopLevelFilesCompiled();
1995
1996             return _globalAsaxBuildResult;
1997         }
1998
1999         internal string[] GetCodeDirectories() {
2000
2001             VirtualPath virtualDir = HttpRuntime.CodeDirectoryVirtualPath;
2002
2003             // If there is no Code directory, return an empty array
2004             if (!virtualDir.DirectoryExists())
2005                 return new string[0];
2006
2007             // Get the list of code sub directories that will be compiled separately
2008             CodeSubDirectoriesCollection codeSubDirectories = CompilationUtil.GetCodeSubDirectories();
2009
2010             // Compute the number of code dirs, including the root one
2011             int numOfCodeDirs = 1;
2012             if (codeSubDirectories != null)
2013                 numOfCodeDirs += codeSubDirectories.Count;
2014
2015             string[] codeDirs = new string[numOfCodeDirs];
2016             int current = 0;
2017
2018             if (codeSubDirectories != null) {
2019                 foreach (CodeSubDirectory entry in codeSubDirectories) {
2020
2021                     VirtualPath virtualSubDir = virtualDir.SimpleCombineWithDir(entry.DirectoryName);
2022                     codeDirs[current++] = virtualSubDir.VirtualPathString;
2023                 }
2024             }
2025
2026             // Add the root code dir at the end of the list (since it's compiled last)
2027             codeDirs[current++] = virtualDir.VirtualPathString;
2028
2029             return codeDirs;
2030         }
2031
2032         internal void GetCodeDirectoryInformation(VirtualPath virtualCodeDir,
2033             out Type codeDomProviderType, out CompilerParameters compilerParameters,
2034             out string generatedFilesDir) {
2035
2036             // Backup the compilation stage, since the call will modify it
2037             CompilationStage savedCompilationStage = _compilationStage;
2038
2039             try {
2040                 GetCodeDirectoryInformationInternal(virtualCodeDir, out codeDomProviderType,
2041                     out compilerParameters, out generatedFilesDir);
2042             }
2043             finally {
2044                 // Restore the compilation stage
2045                 _compilationStage = savedCompilationStage;
2046             }
2047         }
2048
2049         private void GetCodeDirectoryInformationInternal(VirtualPath virtualCodeDir,
2050             out Type codeDomProviderType, out CompilerParameters compilerParameters,
2051             out string generatedFilesDir) {
2052
2053             StringSet excludedSubdirectories = null;
2054
2055             CodeDirectoryType dirType;
2056
2057             // Get the DirectoryType based on the path
2058             if (virtualCodeDir == HttpRuntime.CodeDirectoryVirtualPath) {
2059
2060                 // If it's the top level code directory, make sure we exclude any
2061                 // subdirectories that are compiled separately
2062                 EnsureExcludedCodeSubDirectoriesComputed();
2063
2064                 excludedSubdirectories = _excludedCodeSubdirectories;
2065
2066                 dirType = CodeDirectoryType.MainCode;
2067
2068                 _compilationStage = CompilationStage.TopLevelFiles;
2069             }
2070             else if (virtualCodeDir == HttpRuntime.ResourcesDirectoryVirtualPath) {
2071
2072                 dirType = CodeDirectoryType.AppResources;
2073
2074                 _compilationStage = CompilationStage.TopLevelFiles;
2075             }
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) {
2080
2081                 // Use the top WebReference directory info for its sub directories.
2082                 virtualCodeDir = HttpRuntime.WebRefDirectoryVirtualPath;
2083                 dirType = CodeDirectoryType.WebReferences;
2084
2085                 _compilationStage = CompilationStage.TopLevelFiles;
2086             }
2087             else if (String.Compare(virtualCodeDir.FileName, HttpRuntime.LocalResourcesDirectoryName,
2088                 StringComparison.OrdinalIgnoreCase) == 0) {
2089
2090                 dirType = CodeDirectoryType.LocalResources;
2091
2092                 // LocalResources are compiled *after* top level files
2093                 _compilationStage = CompilationStage.AfterTopLevelFiles;
2094             }
2095             else {
2096                 // If all else fails, treat it as a sub directory
2097                 // 
2098                 dirType = CodeDirectoryType.SubCode;
2099
2100                 // Sub-code dirs are compiled *before* the main code dir
2101                 _compilationStage = CompilationStage.TopLevelFiles;
2102             }
2103
2104             Debug.Assert(virtualCodeDir.HasTrailingSlash);
2105             AssemblyReferenceInfo info = TheBuildManager.TopLevelAssembliesIndexTable[virtualCodeDir.VirtualPathString];
2106             if (info == null) {
2107                 throw new InvalidOperationException(
2108                     SR.GetString(SR.Invalid_CodeSubDirectory_Not_Exist, virtualCodeDir));
2109             }
2110
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);
2116
2117             Assembly resultAssembly = info.Assembly;
2118
2119             if (resultAssembly != null) {
2120                 // Use the runtime generated assembly location. VSWhidbey 400335
2121                 compilerParameters.OutputAssembly = resultAssembly.Location;
2122             }
2123         }
2124
2125         internal static Type GetProfileType() {
2126             return _theBuildManager.GetProfileTypeInternal();
2127         }
2128
2129         private Type GetProfileTypeInternal() {
2130             EnsureTopLevelFilesCompiled();
2131             return _profileType;
2132         }
2133
2134
2135         //
2136         // Caching related code
2137         //
2138
2139
2140         public static ICollection GetVirtualPathDependencies(string virtualPath) {
2141
2142             CompilationSection compConfig = RuntimeConfig.GetRootWebConfig().Compilation;
2143
2144             // Create a BuildProvider based on the virtual path
2145             BuildProvider buildProvider = CreateBuildProvider(VirtualPath.Create(virtualPath), compConfig,
2146                 null, false /*failIfUnknown*/);
2147
2148             if (buildProvider == null)
2149                 return null;
2150
2151             // Get its dependencies
2152             // 
2153             return buildProvider.GetBuildResultVirtualPathDependencies();
2154         }
2155
2156 #if OLD
2157     /*
2158      * Rewrite the virtualPath if appropriate, in order to support ghosting
2159      */
2160     private static void GetGhostedVirtualPath(ref string virtualPath) {
2161
2162         VirtualPathProvider virtualPathProvider = HostingEnvironment.VirtualPathProvider;
2163
2164         string ghostedVirtualPath = virtualPathProvider.GetGhostedVirtualPath(virtualPath);
2165
2166         // If the file is not ghosted, don't change the path
2167         if (ghostedVirtualPath == null)
2168             return;
2169
2170         // 
2171
2172
2173         // Get the list of virtual paths that it depends on (e.g. user controls)
2174         ICollection virtualPathDependencies = GetVirtualPathDependencies(virtualPath);
2175
2176         // If there aren't any, return the ghosted path
2177         if (virtualPathDependencies == null) {
2178             virtualPath = ghostedVirtualPath;
2179             return;
2180         }
2181
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).
2185
2186         foreach (string virtualDependency in virtualPathDependencies) {
2187             string ghostedVirtualDependencyPath = virtualPathProvider.GetGhostedVirtualPath(
2188                 virtualDependency);
2189             if (ghostedVirtualDependencyPath == null)
2190                 return;
2191         }
2192
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;
2196     }
2197 #endif
2198
2199         internal static string GetCacheKeyFromVirtualPath(VirtualPath virtualPath) {
2200             bool keyFromVPP;
2201             return GetCacheKeyFromVirtualPath(virtualPath, out keyFromVPP);
2202         }
2203
2204         /*
2205          * Same as GetCacheKeyFromVirtualPathInternal, but caches the cache keys
2206          * for performance, since creating them is expensive (VSWhidbey 146540)
2207          */
2208         static SimpleRecyclingCache _keyCache = new SimpleRecyclingCache();
2209         private static string GetCacheKeyFromVirtualPath(VirtualPath virtualPath, out bool keyFromVPP) {
2210
2211             // Check if the VirtualPathProvider needs to use a non-default cache key
2212             string key = virtualPath.GetCacheKey();
2213
2214             // If so, just return it
2215             if (key != null) {
2216                 keyFromVPP = true;
2217                 return key.ToLowerInvariant();
2218             }
2219
2220             // The VPP didn't return a key, so use our standard key algorithm
2221             keyFromVPP = false;
2222
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;
2226
2227             // Compute the key
2228             key = GetCacheKeyFromVirtualPathInternal(virtualPath);
2229
2230             // The key should always be lower case
2231             Debug.Assert(key == key.ToLowerInvariant());
2232
2233             // Cache it for next time
2234             _keyCache[virtualPath.VirtualPathString] = key;
2235
2236             return key;
2237         }
2238
2239         /*
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".
2243          */
2244         private static string GetCacheKeyFromVirtualPathInternal(VirtualPath virtualPath) {
2245
2246             // We want the key to be app independent (for precompilation), so we
2247             // change the virtual path to be app relative
2248
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));
2252             */
2253             string virtualPathString = virtualPath.AppRelativeVirtualPathString.ToLowerInvariant();
2254             virtualPathString = UrlPath.RemoveSlashFromPathIfNeeded(virtualPathString);
2255
2256             // Split the path into the directory and the name
2257             int slashIndex = virtualPathString.LastIndexOf('/');
2258             Debug.Assert(slashIndex >= 0 || virtualPathString == "~");
2259
2260             if (virtualPathString == "~")
2261                 return "root";
2262
2263             Debug.Assert(slashIndex != virtualPathString.Length - 1);
2264             string name = virtualPathString.Substring(slashIndex + 1);
2265             string dir;
2266             if (slashIndex <= 0)
2267                 dir = "/";
2268             else {
2269                 dir = virtualPathString.Substring(0, slashIndex);
2270             }
2271
2272             return name + "." + StringUtil.GetStringHashCode(dir).ToString("x", CultureInfo.InvariantCulture);
2273         }
2274
2275         internal static BuildResult GetVPathBuildResultFromCache(VirtualPath virtualPath) {
2276
2277             return TheBuildManager.GetVPathBuildResultFromCacheInternal(virtualPath);
2278         }
2279
2280         private BuildResult GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath, bool ensureIsUpToDate = true) {
2281             bool keyFromVPP;
2282             string cacheKey = GetCacheKeyFromVirtualPath(virtualPath, out keyFromVPP);
2283             return GetBuildResultFromCacheInternal(cacheKey, keyFromVPP, virtualPath, 0 /*hashCode*/, ensureIsUpToDate);
2284         }
2285
2286         internal static BuildResult GetBuildResultFromCache(string cacheKey) {
2287             return _theBuildManager.GetBuildResultFromCacheInternal(cacheKey, false /*keyFromVPP*/, null /*virtualPath*/,
2288                 0 /*hashCode*/);
2289         }
2290
2291         internal static BuildResult GetBuildResultFromCache(string cacheKey, VirtualPath virtualPath) {
2292             return _theBuildManager.GetBuildResultFromCacheInternal(cacheKey, false /*keyFromVPP*/, virtualPath,
2293                 0 /*hashCode*/);
2294         }
2295
2296         private BuildResult GetBuildResultFromCacheInternal(string cacheKey, bool keyFromVPP,
2297             VirtualPath virtualPath, long hashCode, bool ensureIsUpToDate = true) {
2298
2299             BuildResult result = null;
2300
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)
2305                 return null;
2306
2307             // The first cache should always be memory
2308             Debug.Assert(_caches[0] == _memoryCache);
2309
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);
2314             }
2315
2316             Debug.Trace("BuildManager", "Didn't find '" + virtualPath + "' in memory cache before lock");
2317
2318             lock (this) {
2319                 // Try to get the BuildResult from the cheapest to most expensive cache
2320                 int i;
2321                 for (i = 0; i < _caches.Length; i++) {
2322                     result = _caches[i].GetBuildResult(cacheKey, virtualPath, hashCode, ensureIsUpToDate);
2323
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
2328
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);
2334                         }
2335
2336                         break;
2337                     }
2338
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);
2346                     }
2347                 }
2348
2349
2350                 if (result == null)
2351                     return null;
2352
2353                 result = PostProcessFoundBuildResult(result, keyFromVPP, virtualPath);
2354                 if (result == null)
2355                     return null;
2356
2357                 Debug.Assert(_memoryCache != null);
2358
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);
2363
2364                 Debug.Trace("BuildManager", "Found '" + virtualPath + "' in " + _caches[i]);
2365
2366                 return result;
2367             }
2368         }
2369
2370         private BuildResult PostProcessFoundBuildResult(BuildResult result, bool keyFromVPP, VirtualPath virtualPath) {
2371
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.
2375             if (!keyFromVPP) {
2376                 if (virtualPath != null && virtualPath != result.VirtualPath) {
2377                     Debug.Assert(false);
2378                     return null;
2379                 }
2380             }
2381
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;
2386
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);
2392                 }
2393
2394                 throw compileException;
2395             }
2396
2397             return result;
2398         }
2399
2400         internal static bool CacheVPathBuildResult(VirtualPath virtualPath,
2401             BuildResult result, DateTime utcStart) {
2402
2403             return _theBuildManager.CacheVPathBuildResultInternal(virtualPath, result, utcStart);
2404         }
2405
2406         private bool CacheVPathBuildResultInternal(VirtualPath virtualPath,
2407             BuildResult result, DateTime utcStart) {
2408
2409             string cacheKey = GetCacheKeyFromVirtualPath(virtualPath);
2410             return CacheBuildResult(cacheKey, result, utcStart);
2411         }
2412
2413         internal static bool CacheBuildResult(string cacheKey, BuildResult result, DateTime utcStart) {
2414             return _theBuildManager.CacheBuildResultInternal(cacheKey, result, 0 /*hashCode*/, utcStart);
2415         }
2416
2417         private bool CacheBuildResultInternal(string cacheKey, BuildResult result,
2418             long hashCode, DateTime utcStart) {
2419
2420             // Before caching it, make sure the hash has been computed
2421             result.EnsureVirtualPathDependenciesHashComputed();
2422
2423             for (int i = 0; i < _caches.Length; i++) {
2424                 _caches[i].CacheBuildResult(cacheKey, result, hashCode, utcStart);
2425             }
2426
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);
2430                 return false;
2431             }
2432
2433             return true;
2434         }
2435
2436
2437         //
2438         // Precompilation related code
2439         //
2440
2441         internal void SetPrecompilationInfo(HostingEnvironmentParameters hostingParameters) {
2442
2443             if (hostingParameters == null || hostingParameters.ClientBuildManagerParameter == null)
2444                 return;
2445
2446             _precompilationFlags = hostingParameters.ClientBuildManagerParameter.PrecompilationFlags;
2447
2448             _strongNameKeyFile = hostingParameters.ClientBuildManagerParameter.StrongNameKeyFile;
2449             _strongNameKeyContainer = hostingParameters.ClientBuildManagerParameter.StrongNameKeyContainer;
2450
2451             // Check if we're precompiling to a target directory
2452             _precompTargetPhysicalDir = hostingParameters.PrecompilationTargetPhysicalDirectory;
2453             if (_precompTargetPhysicalDir == null)
2454                 return;
2455
2456             // Check if the target dir already exists and is not empty
2457             if (Util.IsNonEmptyDirectory(_precompTargetPhysicalDir)) {
2458
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));
2462                 }
2463
2464                 // Does it contain the precomp marker file
2465                 bool updatable;
2466                 bool precompiled = ReadPrecompMarkerFile(_precompTargetPhysicalDir, out updatable);
2467
2468                 // If not, refuse to delete the directory, even if OverwriteTarget is on (VSWhidbey 425095)
2469                 if (!precompiled) {
2470                     throw new HttpException(SR.GetString(SR.Dir_not_empty_not_precomp));
2471                 }
2472
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...");
2479                     Thread.Sleep(250);
2480
2481                     if (!DeletePrecompTargetDirectory()) {
2482                         Debug.Trace("BuildManager", "Failed to delete " + _precompTargetPhysicalDir + ".  Sleeping and trying once more...");
2483                         // Try again after 1 second.
2484                         Thread.Sleep(1000);
2485
2486                         // If we still couldn't delete it, fail
2487                         if (!DeletePrecompTargetDirectory()) {
2488                             throw new HttpException(SR.GetString(SR.Cant_delete_dir));
2489                         }
2490                     }
2491                 }
2492             }
2493
2494             // Create a marker file to mark the fact that this is a precompiled app
2495             CreatePrecompMarkerFile();
2496         }
2497
2498         private bool DeletePrecompTargetDirectory() {
2499             try {
2500                 if (_precompTargetPhysicalDir != null) {
2501                     // Go through all the files in the directory and delete them.
2502                     foreach (FileData fileData in FileEnumerator.Create(_precompTargetPhysicalDir)) {
2503
2504                         if (fileData.IsDirectory) {
2505                             Directory.Delete(fileData.FullName, true /*recursive*/);
2506                         }
2507                         else {
2508                             Util.DeleteFileNoException(fileData.FullName);
2509                         }
2510                     }
2511                 }
2512             }
2513 #if DEBUG
2514             catch (Exception e) {
2515                 Debug.Trace("BuildManager", "DeletePrecompTargetDirectory failed: " + e.Message);
2516             }
2517 #else
2518             catch { }
2519 #endif
2520             return !Util.IsNonEmptyDirectory(_precompTargetPhysicalDir);
2521         }
2522
2523         private void FailIfPrecompiledApp() {
2524
2525             if (IsPrecompiledApp) {
2526                 throw new HttpException(SR.GetString(SR.Already_precomp));
2527             }
2528         }
2529
2530         internal void PrecompileApp(ClientBuildManagerCallback callback, IEnumerable<string> excludedVirtualPaths) {
2531
2532             // Remember the original setting
2533             bool skipTopLevelExceptions = SkipTopLevelCompilationExceptions;
2534
2535             try {
2536                 _cbmCallback = callback;
2537
2538                 // Don't stop on the first parse errors, process as many errors as possible.
2539                 ThrowOnFirstParseError = false;
2540
2541                 // Don't skip top level compilation exceptions even called by CBM.
2542                 SkipTopLevelCompilationExceptions = false;
2543
2544                 PrecompileApp(HttpRuntime.AppDomainAppVirtualPathObject, excludedVirtualPaths);
2545             }
2546             finally {
2547                 // Revert to original setting
2548                 SkipTopLevelCompilationExceptions = skipTopLevelExceptions;
2549                 ThrowOnFirstParseError = true;
2550
2551                 _cbmCallback = null;
2552             }
2553         }
2554
2555         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
2556         private void PrecompileApp(VirtualPath startingVirtualDir, IEnumerable<string> excludedVirtualPaths) {
2557             using (new ApplicationImpersonationContext()) {
2558                 try {
2559                     PerformingPrecompilation = true;
2560
2561                     PrecompileAppInternal(startingVirtualDir, excludedVirtualPaths);
2562                 }
2563                 catch {
2564                     // If anything fails during precompilation, wipe out the target to avoid
2565                     // leaving it in a random state (VSWhidbey 447338)
2566                     DeletePrecompTargetDirectory();
2567
2568                     throw;
2569                 }
2570                 finally {
2571                     PerformingPrecompilation = false;
2572                 }
2573             }
2574         }
2575
2576         private void PrecompileAppInternal(VirtualPath startingVirtualDir, IEnumerable<string> excludedVirtualPaths) {
2577
2578             // If the app is already precompiled, fail
2579             FailIfPrecompiledApp();
2580
2581             excludedVirtualPaths = excludedVirtualPaths ?? Enumerable.Empty<string>();
2582             _excludedCompilationPaths = excludedVirtualPaths.Select(path => VirtualPath.Create(UrlPath.Combine("~", path))).ToList();
2583
2584             VirtualDirectory appVdir = startingVirtualDir.GetDirectory();
2585
2586             EnsureTopLevelFilesCompiled();
2587
2588             try {
2589                 // Clear the parseError flag first
2590                 _parseErrorReported = false;
2591
2592                 PrecompileWebDirectoriesRecursive(appVdir, topLevel: true);
2593                 PrecompileThemeDirectories();
2594             }
2595             catch (HttpParseException parseException) {
2596                 // if nothing calls callback.reportparseerror yet, report the parse error.
2597                 if (!_parseErrorReported) {
2598                     ReportErrorsFromException(parseException);
2599                 }
2600
2601                 throw;
2602             }
2603
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);
2608             }
2609
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);
2615             }
2616         }
2617
2618         // Create a small file that marks that app as being precompiled
2619         private void CreatePrecompMarkerFile() {
2620
2621             Debug.Assert(PrecompilingForDeployment);
2622
2623             Directory.CreateDirectory(_precompTargetPhysicalDir);
2624             string precompMarkerFile = Path.Combine(_precompTargetPhysicalDir, precompMarkerFileName);
2625
2626             using (StreamWriter writer = new StreamWriter(precompMarkerFile, false /*append*/, Encoding.UTF8)) {
2627                 writer.Write("<precompiledApp version=\"2\" updatable=\"");
2628
2629                 // Write out a flag that determines if the precompiled app is updatable
2630                 if (PrecompilingForUpdatableDeployment)
2631                     writer.Write("true");
2632                 else
2633                     writer.Write("false");
2634                 writer.Write("\"/>");
2635             }
2636         }
2637
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) {
2640
2641             updatable = false;
2642
2643             // Get the full physical path to the precompilation market file
2644             string precompMarkerFile = Path.Combine(appRoot, precompMarkerFileName);
2645
2646             // If the file doesn't exist at all, it's not a precompiled app
2647             if (!File.Exists(precompMarkerFile))
2648                 return false;
2649
2650             XmlDocument doc = new XmlDocument();
2651             try {
2652                 doc.Load(precompMarkerFile);
2653             }
2654             catch {
2655                 // If we fail to read it for any reason, ignore it.
2656                 return false;
2657             }
2658
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")
2663                 return false;
2664
2665             // Check the updatable flag
2666             HandlerBase.GetAndRemoveBooleanAttribute(root, "updatable", ref updatable);
2667
2668             return true;
2669         }
2670
2671         /*
2672          * Are we precompiling the app for deployment (as opposed to in-place)
2673          */
2674         internal static bool PrecompilingForDeployment {
2675             get {
2676                 return (_theBuildManager._precompTargetPhysicalDir != null);
2677             }
2678         }
2679
2680         internal static bool PrecompilingForUpdatableDeployment {
2681             get {
2682                 // The updatebale mode only applies in deployment precompilation mode
2683                 if (!PrecompilingForDeployment)
2684                     return false;
2685
2686                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.Updatable) != 0;
2687             }
2688         }
2689
2690         private static bool PrecompilingForCleanBuild {
2691             get {
2692                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.Clean) != 0;
2693             }
2694         }
2695
2696         internal static bool PrecompilingWithDebugInfo {
2697             get {
2698                 // The ForceDebug flag only applies in deployment precompilation mode
2699                 if (!PrecompilingForDeployment)
2700                     return false;
2701
2702                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.ForceDebug) != 0;
2703             }
2704         }
2705
2706         internal static bool PrecompilingWithCodeAnalysisSymbol {
2707             get {
2708                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.CodeAnalysis) != 0;
2709             }
2710         }
2711
2712         private static bool CompileWithFixedAssemblyNames {
2713             get {
2714                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.FixedNames) != 0;
2715             }
2716         }
2717
2718         internal static bool CompileWithAllowPartiallyTrustedCallersAttribute {
2719             get {
2720                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.AllowPartiallyTrustedCallers) != 0;
2721             }
2722         }
2723
2724         internal static bool CompileWithDelaySignAttribute {
2725             get {
2726                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.DelaySign) != 0;
2727             }
2728         }
2729
2730         internal static bool IgnoreBadImageFormatException {
2731             get {
2732                 return (_theBuildManager._precompilationFlags & PrecompilationFlags.IgnoreBadImageFormatException) != 0;
2733             }
2734         }
2735
2736         internal static string StrongNameKeyFile {
2737             get {
2738                 return _theBuildManager._strongNameKeyFile;
2739             }
2740         }
2741
2742         internal static string StrongNameKeyContainer {
2743             get {
2744                 return _theBuildManager._strongNameKeyContainer;
2745             }
2746         }
2747
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) {
2752
2753             Debug.Assert(fileEncoding != null);
2754
2755             if (!PrecompilingForUpdatableDeployment)
2756                 return null;
2757
2758             Debug.Assert(!virtualPath.IsRelative);
2759
2760             string path = virtualPath.AppRelativeVirtualPathString;
2761
2762             // Skip the "~/" to be left with the relative path
2763             path = path.Substring(2);
2764
2765             // Combine it with the precomp target dir to get the full path
2766             string physicalPath = Path.Combine(_theBuildManager._precompTargetPhysicalDir, path);
2767
2768             // Before trying to create the file, make sure the directory exists
2769             string physicalDir = Path.GetDirectoryName(physicalPath);
2770             Directory.CreateDirectory(physicalDir);
2771
2772             return new StreamWriter(physicalPath, false /*append*/, fileEncoding);
2773         }
2774
2775         private bool IsPrecompiledAppInternal {
2776             get {
2777                 if (!_isPrecompiledAppComputed) {
2778                     _isPrecompiledApp = ReadPrecompMarkerFile(HttpRuntime.AppDomainAppPathInternal,
2779                         out _isUpdatablePrecompiledApp);
2780
2781                     _isPrecompiledAppComputed = true;
2782                 }
2783
2784                 return _isPrecompiledApp;
2785             }
2786         }
2787
2788         public static bool IsPrecompiledApp {
2789             get {
2790                 return _theBuildManager.IsPrecompiledAppInternal;
2791             }
2792         }
2793
2794         private bool IsUpdatablePrecompiledAppInternal {
2795             get {
2796                 return IsPrecompiledApp && _isUpdatablePrecompiledApp;
2797             }
2798         }
2799
2800         public static bool IsUpdatablePrecompiledApp {
2801             get {
2802                 return _theBuildManager.IsUpdatablePrecompiledAppInternal;
2803             }
2804         }
2805
2806         private bool IsNonUpdatablePrecompiledApp {
2807             get {
2808                 return IsPrecompiledApp && !_isUpdatablePrecompiledApp;
2809             }
2810         }
2811
2812         private bool IsExcludedFromPrecompilation(VirtualDirectory dir) {
2813             Debug.Assert(dir != null);
2814             return _excludedCompilationPaths.Any(path => UrlPath.IsEqualOrSubpath(path.VirtualPathString, dir.VirtualPath));
2815         }
2816
2817         private void PrecompileWebDirectoriesRecursive(VirtualDirectory vdir, bool topLevel) {
2818
2819             // Precompile the children directory
2820
2821             foreach (VirtualDirectory childVdir in vdir.Directories) {
2822
2823                 if (topLevel && _excludedTopLevelDirectories.Contains(childVdir.Name))
2824                     continue;
2825
2826                 // Exclude the special FrontPage directory (VSWhidbey 116727, 518602)
2827                 if (childVdir.Name == "_vti_cnf")
2828                     continue;
2829
2830                 // Exclude target directory in precompilation scenarios
2831                 if (SourceDirectoryIsInPrecompilationDestination(childVdir)) {
2832                     continue;
2833                 }
2834
2835                 if (IsExcludedFromPrecompilation(childVdir)) {
2836                     continue;
2837                 }
2838
2839                 PrecompileWebDirectoriesRecursive(childVdir, topLevel: false);
2840             }
2841
2842             // Precompile this directory
2843             try {
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;
2848
2849                 if (IsBatchEnabledForDirectory(vdir.VirtualPathObject)) {
2850                     // batch everything if enabled
2851                     BatchCompileWebDirectory(vdir, virtualDir: null, ignoreErrors: false);
2852                 }
2853                 else {
2854                     // if batching is disabled, compile each web file individually.
2855                     NonBatchDirectoryCompiler dirCompiler = new NonBatchDirectoryCompiler(vdir);
2856                     dirCompiler.Process();
2857                 }
2858             }
2859             finally {
2860                 // Always restore the flag to false when we're done.
2861                 _precompilingApp = false;
2862             }
2863         }
2864
2865         private void PrecompileThemeDirectories() {
2866             string appPhysicalDir = Path.Combine(HttpRuntime.AppDomainAppPathInternal, HttpRuntime.ThemesDirectoryName);
2867
2868             if (Directory.Exists(appPhysicalDir)) {
2869                 string[] themeDirs = Directory.GetDirectories(appPhysicalDir);
2870
2871                 foreach (string themeDirPath in themeDirs) {
2872                     string themeDirName = Path.GetFileName(themeDirPath);
2873                     ThemeDirectoryCompiler.GetThemeBuildResultType(null /*context*/, themeDirName);
2874                 }
2875             }
2876         }
2877
2878         /*
2879          * Recursively copy all the static files from the source directory to the
2880          * target directory of the precompilation
2881          */
2882         private void CopyStaticFilesRecursive(VirtualDirectory sourceVdir, string destPhysicalDir,
2883             bool topLevel) {
2884
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)) {
2889                 return;
2890             }
2891
2892             if (IsExcludedFromPrecompilation(sourceVdir)) {
2893                 return;
2894             }
2895
2896             bool directoryCreationAttempted = false;
2897
2898             foreach (VirtualFileBase child in sourceVdir.Children) {
2899
2900                 string destPhysicalSubDir = Path.Combine(destPhysicalDir, child.Name);
2901
2902                 if (child.IsDirectory) {
2903
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.
2906                     if (topLevel &&
2907                         (StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.CodeDirectoryName) ||
2908                         StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.ResourcesDirectoryName) ||
2909                         StringUtil.EqualsIgnoreCase(child.Name, HttpRuntime.WebRefDirectoryName))) {
2910
2911                         continue;
2912                     }
2913
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)) {
2918                         continue;
2919                     }
2920
2921                     CopyStaticFilesRecursive(child as VirtualDirectory, destPhysicalSubDir, topLevel: false);
2922                     continue;
2923                 }
2924
2925                 // Create the destination directory if needed
2926                 if (!directoryCreationAttempted) {
2927                     directoryCreationAttempted = true;
2928                     Directory.CreateDirectory(destPhysicalDir);
2929                 }
2930
2931                 // Copy the file as appropriate based on its extension
2932                 CopyPrecompiledFile(child as VirtualFile, destPhysicalSubDir);
2933             }
2934         }
2935
2936         /*
2937          * Copy all the assemblies from the codegen dir into the bin directory of the
2938          * target precompiled app.
2939          */
2940         private void CopyCompiledAssembliesToDestinationBin(string fromDir, string toDir) {
2941
2942             bool createdDirectory = false;
2943
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;
2950
2951                 // Recurse on subdirectories.if they contain culture files
2952                 if (fileData.IsDirectory) {
2953
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);
2958                     }
2959
2960                     continue;
2961                 }
2962
2963                 // Only process DLL's and PDB's
2964                 string extension = Path.GetExtension(fileData.Name);
2965                 if (extension != ".dll" && extension != ".pdb")
2966                     continue;
2967
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)) {
2971                     continue;
2972                 }
2973
2974                 string sourcePhysicalPath = Path.Combine(fromDir, fileData.Name);
2975                 string destPhysicalPath = Path.Combine(toDir, fileData.Name);
2976
2977                 // Copy the file to the destination
2978                 // 
2979                 File.Copy(sourcePhysicalPath, destPhysicalPath, true /*overwrite*/);
2980             }
2981         }
2982
2983         // Copy one file from the source app to the precompiled app
2984         private void CopyPrecompiledFile(VirtualFile vfile, string destPhysicalPath) {
2985
2986             bool createStub;
2987
2988             if (CompilationUtil.NeedToCopyFile(vfile.VirtualPathObject, PrecompilingForUpdatableDeployment,
2989                 out createStub)) {
2990
2991                 // 
2992                 string sourcePhysicalPath = HostingEnvironment.MapPathInternal(vfile.VirtualPath);
2993
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)) {
2997
2998                     // In that case, we still need to fix it up to insert the correct type string in the
2999                     // inherits attribute (VSWhidbey 467936)
3000
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);
3005
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);
3009
3010                     // Read in the file
3011                     string newAspxFile = Util.StringFromFile(destPhysicalPath, ref encoding);
3012
3013                     // Replace the placeholder token by the true type with the assembly
3014                     newAspxFile = newAspxFile.Replace(UpdatableInheritReplacementToken,
3015                         Util.GetAssemblyQualifiedTypeName(result.ResultType));
3016
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);
3020                     writer.Close();
3021                 }
3022                 else {
3023                     // Just copy the file to the destination
3024                     File.Copy(sourcePhysicalPath, destPhysicalPath, false /*overwrite*/);
3025                 }
3026
3027                 // If it has a readonly attribute, clear it on the destination (VSWhidbey 122359)
3028                 Util.ClearReadOnlyAttribute(destPhysicalPath);
3029             }
3030             else {
3031                 if (createStub) {
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));
3035                     writer.Close();
3036                 }
3037             }
3038         }
3039
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) {
3044                 return false;
3045             }
3046
3047             string sourcePhysicalDir = HostingEnvironment.MapPathInternal(sourceDir.VirtualPath);
3048
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);
3052
3053             return StringUtil.StringStartsWithIgnoreCase(sourcePhysicalDir, destPhysicalDir);
3054         }
3055
3056         internal static void ReportDirectoryCompilationProgress(VirtualPath virtualDir) {
3057
3058             // Nothing to do if there is no CBM callback
3059             if (CBMCallback == null)
3060                 return;
3061
3062             // Don't report anything if the directory doesn't exist
3063             if (!virtualDir.DirectoryExists())
3064                 return;
3065
3066             string message = SR.GetString(SR.Directory_progress, virtualDir.VirtualPathString);
3067             CBMCallback.ReportProgress(message);
3068         }
3069
3070
3071         //
3072         // Public methods
3073         //
3074
3075
3076         /// <devdoc>
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.
3080         /// </devdoc>
3081         public static Type GetCompiledType(string virtualPath) {
3082             if (virtualPath == null) {
3083                 throw new ArgumentNullException("virtualPath");
3084             }
3085
3086             return GetCompiledType(VirtualPath.Create(virtualPath));
3087         }
3088
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;
3094
3095             try {
3096                 // Don't skip top level compilation exceptions even called by CBM.
3097                 SkipTopLevelCompilationExceptions = false;
3098
3099                 // Don't stop on the first parse error, process as many errors as possible.
3100                 ThrowOnFirstParseError = false;
3101
3102                 _theBuildManager._cbmCallback = callback;
3103                 return GetCompiledType(virtualPath);
3104             }
3105             finally {
3106                 _theBuildManager._cbmCallback = null;
3107
3108                 // Revert to original setting
3109                 SkipTopLevelCompilationExceptions = skipTopLevelExceptions;
3110
3111                 ThrowOnFirstParseError = throwOnFirstParseError;
3112             }
3113         }
3114
3115         internal static Type GetCompiledType(VirtualPath virtualPath) {
3116             ITypedWebObjectFactory factory = GetVirtualPathObjectFactory(virtualPath,
3117                 null /*context*/, false /*allowCrossApp*/);
3118
3119             BuildResultCompiledType resultType = factory as BuildResultCompiledType;
3120             if (resultType == null) return null;
3121
3122             return resultType.ResultType;
3123         }
3124
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*/);
3132         }
3133
3134         /// <devdoc>
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.
3137         /// </devdoc>
3138         internal static object CreateInstanceFromVirtualPath(VirtualPath virtualPath,
3139             Type requiredBaseType, HttpContext context, bool allowCrossApp) {
3140
3141             ITypedWebObjectFactory objectFactory = GetVirtualPathObjectFactory(virtualPath, context, allowCrossApp);
3142             if (objectFactory == null) return null;
3143
3144             // Make sure it has the required base type (VSWhidbey 516771)
3145             Util.CheckAssignableType(requiredBaseType, objectFactory.InstantiatedType);
3146
3147             // impersonate client while executing page ctor (see ASURT 89712)
3148             // (compilation is done while not impersonating client)
3149
3150             Object instance;
3151             using (new ClientImpersonationContext(context)) {
3152                 instance = objectFactory.CreateInstance();
3153             }
3154
3155             return instance;
3156         }
3157
3158         public static IWebObjectFactory GetObjectFactory(string virtualPath, bool throwIfNotFound) {
3159             ITypedWebObjectFactory factory = GetVirtualPathObjectFactory(VirtualPath.Create(virtualPath),
3160                 null /*context*/, false /*allowCrossApp*/, throwIfNotFound);
3161             return factory;
3162         }
3163
3164         private static ITypedWebObjectFactory GetVirtualPathObjectFactory(VirtualPath virtualPath,
3165             HttpContext context, bool allowCrossApp) {
3166             return GetVirtualPathObjectFactory(virtualPath, context, allowCrossApp, true /*throwIfNotFound*/);
3167         }
3168
3169         /// <devdoc>
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.
3173         /// </devdoc>
3174         private static ITypedWebObjectFactory GetVirtualPathObjectFactory(VirtualPath virtualPath,
3175             HttpContext context, bool allowCrossApp, bool throwIfNotFound) {
3176
3177             if (virtualPath == null)
3178                 throw new ArgumentNullException("virtualPath");
3179
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();
3185             }
3186
3187             ITypedWebObjectFactory objectFactory;
3188             BuildResult buildResult;
3189
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)
3193             //
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);
3200             }
3201             else {
3202                 buildResult = GetVPathBuildResultWithAssert(
3203                     context, virtualPath, false /*noBuild*/, allowCrossApp, false /*allowBuildInPrecompile*/, throwIfNotFound);
3204             }
3205
3206             // DevDiv 67952
3207             // The returned build result may not always be castable to ITypedWebObjectFactory.
3208             objectFactory = buildResult as ITypedWebObjectFactory;
3209
3210             return objectFactory;
3211         }
3212
3213         /// <devdoc>
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.
3217         /// </devdoc>
3218         public static Assembly GetCompiledAssembly(string virtualPath) {
3219
3220             BuildResult result = GetVPathBuildResult(VirtualPath.Create(virtualPath));
3221             if (result == null) return null;
3222
3223             BuildResultCompiledAssemblyBase resultAssembly = result as BuildResultCompiledAssemblyBase;
3224             if (resultAssembly == null) return null;
3225
3226             return resultAssembly.ResultAssembly;
3227         }
3228
3229
3230         /// <devdoc>
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.
3235         /// </devdoc>
3236         public static string GetCompiledCustomString(string virtualPath) {
3237
3238             BuildResult result = GetVPathBuildResult(VirtualPath.Create(virtualPath));
3239             if (result == null) return null;
3240
3241             BuildResultCustomString resultCustomString = result as BuildResultCustomString;
3242             if (resultCustomString == null) return null;
3243
3244             return resultCustomString.CustomString;
3245         }
3246
3247         /// <devdoc>
3248         ///     Returns the BuildDependencySet for the passed in virtualPath, assuming
3249         ///     that information is cached.  Otherwise, return null.
3250         /// </devdoc>
3251         public static BuildDependencySet GetCachedBuildDependencySet(
3252             HttpContext context, string virtualPath) {
3253             return GetCachedBuildDependencySet(context, virtualPath, ensureIsUpToDate: true);
3254         }
3255
3256         public static BuildDependencySet GetCachedBuildDependencySet(
3257             HttpContext context, string virtualPath, bool ensureIsUpToDate) {
3258
3259             BuildResult result = GetVPathBuildResult(context, VirtualPath.Create(virtualPath),
3260                 true /*noBuild*/, false /*allowCrossApp*/, allowBuildInPrecompile: false, ensureIsUpToDate: ensureIsUpToDate);
3261
3262             // If it's not cached, return null
3263             if (result == null)
3264                 return null;
3265
3266             // We found it in the cache.  Wrap it with a BuildDependencySet object.
3267             return new BuildDependencySet(result);
3268         }
3269
3270         /// <summary>
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.
3274         /// </summary>
3275         public static FrameworkName TargetFramework {
3276             get {
3277                 return MultiTargetingUtil.TargetFrameworkName;
3278             }
3279         }
3280
3281         private Assembly ResolveAssembly(object sender, ResolveEventArgs e) {
3282
3283             if (_assemblyResolveMapping == null)
3284                 return null;
3285
3286             string name = e.Name;
3287             Assembly assembly = (Assembly)_assemblyResolveMapping[name];
3288
3289             // Return the assembly if we have it in our mapping (VSWhidbey 276776)
3290             if (assembly != null) {
3291                 return assembly;
3292             }
3293
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];
3298             }
3299
3300             return null;
3301         }
3302
3303         internal static string GetNormalizedCodeAssemblyName(string assemblyName) {
3304             // Return the main code assembly.
3305             if (assemblyName.StartsWith(CodeDirectoryAssemblyName, StringComparison.Ordinal)) {
3306                 return CodeDirectoryAssemblyName;
3307             }
3308
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;
3314                 }
3315             }
3316
3317             return null;
3318         }
3319
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;
3325             }
3326
3327             string normalizedTypeName = t.FullName + ", " + normalizedCodeAssemblyName;
3328             return normalizedTypeName;
3329         }
3330
3331         /// <summary>
3332         /// Temporary subdirectory under the codegen folder for buildproviders to generate embedded resource files.
3333         /// </summary>
3334         internal static string CodegenResourceDir {
3335             get {
3336                 string resxDir = _theBuildManager._codegenResourceDir;
3337                 if (resxDir == null) {
3338                     resxDir = Path.Combine(HttpRuntime.CodegenDirInternal, CodegenResourceDirectoryName);
3339                     _theBuildManager._codegenResourceDir = resxDir;
3340                 }
3341                 return resxDir;
3342             }
3343         }
3344
3345         // The Use Cache lives under the codegen folder
3346         private static string _userCachePath;
3347         private static string UserCachePath {
3348             get {
3349                 if (_userCachePath == null) {
3350                     // Build the full path to the User Cache folder
3351                     string userCachePath = Path.Combine(HttpRuntime.CodegenDirInternal, "UserCache");
3352
3353                     // Create it if it doesn't exist
3354                     if (!Directory.Exists(userCachePath)) {
3355                         Directory.CreateDirectory(userCachePath);
3356                     }
3357
3358                     _userCachePath = userCachePath;
3359                 }
3360
3361                 return _userCachePath;
3362             }
3363         }
3364
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);
3369
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();
3373             }
3374
3375             return path;
3376         }
3377
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();
3382
3383             // Get the path to the file in the User Cache folder
3384             string path = GetUserCacheFilePath(fileName);
3385
3386             return File.Create(path);
3387         }
3388
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();
3393
3394             // Get the path to the file in the User Cache folder
3395             string path = GetUserCacheFilePath(fileName);
3396
3397             // If the file doesn't exist, just return null, to convey a cache miss
3398             if (!File.Exists(path))
3399                 return null;
3400
3401             return File.OpenRead(path);
3402         }
3403     }
3404
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
3411     }
3412
3413     internal enum PreStartInitStage {
3414         BeforePreStartInit,
3415         DuringPreStartInit,
3416         AfterPreStartInit,
3417     }
3418
3419     internal class AssemblyReferenceInfo {
3420         internal Assembly Assembly;
3421         internal int ReferenceIndex;
3422
3423         internal AssemblyReferenceInfo(int referenceIndex) {
3424             ReferenceIndex = referenceIndex;
3425         }
3426     }
3427
3428 }