/// <summary> /// Analyzes the ownership of the given-up symbol /// in the candidate callee. /// </summary> /// <param name="givenUpSymbol">GivenUpOwnershipSymbol</param> /// <param name="calleeSummary">MethodSummary</param> /// <param name="call">ExpressionSyntax</param> /// <param name="statement">Statement</param> /// <param name="machine">StateMachine</param> /// <param name="model">SemanticModel</param> /// <param name="trace">TraceInfo</param> protected override void AnalyzeOwnershipInCandidateCallee(GivenUpOwnershipSymbol givenUpSymbol, MethodSummary calleeSummary, ExpressionSyntax call, Statement statement, StateMachine machine, SemanticModel model, TraceInfo trace) { ArgumentListSyntax argumentList = base.AnalysisContext.GetArgumentList(call); if (argumentList == null) { return; } for (int idx = 0; idx < argumentList.Arguments.Count; idx++) { var argIdentifier = base.AnalysisContext.GetRootIdentifier( argumentList.Arguments[idx].Expression); if (argIdentifier == null) { continue; } ISymbol argSymbol = model.GetSymbolInfo(argIdentifier).Symbol; if (statement.Summary.DataFlowAnalysis.FlowsIntoSymbol(argSymbol, givenUpSymbol.ContainingSymbol, statement, givenUpSymbol.Statement)) { if (calleeSummary.SideEffectsInfo.FieldFlowParamIndexes.Any(v => v.Value.Contains(idx) && base.IsFieldAccessedInSuccessor(v.Key, statement.Summary, machine))) { AnalysisErrorReporter.ReportGivenUpFieldOwnershipError(trace, argSymbol); } } } }
/// <summary> /// Analyzes the ownership of the given-up symbol /// in the call. /// </summary> /// <param name="givenUpSymbol">GivenUpOwnershipSymbol</param> /// <param name="call">ExpressionSyntax</param> /// <param name="statement">Statement</param> /// <param name="machine">StateMachine</param> /// <param name="model">SemanticModel</param> /// <param name="trace">TraceInfo</param> /// <returns>Set of return symbols</returns> protected HashSet <ISymbol> AnalyzeOwnershipInCall(GivenUpOwnershipSymbol givenUpSymbol, ExpressionSyntax call, Statement statement, StateMachine machine, SemanticModel model, TraceInfo trace) { var potentialReturnSymbols = new HashSet <ISymbol>(); var invocation = call as InvocationExpressionSyntax; var objCreation = call as ObjectCreationExpressionSyntax; if ((invocation == null && objCreation == null)) { return(potentialReturnSymbols); } TraceInfo callTrace = new TraceInfo(); callTrace.Merge(trace); callTrace.AddErrorTrace(call); var callSymbol = model.GetSymbolInfo(call).Symbol; if (callSymbol == null) { base.ErrorReporter.ReportExternalInvocation(callTrace); return(potentialReturnSymbols); } if (callSymbol.ContainingType.ToString().Equals("Microsoft.PSharp.Machine")) { this.AnalyzeOwnershipInGivesUpCall(givenUpSymbol, invocation, statement, machine, model, callTrace); return(potentialReturnSymbols); } if (SymbolFinder.FindSourceDefinitionAsync(callSymbol, this.AnalysisContext.Solution).Result == null) { base.ErrorReporter.ReportExternalInvocation(callTrace); return(potentialReturnSymbols); } var candidateSummaries = MethodSummary.GetCachedSummaries(callSymbol, statement); foreach (var candidateSummary in candidateSummaries) { this.AnalyzeOwnershipInCandidateCallee(givenUpSymbol, candidateSummary, call, statement, machine, model, callTrace); if (invocation != null) { var resolvedReturnSymbols = candidateSummary.GetResolvedReturnSymbols(invocation, model); foreach (var resolvedReturnSymbol in resolvedReturnSymbols) { potentialReturnSymbols.Add(resolvedReturnSymbol); } } } return(potentialReturnSymbols); }
/// <summary> /// Computes the 'gives_up' set for the given argument. /// </summary> /// <param name="arg">Argument</param> /// <param name="cfgNode">ControlFlowGraphNode</param> /// <param name="summary">MethodSummary</param> private static void ComputeGivesUpSetForArgument(ExpressionSyntax arg, ControlFlowGraphNode cfgNode, MethodSummary summary) { var model = AnalysisContext.Compilation.GetSemanticModel(arg.SyntaxTree); if (arg is IdentifierNameSyntax || arg is MemberAccessExpressionSyntax) { for (int idx = 0; idx < summary.Method.ParameterList.Parameters.Count; idx++) { if (Utilities.IsTypeAllowedToBeSend(summary.Method.ParameterList.Parameters[idx].Type, model)) { continue; } var paramSymbol = model.GetDeclaredSymbol(summary.Method.ParameterList.Parameters[idx]); if (DataFlowAnalysis.FlowsFromTarget(arg, paramSymbol, summary.Node.SyntaxNodes.First(), summary.Node, cfgNode.SyntaxNodes.First(), cfgNode, model)) { summary.GivesUpSet.Add(idx); } } } else if (arg is ObjectCreationExpressionSyntax) { var payload = arg as ObjectCreationExpressionSyntax; foreach (var item in payload.ArgumentList.Arguments) { MethodSummaryAnalysis.ComputeGivesUpSetForArgument(item.Expression, cfgNode, summary); } } }
/// <summary> /// Tries to compute the 'gives_up' set of indexes for the given control flow graph node. /// If the node does not contain a 'Send' operation, then it returns false. /// </summary> /// <param name="cfgNode">ControlFlowGraphNode</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean value</returns> private bool TryComputeGivesUpSetForSendControlFlowGraphNode(ControlFlowGraphNode cfgNode, MethodSummary summary) { var sendExpr = cfgNode.SyntaxNodes.First() as ExpressionStatementSyntax; if (sendExpr == null) { return(false); } var send = sendExpr.Expression as InvocationExpressionSyntax; if (send == null || !((send.Expression is MemberAccessExpressionSyntax) || (send.Expression is IdentifierNameSyntax))) { return(false); } if (((send.Expression is MemberAccessExpressionSyntax) && !(send.Expression as MemberAccessExpressionSyntax). Name.Identifier.ValueText.Equals("Send")) || ((send.Expression is IdentifierNameSyntax) && !(send.Expression as IdentifierNameSyntax). Identifier.ValueText.Equals("Send"))) { return(false); } if (send.ArgumentList.Arguments[1].Expression is ObjectCreationExpressionSyntax) { var objCreation = send.ArgumentList.Arguments[1].Expression as ObjectCreationExpressionSyntax; foreach (var arg in objCreation.ArgumentList.Arguments) { this.ComputeGivesUpSetForArgument(arg.Expression, cfgNode, summary); } } else if (send.ArgumentList.Arguments[1].Expression is BinaryExpressionSyntax && send.ArgumentList.Arguments[1].Expression.IsKind(SyntaxKind.AsExpression)) { var binExpr = send.ArgumentList.Arguments[1].Expression as BinaryExpressionSyntax; if ((binExpr.Left is IdentifierNameSyntax) || (binExpr.Left is MemberAccessExpressionSyntax)) { this.ComputeGivesUpSetForArgument(binExpr.Left, cfgNode, summary); } else if (binExpr.Left is InvocationExpressionSyntax) { var invocation = binExpr.Left as InvocationExpressionSyntax; for (int i = 1; i < invocation.ArgumentList.Arguments.Count; i++) { this.ComputeGivesUpSetForArgument(invocation.ArgumentList. Arguments[i].Expression, cfgNode, summary); } } } return(true); }
/// <summary> /// Constructs the node using information from the given state transitions /// and action bindings. /// </summary> /// <param name="stateTransitions">State transitions</param> /// <param name="actionBindings">Action bindings</param> /// <param name="visited">Already visited nodes</param> private void Construct(Dictionary <ClassDeclarationSyntax, HashSet <ClassDeclarationSyntax> > stateTransitions, Dictionary <ClassDeclarationSyntax, HashSet <MethodDeclarationSyntax> > actionBindings, HashSet <StateTransitionGraphNode> visited) { visited.Add(this); foreach (var method in this.State.ChildNodes().OfType <MethodDeclarationSyntax>()) { var summary = MethodSummary.Factory.Summarize(method); if (method.Modifiers.Any(SyntaxKind.OverrideKeyword) && method.Identifier.ValueText.Equals("OnEntry")) { this.OnEntry = summary; } else if (method.Modifiers.Any(SyntaxKind.OverrideKeyword) && method.Identifier.ValueText.Equals("OnExit")) { this.OnExit = summary; } } var actions = new HashSet <MethodDeclarationSyntax>(); if (actionBindings.ContainsKey(this.State)) { actions = actionBindings[this.State]; } foreach (var action in actions) { var actionSummary = MethodSummary.Factory.Summarize(action); this.Actions.Add(actionSummary); } var transitions = new HashSet <ClassDeclarationSyntax>(); if (stateTransitions.ContainsKey(this.State)) { transitions = stateTransitions[this.State]; } foreach (var successorState in transitions) { var successor = visited.FirstOrDefault(v => v.State.Equals(successorState)); if (successor != null) { this.ISuccessors.Add(successor); successor.IPredecessors.Add(this); } else { successor = new StateTransitionGraphNode(successorState, this.Machine); this.ISuccessors.Add(successor); successor.IPredecessors.Add(this); successor.Construct(stateTransitions, actionBindings, visited); } } }
/// <summary> /// Default constructor. /// </summary> /// <param name="summary">MethodSummary</param> internal ControlFlowGraphNode(MethodSummary summary) { this.Id = ControlFlowGraphNode.IdCounter++; this.Summary = summary; this.SyntaxNodes = new List <SyntaxNode>(); this.IPredecessors = new HashSet <ControlFlowGraphNode>(); this.ISuccessors = new HashSet <ControlFlowGraphNode>(); this.IsGivesUpNode = false; this.IsJumpNode = false; this.IsLoopHeadNode = false; }
/// <summary> /// Returns all the successor summaries. /// </summary> /// <param name="summary">MethodSummary</param> /// <returns>MethodSummarys</returns> internal ISet <MethodSummary> GetSuccessorSummaries(MethodSummary summary) { var summaries = new HashSet <MethodSummary>(); if (this.MethodSummaries.Count == 0) { return(summaries); } var fromStates = new HashSet <MachineState>(); foreach (var state in this.MachineStates) { var summaryActions = state.MachineActions.FindAll(action => action.MethodDeclaration.Equals(summary.Method)); if (summaryActions.Count == 0) { continue; } foreach (var summaryAction in summaryActions) { if (!(summaryAction is OnExitMachineAction)) { summaries.UnionWith(state.MachineActions.FindAll(action => action is OnEventDoMachineAction || action is OnEventGotoMachineAction || action is OnEventPushMachineAction || action is OnExitMachineAction). Select(action => action.MethodDeclaration). Select(method => this.MethodSummaries[method])); } } fromStates.Add(state); } foreach (var state in fromStates) { foreach (var successor in state.GetSuccessorStates()) { summaries.UnionWith(successor.MachineActions.FindAll(action => action is OnEntryMachineAction || action is OnEventDoMachineAction || action is OnEventGotoMachineAction || action is OnEventPushMachineAction || action is OnExitMachineAction). Select(action => action.MethodDeclaration). Select(method => this.MethodSummaries[method])); } } return(summaries); }
/// <summary> /// Computes the summary for the given method. /// </summary> /// <param name="method">Method</param> /// <param name="machine">Machine</param> /// <param name="state">State</param> private static void ComputeSummaryForMethod(MethodDeclarationSyntax method, ClassDeclarationSyntax machine, ClassDeclarationSyntax state) { List <InvocationExpressionSyntax> givesUpSources = new List <InvocationExpressionSyntax>(); foreach (var call in method.DescendantNodes().OfType <InvocationExpressionSyntax>()) { var model = AnalysisContext.Compilation.GetSemanticModel(call.SyntaxTree); var callSymbol = model.GetSymbolInfo(call).Symbol; if (callSymbol == null) { continue; } var definition = SymbolFinder.FindSourceDefinitionAsync(callSymbol, ProgramInfo.Solution).Result; if (definition == null) { continue; } var callee = Utilities.GetCallee(call); var calleeMethod = definition.DeclaringSyntaxReferences.First().GetSyntax() as BaseMethodDeclarationSyntax; if (Utilities.IsSourceOfGivingUpOwnership(call, model, callee) || AnalysisContext.Summaries.ContainsKey(calleeMethod)) { givesUpSources.Add(call); } else if (machine.ChildNodes().OfType <BaseMethodDeclarationSyntax>().Contains(calleeMethod) && !AnalysisContext.Summaries.ContainsKey(calleeMethod) && !calleeMethod.Modifiers.Any(SyntaxKind.AbstractKeyword)) { return; } } MethodSummary summary = MethodSummary.Factory.Summarize(method, machine, state); foreach (var givesUpNode in summary.GivesUpNodes) { MethodSummaryAnalysis.TryComputeGivesUpSetForSendControlFlowGraphNode( givesUpNode, summary); MethodSummaryAnalysis.TryComputeGivesUpSetForCreateControlFlowGraphNode( givesUpNode, summary); MethodSummaryAnalysis.TryComputeGivesUpSetForGenericControlFlowGraphNode( givesUpNode, summary); } }
/// <summary> /// Returns true if the given field symbol belongs to the machine /// that owns the given method summary. Returns false if not. /// </summary> /// <param name="symbol">Symbol</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean value</returns> internal bool DoesFieldBelongToMachine(ISymbol symbol, MethodSummary summary) { if (symbol == null || summary.Machine == null || !(symbol is IFieldSymbol)) { return(false); } var definition = SymbolFinder.FindSourceDefinitionAsync(symbol, this.Solution).Result; var fieldDecl = definition.DeclaringSyntaxReferences.First().GetSyntax(). AncestorsAndSelf().OfType <FieldDeclarationSyntax>().First(); if (summary.Machine.ChildNodes().OfType <FieldDeclarationSyntax>().Contains(fieldDecl)) { return(true); } return(false); }
/// <summary> /// Returns true if the given field symbol is being accessed /// before being reset. /// </summary> /// <param name="field">Field</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean value</returns> internal static bool IsAccessedBeforeBeingReset(ISymbol field, MethodSummary summary) { StateTransitionGraphNode stateTransitionNode = null; if (!AnalysisContext.StateTransitionGraphs.ContainsKey(summary.Machine)) { return true; } stateTransitionNode = AnalysisContext.StateTransitionGraphs[summary.Machine]. GetGraphNodeForSummary(summary); if (stateTransitionNode == null) { return true; } var result = stateTransitionNode.VisitSelfAndSuccessors(IsAccessedBeforeBeingReset, new Tuple<MethodSummary, ISymbol>(summary, field)); return false; }
/// <summary> /// Returns true if the field symbol is being accessed in a successor summary. /// </summary> protected bool IsFieldAccessedInSuccessor(IFieldSymbol fieldSymbol, MethodSummary summary, StateMachine machine) { if (!this.Configuration.DoStateTransitionAnalysis) { return(true); } var successors = machine.GetSuccessorSummaries(summary); foreach (var successor in successors) { if (successor.SideEffectsInfo.FieldAccesses.ContainsKey(fieldSymbol)) { return(true); } } return(false); }
/// <summary> /// Returns true if the given field symbol is being accessed /// before being reset. /// </summary> /// <param name="field">Field</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean value</returns> internal static bool IsAccessedBeforeBeingReset(ISymbol field, MethodSummary summary) { StateTransitionGraphNode stateTransitionNode = null; if (!AnalysisContext.StateTransitionGraphs.ContainsKey(summary.Machine)) { return(true); } stateTransitionNode = AnalysisContext.StateTransitionGraphs[summary.Machine]. GetGraphNodeForSummary(summary); if (stateTransitionNode == null) { return(true); } var result = stateTransitionNode.VisitSelfAndSuccessors(IsAccessedBeforeBeingReset, new Tuple <MethodSummary, ISymbol>(summary, field)); return(false); }
/// <summary> /// Returns the data flow map for the given control flow graph. /// </summary> /// <param name="summary">MethodSummary</param> /// <returns>DataFlowMap</returns> internal static DataFlowMap AnalyseControlFlowGraph(MethodSummary summary) { var dataFlowMap = new DataFlowMap(); var model = AnalysisContext.Compilation.GetSemanticModel(summary.Method.SyntaxTree); foreach (var param in summary.Method.ParameterList.Parameters) { var declType = model.GetTypeInfo(param.Type).Type; if (Utilities.IsTypeAllowedToBeSend(declType) || Utilities.IsMachineType(declType, model)) { continue; } var paramSymbol = model.GetDeclaredSymbol(param); dataFlowMap.MapRefToSymbol(paramSymbol, paramSymbol, summary.Method.ParameterList, summary.Node, false); } DataFlowAnalysis.AnalyseControlFlowGraphNode(summary.Node, summary.Node, summary.Method.ParameterList, model, dataFlowMap); return dataFlowMap; }
/// <summary> /// Constructs the node using information from the given state transitions /// and action bindings. /// </summary> /// <param name="stateTransitions">State transitions</param> /// <param name="actionBindings">Action bindings</param> /// <param name="visited">Already visited nodes</param> private void Construct(Dictionary<ClassDeclarationSyntax, HashSet<ClassDeclarationSyntax>> stateTransitions, Dictionary<ClassDeclarationSyntax, HashSet<MethodDeclarationSyntax>> actionBindings, HashSet<StateTransitionGraphNode> visited) { visited.Add(this); foreach (var method in this.State.ChildNodes().OfType<MethodDeclarationSyntax>()) { var summary = MethodSummary.Factory.Summarize(this.AnalysisContext, method); if (method.Modifiers.Any(SyntaxKind.OverrideKeyword) && method.Identifier.ValueText.Equals("OnEntry")) { this.OnEntry = summary; } else if (method.Modifiers.Any(SyntaxKind.OverrideKeyword) && method.Identifier.ValueText.Equals("OnExit")) { this.OnExit = summary; } } var actions = new HashSet<MethodDeclarationSyntax>(); if (actionBindings.ContainsKey(this.State)) { actions = actionBindings[this.State]; } foreach (var action in actions) { var actionSummary = MethodSummary.Factory.Summarize(this.AnalysisContext, action); this.Actions.Add(actionSummary); } var transitions = new HashSet<ClassDeclarationSyntax>(); if (stateTransitions.ContainsKey(this.State)) { transitions = stateTransitions[this.State]; } foreach (var successorState in transitions) { var successor = visited.FirstOrDefault(v => v.State.Equals(successorState)); if (successor != null) { this.ISuccessors.Add(successor); successor.IPredecessors.Add(this); } else { successor = new StateTransitionGraphNode(this.AnalysisContext, successorState, this.Machine); this.ISuccessors.Add(successor); successor.IPredecessors.Add(this); successor.Construct(stateTransitions, actionBindings, visited); } } }
/// <summary> /// Analyzes the ownership of the given-up symbol in the candidate callee. /// </summary> protected abstract void AnalyzeOwnershipInCandidateCallee(GivenUpOwnershipSymbol givenUpSymbol, MethodSummary calleeSummary, ExpressionSyntax call, Statement statement, StateMachine machine, SemanticModel model, TraceInfo trace);
/// <summary> /// Computes the summary for the specified method. /// </summary> /// <param name="method">Method</param> private void SummarizeMethod(MethodDeclarationSyntax method) { var summary = MethodSummary.Create(this.AnalysisContext, method); this.MethodSummaries.Add(method, summary); }
/// <summary> /// Tries to compute the 'gives_up' set of indexes for the given control flow graph node. /// If the node does not contain a generic 'gives_up' operation, then it returns false. /// </summary> /// <param name="cfgNode">ControlFlowGraphNode</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean value</returns> private static bool TryComputeGivesUpSetForGenericControlFlowGraphNode(ControlFlowGraphNode cfgNode, MethodSummary summary) { var callLocalDecl = cfgNode.SyntaxNodes.First() as LocalDeclarationStatementSyntax; var callExpr = cfgNode.SyntaxNodes.First() as ExpressionStatementSyntax; InvocationExpressionSyntax call = null; if (callLocalDecl != null) { call = callLocalDecl.DescendantNodesAndSelf().OfType <InvocationExpressionSyntax>().First(); } else if (callExpr != null) { call = callExpr.DescendantNodesAndSelf().OfType <InvocationExpressionSyntax>().First(); } else if (call == null || !((call.Expression is MemberAccessExpressionSyntax) || (call.Expression is IdentifierNameSyntax))) { return(false); } var model = AnalysisContext.Compilation.GetSemanticModel(call.SyntaxTree); if (call.Expression is MemberAccessExpressionSyntax) { var callStmt = call.Expression as MemberAccessExpressionSyntax; if (callStmt.Name.Identifier.ValueText.Equals("Send") || callStmt.Name.Identifier.ValueText.Equals("CreateMachine")) { return(false); } } else if (call.Expression is IdentifierNameSyntax) { var callStmt = call.Expression as IdentifierNameSyntax; if (callStmt.Identifier.ValueText.Equals("Send") || callStmt.Identifier.ValueText.Equals("CreateMachine")) { return(false); } } if (call.ArgumentList.Arguments.Count == 0) { return(false); } var callSymbol = model.GetSymbolInfo(call).Symbol; var definition = SymbolFinder.FindSourceDefinitionAsync(callSymbol, ProgramInfo.Solution).Result; var calleeMethod = definition.DeclaringSyntaxReferences.First().GetSyntax() as BaseMethodDeclarationSyntax; var calleeSummary = MethodSummary.Factory.Summarize(calleeMethod); foreach (int idx in calleeSummary.GivesUpSet) { if (call.ArgumentList.Arguments[idx].Expression is ObjectCreationExpressionSyntax) { var objCreation = call.ArgumentList.Arguments[idx].Expression as ObjectCreationExpressionSyntax; foreach (var arg in objCreation.ArgumentList.Arguments) { MethodSummaryAnalysis.ComputeGivesUpSetForArgument( arg.Expression, cfgNode, summary); } } else if (call.ArgumentList.Arguments[idx].Expression is BinaryExpressionSyntax && call.ArgumentList.Arguments[idx].Expression.IsKind(SyntaxKind.AsExpression)) { var binExpr = call.ArgumentList.Arguments[idx].Expression as BinaryExpressionSyntax; if ((binExpr.Left is IdentifierNameSyntax) || (binExpr.Left is MemberAccessExpressionSyntax)) { MethodSummaryAnalysis.ComputeGivesUpSetForArgument(binExpr.Left, cfgNode, summary); } else if (binExpr.Left is InvocationExpressionSyntax) { var invocation = binExpr.Left as InvocationExpressionSyntax; for (int i = 1; i < invocation.ArgumentList.Arguments.Count; i++) { MethodSummaryAnalysis.ComputeGivesUpSetForArgument(invocation.ArgumentList. Arguments[i].Expression, cfgNode, summary); } } } else if ((call.ArgumentList.Arguments[idx].Expression is IdentifierNameSyntax) || (call.ArgumentList.Arguments[idx].Expression is MemberAccessExpressionSyntax)) { MethodSummaryAnalysis.ComputeGivesUpSetForArgument(call.ArgumentList. Arguments[idx].Expression, cfgNode, summary); } } return(true); }
/// <summary> /// Analyzes the ownership of the given-up symbol /// in the candidate callee. /// </summary> /// <param name="givenUpSymbol">GivenUpOwnershipSymbol</param> /// <param name="calleeSummary">MethodSummary</param> /// <param name="call">ExpressionSyntax</param> /// <param name="statement">Statement</param> /// <param name="machine">StateMachine</param> /// <param name="model">SemanticModel</param> /// <param name="trace">TraceInfo</param> protected override void AnalyzeOwnershipInCandidateCallee(GivenUpOwnershipSymbol givenUpSymbol, MethodSummary calleeSummary, ExpressionSyntax call, Statement statement, StateMachine machine, SemanticModel model, TraceInfo trace) { if (statement.Equals(givenUpSymbol.Statement) && !statement.ControlFlowNode.IsSuccessorOf( givenUpSymbol.Statement.ControlFlowNode)) { return; } var invocation = call as InvocationExpressionSyntax; if (invocation != null) { this.AnalyzeOwnershipInExpression(givenUpSymbol, invocation.Expression, statement, machine, model, trace); } ArgumentListSyntax argumentList = base.AnalysisContext.GetArgumentList(call); if (argumentList != null) { for (int idx = 0; idx < argumentList.Arguments.Count; idx++) { var argType = model.GetTypeInfo(argumentList.Arguments[idx].Expression).Type; if (base.AnalysisContext.IsTypePassedByValueOrImmutable(argType)) { continue; } var argIdentifier = base.AnalysisContext.GetRootIdentifier( argumentList.Arguments[idx].Expression); ISymbol argSymbol = model.GetSymbolInfo(argIdentifier).Symbol; if (statement.Summary.DataFlowAnalysis.FlowsIntoSymbol(argSymbol, givenUpSymbol.ContainingSymbol, statement, givenUpSymbol.Statement)) { if (calleeSummary.SideEffectsInfo.ParameterAccesses.ContainsKey(idx)) { foreach (var access in calleeSummary.SideEffectsInfo.ParameterAccesses[idx]) { TraceInfo newTrace = new TraceInfo(); newTrace.Merge(trace); newTrace.AddErrorTrace(access.SyntaxNode); AnalysisErrorReporter.ReportGivenUpOwnershipAccess(newTrace); } } var fieldSymbols = calleeSummary.SideEffectsInfo.FieldFlowParamIndexes.Where( v => v.Value.Contains(idx)).Select(v => v.Key); foreach (var fieldSymbol in fieldSymbols) { if (base.IsFieldAccessedInSuccessor(fieldSymbol, statement.Summary, machine)) { AnalysisErrorReporter.ReportGivenUpOwnershipFieldAssignment(trace, fieldSymbol); } } if (calleeSummary.SideEffectsInfo.GivesUpOwnershipParamIndexes.Contains(idx)) { AnalysisErrorReporter.ReportGivenUpOwnershipSending(trace, argSymbol); } } } } foreach (var fieldAccess in calleeSummary.SideEffectsInfo.FieldAccesses) { foreach (var access in fieldAccess.Value) { if (statement.Summary.DataFlowAnalysis.FlowsIntoSymbol(givenUpSymbol.ContainingSymbol, fieldAccess.Key, givenUpSymbol.Statement, statement)) { TraceInfo newTrace = new TraceInfo(); newTrace.Merge(trace); newTrace.AddErrorTrace(access.SyntaxNode); AnalysisErrorReporter.ReportGivenUpOwnershipFieldAccess(newTrace, fieldAccess.Key); } } } }
/// <summary> /// Default constructor. /// </summary> /// <param name="summary">MethodSummary</param> internal ControlFlowGraphNode(MethodSummary summary) { this.Id = ControlFlowGraphNode.IdCounter++; this.Summary = summary; this.SyntaxNodes = new List<SyntaxNode>(); this.IPredecessors = new HashSet<ControlFlowGraphNode>(); this.ISuccessors = new HashSet<ControlFlowGraphNode>(); this.IsGivesUpNode = false; this.IsJumpNode = false; this.IsLoopHeadNode = false; }
/// <summary> /// Returns true if the given field symbol belongs to the machine /// that owns the given method summary. Returns false if not. /// </summary> /// <param name="symbol">Symbol</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean</returns> internal bool DoesFieldBelongToMachine(ISymbol symbol, MethodSummary summary) { if (symbol == null || summary.Machine == null || !(symbol is IFieldSymbol)) { return false; } var definition = SymbolFinder.FindSourceDefinitionAsync(symbol, this.Solution).Result; var fieldDecl = definition.DeclaringSyntaxReferences.First().GetSyntax(). AncestorsAndSelf().OfType<FieldDeclarationSyntax>().First(); if (summary.Machine.ChildNodes().OfType<FieldDeclarationSyntax>().Contains(fieldDecl)) { return true; } return false; }
/// <summary> /// Tries to compute the 'gives_up' set of indexes for the given control flow graph node. /// If the node does not contain a 'Send' operation, then it returns false. /// </summary> /// <param name="cfgNode">ControlFlowGraphNode</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean value</returns> private bool TryComputeGivesUpSetForSendControlFlowGraphNode(ControlFlowGraphNode cfgNode, MethodSummary summary) { var sendExpr = cfgNode.SyntaxNodes.First() as ExpressionStatementSyntax; if (sendExpr == null) { return false; } var send = sendExpr.Expression as InvocationExpressionSyntax; if (send == null || !((send.Expression is MemberAccessExpressionSyntax) || (send.Expression is IdentifierNameSyntax))) { return false; } if (((send.Expression is MemberAccessExpressionSyntax) && !(send.Expression as MemberAccessExpressionSyntax). Name.Identifier.ValueText.Equals("Send")) || ((send.Expression is IdentifierNameSyntax) && !(send.Expression as IdentifierNameSyntax). Identifier.ValueText.Equals("Send"))) { return false; } if (send.ArgumentList.Arguments[1].Expression is ObjectCreationExpressionSyntax) { var objCreation = send.ArgumentList.Arguments[1].Expression as ObjectCreationExpressionSyntax; foreach (var arg in objCreation.ArgumentList.Arguments) { this.ComputeGivesUpSetForArgument(arg.Expression, cfgNode, summary); } } else if (send.ArgumentList.Arguments[1].Expression is BinaryExpressionSyntax && send.ArgumentList.Arguments[1].Expression.IsKind(SyntaxKind.AsExpression)) { var binExpr = send.ArgumentList.Arguments[1].Expression as BinaryExpressionSyntax; if ((binExpr.Left is IdentifierNameSyntax) || (binExpr.Left is MemberAccessExpressionSyntax)) { this.ComputeGivesUpSetForArgument(binExpr.Left, cfgNode, summary); } else if (binExpr.Left is InvocationExpressionSyntax) { var invocation = binExpr.Left as InvocationExpressionSyntax; for (int i = 1; i < invocation.ArgumentList.Arguments.Count; i++) { this.ComputeGivesUpSetForArgument(invocation.ArgumentList. Arguments[i].Expression, cfgNode, summary); } } } return true; }
/// <summary> /// Tries to compute the 'gives_up' set of indexes for the given control flow graph node. /// If the node does not contain a generic 'gives_up' operation, then it returns false. /// </summary> /// <param name="cfgNode">ControlFlowGraphNode</param> /// <param name="summary">MethodSummary</param> /// <returns>Boolean value</returns> private bool TryComputeGivesUpSetForGenericControlFlowGraphNode(ControlFlowGraphNode cfgNode, MethodSummary summary) { var callLocalDecl = cfgNode.SyntaxNodes.First() as LocalDeclarationStatementSyntax; var callExpr = cfgNode.SyntaxNodes.First() as ExpressionStatementSyntax; InvocationExpressionSyntax call = null; if (callLocalDecl != null) { call = callLocalDecl.DescendantNodesAndSelf().OfType<InvocationExpressionSyntax>().First(); } else if (callExpr != null) { call = callExpr.DescendantNodesAndSelf().OfType<InvocationExpressionSyntax>().First(); } else if (call == null || !((call.Expression is MemberAccessExpressionSyntax) || (call.Expression is IdentifierNameSyntax))) { return false; } var model = this.AnalysisContext.Compilation.GetSemanticModel(call.SyntaxTree); if (call.Expression is MemberAccessExpressionSyntax) { var callStmt = call.Expression as MemberAccessExpressionSyntax; if (callStmt.Name.Identifier.ValueText.Equals("Send") || callStmt.Name.Identifier.ValueText.Equals("CreateMachine")) { return false; } } else if (call.Expression is IdentifierNameSyntax) { var callStmt = call.Expression as IdentifierNameSyntax; if (callStmt.Identifier.ValueText.Equals("Send") || callStmt.Identifier.ValueText.Equals("CreateMachine")) { return false; } } if (call.ArgumentList.Arguments.Count == 0) { return false; } var callSymbol = model.GetSymbolInfo(call).Symbol; var definition = SymbolFinder.FindSourceDefinitionAsync(callSymbol, this.AnalysisContext.Solution).Result; var calleeMethod = definition.DeclaringSyntaxReferences.First().GetSyntax() as BaseMethodDeclarationSyntax; var calleeSummary = MethodSummary.Factory.Summarize(this.AnalysisContext, calleeMethod); foreach (int idx in calleeSummary.GivesUpSet) { if (call.ArgumentList.Arguments[idx].Expression is ObjectCreationExpressionSyntax) { var objCreation = call.ArgumentList.Arguments[idx].Expression as ObjectCreationExpressionSyntax; foreach (var arg in objCreation.ArgumentList.Arguments) { this.ComputeGivesUpSetForArgument(arg.Expression, cfgNode, summary); } } else if (call.ArgumentList.Arguments[idx].Expression is BinaryExpressionSyntax && call.ArgumentList.Arguments[idx].Expression.IsKind(SyntaxKind.AsExpression)) { var binExpr = call.ArgumentList.Arguments[idx].Expression as BinaryExpressionSyntax; if ((binExpr.Left is IdentifierNameSyntax) || (binExpr.Left is MemberAccessExpressionSyntax)) { this.ComputeGivesUpSetForArgument(binExpr.Left, cfgNode, summary); } else if (binExpr.Left is InvocationExpressionSyntax) { var invocation = binExpr.Left as InvocationExpressionSyntax; for (int i = 1; i < invocation.ArgumentList.Arguments.Count; i++) { this.ComputeGivesUpSetForArgument(invocation.ArgumentList. Arguments[i].Expression, cfgNode, summary); } } } else if ((call.ArgumentList.Arguments[idx].Expression is IdentifierNameSyntax) || (call.ArgumentList.Arguments[idx].Expression is MemberAccessExpressionSyntax)) { this.ComputeGivesUpSetForArgument(call.ArgumentList.Arguments[idx]. Expression, cfgNode, summary); } } return true; }
/// <summary> /// Computes the 'gives_up' set for the given argument. /// </summary> /// <param name="arg">Argument</param> /// <param name="cfgNode">ControlFlowGraphNode</param> /// <param name="summary">MethodSummary</param> private void ComputeGivesUpSetForArgument(ExpressionSyntax arg, ControlFlowGraphNode cfgNode, MethodSummary summary) { var model = this.AnalysisContext.Compilation.GetSemanticModel(arg.SyntaxTree); if (arg is IdentifierNameSyntax || arg is MemberAccessExpressionSyntax) { for (int idx = 0; idx < summary.Method.ParameterList.Parameters.Count; idx++) { if (this.AnalysisContext.IsTypeAllowedToBeSend(summary.Method.ParameterList. Parameters[idx].Type, model)) { continue; } var paramSymbol = model.GetDeclaredSymbol(summary.Method.ParameterList.Parameters[idx]); if (DataFlowAnalysis.FlowsFromTarget(arg, paramSymbol, summary.Node.SyntaxNodes.First(), summary.Node, cfgNode.SyntaxNodes.First(), cfgNode, model, this.AnalysisContext)) { summary.GivesUpSet.Add(idx); } } } else if (arg is ObjectCreationExpressionSyntax) { var payload = arg as ObjectCreationExpressionSyntax; foreach (var item in payload.ArgumentList.Arguments) { this.ComputeGivesUpSetForArgument(item.Expression, cfgNode, summary); } } }
/// <summary> /// Resolves and returns all possible side effects at the point of the /// given call argument list. /// </summary> /// <param name="argumentList">Argument list</param> /// <param name="model">SemanticModel</param> /// <returns>Set of side effects</returns> internal Dictionary <ISymbol, HashSet <ISymbol> > GetResolvedSideEffects(ArgumentListSyntax argumentList, SemanticModel model) { Dictionary <ISymbol, HashSet <ISymbol> > sideEffects = new Dictionary <ISymbol, HashSet <ISymbol> >(); foreach (var sideEffect in this.SideEffects) { HashSet <ISymbol> argSymbols = new HashSet <ISymbol>(); foreach (var index in sideEffect.Value) { IdentifierNameSyntax arg = null; var argExpr = argumentList.Arguments[index].Expression; if (argExpr is IdentifierNameSyntax) { arg = argExpr as IdentifierNameSyntax; var argType = model.GetTypeInfo(arg).Type; if (this.AnalysisContext.IsTypeAllowedToBeSend(argType) || this.AnalysisContext.IsMachineType(argType, model)) { continue; } argSymbols.Add(model.GetSymbolInfo(arg).Symbol); } else if (argExpr is MemberAccessExpressionSyntax) { var name = (argExpr as MemberAccessExpressionSyntax).Name; var argType = model.GetTypeInfo(name).Type; if (this.AnalysisContext.IsTypeAllowedToBeSend(argType) || this.AnalysisContext.IsMachineType(argType, model)) { continue; } arg = this.AnalysisContext.GetFirstNonMachineIdentifier(argExpr, model); argSymbols.Add(model.GetSymbolInfo(arg).Symbol); } else if (argExpr is ObjectCreationExpressionSyntax) { var objCreation = argExpr as ObjectCreationExpressionSyntax; var summary = MethodSummary.TryGetSummary(objCreation, model, this.AnalysisContext); if (summary == null) { continue; } var nestedSideEffects = summary.GetResolvedSideEffects( objCreation.ArgumentList, model); foreach (var nestedSideEffect in nestedSideEffects) { sideEffects.Add(nestedSideEffect.Key, nestedSideEffect.Value); } } else if (argExpr is InvocationExpressionSyntax) { var invocation = argExpr as InvocationExpressionSyntax; var summary = MethodSummary.TryGetSummary(invocation, model, this.AnalysisContext); if (summary == null) { continue; } var nestedSideEffects = summary.GetResolvedSideEffects( invocation.ArgumentList, model); foreach (var nestedSideEffect in nestedSideEffects) { sideEffects.Add(nestedSideEffect.Key, nestedSideEffect.Value); } } } sideEffects.Add(sideEffect.Key, argSymbols); } return(sideEffects); }
/// <summary> /// Visits the state transition graph to find and return /// the node for the given summary. /// </summary> /// <param name="summary">MethodSummary</param> /// <returns>StateTransitionGraphNode</returns> internal StateTransitionGraphNode GetGraphNodeForSummary(MethodSummary summary) { return(this.GetGraphNodeForSummary(summary, new HashSet <StateTransitionGraphNode>())); }
/// <summary> /// Resolves the side effects from the given invocation summary. /// </summary> /// <param name="call">Call</param> /// <param name="summary">MethodSummary</param> /// <param name="syntaxNode">SyntaxNode</param> /// <param name="cfgNode">ControlFlowGraphNode</param> /// <param name="model">SemanticModel</param> /// <param name="dataFlowMap">DataFlowMap</param> /// <returns>Set of reachable field symbols</returns> private static HashSet<ISymbol> ResolveSideEffectsInCall(InvocationExpressionSyntax call, MethodSummary summary, SyntaxNode syntaxNode, ControlFlowGraphNode cfgNode, SemanticModel model, DataFlowMap dataFlowMap) { if (summary == null) { return new HashSet<ISymbol>(); } HashSet<ISymbol> reachableFields = new HashSet<ISymbol>(); var sideEffects = summary.GetResolvedSideEffects(call.ArgumentList, model); foreach (var sideEffect in sideEffects) { dataFlowMap.MapRefsToSymbol(sideEffect.Value, sideEffect.Key, syntaxNode, cfgNode); reachableFields.Add(sideEffect.Key); } foreach (var fieldAccess in summary.FieldAccessSet) { foreach (var access in fieldAccess.Value) { if (cfgNode.Summary.FieldAccessSet.ContainsKey(fieldAccess.Key as IFieldSymbol)) { cfgNode.Summary.FieldAccessSet[fieldAccess.Key as IFieldSymbol].Add(access); } else { cfgNode.Summary.FieldAccessSet.Add(fieldAccess.Key as IFieldSymbol, new HashSet<SyntaxNode>()); cfgNode.Summary.FieldAccessSet[fieldAccess.Key as IFieldSymbol].Add(access); } } } return reachableFields; }
/// <summary> /// Visits the state transition graph to find and return /// the node for the given summary. /// </summary> /// <param name="summary">MethodSummary</param> /// <returns>StateTransitionGraphNode</returns> internal StateTransitionGraphNode GetGraphNodeForSummary(MethodSummary summary) { return this.GetGraphNodeForSummary(summary, new HashSet<StateTransitionGraphNode>()); }
/// <summary> /// Returns the return symbols fromt he given object creation summary. /// </summary> /// <param name="call">Call</param> /// <param name="summary">MethodSummary</param> /// <param name="model">SemanticModel</param> /// <returns>Set of return symbols</returns> private static HashSet<ISymbol> GetReturnSymbols(ObjectCreationExpressionSyntax call, MethodSummary summary, SemanticModel model) { if (summary == null) { return new HashSet<ISymbol>(); } return summary.GetResolvedReturnSymbols(call.ArgumentList, model); }
/// <summary> /// Visits the state transition graph to find and return /// the node for the given summary. /// </summary> /// <param name="summary">MethodSummary</param> /// <param name="visited">Already visited nodes</param> /// <returns>StateTransitionGraphNode</returns> private StateTransitionGraphNode GetGraphNodeForSummary(MethodSummary summary, HashSet<StateTransitionGraphNode> visited) { visited.Add(this); StateTransitionGraphNode stateNode = null; if ((this.OnEntry != null && this.OnEntry.Equals(summary)) || (this.OnExit != null && this.OnExit.Equals(summary)) || this.Actions.Any(v => v.Equals(summary))) { stateNode = this; } else { foreach (var successor in this.ISuccessors.Where(v => !visited.Contains(v))) { var node = successor.GetGraphNodeForSummary(summary, visited); if (node != null) { stateNode = node; break; } } } return stateNode; }