//------------------------------------------------------------------------------
// <copyright file="BuildProvidersCompiler.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------



namespace System.Web.Compilation {

using System;
using System.IO;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Specialized;
using System.Reflection;
using System.Globalization;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Configuration;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using System.Web.Hosting;
using System.Web.Util;
using System.Web.Caching;
using System.Web.UI;
using System.Web.Configuration;

internal class BuildProvidersCompiler {
    private ICollection _buildProviders;
    private VirtualPath _configPath;
    private bool _supportLocalization;

    // The set of assemblies that we need to reference when compiling
    private ICollection _referencedAssemblies;
    internal ICollection ReferencedAssemblies { get { return _referencedAssemblies; } }

    private AssemblyBuilder _assemblyBuilder;

    // Key: CultureName string, Value: AssemblyBuilder
    private IDictionary _satelliteAssemblyBuilders = null;

    // If this is set, we only generate the source files into this directory
    // without compiling them.
    // This is used to implement ClientBuildManager.GetCodeDirectoryInformation
    private string _generatedFilesDir;

    internal BuildProvidersCompiler(VirtualPath configPath, string outputAssemblyName) : 
        this(configPath, false, outputAssemblyName) { }

    internal BuildProvidersCompiler(VirtualPath configPath, bool supportLocalization, 
        string outputAssemblyName) {
        _configPath = configPath;
        _supportLocalization = supportLocalization;
        _compConfig = MTConfigUtil.GetCompilationConfig(_configPath);
        _referencedAssemblies = BuildManager.GetReferencedAssemblies(CompConfig);
        _outputAssemblyName = outputAssemblyName;
    }

    internal BuildProvidersCompiler(VirtualPath configPath, bool supportLocalization,
        string generatedFilesDir, int index) {
        _configPath = configPath;
        _supportLocalization = supportLocalization;
        _compConfig = MTConfigUtil.GetCompilationConfig(_configPath);
        _referencedAssemblies = BuildManager.GetReferencedAssemblies(CompConfig, index);
        _generatedFilesDir = generatedFilesDir;
    }

    // The <compilation> config section for the set of build providers that we handle
    private CompilationSection _compConfig;
    internal CompilationSection CompConfig { get { return _compConfig; } }

    private string _outputAssemblyName;
    internal string OutputAssemblyName {
        get { return _outputAssemblyName; }
    }

    private bool CbmGenerateOnlyMode {
        get { return _generatedFilesDir != null; }
    }

    internal void SetBuildProviders(ICollection buildProviders) {
        _buildProviders = buildProviders;
    }

