|
//------------------------------------------------------------------------------
// <copyright file="CompilationConfiguration.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* Code related to the <assemblies> config section
*
* Copyright (c) 1999 Microsoft Corporation
*/
namespace System.Web.Compilation {
using System;
using System.Web;
using System.Configuration;
using System.Web.UI;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Util;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.CodeDom.Compiler;
using System.Linq;
using System.Security;
using System.Security.Permissions;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
internal static class CompilationUtil {
internal const string CodeDomProviderOptionPath = "system.codedom/compilers/compiler/ProviderOption/";
private const string CompilerDirectoryPath = "CompilerDirectoryPath";
private static int _maxConcurrentCompilations;
internal static bool IsDebuggingEnabled(HttpContext context) {
CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(context);
return compConfig.Debug;
}
internal static bool IsBatchingEnabled(string configPath) {
CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
return config.Batch;
}
internal static int GetRecompilationsBeforeAppRestarts() {
CompilationSection config = MTConfigUtil.GetCompilationAppConfig();
return config.NumRecompilesBeforeAppRestart;
}
internal static CompilerType GetCodeDefaultLanguageCompilerInfo() {
return new CompilerType(typeof(Microsoft.VisualBasic.VBCodeProvider), null);
}
internal static CompilerType GetDefaultLanguageCompilerInfo(CompilationSection compConfig, VirtualPath configPath) {
if (compConfig == null) {
// Get the <compilation> config object
compConfig = MTConfigUtil.GetCompilationConfig(configPath);
}
// If no default language was specified in config, use VB
if (compConfig.DefaultLanguage == null) {
return GetCodeDefaultLanguageCompilerInfo();
}
else {
return compConfig.GetCompilerInfoFromLanguage(compConfig.DefaultLanguage);
}
}
/*
* Return a CompilerType that a file name's extension maps to.
*/
internal static CompilerType GetCompilerInfoFromVirtualPath(VirtualPath virtualPath) {
// Get the extension of the source file to compile
string extension = virtualPath.Extension;
// Make sure there is an extension
if (extension.Length == 0) {
throw new HttpException(
SR.GetString(SR.Empty_extension, virtualPath));
}
return GetCompilerInfoFromExtension(virtualPath, extension);
}
/*
* Return a CompilerType that a extension maps to.
*/
private static CompilerType GetCompilerInfoFromExtension(VirtualPath configPath, string extension) {
// Get the <compilation> config object
CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
return config.GetCompilerInfoFromExtension(extension, true /*throwOnFail*/);
}
/*
* Return a CompilerType that a language maps to.
*/
internal static CompilerType GetCompilerInfoFromLanguage(VirtualPath configPath, string language) {
// Get the <compilation> config object
CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
return config.GetCompilerInfoFromLanguage(language);
}
internal static CompilerType GetCSharpCompilerInfo(
CompilationSection compConfig, VirtualPath configPath) {
if (compConfig == null) {
// Get the <compilation> config object
compConfig = MTConfigUtil.GetCompilationConfig(configPath);
}
if (compConfig.DefaultLanguage == null)
return new CompilerType(typeof(Microsoft.CSharp.CSharpCodeProvider), null);
return compConfig.GetCompilerInfoFromLanguage("c#");
}
internal static CodeSubDirectoriesCollection GetCodeSubDirectories() {
// Get the <compilation> config object
CompilationSection config = MTConfigUtil.GetCompilationAppConfig();
CodeSubDirectoriesCollection codeSubDirectories = config.CodeSubDirectories;
// Make sure the config data is valid
if (codeSubDirectories != null) {
codeSubDirectories.EnsureRuntimeValidation();
}
return codeSubDirectories;
}
internal static long GetRecompilationHash(CompilationSection ps) {
HashCodeCombiner recompilationHash = new HashCodeCombiner();
AssemblyCollection assemblies;
BuildProviderCollection builders;
FolderLevelBuildProviderCollection buildProviders;
CodeSubDirectoriesCollection codeSubDirs;
// Combine items from Compilation section
recompilationHash.AddObject(ps.Debug);
recompilationHash.AddObject(ps.TargetFramework);
recompilationHash.AddObject(ps.Strict);
recompilationHash.AddObject(ps.Explicit);
recompilationHash.AddObject(ps.Batch);
recompilationHash.AddObject(ps.OptimizeCompilations);
recompilationHash.AddObject(ps.BatchTimeout);
recompilationHash.AddObject(ps.MaxBatchGeneratedFileSize);
recompilationHash.AddObject(ps.MaxBatchSize);
recompilationHash.AddObject(ps.NumRecompilesBeforeAppRestart);
recompilationHash.AddObject(ps.DefaultLanguage);
recompilationHash.AddObject(ps.UrlLinePragmas);
recompilationHash.AddObject(ps.DisableObsoleteWarnings);
if (ps.AssemblyPostProcessorTypeInternal != null) {
recompilationHash.AddObject(ps.AssemblyPostProcessorTypeInternal.FullName);
}
if (!String.IsNullOrWhiteSpace(ps.ControlBuilderInterceptorType)) {
recompilationHash.AddObject(ps.ControlBuilderInterceptorType);
}
// Combine items from Compilers collection
foreach (Compiler compiler in ps.Compilers) {
recompilationHash.AddObject(compiler.Language);
recompilationHash.AddObject(compiler.Extension);
recompilationHash.AddObject(compiler.Type);
recompilationHash.AddObject(compiler.WarningLevel);
recompilationHash.AddObject(compiler.CompilerOptions);
}
// Combine items from <expressionBuilders> section
foreach (System.Web.Configuration.ExpressionBuilder eb in ps.ExpressionBuilders) {
recompilationHash.AddObject(eb.ExpressionPrefix);
recompilationHash.AddObject(eb.Type);
}
// Combine items from the Assembly collection
assemblies = ps.Assemblies;
if (assemblies.Count == 0) {
recompilationHash.AddObject("__clearassemblies");
}
else {
foreach (AssemblyInfo ai in assemblies) {
recompilationHash.AddObject(ai.Assembly);
}
}
// Combine items from the Builders Collection
builders = ps.BuildProviders;
if (builders.Count == 0) {
recompilationHash.AddObject("__clearbuildproviders");
}
else {
foreach (System.Web.Configuration.BuildProvider bp in builders) {
recompilationHash.AddObject(bp.Type);
recompilationHash.AddObject(bp.Extension);
}
}
// Combine items from the FolderLevelBuildProviderCollection
buildProviders = ps.FolderLevelBuildProviders;
if (buildProviders.Count == 0) {
recompilationHash.AddObject("__clearfolderlevelbuildproviders");
}
else {
foreach (System.Web.Configuration.FolderLevelBuildProvider bp in buildProviders) {
recompilationHash.AddObject(bp.Type);
recompilationHash.AddObject(bp.Name);
}
}
codeSubDirs = ps.CodeSubDirectories;
if (codeSubDirs.Count == 0) {
recompilationHash.AddObject("__clearcodesubdirs");
}
else {
foreach (CodeSubDirectory csd in codeSubDirs) {
recompilationHash.AddObject(csd.DirectoryName);
}
}
// Make sure the <system.CodeDom> section is hashed properly.
CompilerInfo[] compilerInfoArray = CodeDomProvider.GetAllCompilerInfo();
if (compilerInfoArray != null) {
CompilerInfo cppCodeProvider = CodeDomProvider.GetCompilerInfo("cpp");
foreach (CompilerInfo info in compilerInfoArray) {
// Skip cpp code provider (Dev11 193323).
if (info == cppCodeProvider) {
continue;
}
// Ignore it if the type is not valid.
if (!info.IsCodeDomProviderTypeValid) {
continue;
}
CompilerParameters parameters = info.CreateDefaultCompilerParameters();
string option = parameters.CompilerOptions;
if (!String.IsNullOrEmpty(option)) {
Type type = info.CodeDomProviderType;
if (type != null) {
recompilationHash.AddObject(type.FullName);
}
// compilerOptions need to be hashed.
recompilationHash.AddObject(option);
}
// DevDiv 62998
// The tag providerOption needs to be added to the hash,
// as the user could switch between v2 and v3.5.
if (info.CodeDomProviderType == null)
continue;
// Add a hash for each providerOption added, specific for each codeDomProvider, so that
// if some codedom setting has changed, we know we have to recompile.
IDictionary<string, string> providerOptions = GetProviderOptions(info);
if (providerOptions != null && providerOptions.Count > 0) {
string codeDomProviderType = info.CodeDomProviderType.FullName;
foreach (string key in providerOptions.Keys) {
string value = providerOptions[key];
recompilationHash.AddObject(codeDomProviderType + ":" + key + "=" + value);
}
}
}
}
return recompilationHash.CombinedHash;
}
/*
* Return a file provider Type that an extension maps to.
*/
internal static Type GetBuildProviderTypeFromExtension(VirtualPath configPath, string extension,
BuildProviderAppliesTo neededFor, bool failIfUnknown) {
// Get the <compilation> config object
CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
return GetBuildProviderTypeFromExtension(config, extension, neededFor, failIfUnknown);
}
internal static Type GetBuildProviderTypeFromExtension(CompilationSection config, string extension,
BuildProviderAppliesTo neededFor, bool failIfUnknown) {
BuildProviderInfo providerInfo = BuildProvider.GetBuildProviderInfo(config, extension);
Type buildProviderType = null;
// Never return an IgnoreFileBuildProvider/ForceCopyBuildProvider, since it's just a marker
if (providerInfo != null &&
providerInfo.Type != typeof(IgnoreFileBuildProvider) &&
providerInfo.Type != typeof(ForceCopyBuildProvider)) {
buildProviderType = providerInfo.Type;
}
// In updatable precomp mode, only aspx/ascx/master web files need processing. Ignore the rest.
if (neededFor == BuildProviderAppliesTo.Web &&
BuildManager.PrecompilingForUpdatableDeployment &&
!typeof(BaseTemplateBuildProvider).IsAssignableFrom(buildProviderType)) {
buildProviderType = null;
}
if (buildProviderType != null) {
// Only return it if it applies to what it's needed for
if ((neededFor & providerInfo.AppliesTo) != 0)
return buildProviderType;
}
// If the extension is registered as a compiler extension, use
// a SourceFileBuildProvider to handle it (not supported in Resources directory)
else if (neededFor != BuildProviderAppliesTo.Resources &&
config.GetCompilerInfoFromExtension(extension, false /*throwOnFail*/) != null) {
return typeof(SourceFileBuildProvider);
}
if (failIfUnknown) {
throw new HttpException( SR.GetString(SR.Unknown_buildprovider_extension, extension, neededFor.ToString()));
}
return null;
}
// Returns the list of buildProvider types associated to the specified appliesTo
internal static List<Type> GetFolderLevelBuildProviderTypes(CompilationSection config,
FolderLevelBuildProviderAppliesTo appliesTo) {
FolderLevelBuildProviderCollection buildProviders = config.FolderLevelBuildProviders;
return buildProviders.GetBuildProviderTypes(appliesTo);
}
// In partial trust, do not allow the CompilerDirectoryPath provider option in codedom settings (Dev10 bug 462348)
internal static void CheckCompilerDirectoryPathAllowed(IDictionary<string, string> providerOptions) {
if (providerOptions == null) {
return;
}
if (!providerOptions.ContainsKey(CompilerDirectoryPath)) {
return;
}
if (!HttpRuntime.HasUnmanagedPermission()) {
string errorString = SR.GetString(SR.Insufficient_trust_for_attribute, CompilerDirectoryPath);
throw new HttpException(errorString);
}
}
internal static void CheckCompilerOptionsAllowed(string compilerOptions, bool config, string file, int line) {
// If it's empty, we never block it
if (String.IsNullOrEmpty(compilerOptions))
return;
// Only allow the use of compilerOptions when we have UnmanagedCode access (ASURT 73678)
if (!HttpRuntime.HasUnmanagedPermission()) {
string errorString = SR.GetString(SR.Insufficient_trust_for_attribute, "compilerOptions");
if (config)
throw new ConfigurationErrorsException(errorString, file, line);
else
throw new HttpException(errorString);
}
}
// This is used to determine what files need to be copied, and what stub files
// need to be created during deployment precompilation.
// Note: createStub only applies if the method returns false.
internal static bool NeedToCopyFile(VirtualPath virtualPath, bool updatable, out bool createStub) {
createStub = false;
// Get the <compilation> config object
CompilationSection config = MTConfigUtil.GetCompilationConfig(virtualPath);
string extension = virtualPath.Extension;
BuildProviderInfo providerInfo = BuildProvider.GetBuildProviderInfo(config, extension);
if (providerInfo != null) {
// We only care about 'web' providers. Everything else we treat as static
if ((BuildProviderAppliesTo.Web & providerInfo.AppliesTo) == 0)
return true;
// If the provider is a ForceCopyBuildProvider, treat as static
if (providerInfo.Type == typeof(ForceCopyBuildProvider))
return true;
// During updatable precomp, everything needs to be copied over. However,
// aspx files that use code beside will later be overwritten by modified
// versions (see TemplateParser.CreateModifiedMainDirectiveFileIfNeeded)
if (providerInfo.Type != typeof(IgnoreFileBuildProvider) &&
BuildManager.PrecompilingForUpdatableDeployment) {
return true;
}
// There is a real provider, so don't copy the file. We also need to determine whether
// a stub file needs to be created.
createStub = true;
// Skip the stub file for some non-requestable types
if (providerInfo.Type == typeof(UserControlBuildProvider) ||
providerInfo.Type == typeof(MasterPageBuildProvider) ||
providerInfo.Type == typeof(IgnoreFileBuildProvider)) {
createStub = false;
}
return false;
}
// If the extension is registered as a compiler extension, don't copy
if (config.GetCompilerInfoFromExtension(extension, false /*throwOnFail*/) != null) {
return false;
}
// Skip the copying for asax and skin files, which are not static even though they
// don't have a registered BuildProvider (but don't skip .skin files during
// updatable precomp).
//
if (StringUtil.EqualsIgnoreCase(extension, ".asax"))
return false;
if (!updatable && StringUtil.EqualsIgnoreCase(extension, ThemeDirectoryCompiler.skinExtension))
return false;
//
// If there is no BuildProvider registered, it's a static file, and should be copied
//
return true;
}
internal static Type LoadTypeWithChecks(string typeName, Type requiredBaseType, Type requiredBaseType2, ConfigurationElement elem, string propertyName) {
Type t = ConfigUtil.GetType(typeName, propertyName, elem);
if (requiredBaseType2 == null) {
ConfigUtil.CheckAssignableType(requiredBaseType, t, elem, propertyName);
}
else {
ConfigUtil.CheckAssignableType(requiredBaseType, requiredBaseType2, t, elem, propertyName);
}
return t;
}
// Devdiv Bug 57600
// We need to use the constructor with ProviderOptions to get the v3.5/v4.0 compiler that was possibly set in config.
// We first check if there is any providerOptions and invoke the constructor if so.
// Otherwise, we fall back to the default constructor.
internal static CodeDomProvider CreateCodeDomProvider(Type codeDomProviderType) {
CodeDomProvider codeDomProvider = CreateCodeDomProviderWithPropertyOptions(codeDomProviderType);
if (codeDomProvider != null) {
return codeDomProvider;
}
return (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
}
internal static CodeDomProvider CreateCodeDomProviderNonPublic(Type codeDomProviderType) {
CodeDomProvider codeDomProvider = CreateCodeDomProviderWithPropertyOptions(codeDomProviderType);
if (codeDomProvider != null) {
return codeDomProvider;
}
return (CodeDomProvider)HttpRuntime.CreateNonPublicInstance(codeDomProviderType);
}
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
private static CodeDomProvider CreateCodeDomProviderWithPropertyOptions(Type codeDomProviderType) {
// The following resembles the code in System.CodeDom.CompilerInfo.CreateProvider
// Make a copy to avoid modifying the original.
var originalProviderOptions = GetProviderOptions(codeDomProviderType);
IDictionary<string, string> providerOptions = null;
if (originalProviderOptions != null) {
providerOptions = new Dictionary<string, string>(originalProviderOptions);
} else {
providerOptions = new Dictionary<string, string>();
}
// Block CompilerDirectoryPath if we are in partial trust
CheckCompilerDirectoryPathAllowed(providerOptions);
// Check whether the user supplied the compilerDirectoryPath or was it added by us
bool addedCompilerDirectoryPath = false;
if (MultiTargetingUtil.IsTargetFramework20) {
// If the target framework is v2.0, there won't be any codedom settings, so we need
// to explicitly set the compiler to be the v2.0 compiler using compilerVersion=v2.0.
providerOptions["CompilerVersion"] = "v2.0";
}
else if (MultiTargetingUtil.IsTargetFramework35) {
// We need to explicitly set to v3.5, as it is possible for the
// user to only have specified it for one compiler but not
// the other.
// Dev10 bug 809212
providerOptions["CompilerVersion"] = "v3.5";
}
else {
// If we are targeting 4.0 but the compiler version is less than 4.0, set it to 4.0.
// This can happen if a user tries to run a 2.0/3.5 web site in a 4.0 application pool without
// upgrading it, and the codedom section still has 3.5 as the compilerVersion,
// so we have to set the compilerVersion to 4.0 explicitly.
string version = GetCompilerVersion(codeDomProviderType);
Version v = GetVersionFromVString(version);
if (v != null && v < MultiTargetingUtil.Version40) {
providerOptions["CompilerVersion"] = "v4.0";
}
}
if (providerOptions != null && providerOptions.Count > 0) {
Debug.Assert(codeDomProviderType != null, "codeDomProviderType should not be null");
// Check whether the codedom provider supports a constructor that takes in providerOptions.
// Currently only VB and C# support providerOptions for sure, while others such as JScript might not.
ConstructorInfo ci = codeDomProviderType.GetConstructor(new Type[] { typeof(IDictionary<string, string>) });
CodeDomProvider provider = null;
if (ci != null) {
// First, obtain the language for the given codedom provider type.
CodeDomProvider defaultProvider = (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
string extension = defaultProvider.FileExtension;
var language = CodeDomProvider.GetLanguageFromExtension(extension);
// Then, use the new createProvider API to create an instance.
provider = CodeDomProvider.CreateProvider(language, providerOptions);
}
// Restore the provider options if we previously manually added the compilerDirectoryPath.
// Otherwise, we might incorrectly invalidate the compilerDirectoryPath in medium trust (Dev10 bug 550299).
if (addedCompilerDirectoryPath) {
providerOptions.Remove(CompilerDirectoryPath);
}
return provider;
}
return null;
}
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
internal static IDictionary<string, string> GetProviderOptions(Type codeDomProviderType) {
// Using reflection to get the property for the time being.
// This could simply return CompilerInfo.PropertyOptions if it goes public in future.
CodeDomProvider provider = (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
string extension = provider.FileExtension;
if (CodeDomProvider.IsDefinedExtension(extension)) {
CompilerInfo ci = CodeDomProvider.GetCompilerInfo(CodeDomProvider.GetLanguageFromExtension(extension));
return GetProviderOptions(ci);
}
return null;
}
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
private static IDictionary<string, string> GetProviderOptions(CompilerInfo ci) {
Debug.Assert(ci != null, "CompilerInfo ci should not be null");
PropertyInfo pi = ci.GetType().GetProperty("ProviderOptions",
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
if (pi != null)
return (IDictionary<string, string>)pi.GetValue(ci, null);
return null;
}
/// <summary>
/// Returns the compilerVersion providerOption specified for the codedom provider type.
/// Returns null if the providerOption is not found.
/// </summary>
internal static string GetCompilerVersion(Type codeDomProviderType) {
return GetProviderOption(codeDomProviderType, "CompilerVersion");
}
/// <summary>
/// Returns the value of the providerOption specified for the codedom provider type.
/// Returns null if the providerOption is not found.
/// </summary>
internal static string GetProviderOption(Type codeDomProviderType, string providerOption) {
IDictionary<string, string> providerOptions = CompilationUtil.GetProviderOptions(codeDomProviderType);
if (providerOptions != null) {
string version;
if (providerOptions.TryGetValue(providerOption, out version)) {
return version;
}
}
return null;
}
/// <summary>
/// Returns true if the string matches "v3.5" exactly.
/// </summary>
internal static bool IsCompilerVersion35(string compilerVersion) {
if (compilerVersion == "v3.5") {
return true;
}
return false;
}
/// <summary>
/// This returns true only if the codedom CompilverVersion provider option is exactly v3.5.
/// </summary>
internal static bool IsCompilerVersion35(Type codeDomProviderType) {
string compilerVersion = GetCompilerVersion(codeDomProviderType);
bool result = IsCompilerVersion35(compilerVersion);
return result;
}
/// <summary>
/// Returns true if the codedom CompilerVersion provider option is at least v3.5.
/// </summary>
/// <param name="codeDomProviderType"></param>
/// <returns></returns>
internal static bool IsCompilerVersion35OrAbove(Type codeDomProviderType) {
string compilerVersion = GetCompilerVersion(codeDomProviderType);
if (IsCompilerVersion35(compilerVersion)) {
return true;
}
// The compilerVersion provider option is known to exist only for v3.5.
// If it does not exist, then we need to rely on the target framework version to
// determine whether we need to use the 2.0 or 4.0 compiler.
if (MultiTargetingUtil.IsTargetFramework20) {
return false;
}
// If it isn't 2.0 or 3.5, assume it is 4.0 and above.
return true;
}
/// <summary>
/// Returns true if the codedom provider has warnAsError set to true
/// </summary>
internal static bool WarnAsError(Type codeDomProviderType) {
string value = GetProviderOption(codeDomProviderType, "WarnAsError");
bool result;
if (value != null && bool.TryParse(value, out result)) {
return result;
}
// Assume false if the value wasn't set
return false;
}
// Returns the version when given string of form "v4.0"
internal static Version GetVersionFromVString(string version) {
if (string.IsNullOrEmpty(version)) {
return null;
}
Debug.Assert(version.Length > 1, "Version has invalid length");
return new Version(version.Substring(1));
}
// Returns maximum number of concurrent compilations
internal static int MaxConcurrentCompilations {
get {
if (_maxConcurrentCompilations == 0) {
int maxConcurrentCompilations;
if (AppSettings.MaxConcurrentCompilations.HasValue && AppSettings.MaxConcurrentCompilations.Value >= 0) {
maxConcurrentCompilations = AppSettings.MaxConcurrentCompilations.Value;
}
else {
CompilationSection config = MTConfigUtil.GetCompilationAppConfig();
maxConcurrentCompilations = config.MaxConcurrentCompilations;
}
if (maxConcurrentCompilations <= 0) {
maxConcurrentCompilations = Environment.ProcessorCount;
}
Interlocked.CompareExchange(ref _maxConcurrentCompilations, maxConcurrentCompilations, 0);
}
return _maxConcurrentCompilations;
}
}
}
}
|