Browse Source

Allow performing definite assignment analysis without providing an ITypeResolveContext.

pull/100/head
Daniel Grunwald 15 years ago
parent
commit
2793b233e3
  1. 49
      NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs
  2. 23
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  3. 35
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  4. 116
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs
  5. 2
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  6. 1
      NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

49
NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs

@ -148,5 +148,54 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -148,5 +148,54 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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));
}
}
}

23
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs

@ -146,7 +146,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -146,7 +146,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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)
@ -249,12 +250,21 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -249,12 +250,21 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
#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;
}
@ -262,11 +272,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -262,11 +272,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// 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;
}
@ -328,6 +338,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -328,6 +338,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
foreach (Statement stmt in statements) {
if (childNode == null) {
childNode = builder.CreateStartNode(stmt);
if (source != null)
Connect(source, childNode);
}
Debug.Assert(childNode.NextStatement == stmt);

35
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs

@ -44,10 +44,12 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -44,10 +44,12 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// </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>();
@ -58,8 +60,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -58,8 +60,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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))
{
}
@ -72,16 +80,18 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -72,16 +80,18 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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());
@ -91,6 +101,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -91,6 +101,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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);
}
}
@ -136,6 +148,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -136,6 +148,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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>
@ -304,7 +321,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -304,7 +321,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <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>
@ -313,11 +330,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -313,11 +330,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <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)

116
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs

@ -0,0 +1,116 @@ @@ -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
}
}
}

2
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -428,7 +428,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -428,7 +428,7 @@ namespace ICSharpCode.NRefactory.CSharp
if (block != null)
VisitBlockStatement(block, null);
else
throw new NotImplementedException();
embeddedStatement.AcceptVisitor(this, null);
}
void WriteMethodBody(BlockStatement body)

1
NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -63,6 +63,7 @@ @@ -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" />

Loading…
Cancel
Save