1 //------------------------------------------------------------------------------
2 // <copyright file="CompilationConfiguration.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 /*
8  * Code related to the <assemblies> config section
9  *
10  * Copyright (c) 1999 Microsoft Corporation
11  */
12 namespace System.Web.Compilation {
13 
14     using System;
15     using System.Web;
16     using System.Configuration;
17     using System.Web.UI;
18     using System.Web.Configuration;
19     using System.Web.Hosting;
20     using System.Web.Util;
21     using System.Globalization;
22     using System.Collections;
23     using System.Collections.Generic;
24     using System.CodeDom.Compiler;
25     using System.Linq;
26     using System.Security;
27     using System.Security.Permissions;
28     using System.Reflection;
29     using System.Runtime.ExceptionServices;
30     using System.Threading;
31     using System.Threading.Tasks;
32 
33     internal static class CompilationUtil {
34 
35         internal const string CodeDomProviderOptionPath = "system.codedom/compilers/compiler/ProviderOption/";
36         private const string CompilerDirectoryPath = "CompilerDirectoryPath";
37         private static int _maxConcurrentCompilations;
38 
IsDebuggingEnabled(HttpContext context)39         internal static bool IsDebuggingEnabled(HttpContext context) {
40             CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(context);
41             return compConfig.Debug;
42         }
43 
IsBatchingEnabled(string configPath)44         internal static bool IsBatchingEnabled(string configPath) {
45             CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
46             return config.Batch;
47         }
48 
GetRecompilationsBeforeAppRestarts()49         internal static int GetRecompilationsBeforeAppRestarts() {
50             CompilationSection config = MTConfigUtil.GetCompilationAppConfig();
51             return config.NumRecompilesBeforeAppRestart;
52         }
53 
GetCodeDefaultLanguageCompilerInfo()54         internal static CompilerType GetCodeDefaultLanguageCompilerInfo() {
55             return new CompilerType(typeof(Microsoft.VisualBasic.VBCodeProvider), null);
56         }
57 
GetDefaultLanguageCompilerInfo(CompilationSection compConfig, VirtualPath configPath)58         internal static CompilerType GetDefaultLanguageCompilerInfo(CompilationSection compConfig, VirtualPath configPath) {
59             if (compConfig == null) {
60                 // Get the <compilation> config object
61                 compConfig = MTConfigUtil.GetCompilationConfig(configPath);
62             }
63 
64             // If no default language was specified in config, use VB
65             if (compConfig.DefaultLanguage == null) {
66                 return GetCodeDefaultLanguageCompilerInfo();
67             }
68             else {
69                 return compConfig.GetCompilerInfoFromLanguage(compConfig.DefaultLanguage);
70             }
71         }
72 
73         /*
74          * Return a CompilerType that a file name's extension maps to.
75          */
GetCompilerInfoFromVirtualPath(VirtualPath virtualPath)76         internal static CompilerType GetCompilerInfoFromVirtualPath(VirtualPath virtualPath) {
77 
78             // Get the extension of the source file to compile
79             string extension = virtualPath.Extension;
80 
81             // Make sure there is an extension
82             if (extension.Length == 0) {
83                 throw new HttpException(
84                     SR.GetString(SR.Empty_extension, virtualPath));
85             }
86 
87             return GetCompilerInfoFromExtension(virtualPath, extension);
88         }
89 
90         /*
91          * Return a CompilerType that a extension maps to.
92          */
GetCompilerInfoFromExtension(VirtualPath configPath, string extension)93         private static CompilerType GetCompilerInfoFromExtension(VirtualPath configPath, string extension) {
94             // Get the <compilation> config object
95             CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
96 
97             return config.GetCompilerInfoFromExtension(extension, true /*throwOnFail*/);
98         }
99 
100         /*
101          * Return a CompilerType that a language maps to.
102          */
GetCompilerInfoFromLanguage(VirtualPath configPath, string language)103         internal static CompilerType GetCompilerInfoFromLanguage(VirtualPath configPath, string language) {
104             // Get the <compilation> config object
105             CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
106 
107             return config.GetCompilerInfoFromLanguage(language);
108         }
109 
GetCSharpCompilerInfo( CompilationSection compConfig, VirtualPath configPath)110         internal static CompilerType GetCSharpCompilerInfo(
111             CompilationSection compConfig, VirtualPath configPath) {
112 
113             if (compConfig == null) {
114                 // Get the <compilation> config object
115                 compConfig = MTConfigUtil.GetCompilationConfig(configPath);
116             }
117 
118             if (compConfig.DefaultLanguage == null)
119                 return new CompilerType(typeof(Microsoft.CSharp.CSharpCodeProvider), null);
120 
121             return compConfig.GetCompilerInfoFromLanguage("c#");
122         }
123 
GetCodeSubDirectories()124         internal static CodeSubDirectoriesCollection GetCodeSubDirectories() {
125             // Get the <compilation> config object
126             CompilationSection config = MTConfigUtil.GetCompilationAppConfig();
127 
128             CodeSubDirectoriesCollection codeSubDirectories = config.CodeSubDirectories;
129 
130             // Make sure the config data is valid
131             if (codeSubDirectories != null) {
132                 codeSubDirectories.EnsureRuntimeValidation();
133             }
134 
135             return codeSubDirectories;
136         }
137 
GetRecompilationHash(CompilationSection ps)138         internal static long GetRecompilationHash(CompilationSection ps) {
139             HashCodeCombiner recompilationHash = new HashCodeCombiner();
140             AssemblyCollection assemblies;
141             BuildProviderCollection builders;
142             FolderLevelBuildProviderCollection buildProviders;
143             CodeSubDirectoriesCollection codeSubDirs;
144 
145             // Combine items from Compilation section
146             recompilationHash.AddObject(ps.Debug);
147             recompilationHash.AddObject(ps.TargetFramework);
148             recompilationHash.AddObject(ps.Strict);
149             recompilationHash.AddObject(ps.Explicit);
150             recompilationHash.AddObject(ps.Batch);
151             recompilationHash.AddObject(ps.OptimizeCompilations);
152             recompilationHash.AddObject(ps.BatchTimeout);
153             recompilationHash.AddObject(ps.MaxBatchGeneratedFileSize);
154             recompilationHash.AddObject(ps.MaxBatchSize);
155             recompilationHash.AddObject(ps.NumRecompilesBeforeAppRestart);
156             recompilationHash.AddObject(ps.DefaultLanguage);
157             recompilationHash.AddObject(ps.UrlLinePragmas);
158             recompilationHash.AddObject(ps.DisableObsoleteWarnings);
159             if (ps.AssemblyPostProcessorTypeInternal != null) {
160                 recompilationHash.AddObject(ps.AssemblyPostProcessorTypeInternal.FullName);
161             }
162             if (!String.IsNullOrWhiteSpace(ps.ControlBuilderInterceptorType)) {
163                 recompilationHash.AddObject(ps.ControlBuilderInterceptorType);
164             }
165 
166             // Combine items from Compilers collection
167             foreach (Compiler compiler in ps.Compilers) {
168                 recompilationHash.AddObject(compiler.Language);
169                 recompilationHash.AddObject(compiler.Extension);
170                 recompilationHash.AddObject(compiler.Type);
171                 recompilationHash.AddObject(compiler.WarningLevel);
172                 recompilationHash.AddObject(compiler.CompilerOptions);
173             }
174 
175             // Combine items from <expressionBuilders> section
176             foreach (System.Web.Configuration.ExpressionBuilder eb in ps.ExpressionBuilders) {
177                 recompilationHash.AddObject(eb.ExpressionPrefix);
178                 recompilationHash.AddObject(eb.Type);
179             }
180 
181             // Combine items from the Assembly collection
182             assemblies = ps.Assemblies;
183 
184             if (assemblies.Count == 0) {
185                 recompilationHash.AddObject("__clearassemblies");
186             }
187             else {
188                 foreach (AssemblyInfo ai in assemblies) {
189                     recompilationHash.AddObject(ai.Assembly);
190                 }
191             }
192 
193             // Combine items from the Builders Collection
194             builders = ps.BuildProviders;
195 
196             if (builders.Count == 0) {
197                 recompilationHash.AddObject("__clearbuildproviders");
198             }
199             else {
200                 foreach (System.Web.Configuration.BuildProvider bp in builders) {
201                     recompilationHash.AddObject(bp.Type);
202                     recompilationHash.AddObject(bp.Extension);
203                 }
204             }
205 
206             // Combine items from the FolderLevelBuildProviderCollection
207             buildProviders = ps.FolderLevelBuildProviders;
208 
209             if (buildProviders.Count == 0) {
210                 recompilationHash.AddObject("__clearfolderlevelbuildproviders");
211             }
212             else {
213                 foreach (System.Web.Configuration.FolderLevelBuildProvider bp in buildProviders) {
214                     recompilationHash.AddObject(bp.Type);
215                     recompilationHash.AddObject(bp.Name);
216                 }
217             }
218 
219             codeSubDirs = ps.CodeSubDirectories;
220             if (codeSubDirs.Count == 0) {
221                 recompilationHash.AddObject("__clearcodesubdirs");
222             }
223             else {
224                 foreach (CodeSubDirectory csd in codeSubDirs) {
225                     recompilationHash.AddObject(csd.DirectoryName);
226                 }
227             }
228 
229             // Make sure the <system.CodeDom> section is hashed properly.
230             CompilerInfo[] compilerInfoArray = CodeDomProvider.GetAllCompilerInfo();
231             if (compilerInfoArray != null) {
232                 CompilerInfo cppCodeProvider = CodeDomProvider.GetCompilerInfo("cpp");
233                 foreach (CompilerInfo info in compilerInfoArray) {
234                     // Skip cpp code provider (Dev11 193323).
235                     if (info == cppCodeProvider) {
236                         continue;
237                     }
238 
239                     // Ignore it if the type is not valid.
240                     if (!info.IsCodeDomProviderTypeValid) {
241                         continue;
242                     }
243 
244                     CompilerParameters parameters = info.CreateDefaultCompilerParameters();
245                     string option = parameters.CompilerOptions;
246                     if (!String.IsNullOrEmpty(option)) {
247                         Type type = info.CodeDomProviderType;
248                         if (type != null) {
249                             recompilationHash.AddObject(type.FullName);
250                         }
251                         // compilerOptions need to be hashed.
252                         recompilationHash.AddObject(option);
253                     }
254 
255                     // DevDiv 62998
256                     // The tag providerOption needs to be added to the hash,
257                     // as the user could switch between v2 and v3.5.
258                     if (info.CodeDomProviderType == null)
259                         continue;
260 
261                     // Add a hash for each providerOption added, specific for each codeDomProvider, so that
262                     // if some codedom setting has changed, we know we have to recompile.
263                     IDictionary<string, string> providerOptions = GetProviderOptions(info);
264                     if (providerOptions != null && providerOptions.Count > 0) {
265                         string codeDomProviderType = info.CodeDomProviderType.FullName;
266                         foreach (string key in providerOptions.Keys) {
267                             string value = providerOptions[key];
268                             recompilationHash.AddObject(codeDomProviderType + ":" +  key + "=" + value);
269                         }
270                     }
271                 }
272             }
273 
274             return recompilationHash.CombinedHash;
275         }
276 
277 
278         /*
279          * Return a file provider Type that an extension maps to.
280          */
GetBuildProviderTypeFromExtension(VirtualPath configPath, string extension, BuildProviderAppliesTo neededFor, bool failIfUnknown)281         internal static Type GetBuildProviderTypeFromExtension(VirtualPath configPath, string extension,
282             BuildProviderAppliesTo neededFor, bool failIfUnknown) {
283 
284             // Get the <compilation> config object
285             CompilationSection config = MTConfigUtil.GetCompilationConfig(configPath);
286 
287             return GetBuildProviderTypeFromExtension(config, extension, neededFor, failIfUnknown);
288         }
289 
GetBuildProviderTypeFromExtension(CompilationSection config, string extension, BuildProviderAppliesTo neededFor, bool failIfUnknown)290         internal static Type GetBuildProviderTypeFromExtension(CompilationSection config, string extension,
291             BuildProviderAppliesTo neededFor, bool failIfUnknown) {
292 
293             BuildProviderInfo providerInfo = BuildProvider.GetBuildProviderInfo(config, extension);
294 
295             Type buildProviderType = null;
296             // Never return an IgnoreFileBuildProvider/ForceCopyBuildProvider, since it's just a marker
297             if (providerInfo != null &&
298                 providerInfo.Type != typeof(IgnoreFileBuildProvider) &&
299                 providerInfo.Type != typeof(ForceCopyBuildProvider)) {
300                 buildProviderType = providerInfo.Type;
301             }
302 
303             // In updatable precomp mode, only aspx/ascx/master web files need processing.  Ignore the rest.
304             if (neededFor == BuildProviderAppliesTo.Web &&
305                 BuildManager.PrecompilingForUpdatableDeployment &&
306                 !typeof(BaseTemplateBuildProvider).IsAssignableFrom(buildProviderType)) {
307                 buildProviderType = null;
308             }
309 
310             if (buildProviderType != null) {
311                 // Only return it if it applies to what it's needed for
312                 if ((neededFor & providerInfo.AppliesTo) != 0)
313                     return buildProviderType;
314             }
315             // If the extension is registered as a compiler extension, use
316             // a SourceFileBuildProvider to handle it (not supported in Resources directory)
317             else if (neededFor != BuildProviderAppliesTo.Resources &&
318                 config.GetCompilerInfoFromExtension(extension, false /*throwOnFail*/) != null) {
319                 return typeof(SourceFileBuildProvider);
320             }
321 
322             if (failIfUnknown) {
323                 throw new HttpException( SR.GetString(SR.Unknown_buildprovider_extension, extension, neededFor.ToString()));
324             }
325 
326             return null;
327         }
328 
329         // Returns the list of buildProvider types associated to the specified appliesTo
GetFolderLevelBuildProviderTypes(CompilationSection config, FolderLevelBuildProviderAppliesTo appliesTo)330         internal static List<Type> GetFolderLevelBuildProviderTypes(CompilationSection config,
331             FolderLevelBuildProviderAppliesTo appliesTo) {
332             FolderLevelBuildProviderCollection buildProviders = config.FolderLevelBuildProviders;
333             return buildProviders.GetBuildProviderTypes(appliesTo);
334         }
335 
336         // In partial trust, do not allow the CompilerDirectoryPath provider option in codedom settings (Dev10 bug 462348)
CheckCompilerDirectoryPathAllowed(IDictionary<string, string> providerOptions)337         internal static void CheckCompilerDirectoryPathAllowed(IDictionary<string, string> providerOptions) {
338             if (providerOptions == null) {
339                 return;
340             }
341             if (!providerOptions.ContainsKey(CompilerDirectoryPath)) {
342                 return;
343             }
344 
345             if (!HttpRuntime.HasUnmanagedPermission()) {
346                 string errorString = SR.GetString(SR.Insufficient_trust_for_attribute, CompilerDirectoryPath);
347                 throw new HttpException(errorString);
348             }
349         }
350 
CheckCompilerOptionsAllowed(string compilerOptions, bool config, string file, int line)351         internal static void CheckCompilerOptionsAllowed(string compilerOptions, bool config, string file, int line) {
352 
353             // If it's empty, we never block it
354             if (String.IsNullOrEmpty(compilerOptions))
355                 return;
356 
357             // Only allow the use of compilerOptions when we have UnmanagedCode access (ASURT 73678)
358             if (!HttpRuntime.HasUnmanagedPermission()) {
359                 string errorString = SR.GetString(SR.Insufficient_trust_for_attribute, "compilerOptions");
360 
361                 if (config)
362                     throw new ConfigurationErrorsException(errorString, file, line);
363                 else
364                     throw new HttpException(errorString);
365             }
366         }
367 
368         // This is used to determine what files need to be copied, and what stub files
369         // need to be created during deployment precompilation.
370         // Note: createStub only applies if the method returns false.
NeedToCopyFile(VirtualPath virtualPath, bool updatable, out bool createStub)371         internal static bool NeedToCopyFile(VirtualPath virtualPath, bool updatable, out bool createStub) {
372 
373             createStub = false;
374 
375             // Get the <compilation> config object
376             CompilationSection config = MTConfigUtil.GetCompilationConfig(virtualPath);
377 
378             string extension = virtualPath.Extension;
379 
380             BuildProviderInfo providerInfo = BuildProvider.GetBuildProviderInfo(config, extension);
381 
382             if (providerInfo != null) {
383                 // We only care about 'web' providers.  Everything else we treat as static
384                 if ((BuildProviderAppliesTo.Web & providerInfo.AppliesTo) == 0)
385                     return true;
386 
387                 // If the provider is a ForceCopyBuildProvider, treat as static
388                 if (providerInfo.Type == typeof(ForceCopyBuildProvider))
389                     return true;
390 
391                 // During updatable precomp, everything needs to be copied over.  However,
392                 // aspx files that use code beside will later be overwritten by modified
393                 // versions (see TemplateParser.CreateModifiedMainDirectiveFileIfNeeded)
394                 if (providerInfo.Type != typeof(IgnoreFileBuildProvider) &&
395                     BuildManager.PrecompilingForUpdatableDeployment) {
396                     return true;
397                 }
398 
399                 // There is a real provider, so don't copy the file.  We also need to determine whether
400                 // a stub file needs to be created.
401 
402                 createStub = true;
403 
404                 // Skip the stub file for some non-requestable types
405                 if (providerInfo.Type == typeof(UserControlBuildProvider) ||
406                     providerInfo.Type == typeof(MasterPageBuildProvider) ||
407                     providerInfo.Type == typeof(IgnoreFileBuildProvider)) {
408                     createStub = false;
409                 }
410 
411                 return false;
412             }
413 
414             // If the extension is registered as a compiler extension, don't copy
415             if (config.GetCompilerInfoFromExtension(extension, false /*throwOnFail*/) != null) {
416                 return false;
417             }
418 
419             // Skip the copying for asax and skin files, which are not static even though they
420             // don't have a registered BuildProvider (but don't skip .skin files during
421             // updatable precomp).
422             //
423             if (StringUtil.EqualsIgnoreCase(extension, ".asax"))
424                 return false;
425             if (!updatable && StringUtil.EqualsIgnoreCase(extension, ThemeDirectoryCompiler.skinExtension))
426                 return false;
427 
428             //
429             // If there is no BuildProvider registered, it's a static file, and should be copied
430             //
431 
432             return true;
433         }
434 
LoadTypeWithChecks(string typeName, Type requiredBaseType, Type requiredBaseType2, ConfigurationElement elem, string propertyName)435         internal static Type LoadTypeWithChecks(string typeName, Type requiredBaseType, Type requiredBaseType2, ConfigurationElement elem, string propertyName) {
436             Type t = ConfigUtil.GetType(typeName, propertyName, elem);
437 
438             if (requiredBaseType2 == null) {
439                 ConfigUtil.CheckAssignableType(requiredBaseType, t, elem, propertyName);
440             }
441             else {
442                 ConfigUtil.CheckAssignableType(requiredBaseType, requiredBaseType2, t, elem, propertyName);
443             }
444 
445             return t;
446         }
447 
448         // Devdiv Bug 57600
449         // We need to use the constructor with ProviderOptions to get the v3.5/v4.0 compiler that was possibly set in config.
450         // We first check if there is any providerOptions and invoke the constructor if so.
451         // Otherwise, we fall back to the default constructor.
CreateCodeDomProvider(Type codeDomProviderType)452         internal static CodeDomProvider CreateCodeDomProvider(Type codeDomProviderType) {
453             CodeDomProvider codeDomProvider = CreateCodeDomProviderWithPropertyOptions(codeDomProviderType);
454             if (codeDomProvider != null) {
455                 return codeDomProvider;
456             }
457             return (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
458         }
459 
CreateCodeDomProviderNonPublic(Type codeDomProviderType)460         internal static CodeDomProvider CreateCodeDomProviderNonPublic(Type codeDomProviderType) {
461             CodeDomProvider codeDomProvider = CreateCodeDomProviderWithPropertyOptions(codeDomProviderType);
462             if (codeDomProvider != null) {
463                 return codeDomProvider;
464             }
465             return (CodeDomProvider)HttpRuntime.CreateNonPublicInstance(codeDomProviderType);
466         }
467 
468         [ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
CreateCodeDomProviderWithPropertyOptions(Type codeDomProviderType)469         private static CodeDomProvider CreateCodeDomProviderWithPropertyOptions(Type codeDomProviderType) {
470             // The following resembles the code in System.CodeDom.CompilerInfo.CreateProvider
471 
472             // Make a copy to avoid modifying the original.
473             var originalProviderOptions = GetProviderOptions(codeDomProviderType);
474             IDictionary<string, string> providerOptions = null;
475             if (originalProviderOptions != null) {
476                 providerOptions = new Dictionary<string, string>(originalProviderOptions);
477             } else {
478                 providerOptions = new Dictionary<string, string>();
479             }
480 
481             // Block CompilerDirectoryPath if we are in partial trust
482             CheckCompilerDirectoryPathAllowed(providerOptions);
483 
484             // Check whether the user supplied the compilerDirectoryPath or was it added by us
485             bool addedCompilerDirectoryPath = false;
486 
487             if (MultiTargetingUtil.IsTargetFramework20) {
488                 // If the target framework is v2.0, there won't be any codedom settings, so we need
489                 // to explicitly set the compiler to be the v2.0 compiler using compilerVersion=v2.0.
490                 providerOptions["CompilerVersion"] = "v2.0";
491             }
492             else if (MultiTargetingUtil.IsTargetFramework35) {
493                 // We need to explicitly set to v3.5, as it is possible for the
494                 // user to only have specified it for one compiler but not
495                 // the other.
496                 // Dev10 bug 809212
497                 providerOptions["CompilerVersion"] = "v3.5";
498             }
499             else {
500                 // If we are targeting 4.0 but the compiler version is less than 4.0, set it to 4.0.
501                 // This can happen if a user tries to run a 2.0/3.5 web site in a 4.0 application pool without
502                 // upgrading it, and the codedom section still has 3.5 as the compilerVersion,
503                 // so we have to set the compilerVersion to 4.0 explicitly.
504                 string version = GetCompilerVersion(codeDomProviderType);
505                 Version v = GetVersionFromVString(version);
506                 if (v != null && v < MultiTargetingUtil.Version40) {
507                     providerOptions["CompilerVersion"] = "v4.0";
508                 }
509             }
510 
511             if (providerOptions != null && providerOptions.Count > 0) {
512                 Debug.Assert(codeDomProviderType != null, "codeDomProviderType should not be null");
513                 // Check whether the codedom provider supports a constructor that takes in providerOptions.
514                 // Currently only VB and C# support providerOptions for sure, while others such as JScript might not.
515                 ConstructorInfo ci = codeDomProviderType.GetConstructor(new Type[] { typeof(IDictionary<string, string>) });
516                 CodeDomProvider provider = null;
517                 if (ci != null) {
518                     // First, obtain the language for the given codedom provider type.
519                     CodeDomProvider defaultProvider = (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
520                     string extension = defaultProvider.FileExtension;
521                     var language = CodeDomProvider.GetLanguageFromExtension(extension);
522                     // Then, use the new createProvider API to create an instance.
523                     provider = CodeDomProvider.CreateProvider(language, providerOptions);
524                 }
525                 // Restore the provider options if we previously manually added the compilerDirectoryPath.
526                 // Otherwise, we might incorrectly invalidate the compilerDirectoryPath in medium trust (Dev10 bug 550299).
527                 if (addedCompilerDirectoryPath) {
528                     providerOptions.Remove(CompilerDirectoryPath);
529                 }
530                 return provider;
531             }
532 
533             return null;
534         }
535 
536         [ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
GetProviderOptions(Type codeDomProviderType)537         internal static IDictionary<string, string> GetProviderOptions(Type codeDomProviderType) {
538             // Using reflection to get the property for the time being.
539             // This could simply return CompilerInfo.PropertyOptions if it goes public in future.
540             CodeDomProvider provider = (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
541             string extension = provider.FileExtension;
542             if (CodeDomProvider.IsDefinedExtension(extension)) {
543                 CompilerInfo ci = CodeDomProvider.GetCompilerInfo(CodeDomProvider.GetLanguageFromExtension(extension));
544                 return GetProviderOptions(ci);
545             }
546             return null;
547         }
548 
549         [ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
GetProviderOptions(CompilerInfo ci)550         private static IDictionary<string, string> GetProviderOptions(CompilerInfo ci) {
551             Debug.Assert(ci != null, "CompilerInfo ci should not be null");
552             PropertyInfo pi = ci.GetType().GetProperty("ProviderOptions",
553                 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
554             if (pi != null)
555                 return (IDictionary<string, string>)pi.GetValue(ci, null);
556             return null;
557         }
558 
559         /// <summary>
560         /// Returns the compilerVersion providerOption specified for the codedom provider type.
561         /// Returns null if the providerOption is not found.
562         /// </summary>
GetCompilerVersion(Type codeDomProviderType)563         internal static string GetCompilerVersion(Type codeDomProviderType) {
564             return GetProviderOption(codeDomProviderType, "CompilerVersion");
565         }
566 
567         /// <summary>
568         /// Returns the value of the providerOption specified for the codedom provider type.
569         /// Returns null if the providerOption is not found.
570         /// </summary>
GetProviderOption(Type codeDomProviderType, string providerOption)571         internal static string GetProviderOption(Type codeDomProviderType, string providerOption) {
572             IDictionary<string, string> providerOptions = CompilationUtil.GetProviderOptions(codeDomProviderType);
573             if (providerOptions != null) {
574                 string version;
575                 if (providerOptions.TryGetValue(providerOption, out version)) {
576                     return version;
577                 }
578             }
579             return null;
580         }
581 
582         /// <summary>
583         /// Returns true if the string matches "v3.5" exactly.
584         /// </summary>
IsCompilerVersion35(string compilerVersion)585         internal static bool IsCompilerVersion35(string compilerVersion) {
586             if (compilerVersion == "v3.5") {
587                 return true;
588             }
589             return false;
590         }
591 
592         /// <summary>
593         /// This returns true only if the codedom CompilverVersion provider option is exactly v3.5.
594         /// </summary>
IsCompilerVersion35(Type codeDomProviderType)595         internal static bool IsCompilerVersion35(Type codeDomProviderType) {
596             string compilerVersion = GetCompilerVersion(codeDomProviderType);
597             bool result = IsCompilerVersion35(compilerVersion);
598             return result;
599         }
600 
601         /// <summary>
602         /// Returns true if the codedom CompilerVersion provider option is at least v3.5.
603         /// </summary>
604         /// <param name="codeDomProviderType"></param>
605         /// <returns></returns>
IsCompilerVersion35OrAbove(Type codeDomProviderType)606         internal static bool IsCompilerVersion35OrAbove(Type codeDomProviderType) {
607             string compilerVersion = GetCompilerVersion(codeDomProviderType);
608             if (IsCompilerVersion35(compilerVersion)) {
609                 return true;
610             }
611             // The compilerVersion provider option is known to exist only for v3.5.
612             // If it does not exist, then we need to rely on the target framework version to
613             // determine whether we need to use the 2.0 or 4.0 compiler.
614             if (MultiTargetingUtil.IsTargetFramework20) {
615                 return false;
616             }
617 
618             // If it isn't 2.0 or 3.5, assume it is 4.0 and above.
619             return true;
620         }
621 
622         /// <summary>
623         /// Returns true if the codedom provider has warnAsError set to true
624         /// </summary>
WarnAsError(Type codeDomProviderType)625         internal static bool WarnAsError(Type codeDomProviderType) {
626             string value = GetProviderOption(codeDomProviderType, "WarnAsError");
627             bool result;
628             if (value != null && bool.TryParse(value, out result)) {
629                 return result;
630             }
631 
632             // Assume false if the value wasn't set
633             return false;
634         }
635 
636         // Returns the version when given string of form "v4.0"
GetVersionFromVString(string version)637         internal static Version GetVersionFromVString(string version) {
638             if (string.IsNullOrEmpty(version)) {
639                 return null;
640             }
641             Debug.Assert(version.Length > 1, "Version has invalid length");
642             return new Version(version.Substring(1));
643         }
644 
645         // Returns maximum number of concurrent compilations
646         internal static int MaxConcurrentCompilations {
647             get {
648                 if (_maxConcurrentCompilations == 0) {
649                     int maxConcurrentCompilations;
650 
651                     if (AppSettings.MaxConcurrentCompilations.HasValue && AppSettings.MaxConcurrentCompilations.Value >= 0) {
652                         maxConcurrentCompilations = AppSettings.MaxConcurrentCompilations.Value;
653                     }
654                     else {
655                         CompilationSection config = MTConfigUtil.GetCompilationAppConfig();
656                         maxConcurrentCompilations = config.MaxConcurrentCompilations;
657                     }
658 
659                     if (maxConcurrentCompilations <= 0) {
660                         maxConcurrentCompilations = Environment.ProcessorCount;
661                     }
662 
663                     Interlocked.CompareExchange(ref _maxConcurrentCompilations, maxConcurrentCompilations, 0);
664                 }
665 
666                 return _maxConcurrentCompilations;
667             }
668         }
669     }
670 }
671