internal void AddRecord(DisplayNodeRecord record) { Contract.Requires <FrozenObjectModificationException>(!this.IsFrozen); this.MutableRecords.Add(record); }
// TODO: Consider changing the branching into assertions where possible private void FinishDisplayGraph() { foreach (var buildNode in this.BuildGraph.Nodes) { var flowNodeInfo = this.buildToFlowNodesMap[buildNode]; if (flowNodeInfo.FlowNode != null) { var displayNode = buildNode.DisplayNode; if (displayNode == null) { continue; } if (buildNode.Operation?.Kind == SpecialOperationKind.Enter) { int valAssignOffset = 0; int refAssignOffset = 0; ParameterListSyntax parameterList = ((ParameterListSyntax)buildNode.Syntax); var thisParam = this.BuildGraph.Variables.FirstOrDefault(v => v.Origin == VariableOrigin.This); if (thisParam != null) { // Instance methods have the instance reference as the first parameter var record = new DisplayNodeRecord( flowNodeInfo.FlowNode, new TextSpan(parameterList.OpenParenToken.Span.End, 0), refAssignOffset, this.BuildGraph.LocalInstanceModel.Type, thisParam.DisplayName); displayNode.AddRecord(record); refAssignOffset++; } foreach (var parameterSyntax in parameterList.Parameters) { string parameterName = parameterSyntax.Identifier.Text; var parameterModel = (from kvp in this.BuildGraph.DefinedVariableModels where kvp.Key.Kind == SymbolKind.Parameter && kvp.Key.Name == parameterName select kvp.Value).FirstOrDefault(); if (parameterModel != null) { bool isRefModel = parameterModel.Factory.ValueKind == ValueModelKind.Reference; var record = new DisplayNodeRecord( flowNodeInfo.FlowNode, parameterSyntax.Span, isRefModel ? valAssignOffset : refAssignOffset, parameterModel.Type, parameterSyntax.Identifier.Text); displayNode.AddRecord(record); if (isRefModel) { refAssignOffset += parameterModel.AssignmentLeft.Count; } else { valAssignOffset += parameterModel.AssignmentLeft.Count; } } } } else { // Offset and type int assignmentOffset = -1; int operationOffset = -1; ITypeSymbol type = null; string variableName = null; if (buildNode.VariableModel != null) { assignmentOffset = flowNodeInfo.AssignmentOffset; operationOffset = flowNodeInfo.OperationOffset; type = buildNode.VariableModel.Type; if (buildNode.VariableModel.AssignmentLeft.FirstOrDefault() is BuildVariable buildVar && buildVar.Origin != VariableOrigin.Temporary) { variableName = buildVar.DisplayName; } } else if (buildNode.Operation?.Kind == SpecialOperationKind.FieldWrite) { assignmentOffset = flowNodeInfo.AssignmentOffset; operationOffset = flowNodeInfo.OperationOffset; type = ((HeapOperation)buildNode.Operation).Reference.Type; } var record = new DisplayNodeRecord(flowNodeInfo.FlowNode, buildNode.Label.Span, assignmentOffset, type, variableName, operationOffset); displayNode.AddRecord(record); } foreach (var buildEdge in buildNode.OutgoingEdges) { if (buildEdge.To.DisplayNode == null) { continue; } // Prevent self loops and multiple edges var targetDisplayNode = buildEdge.To.DisplayNode; if (displayNode != targetDisplayNode && !displayNode.OutgoingEdges.Any(displayEdge => displayEdge.To == targetDisplayNode)) { // TODO: Edge label this.DisplayGraph.AddEdge(displayNode, targetDisplayNode); } //var targetFlowNodeInfo = this.buildToFlowNodesMap[buildEdge.To]; //if (targetFlowNodeInfo.FlowNode != null) //{ // // We cannot step into the middle of the CFG node // Contract.Assert(targetFlowNodeInfo.AssignmentOffset == 0); //} } } } this.DisplayGraph.Freeze(); }