    private void ProcessBuildProviders() {

        CompilerType compilerType = null;
        BuildProvider firstLanguageBuildProvider = null;

        // First, delete all the existing satellite assemblies of the assembly
        // we're about to build (VSWhidbey 87022) (only if it has a fixed name)
        if (OutputAssemblyName != null) {
            Debug.Assert(!CbmGenerateOnlyMode);
            StandardDiskBuildResultCache.RemoveSatelliteAssemblies(OutputAssemblyName);
        }

        // List of BuildProvider's that don't ask for a specific language
        ArrayList languageFreeBuildProviders = null;

        foreach (BuildProvider buildProvider in _buildProviders) {

            // If it's an InternalBuildProvider, give it the assembly references early on
            buildProvider.SetReferencedAssemblies(_referencedAssemblies);

            // Instruct the internal build providers to continue processing for more parse errors.
            if (!BuildManager.ThrowOnFirstParseError) {
                InternalBuildProvider provider = buildProvider as InternalBuildProvider;
                if (provider != null) {
                    provider.ThrowOnFirstParseError = false;
                }
            }

            // Get the language and culture
            CompilerType ctwp = BuildProvider.GetCompilerTypeFromBuildProvider(buildProvider);

            // Only look for a culture if we're supposed to (basically, in the resources directories)
            string cultureName = null;
            if (_supportLocalization)
                cultureName = buildProvider.GetCultureName();

            // Is it asking for a specific language?
            if (ctwp != null) {

                // If it specifies a language, it can't also have a culture
                if (cultureName != null) {
                    throw new HttpException(SR.GetString(SR.Both_culture_and_language, BuildProvider.GetDisplayName(buildProvider)));
                }

                // Do we already know the language we'll be using
                if (compilerType != null) {

                    // If it's different from the current one, fail
                    if (!ctwp.Equals(compilerType)) {
                        throw new HttpException(SR.GetString(SR.Inconsistent_language,
                            BuildProvider.GetDisplayName(buildProvider),
                            BuildProvider.GetDisplayName(firstLanguageBuildProvider)));
                    }
                }
                else {
                    // Keep track of the build provider of error handling purpose
                    firstLanguageBuildProvider = buildProvider;

                    // Keep track of the language
                    compilerType = ctwp;
                    _assemblyBuilder = compilerType.CreateAssemblyBuilder(
                        CompConfig, _referencedAssemblies, _generatedFilesDir, OutputAssemblyName);
                }
            }
            else {
                if (cultureName != null) {
                    // Ignore the culture files in generate-only mode
                    if (CbmGenerateOnlyMode)
                        continue;

                    if (_satelliteAssemblyBuilders == null) {
                        _satelliteAssemblyBuilders = new Hashtable(
                            StringComparer.OrdinalIgnoreCase);
                    }

                    // Check if we already have an assembly builder for this culture
                    AssemblyBuilder satelliteAssemblyBuilder =
                        (AssemblyBuilder) _satelliteAssemblyBuilders[cultureName];

                    // If not, create one and store it in the hashtable
                    if (satelliteAssemblyBuilder == null) {
                        satelliteAssemblyBuilder = CompilerType.GetDefaultAssemblyBuilder(
                            CompConfig, _referencedAssemblies, _configPath, OutputAssemblyName);
                        satelliteAssemblyBuilder.CultureName = cultureName;
                        _satelliteAssemblyBuilders[cultureName] = satelliteAssemblyBuilder;
                    }

                    satelliteAssemblyBuilder.AddBuildProvider(buildProvider);
                    continue;
                }

                if (_assemblyBuilder == null) {
                    // If this provider doesn't need a specific language, and we don't know
                    // the language yet, just keep track of it
                    if (languageFreeBuildProviders == null)
                        languageFreeBuildProviders = new ArrayList();
                    languageFreeBuildProviders.Add(buildProvider);
                    continue;
                }
            }

            _assemblyBuilder.AddBuildProvider(buildProvider);
        }

        // If we didn't get an AssemblyBuilder, use a default
        if (_assemblyBuilder == null && languageFreeBuildProviders != null) {
            _assemblyBuilder = CompilerType.GetDefaultAssemblyBuilder(
                CompConfig, _referencedAssemblies, _configPath,
                _generatedFilesDir, OutputAssemblyName);
        }

        // Add all the language free providers (if any) to the AssemblyBuilder
        if (_assemblyBuilder != null && languageFreeBuildProviders != null) {
            foreach (BuildProvider languageFreeBuildProvider in languageFreeBuildProviders) {
                _assemblyBuilder.AddBuildProvider(languageFreeBuildProvider);
            }
        }
    }

    internal CompilerResults PerformBuild() {

        ProcessBuildProviders();
                
        // Build all the satellite assemblies
        if (_satelliteAssemblyBuilders != null) {
            int maxConcurrent = Math.Min(_satelliteAssemblyBuilders.Count, CompilationUtil.MaxConcurrentCompilations);
            try {
                Parallel.ForEach(_satelliteAssemblyBuilders.Values.Cast<AssemblyBuilder>(),
                    new ParallelOptions { MaxDegreeOfParallelism = maxConcurrent },
                    assemblyBuilder =>
                    {
                        assemblyBuilder.Compile();
                    });
            }
            catch (AggregateException ae) {
                ExceptionDispatchInfo.Capture(ae.GetBaseException()).Throw();
            }
        }

        // Build the main assembly
        if (_assemblyBuilder != null)
            return _assemblyBuilder.Compile();

        return null;
    }

