/// <summary>
        /// Returns the state of the changes made to the source. 
        /// The EnC manager calls this to determine whether there are any changes to the source 
        /// and if so whether there are any rude edits.
        /// </summary>
        public int GetENCBuildState(ShellInterop.ENC_BUILD_STATE[] pENCBuildState)
        {
            try
            {
                using (NonReentrantContext)
                {
                    Debug.Assert(pENCBuildState != null && pENCBuildState.Length == 1);

                    // GetENCBuildState is called outside of edit session (at least) in following cases:
                    // 1) when the debugger is determining whether a source file checksum matches the one in PDB.
                    // 2) when the debugger is setting the next statement and a change is pending
                    //    See CDebugger::SetNextStatement(CTextPos* pTextPos, bool WarnOnFunctionChange):
                    // 
                    //    pENC2->ExitBreakState();
                    //    >>> hr = GetCodeContextOfPosition(pTextPos, &pCodeContext, &pProgram, true, true);
                    //    pENC2->EnterBreakState(m_pSession, GetEncBreakReason());
                    //
                    // The debugger seem to expect ENC_NOT_MODIFIED in these cases, otherwise errors occur.

                    if (_changesApplied || _encService.EditSession == null)
                    {
                        _lastEditSessionSummary = ProjectAnalysisSummary.NoChanges;
                    }
                    else
                    {
                        // Fetch the latest snapshot of the project and get an analysis summary for any changes 
                        // made since the break mode was entered.
                        var currentProject = _vsProject.VisualStudioWorkspace.CurrentSolution.GetProject(_vsProject.Id);
                        if (currentProject == null)
                        {
                            // If the project has yet to be loaded into the solution (which may be the case,
                            // since they are loaded on-demand), then it stands to reason that it has not yet
                            // been modified.
                            // TODO (https://github.com/dotnet/roslyn/issues/1204): this check should be unnecessary.
                            _lastEditSessionSummary = ProjectAnalysisSummary.NoChanges;
                            log.Write($"Project '{_vsProject.DisplayName}' has not yet been loaded into the solution");
                        }
                        else
                        {
                            _projectBeingEmitted = currentProject;
                            _lastEditSessionSummary = GetProjectAnalysisSummary(_projectBeingEmitted);
                        }

                        _encService.EditSession.LogBuildState(_lastEditSessionSummary);
                    }

                    switch (_lastEditSessionSummary)
                    {
                        case ProjectAnalysisSummary.NoChanges:
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_NOT_MODIFIED;
                            break;

                        case ProjectAnalysisSummary.CompilationErrors:
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_COMPILE_ERRORS;
                            break;

                        case ProjectAnalysisSummary.RudeEdits:
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_NONCONTINUABLE_ERRORS;
                            break;

                        case ProjectAnalysisSummary.ValidChanges:
                        case ProjectAnalysisSummary.ValidInsignificantChanges:
                            // The debugger doesn't distinguish between these two.
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_APPLY_READY;
                            break;

                        default:
                            throw ExceptionUtilities.Unreachable;
                    }

                    log.Write("EnC state of '{0}' queried: {1}{2}",
                        _vsProject.DisplayName,
                        pENCBuildState[0],
                        _encService.EditSession != null ? "" : " (no session)");

                    return VSConstants.S_OK;
                }
            }
            catch (Exception e) when (FatalError.ReportWithoutCrash(e))
            {
                return VSConstants.E_FAIL;
            }
        }
        private void AddActiveStatements(Solution solution, ShellInterop.ENC_ACTIVE_STATEMENT[] vsActiveStatements)
        {
            Debug.Assert(_activeMethods.Count == 0);
            Debug.Assert(_exceptionRegions.Count == 0);

            foreach (var vsActiveStatement in vsActiveStatements)
            {
                log.DebugWrite("+AS[{0}]: {1} {2} {3} {4} '{5}'",
                    vsActiveStatement.id,
                    vsActiveStatement.tsPosition.iStartLine,
                    vsActiveStatement.tsPosition.iStartIndex,
                    vsActiveStatement.tsPosition.iEndLine,
                    vsActiveStatement.tsPosition.iEndIndex,
                    vsActiveStatement.filename);

                // TODO (tomat):
                // Active statement is in user hidden code. The only information that we have from the debugger
                // is the method token. We don't need to track the statement (it's not in user code anyways),
                // but we should probably track the list of such methods in order to preserve their local variables.
                // Not sure what's exactly the scenario here, perhaps modifying async method/iterator? 
                // Dev12 just ignores these.
                if (vsActiveStatement.posType != TEXT_POSITION_ACTIVE_STATEMENT)
                {
                    continue;
                }

                var flags = (ActiveStatementFlags)vsActiveStatement.ASINFO;

                // Finds a document id in the solution with the specified file path.
                DocumentId documentId = solution.GetDocumentIdsWithFilePath(vsActiveStatement.filename)
                    .Where(dId => dId.ProjectId == _vsProject.Id).SingleOrDefault();

                if (documentId != null)
                {
                    var document = solution.GetDocument(documentId);
                    Debug.Assert(document != null);

                    SourceText source = document.GetTextAsync(default(CancellationToken)).Result;
                    LinePositionSpan lineSpan = vsActiveStatement.tsPosition.ToLinePositionSpan();

                    // If the PDB is out of sync with the source we might get bad spans.
                    var sourceLines = source.Lines;
                    if (lineSpan.End.Line >= sourceLines.Count || sourceLines.GetPosition(lineSpan.End) > sourceLines[sourceLines.Count - 1].EndIncludingLineBreak)
                    {
                        log.Write("AS out of bounds (line count is {0})", source.Lines.Count);
                        continue;
                    }

                    SyntaxNode syntaxRoot = document.GetSyntaxRootAsync(default(CancellationToken)).Result;

                    var analyzer = document.Project.LanguageServices.GetService<IEditAndContinueAnalyzer>();

                    s_pendingActiveStatements.Add(new VsActiveStatement(
                        this,
                        vsActiveStatement.id,
                        document.Id,
                        new ActiveStatementSpan(flags, lineSpan)));

                    bool isLeaf = (flags & ActiveStatementFlags.LeafFrame) != 0;
                    var ehRegions = analyzer.GetExceptionRegions(source, syntaxRoot, lineSpan, isLeaf);

                    for (int i = 0; i < ehRegions.Length; i++)
                    {
                        _exceptionRegions.Add(new VsExceptionRegion(
                            vsActiveStatement.id,
                            i,
                            vsActiveStatement.methodToken,
                            ehRegions[i]));
                    }
                }

                _activeMethods.Add(vsActiveStatement.methodToken);
            }
        }
        /// <summary>
        /// Returns information about exception handlers in the source.
        /// </summary>
        /// <remarks>
        /// Called by EnC manager.
        /// </remarks>
        public int GetExceptionSpans(uint celt, ShellInterop.ENC_EXCEPTION_SPAN[] rgelt, ref uint pceltFetched)
        {
            Debug.Assert(celt == rgelt.Length);
            Debug.Assert(celt == _exceptionRegions.Count);

            for (int i = 0; i < _exceptionRegions.Count; i++)
            {
                rgelt[i] = new ShellInterop.ENC_EXCEPTION_SPAN()
                {
                    id = (uint)i,
                    methodToken = _exceptionRegions[i].MethodToken,
                    tsPosition = _exceptionRegions[i].Span.ToVsTextSpan()
                };
            }

            pceltFetched = celt;
            return VSConstants.S_OK;
        }
        /// <summary>
        /// Returns the state of the changes made to the source. 
        /// The EnC manager calls this to determine whether there are any changes to the source 
        /// and if so whether there are any rude edits.
        /// </summary>
        public int GetENCBuildState(ShellInterop.ENC_BUILD_STATE[] pENCBuildState)
        {
            try
            {
                using (NonReentrantContext)
                {
                    Debug.Assert(pENCBuildState != null && pENCBuildState.Length == 1);

                    // GetENCBuildState is called outside of edit session (at least) in following cases:
                    // 1) when the debugger is determining whether a source file checksum matches the one in PDB.
                    // 2) when the debugger is setting the next statement and a change is pending
                    //    See CDebugger::SetNextStatement(CTextPos* pTextPos, bool WarnOnFunctionChange):
                    // 
                    //    pENC2->ExitBreakState();
                    //    >>> hr = GetCodeContextOfPosition(pTextPos, &pCodeContext, &pProgram, true, true);
                    //    pENC2->EnterBreakState(m_pSession, GetEncBreakReason());

                    if (_changesApplied)
                    {
                        _lastEditSessionSummary = ProjectAnalysisSummary.NoChanges;
                    }
                    else if (_encService.EditSession != null)
                    {
                        _projectBeingEmitted = _vsProject.VisualStudioWorkspace.CurrentSolution.GetProject(_vsProject.Id);
                        _lastEditSessionSummary = GetProjectAnalysisSummary(_projectBeingEmitted);
                        _encService.EditSession.LogBuildState(_lastEditSessionSummary);
                    }

                    switch (_lastEditSessionSummary)
                    {
                        case ProjectAnalysisSummary.NoChanges:
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_NOT_MODIFIED;
                            break;

                        case ProjectAnalysisSummary.CompilationErrors:
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_COMPILE_ERRORS;
                            break;

                        case ProjectAnalysisSummary.RudeEdits:
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_NONCONTINUABLE_ERRORS;
                            break;

                        case ProjectAnalysisSummary.ValidChanges:
                        case ProjectAnalysisSummary.ValidInsignificantChanges:
                            // The debugger doesn't distinguish between these two.
                            pENCBuildState[0] = ShellInterop.ENC_BUILD_STATE.ENC_APPLY_READY;
                            break;

                        default:
                            throw ExceptionUtilities.Unreachable;
                    }

                    log.Write("EnC state of '{0}' queried: {1}{2}",
                        _vsProject.DisplayName,
                        pENCBuildState[0],
                        _encService.EditSession != null ? "" : " (no session)");

                    return VSConstants.S_OK;
                }
            }
            catch (Exception e) when(FatalError.Report(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
            }
        /// <summary>
        /// Called by the debugger when entering a Break state. 
        /// </summary>
        /// <param name="encBreakReason">Reason for transition to Break state.</param>
        /// <param name="pActiveStatements">Statements active when the debuggee is stopped.</param>
        /// <param name="cActiveStatements">Length of <paramref name="pActiveStatements"/>.</param>
        public int EnterBreakStateOnPE(Interop.ENC_BREAKSTATE_REASON encBreakReason, ShellInterop.ENC_ACTIVE_STATEMENT[] pActiveStatements, uint cActiveStatements)
        {
            try
            {
                using (NonReentrantContext)
                {
                    log.Write("Enter {2}Break Mode: project '{0}', AS#: {1}", _vsProject.DisplayName, pActiveStatements != null ? pActiveStatements.Length : -1, encBreakReason == ENC_BREAKSTATE_REASON.ENC_BREAK_EXCEPTION ? "Exception " : "");

                    Debug.Assert(cActiveStatements == (pActiveStatements != null ? pActiveStatements.Length : 0));
                    Debug.Assert(s_breakStateProjectCount < s_debugStateProjectCount);
                    Debug.Assert(s_breakStateProjectCount > 0 || _exceptionRegions.Count == 0);
                    Debug.Assert(s_breakStateProjectCount == s_breakStateEnteredProjects.Count);
                    Debug.Assert(IsDebuggable);

                    if (s_breakStateEntrySolution == null)
                    {
                        _encService.OnBeforeDebuggingStateChanged(DebuggingState.Run, DebuggingState.Break);

                        s_breakStateEntrySolution = _vsProject.VisualStudioWorkspace.CurrentSolution;

                        // TODO: This is a workaround for a debugger bug in which not all projects exit the break state.
                        // Reset the project count.
                        s_breakStateProjectCount = 0;
                    }

                    ProjectReadOnlyReason state;
                    if (pActiveStatements != null)
                    {
                        AddActiveStatements(s_breakStateEntrySolution, pActiveStatements);
                        state = ProjectReadOnlyReason.None;
                    }
                    else
                    {
                        // unfortunately the debugger doesn't provide details:
                        state = ProjectReadOnlyReason.NotLoaded;
                    }

                    // If pActiveStatements is null the EnC Manager failed to retrieve the module corresponding 
                    // to the project in the debuggee. We won't include such projects in the edit session.
                    s_breakStateEnteredProjects.Add(KeyValuePair.Create(_vsProject.Id, state));
                    s_breakStateProjectCount++;

                    // EnC service is global, but the debugger calls this for each project.
                    // Avoid starting the edit session until all projects enter break state.
                    if (s_breakStateEnteredProjects.Count == s_debugStateProjectCount)
                    {
                        Debug.Assert(_encService.EditSession == null);
                        Debug.Assert(s_pendingActiveStatements.TrueForAll(s => s.Owner._activeStatementIds.Count == 0));

                        var byDocument = new Dictionary<DocumentId, ImmutableArray<ActiveStatementSpan>>();

                        // note: fills in activeStatementIds of projects that own the active statements:
                        GroupActiveStatements(s_pendingActiveStatements, byDocument);

                        // When stopped at exception: All documents are read-only, but the files might be changed outside of VS.
                        // So we start an edit session as usual and report a rude edit for all changes we see.
                        bool stoppedAtException = encBreakReason == ENC_BREAKSTATE_REASON.ENC_BREAK_EXCEPTION;

                        var projectStates = ImmutableDictionary.CreateRange(s_breakStateEnteredProjects);

                        _encService.StartEditSession(s_breakStateEntrySolution, byDocument, projectStates, stoppedAtException);
                        _trackingService.StartTracking(_encService.EditSession);

                        s_readOnlyDocumentTracker.UpdateWorkspaceDocuments();

                        // When tracking is started the tagger is notified and the active statements are highlighted.
                        // Add the handler that notifies the debugger *after* that initial tagger notification,
                        // so that it's not triggered unless an actual change in leaf AS occurs.
                        _trackingService.TrackingSpansChanged += TrackingSpansChanged;
                    }
                }

                // The debugger ignores the result.
                return VSConstants.S_OK;
            }
            catch (Exception e) when (FatalError.ReportWithoutCrash(e))
            {
                return VSConstants.E_FAIL;
            }
            finally
            {
                // TODO: This is a workaround for a debugger bug.
                // Ensure that the state gets reset even if if `GroupActiveStatements` throws an exception.
                if (s_breakStateEnteredProjects.Count == s_debugStateProjectCount)
                {
                    // we don't need these anymore:
                    s_pendingActiveStatements.Clear();
                    s_breakStateEnteredProjects.Clear();
                    s_breakStateEntrySolution = null;
                }
            }
        }
 public int GetENCBuildState(ShellInterop.ENC_BUILD_STATE[] pENCBuildState)
 {
     return EditAndContinueImplOpt?.GetENCBuildState(pENCBuildState) ?? VSConstants.E_FAIL;
 }
 public int GetExceptionSpans(uint celt, ShellInterop.ENC_EXCEPTION_SPAN[] rgelt, ref uint pceltFetched)
 {
     return EditAndContinueImplOpt?.GetExceptionSpans(celt, rgelt, ref pceltFetched) ?? VSConstants.E_FAIL;
 }
 public int EnterBreakStateOnPE(EncInterop.ENC_BREAKSTATE_REASON encBreakReason, ShellInterop.ENC_ACTIVE_STATEMENT[] pActiveStatements, uint cActiveStatements)
 {
     return EditAndContinueImplOpt?.EnterBreakStateOnPE(encBreakReason, pActiveStatements, cActiveStatements) ?? VSConstants.S_OK;
 }
예제 #9
0
		override ModelingDocData CreateDocData(string fileName, VsShell.IVsHierarchy hierarchy, uint itemId)
		{
			return new ORMDesignerDocData(this.ServiceProvider, ORMDesignerEditorFactory.Id);
		}
 public int GetENCBuildState(ShellInterop.ENC_BUILD_STATE[] pENCBuildState)
 {
     return EditAndContinueImplOpt.GetENCBuildState(pENCBuildState);
 }