File: Compilation\BuildResult.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="BuildResult.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
 
 
/*********************************
 
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;
using System.Diagnostics.CodeAnalysis;
 
    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);
            }
        }
    }
 
        [SuppressMessage("Microsoft.Security.Xml", "CA3003 ReviewCodeForFileCanonicalizationVulnerabilities", Justification = "Developer-controlled contents are implicitly trusted.")]
        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 <compilation> 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.CreatePublicInstanceByWebObjectActivator(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 <pages> 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 <object> 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 <page> and <compilation> 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);
    }
}
 
}