    internal void GenerateSources(out Type codeDomProviderType,
        out CompilerParameters compilerParameters) {

        ProcessBuildProviders();

        // If we didn't get an AssemblyBuilder (happens when there was nothing to build),
        // get a default one.
        if (_assemblyBuilder == null) {
            _assemblyBuilder = CompilerType.GetDefaultAssemblyBuilder(
                CompConfig, _referencedAssemblies, _configPath, 
                _generatedFilesDir, null /*outputAssemblyName*/);
        }

        codeDomProviderType = _assemblyBuilder.CodeDomProviderType;
        compilerParameters = _assemblyBuilder.GetCompilerParameters();
    }
}


/*
 * This class handles the batch compilation of one directory.  It may
 * produce several assemblies out of them, based on dependencies and language
 * differences.  All the BuildProvider's are expected to share the same
 * configuration (i.e. they live in the same directory).
 */
internal class WebDirectoryBatchCompiler {

    private DateTime _utcStart;

    // The set of assemblies that we will link with
    private ICollection _referencedAssemblies;

    // The <compilation> config section for the set of build providers that we handle
    private CompilationSection _compConfig;

    // [VirtualPathString,InternalBuildProvider]
    private IDictionary _buildProviders = new Hashtable(
        StringComparer.OrdinalIgnoreCase);

    private VirtualDirectory _vdir;
    private ArrayList[] _nonDependentBuckets;

    private bool _ignoreProvidersWithErrors;

    // The set of parser errors detected during parsing.
    private ParserErrorCollection _parserErrors;

    // The first parse exceptions thrown during parsing.
    private HttpParseException _firstException;

    internal WebDirectoryBatchCompiler(VirtualDirectory vdir) {
        _vdir = vdir;

        _utcStart = DateTime.UtcNow;

        _compConfig = MTConfigUtil.GetCompilationConfig(_vdir.VirtualPath);

        _referencedAssemblies = BuildManager.GetReferencedAssemblies(_compConfig);
    }

    internal void SetIgnoreErrors() {
        _ignoreProvidersWithErrors = true;
    }

    internal void Process() {

        AddBuildProviders(true /*retryIfDeletionHappens*/);

        // If there are no BuildProvider's, we're done
        if (_buildProviders.Count == 0)
            return;

        BuildManager.ReportDirectoryCompilationProgress(_vdir.VirtualPathObject);

        GetBuildResultDependencies();
        ProcessDependencies();

        foreach (ICollection buildProviders in _nonDependentBuckets) {
            if (!CompileNonDependentBuildProviders(buildProviders))
                break;
        }

        // Report all parse exceptions
        if (_parserErrors != null && _parserErrors.Count > 0) {
            Debug.Assert(!_ignoreProvidersWithErrors);

            // Throw the first exception as inner exception along with the parse errors.
            HttpParseException newException = 
                new HttpParseException(_firstException.Message, _firstException, _firstException.VirtualPath,
                    _firstException.Source, _firstException.Line);

            // Add the rest of the parser errors to the exception.
            // The first one is already added.
            for (int i = 1; i < _parserErrors.Count; i++) {
                newException.ParserErrors.Add(_parserErrors[i]);
            }

            // rethrow the new exception
            throw newException;
        }
    }

