Skip to content

Commit

Permalink
Allow performing definite assignment analysis without providing an IT…
Browse files Browse the repository at this point in the history
…ypeResolveContext.
  • Loading branch information
dgrunwald committed Mar 19, 2011
1 parent c0a05d6 commit 2793b23
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,54 @@ public void WhileTrue()
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop));
}

[Test]
public void ForLoop()
{
ForStatement loop = new ForStatement {
Initializers = {
new ExpressionStatement(
new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(0))
)
},
Condition = new BinaryOperatorExpression(new IdentifierExpression("i"), BinaryOperatorType.LessThan, new PrimitiveExpression(1000)),
Iterators = {
new ExpressionStatement(
new AssignmentExpression {
Left = new IdentifierExpression("i"),
Operator = AssignmentOperatorType.Add,
Right = new IdentifierExpression("j")
}
)
},
EmbeddedStatement = new ExpressionStatement(
new AssignmentExpression(new IdentifierExpression("j"), new IdentifierExpression("i"))
)};

DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(loop, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBeforeLoopCondition(loop));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop));

da.Analyze("j");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBeforeLoopCondition(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(loop));
}
}
}
25 changes: 18 additions & 7 deletions NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNo
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
{
return BuildControlFlowGraph(statement, new ResolveVisitor(
new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip));
new CSharpResolver(context, cancellationToken),
null, ConstantModeResolveVisitorNavigator.Skip));
}

public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor)
Expand Down Expand Up @@ -249,24 +250,33 @@ ControlFlowNode CreateEndNode(Statement statement)
#endregion

#region Constant evaluation
/// <summary>
/// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b").
/// </summary>
public bool EvaluateOnlyPrimitiveConstants { get; set; }

/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
internal ConstantResolveResult EvaluateConstant(Expression expr)
{
if (EvaluateOnlyPrimitiveConstants) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}

/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr)
internal bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult crr = EvaluateConstant(expr);
if (crr != null)
return crr.ConstantValue as bool?;
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
return rr.ConstantValue as bool?;
else
return null;
}
Expand Down Expand Up @@ -328,7 +338,8 @@ ControlFlowNode HandleStatementList(AstNodeCollection<Statement> statements, Con
foreach (Statement stmt in statements) {
if (childNode == null) {
childNode = builder.CreateStartNode(stmt);
Connect(source, childNode);
if (source != null)
Connect(source, childNode);
}
Debug.Assert(childNode.NextStatement == stmt);
childNode = stmt.AcceptVisitor(this, childNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ public enum DefiniteAssignmentStatus
/// </summary>
public class DefiniteAssignmentAnalysis
{
readonly ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder();
readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor();
readonly List<ControlFlowNode> allNodes = new List<ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> beginNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> endNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> conditionNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly ResolveVisitor resolveVisitor;
readonly CancellationToken cancellationToken;
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
Expand All @@ -58,8 +60,14 @@ public class DefiniteAssignmentAnalysis

Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>();

public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, null, cancellationToken)
{
}

public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, new ResolveVisitor(new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip))
: this(rootStatement, new ResolveVisitor(new CSharpResolver(context ?? MinimalResolveContext.Instance, cancellationToken),
null, ConstantModeResolveVisitorNavigator.Skip))
{
}

Expand All @@ -72,16 +80,18 @@ public DefiniteAssignmentAnalysis(Statement rootStatement, ResolveVisitor resolv
this.resolveVisitor = resolveVisitor;
this.cancellationToken = resolveVisitor.CancellationToken;
visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder();
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement, resolveVisitor));
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
}
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolveVisitor));
foreach (AstNode descendant in rootStatement.Descendants) {
// Anonymous methods have separate control flow graphs, but we also need to analyze those.
AnonymousMethodExpression ame = descendant as AnonymousMethodExpression;
if (ame != null)
allNodes.AddRange(b.BuildControlFlowGraph(ame.Body, resolveVisitor));
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(ame.Body, resolveVisitor));
LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor));
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor));
}
// Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
Expand All @@ -91,6 +101,8 @@ public DefiniteAssignmentAnalysis(Statement rootStatement, ResolveVisitor resolv
beginNodeDict.Add(node.NextStatement, node);
if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
endNodeDict.Add(node.PreviousStatement, node);
if (node.Type == ControlFlowNodeType.LoopCondition)
conditionNodeDict.Add(node.NextStatement, node);
}
}

Expand Down Expand Up @@ -136,6 +148,11 @@ public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
return nodeStatus[endNodeDict[statement]];
}

public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement)
{
return nodeStatus[conditionNodeDict[statement]];
}

