e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
715 lines
26 KiB
C#
715 lines
26 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="BuildManagerHost.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
/************************************************************************************************************/
|
|
|
|
|
|
namespace System.Web.Compilation {
|
|
|
|
using System;
|
|
using System.CodeDom;
|
|
using System.CodeDom.Compiler;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Security.Permissions;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Web;
|
|
using System.Web.Caching;
|
|
using System.Web.Configuration;
|
|
using System.Web.Hosting;
|
|
using System.Web.UI;
|
|
using System.Web.Util;
|
|
using Debug = System.Web.Util.Debug;
|
|
|
|
|
|
//
|
|
// Instances of this class are created in the ASP.NET app domain. The class
|
|
// methods are called by ClientBuildManager (cross app domain)
|
|
//
|
|
internal class BuildManagerHost : MarshalByRefObject, IRegisteredObject {
|
|
|
|
private ClientBuildManager _client;
|
|
private BuildManager _buildManager;
|
|
|
|
private int _pendingCallsCount;
|
|
|
|
private EventHandler _onAppDomainUnload;
|
|
|
|
private bool _ignorePendingCalls;
|
|
private IDictionary _assemblyCollection;
|
|
|
|
private object _lock = new object();
|
|
|
|
private static bool _inClientBuildManager;
|
|
|
|
internal static bool InClientBuildManager {
|
|
get { return _inClientBuildManager; }
|
|
set { _inClientBuildManager = true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether there is support for multitargeting. This is usually only true in VS as they will supply a
|
|
/// TypeDescriptionProvider that is required for correctly reflecting over types in the target framework.
|
|
/// When running in the 4.0 runtime application pool, or when running from aspnet_compiler, this value
|
|
/// will be false.
|
|
/// </summary>
|
|
internal static bool SupportsMultiTargeting {
|
|
get; set;
|
|
}
|
|
|
|
private ClientVirtualPathProvider _virtualPathProvider;
|
|
|
|
public BuildManagerHost() {
|
|
HostingEnvironment.RegisterObject(this);
|
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.ResolveAssembly);
|
|
}
|
|
|
|
void IRegisteredObject.Stop(bool immediate) {
|
|
|
|
// Make sure all the pending calls complete
|
|
WaitForPendingCallsToFinish();
|
|
|
|
HostingEnvironment.UnregisterObject(this);
|
|
|
|
if (_client != null) {
|
|
_client.ResetHost();
|
|
}
|
|
}
|
|
|
|
internal IApplicationHost ApplicationHost {
|
|
get {
|
|
return HostingEnvironment.ApplicationHostInternal;
|
|
}
|
|
}
|
|
|
|
internal string CodeGenDir {
|
|
get {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
return HttpRuntime.CodegenDirInternal;
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void RegisterAssembly(String assemblyName, String assemblyLocation) {
|
|
Debug.Trace("BuildManagerHost", "RegisterAssembly '" + assemblyName + "','" + assemblyLocation + "'");
|
|
|
|
if (_assemblyCollection == null) {
|
|
lock (_lock) {
|
|
if (_assemblyCollection == null) {
|
|
_assemblyCollection = Hashtable.Synchronized(new Hashtable());
|
|
}
|
|
}
|
|
}
|
|
|
|
AssemblyName asmName = new AssemblyName(assemblyName);
|
|
_assemblyCollection[asmName.FullName] = assemblyLocation;
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
|
private Assembly ResolveAssembly(object sender, ResolveEventArgs e) {
|
|
Debug.Trace("BuildManagerHost", "ResolveAssembly '" + e.Name + "'");
|
|
if (_assemblyCollection == null)
|
|
return null;
|
|
|
|
String assemblyLocation = (String)_assemblyCollection[e.Name];
|
|
if (assemblyLocation == null)
|
|
return null;
|
|
|
|
Debug.Trace("BuildManagerHost", "ResolveAssembly: found");
|
|
|
|
return Assembly.LoadFrom(assemblyLocation);
|
|
}
|
|
|
|
/*
|
|
* Make sure all the (non-request based) pending calls complete
|
|
*/
|
|
private void WaitForPendingCallsToFinish() {
|
|
for (;;) {
|
|
if (_pendingCallsCount <= 0 || _ignorePendingCalls)
|
|
break;
|
|
|
|
Thread.Sleep(250);
|
|
}
|
|
}
|
|
|
|
internal void AddPendingCall() {
|
|
Interlocked.Increment(ref _pendingCallsCount);
|
|
}
|
|
|
|
internal void RemovePendingCall() {
|
|
Interlocked.Decrement(ref _pendingCallsCount);
|
|
}
|
|
|
|
private void OnAppDomainShutdown(object o, BuildManagerHostUnloadEventArgs args) {
|
|
_client.OnAppDomainShutdown(args.Reason);
|
|
}
|
|
|
|
internal void CompileApplicationDependencies() {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
_buildManager.EnsureTopLevelFilesCompiled();
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal void PrecompileApp(ClientBuildManagerCallback callback, List<string> excludedVirtualPaths) {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
_buildManager.PrecompileApp(callback, excludedVirtualPaths);
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal IDictionary GetBrowserDefinitions() {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
return BrowserCapabilitiesCompiler.BrowserCapabilitiesFactory.InternalGetBrowserElements();
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal string[] GetVirtualCodeDirectories() {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
return _buildManager.GetCodeDirectories();
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
|
internal void GetCodeDirectoryInformation(VirtualPath virtualCodeDir,
|
|
out Type codeDomProviderType, out CompilerParameters compParams,
|
|
out string generatedFilesDir) {
|
|
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
BuildManager.SkipTopLevelCompilationExceptions = true;
|
|
_buildManager.EnsureTopLevelFilesCompiled();
|
|
|
|
// Treat it as relative to the app root
|
|
virtualCodeDir = virtualCodeDir.CombineWithAppRoot();
|
|
|
|
_buildManager.GetCodeDirectoryInformation(virtualCodeDir,
|
|
out codeDomProviderType, out compParams, out generatedFilesDir);
|
|
}
|
|
finally {
|
|
BuildManager.SkipTopLevelCompilationExceptions = false;
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal void GetCompilerParams(VirtualPath virtualPath, out Type codeDomProviderType,
|
|
out CompilerParameters compParams) {
|
|
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
BuildManager.SkipTopLevelCompilationExceptions = true;
|
|
_buildManager.EnsureTopLevelFilesCompiled();
|
|
|
|
// Ignore the BuildProvider return value
|
|
GetCompilerParamsAndBuildProvider(virtualPath, out codeDomProviderType, out compParams);
|
|
|
|
// This is the no-compile case
|
|
if (compParams == null)
|
|
return;
|
|
|
|
FixupReferencedAssemblies(virtualPath, compParams);
|
|
}
|
|
finally {
|
|
BuildManager.SkipTopLevelCompilationExceptions = false;
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal string[] GetCompiledTypeAndAssemblyName(VirtualPath virtualPath, ClientBuildManagerCallback callback) {
|
|
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
// Treat it as relative to the app root
|
|
virtualPath.CombineWithAppRoot();
|
|
|
|
Type t = BuildManager.GetCompiledType(virtualPath, callback);
|
|
|
|
if (t == null) return null;
|
|
|
|
string assemblyPath = Util.GetAssemblyPathFromType(t);
|
|
return new string[] { t.FullName, assemblyPath };
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal string GetGeneratedSourceFile(VirtualPath virtualPath) {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
Type codeDomProviderType;
|
|
CompilerParameters compilerParameters;
|
|
string generatedFilesDir;
|
|
|
|
try {
|
|
if (!virtualPath.DirectoryExists()) {
|
|
throw new ArgumentException(SR.GetString(SR.GetGeneratedSourceFile_Directory_Only,
|
|
virtualPath.VirtualPathString), "virtualPath");
|
|
}
|
|
|
|
// Calls GetCodeDirectoryInformation to ensure the source files are created for the
|
|
// directory specified by virtualPath
|
|
GetCodeDirectoryInformation(virtualPath,
|
|
out codeDomProviderType, out compilerParameters,
|
|
out generatedFilesDir);
|
|
|
|
return BuildManager.GenerateFileTable[virtualPath.VirtualPathStringNoTrailingSlash];
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal string GetGeneratedFileVirtualPath(string filePath) {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
// Performs reverse hashtable lookup to find the filePath in the Value collection.
|
|
Dictionary<String, String>.Enumerator e = BuildManager.GenerateFileTable.GetEnumerator();
|
|
while (e.MoveNext()) {
|
|
KeyValuePair<String, String> pair = e.Current;
|
|
if (filePath.Equals(pair.Value, StringComparison.Ordinal)) {
|
|
return pair.Key;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns an array of the assemblies defined in the bin and assembly reference config section
|
|
*/
|
|
internal String[] GetTopLevelAssemblyReferences(VirtualPath virtualPath) {
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
List<Assembly> assemblyList = new List<Assembly>();
|
|
try {
|
|
// Treat it as relative to the app root
|
|
virtualPath.CombineWithAppRoot();
|
|
|
|
CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath);
|
|
|
|
// Add all the config assemblies to the list
|
|
foreach (AssemblyInfo assemblyInfo in compConfig.Assemblies) {
|
|
Assembly[] assemblies = assemblyInfo.AssemblyInternal;
|
|
for (int i = 0; i < assemblies.Length; i++) {
|
|
if (assemblies[i] != null) {
|
|
assemblyList.Add(assemblies[i]);
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
RemovePendingCall();
|
|
}
|
|
StringCollection paths = new StringCollection();
|
|
Util.AddAssembliesToStringCollection(assemblyList, paths);
|
|
string[] references = new string[paths.Count];
|
|
paths.CopyTo(references, 0);
|
|
return references;
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
|
internal string GenerateCode(
|
|
VirtualPath virtualPath, string virtualFileString, out IDictionary linePragmasTable) {
|
|
|
|
AddPendingCall();
|
|
try {
|
|
string code = null;
|
|
Type codeDomProviderType;
|
|
CompilerParameters compilerParameters;
|
|
|
|
CodeCompileUnit ccu = GenerateCodeCompileUnit(virtualPath, virtualFileString, out codeDomProviderType,
|
|
out compilerParameters, out linePragmasTable);
|
|
|
|
if (ccu != null && codeDomProviderType != null) {
|
|
CodeDomProvider codeProvider = CompilationUtil.CreateCodeDomProvider(codeDomProviderType);
|
|
|
|
CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();
|
|
codeGeneratorOptions.BlankLinesBetweenMembers = false;
|
|
codeGeneratorOptions.IndentString = string.Empty;
|
|
|
|
StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
|
|
codeProvider.GenerateCodeFromCompileUnit(ccu, sw, codeGeneratorOptions);
|
|
|
|
code = sw.ToString();
|
|
}
|
|
|
|
return code;
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
|
internal CodeCompileUnit GenerateCodeCompileUnit(
|
|
VirtualPath virtualPath, string virtualFileString, out Type codeDomProviderType,
|
|
out CompilerParameters compilerParameters, out IDictionary linePragmasTable) {
|
|
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
BuildManager.SkipTopLevelCompilationExceptions = true;
|
|
_buildManager.EnsureTopLevelFilesCompiled();
|
|
|
|
// Get the virtual file content so that we can use the correct hash code.
|
|
if (virtualFileString == null) {
|
|
using (Stream stream = virtualPath.OpenFile()) {
|
|
TextReader reader = Util.ReaderFromStream(stream, virtualPath);
|
|
virtualFileString = reader.ReadToEnd();
|
|
}
|
|
}
|
|
|
|
_virtualPathProvider.RegisterVirtualFile(virtualPath, virtualFileString);
|
|
|
|
string cacheKey = BuildManager.GetCacheKeyFromVirtualPath(virtualPath) + "_CBMResult";
|
|
BuildResultCodeCompileUnit result = (BuildResultCodeCompileUnit)BuildManager.GetBuildResultFromCache(cacheKey, virtualPath);
|
|
|
|
if (result == null) {
|
|
lock (_lock) {
|
|
// Don't need to check the result again since it's very unlikely in CBM scenarios.
|
|
DateTime utcStart = DateTime.UtcNow;
|
|
|
|
BuildProvider internalBuildProvider = GetCompilerParamsAndBuildProvider(
|
|
virtualPath, out codeDomProviderType, out compilerParameters);
|
|
|
|
// This is the no-compile case
|
|
if (internalBuildProvider == null) {
|
|
linePragmasTable = null;
|
|
return null;
|
|
}
|
|
|
|
CodeCompileUnit ccu = internalBuildProvider.GetCodeCompileUnit(out linePragmasTable);
|
|
|
|
result = new BuildResultCodeCompileUnit(codeDomProviderType, ccu, compilerParameters, linePragmasTable);
|
|
result.VirtualPath = virtualPath;
|
|
result.SetCacheKey(cacheKey);
|
|
|
|
FixupReferencedAssemblies(virtualPath, compilerParameters);
|
|
|
|
// CodeCompileUnit could be null, do not try to fix referenced assemblies.
|
|
// This happens for example when an .asmx file does not contain any code.
|
|
if (ccu != null) {
|
|
// VSWhidbey 501260 Add all the referenced assemblies to the CodeCompileUnit
|
|
// in case the CodeDom provider needs them for code generation
|
|
foreach (String assemblyString in compilerParameters.ReferencedAssemblies) {
|
|
ccu.ReferencedAssemblies.Add(assemblyString);
|
|
}
|
|
}
|
|
|
|
// Add all the dependencies, so that the ccu gets cached correctly (VSWhidbey 275091)
|
|
ICollection dependencies = internalBuildProvider.VirtualPathDependencies;
|
|
if (dependencies != null)
|
|
result.AddVirtualPathDependencies(dependencies);
|
|
|
|
BuildManager.CacheBuildResult(cacheKey, result, utcStart);
|
|
|
|
return ccu;
|
|
}
|
|
}
|
|
|
|
codeDomProviderType = result.CodeDomProviderType;
|
|
compilerParameters = result.CompilerParameters;
|
|
linePragmasTable = result.LinePragmasTable;
|
|
|
|
FixupReferencedAssemblies(virtualPath, compilerParameters);
|
|
|
|
return result.CodeCompileUnit;
|
|
}
|
|
finally {
|
|
if (virtualFileString != null) {
|
|
_virtualPathProvider.RevertVirtualFile(virtualPath);
|
|
}
|
|
BuildManager.SkipTopLevelCompilationExceptions = false;
|
|
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal bool IsCodeAssembly(string assemblyName) {
|
|
return BuildManager.GetNormalizedCodeAssemblyName(assemblyName) != null;
|
|
}
|
|
|
|
// Add the referenced assemblies into the compileParameters. Notice that buildProviders do not have
|
|
// the correct referenced assemblies and we don't cache them since the assemblies could change
|
|
// between appdomains. (removing assemblies from bin, etc)
|
|
private void FixupReferencedAssemblies(VirtualPath virtualPath, CompilerParameters compilerParameters) {
|
|
CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath);
|
|
|
|
ICollection referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig);
|
|
Util.AddAssembliesToStringCollection(referencedAssemblies, compilerParameters.ReferencedAssemblies);
|
|
}
|
|
|
|
private BuildProvider GetCompilerParamsAndBuildProvider(VirtualPath virtualPath,
|
|
out Type codeDomProviderType, out CompilerParameters compilerParameters) {
|
|
|
|
virtualPath.CombineWithAppRoot();
|
|
|
|
CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath);
|
|
|
|
ICollection referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig);
|
|
|
|
// Create the buildprovider for the passed in virtualPath
|
|
BuildProvider buildProvider = null;
|
|
|
|
// Special case global asax build provider here since we do not want to compile every files with ".asax" extension.
|
|
if (StringUtil.EqualsIgnoreCase(virtualPath.VirtualPathString, BuildManager.GlobalAsaxVirtualPath.VirtualPathString)) {
|
|
ApplicationBuildProvider provider = new ApplicationBuildProvider();
|
|
provider.SetVirtualPath(virtualPath);
|
|
provider.SetReferencedAssemblies(referencedAssemblies);
|
|
buildProvider = provider;
|
|
}
|
|
else {
|
|
buildProvider = BuildManager.CreateBuildProvider(virtualPath, compConfig,
|
|
referencedAssemblies, true /*failIfUnknown*/);
|
|
}
|
|
|
|
// DevDiv 69017
|
|
// The methods restricted to internalBuildProvider have been moved up to BuildProvider
|
|
// to allow WCFBuildProvider to support .svc syntax highlighting.
|
|
|
|
// Ignore parse errors, since they should not break the designer
|
|
buildProvider.IgnoreParseErrors = true;
|
|
|
|
// Ignore all control properties, since we do not generate code for the properties
|
|
buildProvider.IgnoreControlProperties = true;
|
|
|
|
// Process as many errors as possible, do not rethrow on first error
|
|
buildProvider.ThrowOnFirstParseError = false;
|
|
|
|
// Get the language (causes the file to be parsed)
|
|
CompilerType compilerType = buildProvider.CodeCompilerType;
|
|
|
|
// compilerType could be null in the no-compile case (VSWhidbey 221749)
|
|
if (compilerType == null) {
|
|
codeDomProviderType = null;
|
|
compilerParameters = null;
|
|
return null;
|
|
}
|
|
|
|
// Return the provider type and compiler params
|
|
codeDomProviderType = compilerType.CodeDomProviderType;
|
|
compilerParameters = compilerType.CompilerParameters;
|
|
|
|
IAssemblyDependencyParser parser = buildProvider.AssemblyDependencyParser;
|
|
|
|
// Add all the assemblies that the page depends on (e.g. user controls)
|
|
if (parser != null && parser.AssemblyDependencies != null) {
|
|
Util.AddAssembliesToStringCollection(parser.AssemblyDependencies,
|
|
compilerParameters.ReferencedAssemblies);
|
|
}
|
|
|
|
// Make any fix up adjustments to the CompilerParameters to work around some issues
|
|
AssemblyBuilder.FixUpCompilerParameters(compConfig, codeDomProviderType, compilerParameters);
|
|
|
|
return buildProvider;
|
|
}
|
|
|
|
public override Object InitializeLifetimeService() {
|
|
return null; // never expire lease
|
|
}
|
|
|
|
internal void Configure(ClientBuildManager client) {
|
|
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try {
|
|
_virtualPathProvider = new ClientVirtualPathProvider();
|
|
HostingEnvironment.RegisterVirtualPathProviderInternal(_virtualPathProvider);
|
|
|
|
_client = client;
|
|
|
|
// Type description provider required for multitargeting compilation support in VS.
|
|
if (_client.CBMTypeDescriptionProviderBridge != null) {
|
|
TargetFrameworkUtil.CBMTypeDescriptionProviderBridge = _client.CBMTypeDescriptionProviderBridge;
|
|
}
|
|
|
|
// start watching for app domain unloading
|
|
_onAppDomainUnload = new EventHandler(OnAppDomainUnload);
|
|
Thread.GetDomain().DomainUnload += _onAppDomainUnload;
|
|
|
|
_buildManager = BuildManager.TheBuildManager;
|
|
|
|
// Listen to appdomain shutdown.
|
|
HttpRuntime.AppDomainShutdown += new BuildManagerHostUnloadEventHandler(this.OnAppDomainShutdown);
|
|
}
|
|
finally {
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
internal Exception InitializationException {
|
|
get {
|
|
return HostingEnvironment.InitializationException;
|
|
}
|
|
}
|
|
|
|
private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) {
|
|
Thread.GetDomain().DomainUnload -= _onAppDomainUnload;
|
|
|
|
if (_client != null) {
|
|
_client.OnAppDomainUnloaded(HttpRuntime.ShutdownReason);
|
|
_client = null;
|
|
}
|
|
}
|
|
|
|
internal bool UnloadAppDomain() {
|
|
_ignorePendingCalls = true;
|
|
|
|
// Make sure HttpRuntime does not ignore the appdomain shutdown.
|
|
HttpRuntime.SetUserForcedShutdown();
|
|
|
|
// Force unload the appdomain when called from client
|
|
return HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.UnloadAppDomainCalled, "CBM called UnloadAppDomain");
|
|
}
|
|
|
|
// This provider is created in the hosted appdomain for faster access of the virtual file content.
|
|
// Note this is used both in CBM and the aspnet precompilation tool
|
|
internal class ClientVirtualPathProvider : VirtualPathProvider {
|
|
private IDictionary _stringDictionary;
|
|
|
|
internal ClientVirtualPathProvider() {
|
|
_stringDictionary = new HybridDictionary(true);
|
|
}
|
|
|
|
public override bool FileExists(string virtualPath) {
|
|
if (_stringDictionary.Contains(virtualPath)) {
|
|
return true;
|
|
}
|
|
|
|
return base.FileExists(virtualPath);
|
|
}
|
|
|
|
public override CacheDependency GetCacheDependency(string virtualPath,
|
|
IEnumerable virtualPathDependencies, DateTime utcStart) {
|
|
|
|
if (virtualPath != null) {
|
|
virtualPath = UrlPath.MakeVirtualPathAppAbsolute(virtualPath);
|
|
// Return now so the build result will be invalidated based on hashcode.
|
|
// This is for the case that Venus passed in the file content so we don't
|
|
// get file change notification
|
|
if (_stringDictionary.Contains(virtualPath)) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// otherwise creates a cachedependency using MapPathBasedVirtualPathProvider
|
|
return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
|
|
}
|
|
|
|
public override VirtualFile GetFile(string virtualPath) {
|
|
String _virtualFileString = (String)_stringDictionary[virtualPath];
|
|
|
|
return _virtualFileString != null? new ClientVirtualFile(virtualPath, _virtualFileString) : base.GetFile(virtualPath);
|
|
}
|
|
|
|
public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies) {
|
|
HashCodeCombiner hashCodeCombiner = null;
|
|
|
|
ArrayList clonedPaths = new ArrayList();
|
|
foreach (string virtualDependency in virtualPathDependencies) {
|
|
// if the virtual path is previously cached, add the actual content to
|
|
// the hash code combiner.
|
|
if (_stringDictionary.Contains(virtualDependency)) {
|
|
if (hashCodeCombiner == null) {
|
|
hashCodeCombiner = new HashCodeCombiner();
|
|
}
|
|
|
|
hashCodeCombiner.AddInt(StringUtil.GetNonRandomizedHashCode((string)_stringDictionary[virtualDependency]));
|
|
continue;
|
|
}
|
|
|
|
// Otherwise move it to the cloned collection and use the base class (previous provider)
|
|
// to get the hash code.
|
|
clonedPaths.Add(virtualDependency);
|
|
}
|
|
|
|
if (hashCodeCombiner == null) {
|
|
return base.GetFileHash(virtualPath, virtualPathDependencies);
|
|
}
|
|
hashCodeCombiner.AddObject(base.GetFileHash(virtualPath, clonedPaths));
|
|
|
|
return hashCodeCombiner.CombinedHashString;
|
|
}
|
|
|
|
internal void RegisterVirtualFile(VirtualPath virtualPath, String virtualFileString) {
|
|
_stringDictionary[virtualPath.VirtualPathString] = virtualFileString;
|
|
}
|
|
|
|
internal void RevertVirtualFile(VirtualPath virtualPath) {
|
|
_stringDictionary.Remove(virtualPath.VirtualPathString);
|
|
}
|
|
|
|
internal class ClientVirtualFile : VirtualFile {
|
|
String _virtualFileString;
|
|
|
|
internal ClientVirtualFile(string virtualPath, String virtualFileString) : base(virtualPath) {
|
|
_virtualFileString = virtualFileString;
|
|
}
|
|
|
|
public override Stream Open() {
|
|
Stream stream = new MemoryStream();
|
|
StreamWriter writer = new StreamWriter(stream, Encoding.Unicode);
|
|
|
|
writer.Write(_virtualFileString);
|
|
writer.Flush();
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
return stream;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|