    private void AddBuildProviders(bool retryIfDeletionHappens) {

        DiskBuildResultCache.ResetAssemblyDeleted();

        foreach (VirtualFile vfile in _vdir.Files) {

            // If it's already built and up to date, skip it
            BuildResult result = null;
            try {
                result = BuildManager.GetVPathBuildResultFromCache(vfile.VirtualPathObject);
            }
            catch {
                // Ignore the cached error in batch compilation mode, since we want to compile
                // as many files as possible.
                // But don't ignore it in CBM or precompile cases, since we always want to try
                // to compile everything that had failed before.
                if (!BuildManager.PerformingPrecompilation) {
                    // Skip it if an exception occurs (e.g. if a compile error was cached for it)
                    continue;
                }
            }

            if (result != null)
                continue;
            
            BuildProvider buildProvider = BuildManager.CreateBuildProvider(vfile.VirtualPathObject,
                _compConfig, _referencedAssemblies, false /*failIfUnknown*/);

            // Non-supported file type
            if (buildProvider == null)
                continue;

            // IgnoreFileBuildProvider's should never be created
            Debug.Assert(!(buildProvider is IgnoreFileBuildProvider));

            _buildProviders[vfile.VirtualPath] = buildProvider;
        }

        // If an assembly had to be deleted/renamed as a result of calling GetVPathBuildResultFromCache,
        // me way need to run the AddBuildProviders logic again.  The reason is that as a result of
        // deleting the assembly, we may have invalidated other BuildResult that we had earlier found
        // to be up to date (VSWhidbey 269297)
        if (DiskBuildResultCache.InUseAssemblyWasDeleted) {
            Debug.Assert(retryIfDeletionHappens);

            // Only retry if we're doing precompilation.  For standard batching, we can live
            // with the fact that not everything will be built after we're done (and we want to
            // be done as quickly as possible since the user is waiting).
            if (retryIfDeletionHappens && BuildManager.PerformingPrecompilation) {
                Debug.Trace("WebDirectoryBatchCompiler", "Rerunning AddBuildProviders for '" +
                    _vdir.VirtualPath + "' because an assembly was out of date.");

                // Pass false for retryIfDeletionHappens to make sure we don't get in an
                // infinite recursion.
                AddBuildProviders(false /*retryIfDeletionHappens*/);
            }
        }
    }

    private void CacheAssemblyResults(AssemblyBuilder assemblyBuilder, CompilerResults results) {

        foreach (BuildProvider buildProvider in assemblyBuilder.BuildProviders) {

            BuildResult result = buildProvider.GetBuildResult(results);

            // If the provider didn't produce anything, ignore it
            if (result == null)
                continue;

            // If CacheVPathBuildResult returns false, something was found to be invalidated
            // and we need to abort the caching (VSWhidbey 578372)
            if (!BuildManager.CacheVPathBuildResult(buildProvider.VirtualPathObject, result, _utcStart))
                break;

#if DBG
            if (results != null) {
                if (DelayLoadType.Enabled) {
                    Debug.Trace("BuildManager", buildProvider.VirtualPath + " Delay Load Assembly");
                } else {
                    Debug.Trace("BuildManager", buildProvider.VirtualPath + results.CompiledAssembly.EscapedCodeBase);
                }
            }
            else {
                Debug.Trace("BuildManager", buildProvider.VirtualPath + ": no assembly");
            }
#endif
        }
    }

    // Cache the various compile errors found during batching
    private void CacheCompileErrors(AssemblyBuilder assemblyBuilder, CompilerResults results) {

        BuildProvider previous = null;

        // Go through all the compile errors
        foreach (CompilerError error in results.Errors) {

            // Skip warnings
            if (error.IsWarning)
                continue;

            // Try to map the error back to a BuildProvider.  If we can't, skip the error.
            BuildProvider buildProvider = assemblyBuilder.GetBuildProviderFromLinePragma(error.FileName);
            if (buildProvider == null)
                continue;

            // Only cache the error for template controls.  Otherwise, for file types like
            // asmx/ashx, it's too likely that two of them define the same class.
            if (!(buildProvider is BaseTemplateBuildProvider))
                continue;

            // If the error is for the same page as the previous one, ignore it
            if (buildProvider == previous)
                continue;
            previous = buildProvider;

            // Create a new CompilerResults for this error
            CompilerResults newResults = new CompilerResults(null /*tempFiles*/);

            // Copy all the output to the new result.  Note that this will include all the
            // error lines, not just the ones for this BuildProvider.  But that's not a big deal,
            // and we can't easily filter the output here.
            foreach (string s in results.Output)
                newResults.Output.Add(s);

            // Copy various other fields to the new CompilerResults object
            newResults.PathToAssembly = results.PathToAssembly;
            newResults.NativeCompilerReturnValue = results.NativeCompilerReturnValue;

            // Add this error.  It will be the only one in the CompilerResults object.
            newResults.Errors.Add(error);

            // Create a new HttpCompileException & BuildResultCompileError to wrap this error
            HttpCompileException e = new HttpCompileException(newResults,
                assemblyBuilder.GetGeneratedSourceFromBuildProvider(buildProvider));
            BuildResult result = new BuildResultCompileError(buildProvider.VirtualPathObject, e);

            // Add the dependencies to the compile error build provider, so that
            // we will retry compilation when a dependency changes
            buildProvider.SetBuildResultDependencies(result);

            // Cache it
            BuildManager.CacheVPathBuildResult(buildProvider.VirtualPathObject, result, _utcStart);
        }

    }

