/// <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; }
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); }