//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ /********************************* BuildResult BuildResultCompileError BuildResultCompiledAssemblyBase BuildResultCompiledAssembly BuildResultCustomString BuildResultMainCodeAssembly BuildResultResourceAssembly BuildResultCompiledType BuildResultCompiledTemplateType BuildResultCompiledGlobalAsaxType ImageGeneratorBuildResultCompiledType BuildResultNoCompileTemplateControl BuildResultNoCompilePage BuildResultNoCompileUserControl BuildResultNoCompileMasterPage BuildResultCodeCompileUnit **********************************/ namespace System.Web.Compilation { using System; using System.IO; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; using System.ComponentModel.Design; using System.Globalization; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using System.Security; using System.Security.Permissions; using System.Threading; using System.Web.Caching; using System.Web.Hosting; using System.Web.Util; using System.Web.UI; using System.Web.Configuration; internal enum BuildResultTypeCode { Invalid=-1, BuildResultCompiledAssembly = 1, BuildResultCompiledType = 2, BuildResultCompiledTemplateType = 3, BuildResultCustomString = 5, BuildResultMainCodeAssembly = 6, BuildResultCodeCompileUnit = 7, BuildResultCompiledGlobalAsaxType = 8, BuildResultResourceAssembly = 9, } internal abstract class BuildResult { // const masks into the BitVector32 // The 16 lower bits come from the BuildProviderResultFlags enumeration // and should not be used here. They are set from calling // BuildProvider.GetResultFlags. protected const int usesCacheDependency = 0x00010000; protected const int usesExistingAssembly = 0x00020000; private const int noMemoryCache = 0x00040000; protected const int hasAppOrSessionObjects = 0x00080000; protected const int dependenciesHashComputed = 0x00100000; #pragma warning disable 0649 protected SimpleBitVector32 _flags; #pragma warning restore 0649 internal static BuildResult CreateBuildResultFromCode(BuildResultTypeCode code, VirtualPath virtualPath) { BuildResult ret = null; switch (code) { case BuildResultTypeCode.BuildResultCompiledAssembly: ret = new BuildResultCompiledAssembly(); break; case BuildResultTypeCode.BuildResultCompiledType: ret = new BuildResultCompiledType(); break; case BuildResultTypeCode.BuildResultCompiledTemplateType: ret = new BuildResultCompiledTemplateType(); break; case BuildResultTypeCode.BuildResultCompiledGlobalAsaxType: ret = new BuildResultCompiledGlobalAsaxType(); break; case BuildResultTypeCode.BuildResultCustomString: ret = new BuildResultCustomString(); break; case BuildResultTypeCode.BuildResultMainCodeAssembly: ret = new BuildResultMainCodeAssembly(); break; case BuildResultTypeCode.BuildResultResourceAssembly: ret = new BuildResultResourceAssembly(); break; case BuildResultTypeCode.BuildResultCodeCompileUnit: ret = new BuildResultCodeCompileUnit(); break; default: Debug.Assert(false, "code=" + code); return null; } ret.VirtualPath = virtualPath; // Set _nextUpToDateCheck to MinValue, to make sure the next call to IsUpToDate() // actually makes the check ret._nextUpToDateCheck = DateTime.MinValue; return ret; } internal virtual BuildResultTypeCode GetCode() { return BuildResultTypeCode.Invalid; } internal int Flags { get { return _flags.IntegerValue; } set { _flags.IntegerValue = value; } } private VirtualPath _virtualPath; internal VirtualPath VirtualPath { get { return _virtualPath; } set { _virtualPath = value; } } // Are the BuildResult's VirtualPathDependencies being monitored by a CacheDependency. // If so, then we don't need to check validity after finding the BuildResult in the // memory cache (since it would have been kicked out if it was invalid). internal bool UsesCacheDependency { get { return _flags[usesCacheDependency]; } set { _flags[usesCacheDependency] = value; } } // Does the appdomain need to be shut down when this item becomes invalid? internal bool ShutdownAppDomainOnChange { get { return _flags[(int)BuildProviderResultFlags.ShutdownAppDomainOnChange]; } } // The list of files (virtual paths) it depends on (for caching purpose) private ArrayList _virtualPathDependencies; internal ICollection VirtualPathDependencies { get { return _virtualPathDependencies; } } // Hash code based on all the source file dependencies private string _virtualPathDependenciesHash; internal string VirtualPathDependenciesHash { get { EnsureVirtualPathDependenciesHashComputed(); return _virtualPathDependenciesHash; } set { Debug.Assert(_virtualPathDependenciesHash == null); _virtualPathDependenciesHash = value; } } internal bool DependenciesHashComputed { get { return _flags[dependenciesHashComputed]; } } internal void EnsureVirtualPathDependenciesHashComputed() { if (!DependenciesHashComputed) { // We shouldn't already have a hash Debug.Assert(_virtualPathDependenciesHash == null); // Sort the source dependencies to make the hash code predictable if (_virtualPathDependencies != null) _virtualPathDependencies.Sort(InvariantComparer.Default); _virtualPathDependenciesHash = ComputeSourceDependenciesHashCode(null /*virtualPath*/); // It's computed, but it could be null _flags[dependenciesHashComputed] = true; } } // These fields are used to make sure we only check the UpToDate status // of the build result once every few seconds (since it's expensive) private DateTime _nextUpToDateCheck = DateTime.Now.AddSeconds(UpdateInterval); private int _lock; private const int UpdateInterval = 2; // 2 seconds internal void SetVirtualPathDependencies(ArrayList sourceDependencies) { Debug.Assert(_virtualPathDependencies == null); Debug.Assert(sourceDependencies != null); _virtualPathDependencies = sourceDependencies; } internal void AddVirtualPathDependencies(ICollection sourceDependencies) { if (sourceDependencies == null) return; if (_virtualPathDependencies == null) { _virtualPathDependencies = new ArrayList(sourceDependencies); } else { _virtualPathDependencies.AddRange(sourceDependencies); } } /* * Can the result be unloaded from memory. Most objects can, but things like * Assemblies and Types can't. This is used to determine the caching behavior. */ internal virtual bool IsUnloadable { get { return true; } } /* * Should the result be cached to disk. Usually yes, but for things like compile * errors, we only cache them to memory. */ internal virtual bool CacheToDisk { get { return true; } } /* * Should the result be cached to memory. Usually yes, but for things like top level * assemblies, we only cache them to disk. */ internal bool CacheToMemory { get { return !_flags[noMemoryCache]; } set { _flags[noMemoryCache] = !value; } } /* * Time the build result should expire from the memory cache */ internal virtual DateTime MemoryCacheExpiration { get { return Cache.NoAbsoluteExpiration; } } /* * Sliding expiration for the build result */ internal virtual TimeSpan MemoryCacheSlidingExpiration { get { return Cache.NoSlidingExpiration; } } protected void ReadPreservedFlags(PreservationFileReader pfr) { string s = pfr.GetAttribute("flags"); if ((s != null) && (s.Length != 0)) { Flags = Int32.Parse(s, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture); } } internal virtual void GetPreservedAttributes(PreservationFileReader pfr) { ReadPreservedFlags(pfr); } internal virtual void SetPreservedAttributes(PreservationFileWriter pfw) { if (Flags != 0) { pfw.SetAttribute("flags", Flags.ToString("x", CultureInfo.InvariantCulture)); } } /* * Tell the BuildResult that its dependencies are not up to date, in order * to give it a chance to do some cleanup. */ internal virtual void RemoveOutOfDateResources(PreservationFileReader pfw) {} // Compute the current hash code of the preserved data. Return 0 if the // hash code is not valid. internal long ComputeHashCode(long hashCode) { return ComputeHashCode(hashCode, 0); } internal long ComputeHashCode(long hashCode1, long hashCode2) { HashCodeCombiner hashCodeCombiner = new HashCodeCombiner(); // If a hashcode was passed in, start with it if (hashCode1 != 0) hashCodeCombiner.AddObject(hashCode1); if (hashCode2 != 0) hashCodeCombiner.AddObject(hashCode2); ComputeHashCode(hashCodeCombiner); return hashCodeCombiner.CombinedHash; } /* * Compute the hash code of what this buid result depends on, excluding * the virtual path dependencies (which are handled separately by * VirtualPathDependenciesHash). */ protected virtual void ComputeHashCode(HashCodeCombiner hashCodeCombiner) { } internal virtual string ComputeSourceDependenciesHashCode(VirtualPath virtualPath) { // Return an empty string if there are no dependencies. This is different from // null, which means 'don't cache' if (VirtualPathDependencies == null) return String.Empty; // If no virtual path was passed in, use the one from the BuildResult if (virtualPath == null) virtualPath = VirtualPath; return virtualPath.GetFileHash(VirtualPathDependencies); } internal bool IsUpToDate(VirtualPath virtualPath, bool ensureIsUpToDate) { if (!ensureIsUpToDate) { return true; } // This should never be called on a BuildResult that has already been // determined to be out of date. Debug.Assert(_lock >= 0); if (_lock < 0) return false; // Don't check more than every two seconds DateTime now = DateTime.Now; // Due to bug 214038, CBM can be called multiple times in a very short time. if (now < _nextUpToDateCheck && !BuildManagerHost.InClientBuildManager) { Debug.Trace("BuildResult", "IsUpToDate: true since called less than 2 seconds ago. " + _nextUpToDateCheck + "," + now); return true; } // If we don't get the lock, just say it's up to date without checking if (Interlocked.CompareExchange(ref _lock, 1, 0) != 0) { Debug.Trace("BuildResult", "IsUpToDate returning true because it didn't get the lock"); return true; } string newHashCode; try { newHashCode = ComputeSourceDependenciesHashCode(virtualPath); } catch { // Make sure to release the lock if something throws. Interlocked.Exchange(ref _lock, 0); throw; } // Check if we're up to date. A null hash code means the cache should not be used. if (newHashCode == null || newHashCode != _virtualPathDependenciesHash) { Debug.Trace("BuildResult", "IsUpToDate: '" + VirtualPath + "' is out of date"); // Set the lock to -1 to mark that we're not up to date _lock = -1; return false; } Debug.Trace("BuildResult", "IsUpToDate: '" + VirtualPath + "' is up to date"); // We're up to date. Remember the time we checked, and reset the lock _nextUpToDateCheck = now.AddSeconds(UpdateInterval); Interlocked.Exchange(ref _lock, 0); return true; } } internal class BuildResultCompileError: BuildResult { // The exception in case we cached the result of a failed compilation private HttpCompileException _compileException; internal HttpCompileException CompileException { get { return _compileException; } } internal BuildResultCompileError(VirtualPath virtualPath, HttpCompileException compileException) { VirtualPath = virtualPath; _compileException = compileException; } /* * Don't cache compile errors to disk */ internal override bool CacheToDisk { get { return false; } } internal override DateTime MemoryCacheExpiration { get { // Only cache compile errors for 10 seconds. This is to get us out of trouble // if the compilation fails due to some strange timing issue, and might succeed // on retry (VSWhidbey 483169) return DateTime.UtcNow.AddSeconds(10); } } } internal class BuildResultCustomString: BuildResultCompiledAssembly { private string _customString; internal BuildResultCustomString() {} internal BuildResultCustomString(Assembly a, string customString) : base(a) { Debug.Assert(customString != null); _customString = customString; } internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultCustomString; } internal override void GetPreservedAttributes(PreservationFileReader pfr) { base.GetPreservedAttributes(pfr); // Retrieve the custom string _customString = pfr.GetAttribute("customString"); Debug.Assert(_customString != null); } internal override void SetPreservedAttributes(PreservationFileWriter pfw) { base.SetPreservedAttributes(pfw); // Preserve the custom string pfw.SetAttribute("customString", _customString); } internal string CustomString { get { return _customString; } } } internal abstract class BuildResultCompiledAssemblyBase: BuildResult { internal bool UsesExistingAssembly { get { return _flags[usesExistingAssembly]; } set { _flags[usesExistingAssembly] = value; } } // Assemblies are *not* unloadable, so only allow the build result to be unloaded // if there is no assembly internal override bool IsUnloadable { get { return (ResultAssembly == null); } } internal abstract Assembly ResultAssembly { get; set; } internal virtual bool HasResultAssembly { get { return ResultAssembly != null; } } protected virtual bool IsGacAssembly { get { return ResultAssembly.GlobalAssemblyCache; } } protected virtual string ShortAssemblyName { get { return ResultAssembly.GetName().Name; } } static private string s_codegenDir = null; internal static Assembly GetPreservedAssembly(PreservationFileReader pfr) { string assemblyName = pfr.GetAttribute("assembly"); if (assemblyName == null) return null; // Try to load the assembly try { Assembly a = Assembly.Load(assemblyName); // VSWhidbey 564168 // Do not load assemblies or assemblies with references that // do not exist or are marked for deletion // It is possible that Assembly.Load succeeds, even though the // underlying DLL was renamed (to .delete). In that case, we should // not return the assembly, as we would be unable to compile with // a reference to it. if (AssemblyIsInvalid(a)) { // Throw some exception, since the caller doesn't expect null throw new InvalidOperationException(); } // Check references of the assembly, and make sure they exists, // otherwise throw an exception. CheckAssemblyIsValid(a, new Hashtable()); return a; } catch { Debug.Trace("BuildResult", "GetPreservedAssembly: couldn't load assembly '" + assemblyName + "'; deleting associated files."); // Remove the assembly and all the associated files pfr.DiskCache.RemoveAssemblyAndRelatedFiles(assemblyName); throw; } } // DevDiv Bug 98735 // Go through the assembly and all references (including deeper levels) to make sure that // each referenced assembly exists and does not have a dot delete. // If any referenced assembly is removed or marked for deletion, // we invalidate the base assembly by throwing an InvalidOperationException private static void CheckAssemblyIsValid(Assembly a, Hashtable checkedAssemblies) { // Keep track of which assemblies we already checked so we can skip them checkedAssemblies.Add(a, null); foreach (AssemblyName aName in a.GetReferencedAssemblies()) { Assembly referencedAssembly = Assembly.Load(aName); // If it is in the GAC, skip checking it if (referencedAssembly.GlobalAssemblyCache) continue; // Do not validate assemblies other than those we generate. // If the assembly is NOT in the codegen folder, skip it if (!AssemblyIsInCodegenDir(referencedAssembly)) continue; // If we have already checked an assembly, don't check it again if (!checkedAssemblies.Contains(referencedAssembly)) { if (AssemblyIsInvalid(referencedAssembly)) throw new InvalidOperationException(); // Visit nested referenced assemblies CheckAssemblyIsValid(referencedAssembly, checkedAssemblies); } } } internal static bool AssemblyIsInCodegenDir(Assembly a) { string path = Util.GetAssemblyCodeBase(a); FileInfo f = new FileInfo(path); string assemblyDir = FileUtil.RemoveTrailingDirectoryBackSlash(f.Directory.FullName); if (s_codegenDir == null) { s_codegenDir = FileUtil.RemoveTrailingDirectoryBackSlash(HttpRuntime.CodegenDir); } // check if the assembly is directly under codegen // Shadow-copied assemblies are in a deeper directory (eg myapp\zzz\yyy\assembly\dl3\xxxx) if (string.Equals(assemblyDir, s_codegenDir, StringComparison.OrdinalIgnoreCase)) return true; return false; } private static bool AssemblyIsInvalid(Assembly a) { // If the file does not exist, or if it has a .delete file, // then it should not be used string path = Util.GetAssemblyCodeBase(a); return (!FileUtil.FileExists(path) || DiskBuildResultCache.HasDotDeleteFile(path)); } internal override void SetPreservedAttributes(PreservationFileWriter pfw) { base.SetPreservedAttributes(pfw); if (HasResultAssembly) { string assemblyName; if (IsGacAssembly) { // If it's in the GAC, store the full name (VSWhidbey 384416) assemblyName = ResultAssembly.FullName; } else { // Otherwise, store the short name, to avoid uselessly growing the preservation file assemblyName = ShortAssemblyName; } pfw.SetAttribute("assembly", assemblyName); } } /* * Tell the BuildResult that its dependencies are not up to date, in order * to give it a chance to do some cleanup. */ internal override void RemoveOutOfDateResources(PreservationFileReader pfr) { // If the preservation file is pointing to an assembly that was not built // for this result, do not attempt to clean it up (see VSWhidbey 74094) ReadPreservedFlags(pfr); if (UsesExistingAssembly) return; // Remove the assembly and all the associated files string assemblyName = pfr.GetAttribute("assembly"); if (assemblyName != null) { pfr.DiskCache.RemoveAssemblyAndRelatedFiles(assemblyName); } } protected override void ComputeHashCode(HashCodeCombiner hashCodeCombiner) { base.ComputeHashCode(hashCodeCombiner); // Make the hash code depend on the relevant contents of the config section CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(VirtualPath); hashCodeCombiner.AddObject(compConfig.RecompilationHash); } } internal class BuildResultCompiledAssembly: BuildResultCompiledAssemblyBase { private Assembly _assembly; internal BuildResultCompiledAssembly() {} internal BuildResultCompiledAssembly(Assembly a) { _assembly = a; } internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultCompiledAssembly; } internal override Assembly ResultAssembly { get { return _assembly; } set { _assembly = value; } } internal override void GetPreservedAttributes(PreservationFileReader pfr) { base.GetPreservedAttributes(pfr); ResultAssembly = GetPreservedAssembly(pfr); } } /* * Same as BuildResultCompiledAssembly, but with some special behavior specific to * the main code assembly. Specifically, it adds support for the AppInitialize method * and for VB's My.* */ internal class BuildResultMainCodeAssembly: BuildResultCompiledAssembly { private const string appInitializeMethodName = "AppInitialize"; private MethodInfo _appInitializeMethod; internal BuildResultMainCodeAssembly() {} internal BuildResultMainCodeAssembly(Assembly a) : base(a) { // Look for an AppInitialize static method in the assembly FindAppInitializeMethod(); } internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultMainCodeAssembly; } internal override void GetPreservedAttributes(PreservationFileReader pfr) { base.GetPreservedAttributes(pfr); // Does the assembly have an AppInitialize method? string appInitializeClass = pfr.GetAttribute("appInitializeClass"); if (appInitializeClass != null) { // Get the Type that contains the method Type appInitializeType = ResultAssembly.GetType(appInitializeClass); Debug.Assert(appInitializeType != null); // Find the method _appInitializeMethod = FindAppInitializeMethod(appInitializeType); Debug.Assert(_appInitializeMethod != null); } } internal override void SetPreservedAttributes(PreservationFileWriter pfw) { base.SetPreservedAttributes(pfw); // If there is an AppInitialize method, save the class name that it's in if (_appInitializeMethod != null) { pfw.SetAttribute("appInitializeClass", _appInitializeMethod.ReflectedType.FullName); } } private void FindAppInitializeMethod() { Debug.Assert(_appInitializeMethod == null); // Look in all the public types in the assembly foreach (Type t in ResultAssembly.GetExportedTypes()) { // Look for an AppInitialize method MethodInfo tmpAppInitializeMethod = FindAppInitializeMethod(t); if (tmpAppInitializeMethod != null) { // Make sure we didn't already have one if (_appInitializeMethod != null) { throw new HttpException(SR.GetString(SR.Duplicate_appinitialize, _appInitializeMethod.ReflectedType.FullName, t.FullName)); } // Keep track of the method _appInitializeMethod = tmpAppInitializeMethod; } } } private MethodInfo FindAppInitializeMethod(Type t) { return t.GetMethod(appInitializeMethodName, BindingFlags.Public | BindingFlags.Static| BindingFlags.IgnoreCase, null /*Binder*/, new Type[0], // Method with no parameters null ); } // Call the AppInitialize method if there is one internal void CallAppInitializeMethod() { if (_appInitializeMethod != null) { using (new ApplicationImpersonationContext()) { using (HostingEnvironment.SetCultures()) { _appInitializeMethod.Invoke(null, null); } } } } } /* * Same as BuildResultCompiledAssembly, but with some special behavior specific to * resources directory (both global and local) */ internal class BuildResultResourceAssembly : BuildResultCompiledAssembly { internal BuildResultResourceAssembly() { } internal BuildResultResourceAssembly(Assembly a) : base(a) { } internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultResourceAssembly; } internal override string ComputeSourceDependenciesHashCode(VirtualPath virtualPath) { // If no virtual path was passed in, use the one from the BuildResult if (virtualPath == null) virtualPath = VirtualPath; // We don't want to use the default ComputeSourceDependenciesHashCode imnplementation, // as it would use all files in the resources dir to calculate the hash. Instead, // we only want the hash the be based on the culture neutral resources, so that // changes to culture specific files don't cause a rebuild of the main res assembly HashCodeCombiner hashCodeCombiner = new HashCodeCombiner(); hashCodeCombiner.AddResourcesDirectory(virtualPath.MapPathInternal()); return hashCodeCombiner.CombinedHashString; } // In addition to the standard BuildResult hash code (which drives recompilation of the main // resources assembly), we need an additional one so we know when to rebuild satellites. private string _resourcesDependenciesHash; internal string ResourcesDependenciesHash { get { EnsureResourcesDependenciesHashComputed(); return _resourcesDependenciesHash; } set { Debug.Assert(_resourcesDependenciesHash == null); _resourcesDependenciesHash = value; Debug.Assert(_resourcesDependenciesHash != null); } } private void EnsureResourcesDependenciesHashComputed() { if (_resourcesDependenciesHash != null) return; // Even though we make it dependent on all res files, if we get here we know the neutral // ones are up to date, so effectively it's look the culture specific that matter. _resourcesDependenciesHash = HashCodeCombiner.GetDirectoryHash(VirtualPath); } internal override void GetPreservedAttributes(PreservationFileReader pfr) { base.GetPreservedAttributes(pfr); ResourcesDependenciesHash = pfr.GetAttribute("resHash"); } internal override void SetPreservedAttributes(PreservationFileWriter pfw) { base.SetPreservedAttributes(pfw); pfw.SetAttribute("resHash", ResourcesDependenciesHash); } } internal class BuildResultCompiledType : BuildResultCompiledAssemblyBase, ITypedWebObjectFactory { // The delegate for fast object instantiation private InstantiateObject _instObj; private bool _triedToGetInstObj; internal BuildResultCompiledType() {} internal BuildResultCompiledType(Type t) { _builtType = t; } internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultCompiledType; } internal override Assembly ResultAssembly { get { return _builtType.Assembly; } set { Debug.Assert(false); } } internal override bool HasResultAssembly { get { return _builtType != null; } } protected override bool IsGacAssembly { get { if (IsDelayLoadType) { return false; } else { return base.IsGacAssembly; } } } protected override string ShortAssemblyName { get { var delayLoadType = ResultType as DelayLoadType; if (delayLoadType != null) { return delayLoadType.AssemblyName; } else { return base.ShortAssemblyName; } } } private Type _builtType; internal Type ResultType { get { return _builtType; } set { _builtType = value; } } private string FullResultTypeName { get { var delayLoadType = ResultType as DelayLoadType; if (delayLoadType != null) { return delayLoadType.TypeName; } else { return ResultType.FullName; } } } internal bool IsDelayLoadType { get { return ResultType is DelayLoadType; } } static internal bool UsesDelayLoadType(BuildResult result) { BuildResultCompiledType buildResultCompiledType = result as BuildResultCompiledType; if (buildResultCompiledType != null) { return buildResultCompiledType.IsDelayLoadType; } else { return false; } } // IWebObjectFactory.CreateInstance public object CreateInstance() { // Get the fast object creation delegate on demand if (!_triedToGetInstObj) { _instObj = ObjectFactoryCodeDomTreeGenerator.GetFastObjectCreationDelegate(ResultType); _triedToGetInstObj = true; } // If the fast factory is not available, just call CreateInstance // if (_instObj == null) { return HttpRuntime.CreatePublicInstance(ResultType); } // Call it to instantiate the object return _instObj(); } // ITypedWebObjectFactory.CreateInstance public virtual Type InstantiatedType { get { return ResultType; } } protected override void ComputeHashCode(HashCodeCombiner hashCodeCombiner) { base.ComputeHashCode(hashCodeCombiner); // Make pages have a dependency on the main local resources assembly, so that they // get recompiled when it changes (but not when satellites change). VSWhidbey 277357 if (VirtualPath != null) { // Remove the file name to get its directory VirtualPath virtualDir = VirtualPath.Parent; Assembly localResAssembly = BuildManager.GetLocalResourcesAssembly(virtualDir); if (localResAssembly != null) { hashCodeCombiner.AddFile(localResAssembly.Location); } } } internal override void GetPreservedAttributes(PreservationFileReader pfr) { base.GetPreservedAttributes(pfr); // Get the assembly and type Assembly a = GetPreservedAssembly(pfr); Debug.Assert(a != null); string typeName = pfr.GetAttribute("type"); ResultType = a.GetType(typeName, true /*throwOnError*/); } internal override void SetPreservedAttributes(PreservationFileWriter pfw) { base.SetPreservedAttributes(pfw); pfw.SetAttribute("type", FullResultTypeName); } } /* * Used for pages, user controls, and master pages */ internal class BuildResultCompiledTemplateType: BuildResultCompiledType { public BuildResultCompiledTemplateType() {} public BuildResultCompiledTemplateType(Type t) : base(t) {} internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultCompiledTemplateType; } protected override void ComputeHashCode(HashCodeCombiner hashCodeCombiner) { base.ComputeHashCode(hashCodeCombiner); // Make the hash code depend on the relevant contents of the config section PagesSection pagesConfig = MTConfigUtil.GetPagesConfig(VirtualPath); hashCodeCombiner.AddObject(Util.GetRecompilationHash(pagesConfig)); } } /* * Used for global.asax */ internal class BuildResultCompiledGlobalAsaxType : BuildResultCompiledType { public BuildResultCompiledGlobalAsaxType() { } public BuildResultCompiledGlobalAsaxType(Type t) : base(t) { } internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultCompiledGlobalAsaxType; } // Does global.asax contain tags with application or session scope internal bool HasAppOrSessionObjects { get { return _flags[hasAppOrSessionObjects]; } set { _flags[hasAppOrSessionObjects] = value; } } } internal abstract class BuildResultNoCompileTemplateControl : BuildResult, ITypedWebObjectFactory { protected Type _baseType; protected RootBuilder _rootBuilder; protected bool _initialized; internal BuildResultNoCompileTemplateControl(Type baseType, TemplateParser parser) { _baseType = baseType; _rootBuilder = parser.RootBuilder; // Cleanup anything that's no longer needed in the ControlBuilder _rootBuilder.PrepareNoCompilePageSupport(); } internal override BuildResultTypeCode GetCode() { Debug.Assert(false, "BuildResultNoCompileTemplateControl"); return BuildResultTypeCode.Invalid; } /* * Don't cache the result of no-compile pages to disk (they are reparsed in each appdomain) */ internal override bool CacheToDisk { get { return false; } } /* * Give a 5 minute sliding expiration to no-compile pages */ internal override TimeSpan MemoryCacheSlidingExpiration { get { return TimeSpan.FromMinutes(5); } } // Note that since this is a no-compile control, this is not really the 'base' type, // but is in fact the Type we directly instantiate. internal Type BaseType { get { return _baseType; } } // IWebObjectFactory.CreateInstance public virtual object CreateInstance() { // Create the object that the aspx/ascx 'inherits' from TemplateControl templateControl = (TemplateControl) HttpRuntime.FastCreatePublicInstance(_baseType); // Set the virtual path and TemplateSourceDirectory in the control templateControl.TemplateControlVirtualPath = VirtualPath; templateControl.TemplateControlVirtualDirectory = VirtualPath.Parent; // Give the TemplateControl a pointer to us, so it can call us back during FrameworkInitialize templateControl.SetNoCompileBuildResult(this); return templateControl; } // ITypedWebObjectFactory.CreateInstance public virtual Type InstantiatedType { get { return _baseType; } } internal virtual void FrameworkInitialize(TemplateControl templateControl) { HttpContext context = HttpContext.Current; // Storing the filter resolution service and template control into the context // since each thread needs to set them differently. TemplateControl savedTemplateControl = context.TemplateControl; context.TemplateControl = templateControl; try { // Create the control tree // DevDiv Bug 59351 // Lock during the first time we initialize the control builder with the object, // to prevent concurrency issues. if (!_initialized) { lock (this) { _rootBuilder.InitObject(templateControl); } _initialized = true; } else { _rootBuilder.InitObject(templateControl); } } finally { // Restore the previous template control if (savedTemplateControl != null) context.TemplateControl = savedTemplateControl; } } } internal class BuildResultNoCompilePage: BuildResultNoCompileTemplateControl { private TraceEnable _traceEnabled; private TraceMode _traceMode; private OutputCacheParameters _outputCacheData; private string[] _fileDependencies; private bool _validateRequest; private string _stylesheetTheme; internal BuildResultNoCompilePage(Type baseType, TemplateParser parser) : base(baseType, parser) { PageParser pageParser = (PageParser) parser; // // Keep track of relevant info from the parser // _traceEnabled = pageParser.TraceEnabled; _traceMode = pageParser.TraceMode; if (pageParser.OutputCacheParameters != null) { _outputCacheData = pageParser.OutputCacheParameters; // If we're not supposed to cache it, clear out the field if (_outputCacheData.Duration == 0 || _outputCacheData.Location == OutputCacheLocation.None) { _outputCacheData = null; } else { // Since we're going to be output caching, remember all the dependencies _fileDependencies = new string[pageParser.SourceDependencies.Count]; int i = 0; foreach (string dependency in pageParser.SourceDependencies) { _fileDependencies[i++] = dependency; } Debug.Assert(i == pageParser.SourceDependencies.Count); } } _validateRequest = pageParser.ValidateRequest; _stylesheetTheme = pageParser.StyleSheetTheme; } internal override void FrameworkInitialize(TemplateControl templateControl) { Page page = (Page)templateControl; page.StyleSheetTheme = _stylesheetTheme; page.InitializeStyleSheet(); base.FrameworkInitialize(templateControl); if (_traceEnabled != TraceEnable.Default) page.TraceEnabled = (_traceEnabled == TraceEnable.Enable); if (_traceMode != TraceMode.Default) page.TraceModeValue = _traceMode; if (_outputCacheData != null) { page.AddWrappedFileDependencies(_fileDependencies); page.InitOutputCache(_outputCacheData); } if (_validateRequest) { page.Request.ValidateInput(); } else if(MultiTargetingUtil.TargetFrameworkVersion >= VersionUtil.Framework45) { // Only set the ValidateRequestMode property if we are targetting 4.5 or higher // as earlier versions did not have it. page.ValidateRequestMode = ValidateRequestMode.Disabled; } } } internal class BuildResultNoCompileUserControl: BuildResultNoCompileTemplateControl { private PartialCachingAttribute _cachingAttribute; internal BuildResultNoCompileUserControl(Type baseType, TemplateParser parser) : base(baseType, parser) { UserControlParser ucParser = (UserControlParser) parser; OutputCacheParameters cacheSettings = ucParser.OutputCacheParameters; // If the user control has an OutputCache directive, create // a PartialCachingAttribute with the information about it. if (cacheSettings != null && cacheSettings.Duration > 0) { _cachingAttribute = new PartialCachingAttribute( cacheSettings.Duration, cacheSettings.VaryByParam, cacheSettings.VaryByControl, cacheSettings.VaryByCustom, cacheSettings.SqlDependency, ucParser.FSharedPartialCaching); _cachingAttribute.ProviderName = ucParser.Provider; } } internal PartialCachingAttribute CachingAttribute { get { return _cachingAttribute; } } } internal class BuildResultNoCompileMasterPage: BuildResultNoCompileUserControl { private ICollection _placeHolderList; internal BuildResultNoCompileMasterPage(Type baseType, TemplateParser parser) : base(baseType, parser) { _placeHolderList = ((MasterPageParser)parser).PlaceHolderList; } // IWebObjectFactory.CreateInstance public override object CreateInstance() { // Create the master page object that the master 'inherits' from MasterPage masterPage = (MasterPage) base.CreateInstance(); foreach(string placeHolderID in _placeHolderList) { masterPage.ContentPlaceHolders.Add(placeHolderID.ToLower(CultureInfo.InvariantCulture)); } return masterPage; } } /* * This class is used to cache the generated codecompileunit for CBM scenarios, * when cached on disk, it uses BinaryFormatter to serialize the codecompileunit * and other compile params. */ internal class BuildResultCodeCompileUnit : BuildResult { private Type _codeDomProviderType; private CodeCompileUnit _codeCompileUnit; private CompilerParameters _compilerParameters; private IDictionary _linePragmasTable; private string _cacheKey; private const string fileNameAttribute = "CCUpreservationFileName"; internal BuildResultCodeCompileUnit() { } internal BuildResultCodeCompileUnit( Type codeDomProviderType, CodeCompileUnit codeCompileUnit, CompilerParameters compilerParameters, IDictionary linePragmasTable) { _codeDomProviderType = codeDomProviderType; _codeCompileUnit = codeCompileUnit; _compilerParameters = compilerParameters; _linePragmasTable = linePragmasTable; } internal Type CodeDomProviderType { get { return _codeDomProviderType; } } internal CodeCompileUnit CodeCompileUnit { get { return _codeCompileUnit; } } internal CompilerParameters CompilerParameters { get { return _compilerParameters; } } internal IDictionary LinePragmasTable { get { return _linePragmasTable; } } internal override bool CacheToDisk { get { return true; } } internal override BuildResultTypeCode GetCode() { return BuildResultTypeCode.BuildResultCodeCompileUnit; } private string GetPreservationFileName() { return _cacheKey + ".ccu"; } protected override void ComputeHashCode(HashCodeCombiner hashCodeCombiner) { base.ComputeHashCode(hashCodeCombiner); // Make the hash code depend on the relevant contents of the and config sections CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(VirtualPath); hashCodeCombiner.AddObject(compConfig.RecompilationHash); PagesSection pagesConfig = MTConfigUtil.GetPagesConfig(VirtualPath); hashCodeCombiner.AddObject(Util.GetRecompilationHash(pagesConfig)); } internal override void GetPreservedAttributes(PreservationFileReader pfr) { base.GetPreservedAttributes(pfr); String _ccuPreservationFileName = pfr.GetAttribute(fileNameAttribute); _ccuPreservationFileName = Path.Combine(HttpRuntime.CodegenDirInternal, _ccuPreservationFileName); Debug.Assert(FileUtil.FileExists(_ccuPreservationFileName), _ccuPreservationFileName); using (FileStream stream = File.Open(_ccuPreservationFileName, FileMode.Open)) { BinaryFormatter formatter = new BinaryFormatter(); _codeCompileUnit = formatter.Deserialize(stream) as CodeCompileUnit; _codeDomProviderType = (Type)formatter.Deserialize(stream); _compilerParameters = (CompilerParameters)formatter.Deserialize(stream); _linePragmasTable = formatter.Deserialize(stream) as IDictionary; } } internal void SetCacheKey(string cacheKey) { _cacheKey = cacheKey; } internal override void SetPreservedAttributes(PreservationFileWriter pfw) { base.SetPreservedAttributes(pfw); string preservationFileName = GetPreservationFileName(); pfw.SetAttribute(fileNameAttribute, preservationFileName); preservationFileName = Path.Combine(HttpRuntime.CodegenDirInternal, preservationFileName); using (FileStream stream = File.Open(preservationFileName, FileMode.Create)) { BinaryFormatter formatter = new BinaryFormatter(); if (_codeCompileUnit != null) { formatter.Serialize(stream, _codeCompileUnit); } else { formatter.Serialize(stream, new object()); } formatter.Serialize(stream, _codeDomProviderType); formatter.Serialize(stream, _compilerParameters); if (_linePragmasTable != null) { formatter.Serialize(stream, _linePragmasTable); } else { formatter.Serialize(stream, new object()); } } } internal override void RemoveOutOfDateResources(PreservationFileReader pfr) { // Remove the out-of-date .ccu file String ccuPreservationFileName = pfr.GetAttribute(fileNameAttribute); ccuPreservationFileName = Path.Combine(HttpRuntime.CodegenDirInternal, ccuPreservationFileName); File.Delete(ccuPreservationFileName); } } }