private async Task <Compilation> BuildDeclarationCompilationFromInProgressAsync(
                Solution solution, InProgressState state, Compilation inProgressCompilation, CancellationToken cancellationToken)
            {
                try
                {
                    Contract.Requires(inProgressCompilation != null);
                    var intermediateProjects = state.IntermediateProjects;

                    while (intermediateProjects.Length > 0)
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        var intermediateProject = intermediateProjects[0];
                        var inProgressProject   = intermediateProject.Item1;
                        var action = intermediateProject.Item2;

                        inProgressCompilation = await action.InvokeAsync(inProgressCompilation, cancellationToken).ConfigureAwait(false);

                        intermediateProjects = intermediateProjects.RemoveAt(0);

                        this.WriteState(State.Create(
                                            this.Retain(solution, inProgressCompilation),
                                            intermediateProjects));
                    }

                    return(inProgressCompilation);
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }
            /// <summary>
            /// Gets a metadata reference to the metadata-only-image corresponding to the compilation.
            /// </summary>
            private async Task <MetadataReference> GetMetadataOnlyImageReferenceAsync(
                Solution solution, ProjectReference projectReference, CancellationToken cancellationToken)
            {
                try
                {
                    using (Logger.LogBlock(FunctionId.Workspace_SkeletonAssembly_GetMetadataOnlyImage, cancellationToken))
                    {
                        var projectId = this.ProjectState.Id;
                        var version   = await this.GetDependentSemanticVersionAsync(solution, cancellationToken).ConfigureAwait(false);

                        // get or build compilation up to decleration state. this compilation will be used to provide live xml doc comment
                        var declarationCompilation = await this.GetOrBuildDeclarationCompilationAsync(solution, cancellationToken : cancellationToken).ConfigureAwait(false);

                        MetadataReference reference;
                        if (!MetadataOnlyReference.TryGetReference(solution, projectReference, declarationCompilation, version, out reference))
                        {
                            // using async build lock so we don't get multiple consumers attempting to build metadata-only images for the same compilation.
                            using (await this.buildLock.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
                            {
                                // okay, we still don't have one. bring the compilation to final state since we are going to use it to create skeleton assembly
                                var compilation = await this.GetOrBuildCompilationAsync(solution, lockGate : false, cancellationToken : cancellationToken).ConfigureAwait(false);

                                reference = MetadataOnlyReference.GetOrBuildReference(solution, projectReference, compilation, version, cancellationToken);
                            }
                        }

                        return(reference);
                    }
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }
            // Add all appropriate references to the compilation and set it as our final compilation
            // state.
            private async Task <Compilation> FinalizeCompilationAsync(
                Solution solution,
                Compilation compilation,
                CancellationToken cancellationToken)
            {
                try
                {
                    var newReferences = new List <MetadataReference>();
                    newReferences.AddRange(this.ProjectState.MetadataReferences);

                    foreach (var projectReference in this.ProjectState.ProjectReferences)
                    {
                        var referencedProject = solution.GetProject(projectReference.ProjectId);

                        // Even though we're creating a final compilation (vs. an in progress compilation),
                        // it's possible that the target project has been removed.
                        if (referencedProject != null)
                        {
                            // If both projects are submissions, we'll count this as a previous submission link
                            // instead of a regular metadata reference
                            if (referencedProject.IsSubmission)
                            {
                                // if the referenced project is a submission project must be a submission as well:
                                Debug.Assert(this.ProjectState.IsSubmission);

                                var previousSubmissionCompilation =
                                    await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false);

                                compilation = compilation.WithPreviousSubmission(previousSubmissionCompilation);
                            }
                            else
                            {
                                var metadataReference = await solution.GetMetadataReferenceAsync(
                                    projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false);

                                // The compilation doesn't want to receive a null entry in the set
                                // of references it is constructed with. A reference can fail to be
                                // created if a skeleton assembly could not be constructed.
                                if (metadataReference != null)
                                {
                                    newReferences.Add(metadataReference);
                                }
                            }
                        }
                    }

                    if (!Enumerable.SequenceEqual(compilation.References, newReferences))
                    {
                        compilation = compilation.WithReferences(newReferences);
                    }

                    this.WriteState(new FinalState(this.Retain(solution, compilation)));

                    return(compilation);
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }
Esempio n. 4
0
        /// <summary>
        /// Get the text changes between this document and a prior version of the same document.
        /// The changes, when applied to the text of the old document, will produce the text of the current document.
        /// </summary>
        public async Task <IEnumerable <TextChange> > GetTextChangesAsync(Document oldDocument, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                using (Logger.LogBlock(FeatureId.Document, FunctionId.Document_GetTextChanges, this.Name, cancellationToken))
                {
                    if (oldDocument == this)
                    {
                        // no changes
                        return(SpecializedCollections.EmptyEnumerable <TextChange>());
                    }

                    if (this.Id != oldDocument.Id)
                    {
                        throw new ArgumentException(WorkspacesResources.DocumentVersionIsDifferent);
                    }

                    // first try to see if text already knows its changes
                    IList <TextChange> textChanges = null;

                    SourceText text;
                    SourceText oldText;
                    if (this.TryGetText(out text) && oldDocument.TryGetText(out oldText))
                    {
                        if (text == oldText)
                        {
                            return(SpecializedCollections.EmptyEnumerable <TextChange>());
                        }

                        var container = text.Container;
                        if (container != null)
                        {
                            textChanges = text.GetTextChanges(oldText).ToList();

                            // if changes are significant (not the whole document being replaced) then use these changes
                            if (textChanges.Count > 1 || (textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldText.Length)))
                            {
                                return(textChanges);
                            }
                        }
                    }

                    // get changes by diffing the trees
                    SyntaxTree tree = await this.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                    SyntaxTree oldTree = await oldDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                    return(tree.GetChanges(oldTree));
                }
            }
            catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
        }
            private async Task <Compilation> GetOrBuildDeclarationCompilationAsync(Solution solution, CancellationToken cancellationToken)
            {
                try
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    using (await this.buildLock.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
                    {
                        var state = this.ReadState();

                        // we are already in the final stage. just return it.
                        var compilation = state.FinalCompilation.GetValue(cancellationToken);
                        if (compilation != null)
                        {
                            return(compilation);
                        }

                        compilation = state.Compilation.GetValue(cancellationToken);
                        if (compilation == null)
                        {
                            // let's see whether we have declaration only compilation
                            if (state.DeclarationOnlyCompilation != null)
                            {
                                // okay, move to full declaration state. do this so that declaration only compilation never
                                // realize symbols.
                                this.WriteState(new FullDeclarationState(this.Retain(solution, state.DeclarationOnlyCompilation)));
                                return(state.DeclarationOnlyCompilation);
                            }

                            // We've got nothing.  Build it from scratch :(
                            return(await BuildDeclarationCompilationFromScratchAsync(solution, cancellationToken).ConfigureAwait(false));
                        }
                        else if (state is FullDeclarationState)
                        {
                            // we have full declaration, just use it.
                            return(state.Compilation.GetValue(cancellationToken));
                        }
                        else if (state is InProgressState)
                        {
                            // We have an in progress compilation.  Build off of that.
                            return(await BuildDeclarationCompilationFromInProgressAsync(solution, state as InProgressState, compilation, cancellationToken).ConfigureAwait(false));
                        }
                        else
                        {
                            throw Contract.Unreachable;
                        }
                    }
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }
            private async Task <Compilation> BuildFinalStateFromInProgressStateAsync(
                Solution solution, InProgressState state, Compilation inProgressCompilation, CancellationToken cancellationToken)
            {
                try
                {
                    var compilation = await BuildDeclarationCompilationFromInProgressAsync(solution, state, inProgressCompilation, cancellationToken).ConfigureAwait(false);

                    return(await FinalizeCompilationAsync(solution, compilation, cancellationToken).ConfigureAwait(false));
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }
Esempio n. 7
0
        /// <summary>
        /// Gets the semantic model for this document asynchronously.
        /// </summary>
        public async Task <SemanticModel> GetSemanticModelAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                if (!this.SupportsSemanticModel)
                {
                    return(null);
                }

                SemanticModel semanticModel;
                if (this.TryGetSemanticModel(out semanticModel))
                {
                    return(semanticModel);
                }

                var syntaxTree = await this.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                var compilation = await this.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

                var result = compilation.GetSemanticModel(syntaxTree);
                Contract.ThrowIfNull(result);

                // first try set the cache if it has not been set
                var original = Interlocked.CompareExchange(ref this.model, new WeakReference <SemanticModel>(result), null);

                // okay, it is first time.
                if (original == null)
                {
                    return(result);
                }

                // it looks like someone has set it. try to reuse same semantic model
                if (original.TryGetTarget(out semanticModel))
                {
                    return(semanticModel);
                }

                // it looks like cache is gone. reset the cache.
                original.SetTarget(result);
                return(result);
            }
            catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
        }
            private async Task <Compilation> GetOrBuildCompilationAsync(
                Solution solution,
                bool lockGate,
                CancellationToken cancellationToken)
            {
                try
                {
                    using (Logger.LogBlock(FunctionId.Workspace_Project_CompilationTracker_BuildCompilationAsync,
                                           logBuildCompilationAsync, this.ProjectState, cancellationToken))
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        var state = this.ReadState();

                        // Try to get the built compilation.  If it exists, then we can just return that.
                        var finalCompilation = state.FinalCompilation.GetValue(cancellationToken);
                        if (finalCompilation != null)
                        {
                            return(finalCompilation);
                        }

                        // Otherwise, we actually have to build it.  Ensure that only one thread is trying to
                        // build this compilation at a time.
                        if (lockGate)
                        {
                            using (await this.buildLock.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
                            {
                                return(await BuildCompilationAsync(solution, cancellationToken).ConfigureAwait(false));
                            }
                        }
                        else
                        {
                            return(await BuildCompilationAsync(solution, cancellationToken).ConfigureAwait(false));
                        }
                    }
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }
            private async Task <Compilation> BuildDeclarationCompilationFromScratchAsync(
                Solution solution, CancellationToken cancellationToken)
            {
                try
                {
                    var compilation = CreateEmptyCompilation(solution);

                    foreach (var document in this.ProjectState.OrderedDocumentStates)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        compilation = compilation.AddSyntaxTrees(await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false));
                    }

                    this.WriteState(new FullDeclarationState(this.Retain(solution, compilation)));
                    return(compilation);
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }
        public void TestExecuteWithErrorReportingWithSuppressFailFast()
        {
            bool finallyExecuted = false;

            Action a = () =>
            {
                try
                {
                    throw new ArgumentOutOfRangeException();
                }
                finally
                {
                    finallyExecuted = true;
                }
            };

            try
            {
                using (ExceptionHelpers.SuppressFailFast())
                {
                    try
                    {
                        a();
                    }
                    catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                        {
                            throw ExceptionUtilities.Unreachable;
                        }

                    Assert.True(false, "Should not get here because an exception should be thrown before this point.");
                }
            }
            catch (ArgumentOutOfRangeException)
            {
                Assert.True(finallyExecuted);
            }

            Assert.False(ExceptionHelpers.IsFailFastSuppressed());
        }
        public void TestExecuteWithErrorReportingThrowOperationCanceledException()
        {
            bool finallyExecuted = false;

            Action a = () =>
            {
                try
                {
                    throw new OperationCanceledException();
                }
                finally
                {
                    finallyExecuted = true;
                }
            };

            try
            {
                try
                {
                    a();
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }

                Assert.True(false, "Should not get here because an exception should be thrown before this point.");
            }
            catch (OperationCanceledException)
            {
                Assert.True(finallyExecuted);
                return;
            }

            Assert.True(false, "Should have returned in the catch block before this point.");
        }
            /// <summary>
            /// Get a metadata reference to this compilation info's compilation with respect to
            /// another project. For cross language references produce a skeletal assembly. If the
            /// compilation is not available, it is built. If a skeletal assembly reference is
            /// needed and does not exist, it is also built.
            /// </summary>
            internal async Task <MetadataReference> GetMetadataReferenceAsync(
                Solution solution,
                ProjectState fromProject,
                ProjectReference projectReference,
                CancellationToken cancellationToken)
            {
                try
                {
                    Compilation compilation;

                    // if we already have the compilation and its right kind then use it.
                    if (this.ProjectState.LanguageServices == fromProject.LanguageServices &&
                        this.TryGetCompilation(out compilation))
                    {
                        return(compilation.ToMetadataReference(projectReference.Aliases, projectReference.EmbedInteropTypes));
                    }

                    // If same language then we can wrap the other project's compilation into a compilation reference
                    if (this.ProjectState.LanguageServices == fromProject.LanguageServices)
                    {
                        // otherwise, base it off the compilation by building it first.
                        compilation = await this.GetCompilationAsync(solution, cancellationToken).ConfigureAwait(false);

                        return(compilation.ToMetadataReference(projectReference.Aliases, projectReference.EmbedInteropTypes));
                    }
                    else
                    {
                        // otherwise get a metadata only image reference that is built by emitting the metadata from the referenced project's compilation and re-importing it.
                        return(await this.GetMetadataOnlyImageReferenceAsync(solution, projectReference, cancellationToken).ConfigureAwait(false));
                    }
                }
                catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e))
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
            }