/// <summary>
/// Exports the CFG. This method is intended to help debugging issues related to definite assignment.
/// </summary>
Expand Down Expand Up @@ -304,7 +321,7 @@ void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus)
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
{
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
return cfgBuilder.EvaluateConstant(expr);
}

/// <summary>
Expand All @@ -313,11 +330,7 @@ ConstantResolveResult EvaluateConstant(Expression expr)
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult crr = EvaluateConstant(expr);
if (crr != null)
return crr.ConstantValue as bool?;
else
return null;
return cfgBuilder.EvaluateCondition(expr);
}

static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;

namespace ICSharpCode.NRefactory.CSharp.Analysis
{
/// <summary>
/// Resolve context represents the minimal mscorlib required for evaluating constants.
/// </summary>
sealed class MinimalResolveContext : IProjectContent, ISynchronizedTypeResolveContext
{
static readonly Lazy<MinimalResolveContext> instance = new Lazy<MinimalResolveContext>(() => new MinimalResolveContext());

public static MinimalResolveContext Instance {
get { return instance.Value; }
}

readonly ReadOnlyCollection<string> namespaces = Array.AsReadOnly(new string[] { "System" });
readonly IAttribute[] assemblyAttributes = new IAttribute[0];
readonly ITypeDefinition systemObject, systemValueType;
readonly ReadOnlyCollection<ITypeDefinition> types;

private MinimalResolveContext()
{
List<ITypeDefinition> types = new List<ITypeDefinition>();
types.Add(systemObject = new DefaultTypeDefinition(this, "System", "Object"));
types.Add(systemValueType = new DefaultTypeDefinition(this, "System", "ValueType") { BaseTypes = { systemObject } });
types.Add(CreateStruct("System", "Boolean"));
types.Add(CreateStruct("System", "SByte"));
types.Add(CreateStruct("System", "Byte"));
types.Add(CreateStruct("System", "Int16"));
types.Add(CreateStruct("System", "UInt16"));
types.Add(CreateStruct("System", "Int32"));
types.Add(CreateStruct("System", "UInt32"));
types.Add(CreateStruct("System", "Int64"));
types.Add(CreateStruct("System", "UInt64"));
types.Add(CreateStruct("System", "Single"));
types.Add(CreateStruct("System", "Double"));
types.Add(CreateStruct("System", "Decimal"));
types.Add(new DefaultTypeDefinition(this, "System", "String") { BaseTypes = { systemObject } });
foreach (ITypeDefinition type in types)
type.Freeze();
this.types = types.AsReadOnly();
}

ITypeDefinition CreateStruct(string nameSpace, string name)
{
return new DefaultTypeDefinition(this, nameSpace, name) {
ClassType = ClassType.Struct,
BaseTypes = { systemValueType }
};
}

public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
{
foreach (ITypeDefinition type in types) {
if (nameComparer.Equals(type.Name, name) && nameComparer.Equals(type.Namespace, nameSpace) && type.TypeParameterCount == typeParameterCount)
return type;
}
return null;
}

public IEnumerable<ITypeDefinition> GetClasses()
{
return types;
}

public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer)
{
return types.Where(t => nameComparer.Equals(t.Namespace, nameSpace));
}

public IEnumerable<string> GetNamespaces()
{
return namespaces;
}

public string GetNamespace(string nameSpace, StringComparer nameComparer)
{
foreach (string ns in namespaces) {
if (nameComparer.Equals(ns, nameSpace))
return ns;
}
return null;
}

public IList<IAttribute> AssemblyAttributes {
get { return assemblyAttributes; }
}

ICSharpCode.NRefactory.Utils.CacheManager ITypeResolveContext.CacheManager {
get {
// We don't support caching
return null;
}
}

ISynchronizedTypeResolveContext ITypeResolveContext.Synchronize()
{
// This class is immutable
return this;
}

void IDisposable.Dispose()
{
// exit from Synchronize() block
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ void WriteEmbeddedStatement(Statement embeddedStatement)
if (block != null)
VisitBlockStatement(block, null);
else
throw new NotImplementedException();
embeddedStatement.AcceptVisitor(this, null);
}

void WriteMethodBody(BlockStatement body)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<ItemGroup>
<Compile Include="CSharp\Analysis\ControlFlow.cs" />
<Compile Include="CSharp\Analysis\DefiniteAssignmentAnalysis.cs" />
<Compile Include="CSharp\Analysis\MinimalResolveContext.cs" />
<Compile Include="CSharp\Ast\AstNodeCollection.cs" />
<Compile Include="CSharp\Ast\Expressions\TypeReferenceExpression.cs" />
<Compile Include="CSharp\Ast\IAstVisitor.cs" />
Expand Down

0 comments on commit 2793b23

Please sign in to comment.