    private void GetBuildResultDependencies() {
        foreach (BuildProvider buildProvider in _buildProviders.Values) {
            ICollection virtualPathDependencies = buildProvider.GetBuildResultVirtualPathDependencies();
            if (virtualPathDependencies == null)
                continue;

            foreach (string virtualPathDependency in virtualPathDependencies) {
                BuildProvider dependentBuildProvider = (BuildProvider) _buildProviders[virtualPathDependency];

                if (dependentBuildProvider != null)
                    buildProvider.AddBuildProviderDependency(dependentBuildProvider);
            }
        }
    }

    // Split the providers into non dependent buckets
    private void ProcessDependencies() {
        // First phase: compute levels in the dependency tree

        int totaldepth = 0;
        Hashtable depth = new Hashtable();
        Stack stack = new Stack();

        // compute depths
        foreach (BuildProvider buildProvider in _buildProviders.Values) {
            stack.Push(buildProvider);

            while (stack.Count > 0) {
                BuildProvider curnode = (BuildProvider)stack.Peek();

                bool recurse = false;
                int maxdepth = 0;

                if (curnode.BuildProviderDependencies != null) {
                    foreach (BuildProvider child in curnode.BuildProviderDependencies) {

                        if (depth.ContainsKey(child)) {
                            if (maxdepth <= (int)depth[child])
                                maxdepth = (int)depth[child] + 1;
                            else if ((int)depth[child] == -1)
                                throw new HttpException(SR.GetString(SR.File_Circular_Reference, child.VirtualPath));
                        }
                        else {
                            recurse = true;
                            stack.Push(child);
                        }
                    }
                }

                if (recurse)
                    depth[curnode] = -1; // being computed;
                else {
                    stack.Pop();
                    depth[curnode] = maxdepth;
                    if (totaldepth <= maxdepth)
                        totaldepth = maxdepth + 1;
                }
            }
        }

        // drop into buckets by depth
        _nonDependentBuckets = new ArrayList[totaldepth];

        for (IDictionaryEnumerator en = (IDictionaryEnumerator)depth.GetEnumerator(); en.MoveNext();) {
            int level = (int)en.Value;

            if (_nonDependentBuckets[level] == null)
                _nonDependentBuckets[level] = new ArrayList();

            _nonDependentBuckets[level].Add(en.Key);
        }

#if DBG
        int i = 0;
        foreach (ICollection buildProviders in _nonDependentBuckets) {
            Debug.Trace("BuildManager", String.Empty);
            Debug.Trace("BuildManager", "Bucket " + i + " contains " + buildProviders.Count + " files");

            foreach (BuildProvider buildProvider in buildProviders)
                Debug.Trace("BuildManager", buildProvider.VirtualPath);
            i++;
        }
#endif

    }

    private bool IsBuildProviderSkipable(BuildProvider buildProvider) {

        // If another build provider depends on it, we should not skip it
        if (buildProvider.IsDependedOn) return false;

        // No one depends on it (at least in this directory)

        // If it's a source file, skip it.  We need to do this for v1 compatibility,
        // since v1 VS projects contain many source files which have already been
        // precompiled into bin, and that should not be compiled dynamically
        if (buildProvider is SourceFileBuildProvider)
            return true;

        // For the same reason, skip resources
        if (buildProvider is ResXBuildProvider)
            return true;

        return false;
    }

