// TODO: Implement proper exception handling private async Task <GeneratedGraphs> GenerateGraphsImpl(MethodLocation location, FlowGraphId graphId) { var declarationLocation = location.Method.Locations.FirstOrDefault(); Contract.Assert(declarationLocation != null); Contract.Assert(declarationLocation.IsInSource); var root = declarationLocation.SourceTree.GetRoot(); var methodSyntax = root.FindNode(declarationLocation.SourceSpan) as BaseMethodDeclarationSyntax; Contract.Assert(methodSyntax != null); // TODO: Handle the continuation in a logic way var document = this.Solution.GetDocument(root.SyntaxTree); var semanticModel = document.GetSemanticModelAsync().Result; var builder = new CSharpGraphBuilder( this.ModelManager, document.Id, semanticModel, methodSyntax); var buildGraph = await builder.BuildAsync(); var flowGraphTranslator = new FlowGraphTranslator(buildGraph, builder.DisplayGraph, graphId); var result = flowGraphTranslator.Translate(); result.Location = location; return(result); }
private async Task <GeneratedGraphs> LazyGenerateGraphsAsync(MethodLocation location) { FlowGraphId graphId; GeneratedGraphs result; if (this.symbolsToGraphIdMap.TryGetValue((location).Method, out graphId)) { result = this.generatedGraphs[graphId]; } else { graphId = this.graphIdProvider.GenerateNewId(); result = await Task.Run(() => this.GenerateGraphsImpl(location, graphId)); this.generatedGraphs[graphId] = result; this.symbolsToGraphIdMap.Add(location.Method, graphId); } return(result); }
public async Task <IReadOnlyList <OuterFlowEdge> > GetCallEdgesToAsync(EnterFlowNode enterNode) { var results = new List <OuterFlowEdge>(); var calledMethodLocation = this.GetLocation(enterNode.Graph.Id); var references = await SymbolFinder.FindCallersAsync(calledMethodLocation.Method, this.Solution); foreach (var reference in references) { Contract.Assert(reference.CalledSymbol.Equals(calledMethodLocation.Method)); var callingMethod = reference.CallingSymbol as IMethodSymbol; if (callingMethod == null) { continue; } var callingMethodLocation = new MethodLocation(callingMethod); if (!callingMethodLocation.CanBeExplored) { continue; } var graphs = await this.LazyGenerateGraphsAsync(callingMethodLocation); foreach (var callNode in graphs.FlowGraph.Nodes.OfType <CallFlowNode>()) { if (((MethodLocation)callNode.Location).Equals(calledMethodLocation)) { // TODO: Store outer edges instead of recreating them every time var callEdge = OuterFlowEdge.CreateMethodCall(new OuterFlowEdgeId(-1), callNode, enterNode); results.Add(callEdge); } } } return(results.ToArray()); }
private FlowNode TryTranslateBorderNode(BuildNode buildNode) { var borderOp = buildNode.Operation as BorderOperation; if (borderOp == null || borderOp.Kind == SpecialOperationKind.Assertion) { return(null); } if (borderOp.Kind == SpecialOperationKind.MethodCall || borderOp.Kind == SpecialOperationKind.ExceptionThrow) { MethodLocation location; IEnumerable <Expression> flowArguments; if (borderOp.Arguments.Any(arg => arg == null)) { // We cannot model method calls without properly modelling all their arguments first location = new MethodLocation(borderOp.Method, isExplorationDisabled: true); flowArguments = Enumerable.Empty <Expression>(); } else { // TODO: Enable a configurable and extensible approach instead of this hack // Disable exploring the methods from the tool evaluation bool isExplorationDisabled = borderOp.Method.ContainingType.ToString() == "EvaluationTests.Annotations.Evaluation"; location = new MethodLocation(borderOp.Method, isExplorationDisabled); var buildArguments = borderOp.Arguments.SelectMany(typeModel => typeModel.AssignmentRight); flowArguments = buildArguments.Select(expression => this.TranslateExpression(expression)); } if (borderOp.Kind == SpecialOperationKind.MethodCall) { var returnAssignments = buildNode.VariableModel?.AssignmentLeft .Select(buildVar => this.TranslateVariable(buildVar)); // We don't allow calling base constructors, so the only way to call it is with the "new" operator // TODO: Propagate the information about constructor call other way when the above is supported var callKind = (borderOp.Method.MethodKind == MethodKind.Constructor) ? CallKind.ObjectCreation : borderOp.Method.IsStatic ? CallKind.Static : CallKind.Instance; bool isObjectCreation = (borderOp.Method.MethodKind == MethodKind.Constructor); return(this.builder.AddCallNode(location, flowArguments, returnAssignments, callKind, buildNode.Flags)); } else { Contract.Assert(borderOp.Kind == SpecialOperationKind.ExceptionThrow); return(this.builder.AddThrowExceptionNode(location, flowArguments, buildNode.Flags)); } } else { Contract.Assert(borderOp.Kind == SpecialOperationKind.Return); var returnValues = buildNode.ValueModel?.AssignmentRight .Select(expression => this.TranslateExpression(expression)) .ToImmutableArray(); if ((returnValues == null || returnValues.Value.Length == 0) && this.BuildGraph.MethodSyntax.Kind() == SyntaxKind.ConstructorDeclaration) { // A constructor returns "this" variable by convention var buildThis = this.BuildGraph.Variables.First(v => v.Origin == VariableOrigin.This); returnValues = ImmutableArray.Create((Expression)this.TranslateVariable(buildThis)); } return(this.builder.AddReturnNode(returnValues, buildNode.Flags)); } }
// TODO: Consider implementing also == operator public bool Equals(MethodLocation other) { return(this.Method.Equals(other.Method)); }