    private bool CompileNonDependentBuildProviders(ICollection buildProviders) {

        // Key: CompilerType, Value: AssemblyBuilder
        IDictionary assemblyBuilders = new Hashtable();

        // List of InternalBuildProvider's that don't ask for a specific language
        ArrayList languageFreeBuildProviders = null;

        // AssemblyBuilder used for providers that don't need a specific language
        AssemblyBuilder defaultAssemblyBuilder = null;

        bool hasParserErrors = false;

        foreach (BuildProvider buildProvider in buildProviders) {

            if (IsBuildProviderSkipable(buildProvider))
                continue;

            // Instruct the internal build providers to continue processing for more parse errors.
            if (!BuildManager.ThrowOnFirstParseError) {
                InternalBuildProvider provider = buildProvider as InternalBuildProvider;
                if (provider != null) {
                    provider.ThrowOnFirstParseError = false;
                }
            }

            CompilerType compilerType = null;

            // Get the language
            try {
                compilerType = BuildProvider.GetCompilerTypeFromBuildProvider(
                    buildProvider);
            }
            catch (HttpParseException ex) {
                // Ignore the error if we are in that mode.
                if (_ignoreProvidersWithErrors) {
                    continue;
                }

                hasParserErrors = true;

                // Remember the first parse exception
                if (_firstException == null) {
                    _firstException = ex;
                }

                if (_parserErrors == null) {
                    _parserErrors = new ParserErrorCollection();
                }

                _parserErrors.AddRange(ex.ParserErrors);

                continue;
            }
            catch {
                // Ignore the error if we are in that mode.
                if (_ignoreProvidersWithErrors) {
                    continue;
                }

                throw;
            }

            AssemblyBuilder assemblyBuilder = defaultAssemblyBuilder;
            ICollection typeNames = buildProvider.GetGeneratedTypeNames();

            // Is it asking for a specific language?
            if (compilerType == null) {
                // If this provider doesn't need a specific language, and we haven't yet created
                // a default builder that is capable of building this, just keep track of it                
                if (defaultAssemblyBuilder == null || defaultAssemblyBuilder.IsBatchFull ||
                    defaultAssemblyBuilder.ContainsTypeNames(typeNames)) {
                    if (languageFreeBuildProviders == null) {
                        languageFreeBuildProviders = new ArrayList();
                    }

                    languageFreeBuildProviders.Add(buildProvider);
                    continue;
                }
            }
            else {
                // Check if we already have an assembly builder of the right type
                assemblyBuilder = (AssemblyBuilder)assemblyBuilders[compilerType];
            }

            // Starts a new assemblyBuilder if the old one already contains another buildprovider 
            // that uses the same type name
            if (assemblyBuilder == null || assemblyBuilder.IsBatchFull ||
                assemblyBuilder.ContainsTypeNames(typeNames)) {

                // If the assemblyBuilder is full, compile it.
                if (assemblyBuilder != null) {
                    CompileAssemblyBuilder(assemblyBuilder);
                }

                AssemblyBuilder newBuilder = compilerType.CreateAssemblyBuilder(
                    _compConfig, _referencedAssemblies);

                assemblyBuilders[compilerType] = newBuilder;

                // Remember it as the default if we don't already have one,
                // or if the default is already full, switch the default to the new one.
                if (defaultAssemblyBuilder == null ||
                    defaultAssemblyBuilder == assemblyBuilder) {

                    defaultAssemblyBuilder = newBuilder;
                }

                assemblyBuilder = newBuilder;
            }

            assemblyBuilder.AddTypeNames(typeNames);
            assemblyBuilder.AddBuildProvider(buildProvider);
        }

        // Don't try to compile providers, otherwise compile exceptions will be bubbled up,
        // and we lose the parse errors.
        if (hasParserErrors) {
            return false;
        }

        // Handle all the left over language free providers
        if (languageFreeBuildProviders != null) {

            // Indicates whether the default assembly builder is not a language specific builder.
            bool newDefaultAssemblyBuilder = (defaultAssemblyBuilder == null);

            // Add language independent providers to the default assembly builder.
            foreach (BuildProvider languageFreeBuildProvider in languageFreeBuildProviders) {

                ICollection typeNames = languageFreeBuildProvider.GetGeneratedTypeNames();

                // If we don't have a default language assembly builder, get one or
                // starts a new assemblyBuilder if the old one already contains another buildprovider 
                // that uses the same type name
                if (defaultAssemblyBuilder == null || defaultAssemblyBuilder.IsBatchFull ||
                    defaultAssemblyBuilder.ContainsTypeNames(typeNames)) {

                    // If the default assemblyBuilder is full, compile it.
                    if (defaultAssemblyBuilder != null) {
                        CompileAssemblyBuilder(defaultAssemblyBuilder);
                    }

                    defaultAssemblyBuilder = CompilerType.GetDefaultAssemblyBuilder(
                        _compConfig, _referencedAssemblies, _vdir.VirtualPathObject /*configPath*/, 
                        null /*outputAssemblyName*/);

                    // the default assembly builder needs to be compiled separately.
                    newDefaultAssemblyBuilder = true;
                }

                defaultAssemblyBuilder.AddTypeNames(typeNames);
                defaultAssemblyBuilder.AddBuildProvider(languageFreeBuildProvider);
            }

            // Only compile the default assembly builder if it's not part of language specific
            // assembly builder (which will be compiled separately)
            if (newDefaultAssemblyBuilder) {
                // Compile the default assembly builder.
                CompileAssemblyBuilder(defaultAssemblyBuilder);
            }
        }

        CompileAssemblyBuilderParallel(assemblyBuilders.Values);
        
        return true;
    }

    private void CompileAssemblyBuilderParallel(ICollection assemblyBuilders) {

        int maxConcurrent = Math.Min(assemblyBuilders.Count, CompilationUtil.MaxConcurrentCompilations);

        if (maxConcurrent < 2) {
            // Not using Parallel.ForEach to avoid performance penalty
            foreach (AssemblyBuilder assemblyBuilder in assemblyBuilders) {
                CompileAssemblyBuilder(assemblyBuilder);
            }
        }

        else {
            // devdiv bug 666936: ASP.NET compilation related deadlock in Antares scenario. 
            // The main (current) thread holds a global compilation lock. CacheAssemblyResults and CacheCompileErrors may 
            // also require the global compilation lock in case of removing old data and thus may lead to deadlock.
            // Fix: using dictionaries to collect the build results from parallel threads and do caching in the main thread.
            ConcurrentDictionary<AssemblyBuilder, CompilerResults> buildResults = new ConcurrentDictionary<AssemblyBuilder, CompilerResults>();
            ConcurrentDictionary<AssemblyBuilder, CompilerResults> buildErrors = new ConcurrentDictionary<AssemblyBuilder, CompilerResults>();
            
            try {
                Parallel.ForEach(assemblyBuilders.Cast<AssemblyBuilder>(),
                    new ParallelOptions { MaxDegreeOfParallelism = maxConcurrent },
                    builder =>
                    {
                        CompilerResults results;
                        try {
                            results = builder.Compile();
                        }
                        catch (HttpCompileException e) { 
                            buildErrors[builder] = e.Results;                            
                            throw;
                        }                        
                        buildResults[builder] = results;                        
                    });
            }
            catch (AggregateException e) {
                ExceptionDispatchInfo.Capture(e.GetBaseException()).Throw();
            }
            finally {
                // Before throwing the aggregated compilation exception, cache the build results first
                // This follows the execution order for the single thread case
                foreach (var pair in buildErrors) {
                    CacheCompileErrors(pair.Key, pair.Value);
                }
                foreach (var pair in buildResults) {
                    CacheAssemblyResults(pair.Key, pair.Value);
                }                
            }
        }
    }

    private void CompileAssemblyBuilder(AssemblyBuilder builder) {

        CompilerResults results;

        try {
            results = builder.Compile();
        }
        catch (HttpCompileException e) {
            CacheCompileErrors(builder, e.Results);
            throw;
        }

        CacheAssemblyResults(builder, results);
    }
}

}
