Browse Source

Merge NRefactory changes from ILSpy.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
e8d8ff8da8
  1. 75
      ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs
  2. 9
      ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs
  3. 2
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
  4. 2
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  5. 28
      ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs
  6. 101
      ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
  7. 80
      ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  8. 222
      ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  9. 116
      ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs
  10. 57
      ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs
  11. 11
      ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  12. 2
      ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs
  13. 32
      ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs
  14. 6
      ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  15. 8
      ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs
  16. 2
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  17. 12
      ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs
  18. 15
      ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  19. 10
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  20. 260
      ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
  21. 5
      ICSharpCode.NRefactory/TypeSystem/IAccessor.cs
  22. 6
      ICSharpCode.NRefactory/TypeSystem/IAttribute.cs
  23. 2
      ICSharpCode.NRefactory/TypeSystem/IMethod.cs
  24. 6
      ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
  25. 4
      ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs
  26. 17
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs
  27. 60
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs
  28. 9
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs

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

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -39,7 +40,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -39,7 +40,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Statement stmt5 = tryCatchStatement.FinallyBlock.Statements.Single();
LabelStatement label = (LabelStatement)block.Statements.ElementAt(1);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(tryCatchStatement));
@ -89,7 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -89,7 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement()
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
@ -120,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -120,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement()
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
@ -128,5 +129,73 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -128,5 +129,73 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(ifStmt.FalseStatement));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(ifStmt));
}
[Test]
public void WhileTrue()
{
WhileStatement loop = new WhileStatement {
Condition = new PrimitiveExpression(true),
EmbeddedStatement = new BlockStatement {
new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(0)),
new BreakStatement()
}
};
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.EmbeddedStatement));
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));
}
}
}

9
ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs

@ -272,5 +272,14 @@ public abstract class MyClass : MyBase, Interface1, My.Test.Interface2 @@ -272,5 +272,14 @@ public abstract class MyClass : MyBase, Interface1, My.Test.Interface2
Assert.AreEqual("N", ns.Name);
Assert.AreEqual("MyClass", ((TypeDeclaration)ns.Members.Single()).Name);
}
[Test]
public void EnumWithInitializer()
{
TypeDeclaration td = ParseUtilCSharp.ParseGlobal<TypeDeclaration>("enum MyEnum { Val1 = 10 }");
EnumMemberDeclaration member = (EnumMemberDeclaration)td.Members.Single();
Assert.AreEqual("Val1", member.Name);
Assert.AreEqual(10, ((PrimitiveExpression)member.Initializer).Value);
}
}
}

2
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs

@ -171,7 +171,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -171,7 +171,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
protected T Resolve<T>(string code) where T : ResolveResult
{
ResolveResult rr = Resolve(code);
Assert.IsTrue(rr is T);
Assert.IsTrue(rr is T, "Resolve should be " + typeof(T).Name + ", but was " + (rr != null ? rr.GetType().Name : "null"));
return (T)rr;
}

2
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="nunit.framework">
<HintPath>..\lib\nunit.framework.dll</HintPath>
<HintPath>..\..\Mono.Cecil\Test\libs\nunit-2.4.8\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">

28
ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
[assembly: ICSharpCode.NRefactory.TypeSystem.TestCase.TypeTestAttribute(
42, typeof(System.Action<>), typeof(IDictionary<string, IList<NUnit.Framework.TestAttribute>>))]
@ -65,4 +66,31 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase @@ -65,4 +66,31 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase
{
public MyStructWithCtor(int a) {}
}
[Serializable]
public class NonCustomAttributes
{
[NonSerialized]
public readonly int NonSerializedField;
[DllImport("unmanaged.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DllMethod([In, Out] ref int p);
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8)]
public struct ExplicitFieldLayoutStruct
{
[FieldOffset(0)]
public int Field0;
[FieldOffset(100)]
public int Field100;
}
public class ParameterTests
{
public void MethodWithOutParameter(out int x) { x = 0; }
public void MethodWithParamsArray(params object[] x) {}
}
}

101
ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.TypeSystem.TestCase;
using NUnit.Framework;
@ -261,5 +262,105 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -261,5 +262,105 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual(2, ctors.Count());
Assert.IsFalse(ctors.Any(c => c.IsStatic));
}
[Test]
public void SerializableAttribute()
{
IAttribute attr = ctx.GetClass(typeof(NonCustomAttributes)).Attributes.Single();
Assert.AreEqual("System.SerializableAttribute", attr.AttributeType.Resolve(ctx).FullName);
}
[Test]
public void NonSerializedAttribute()
{
IField field = ctx.GetClass(typeof(NonCustomAttributes)).Fields.Single(f => f.Name == "NonSerializedField");
Assert.AreEqual("System.NonSerializedAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName);
}
[Test]
public void ExplicitStructLayoutAttribute()
{
IAttribute attr = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Attributes.Single();
Assert.AreEqual("System.Runtime.InteropServices.StructLayoutAttribute", attr.AttributeType.Resolve(ctx).FullName);
IConstantValue arg1 = attr.PositionalArguments.Single();
Assert.AreEqual("System.Runtime.InteropServices.LayoutKind", arg1.GetValueType(ctx).FullName);
Assert.AreEqual((int)LayoutKind.Explicit, arg1.GetValue(ctx));
var arg2 = attr.NamedArguments[0];
Assert.AreEqual("CharSet", arg2.Key);
Assert.AreEqual("System.Runtime.InteropServices.CharSet", arg2.Value.GetValueType(ctx).FullName);
Assert.AreEqual((int)CharSet.Unicode, arg2.Value.GetValue(ctx));
var arg3 = attr.NamedArguments[1];
Assert.AreEqual("Pack", arg3.Key);
Assert.AreEqual("System.Int32", arg3.Value.GetValueType(ctx).FullName);
Assert.AreEqual(8, arg3.Value.GetValue(ctx));
}
[Test]
public void FieldOffsetAttribute()
{
IField field = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field0");
Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName);
IConstantValue arg = field.Attributes.Single().PositionalArguments.Single();
Assert.AreEqual("System.Int32", arg.GetValueType(ctx).FullName);
Assert.AreEqual(0, arg.GetValue(ctx));
field = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field100");
Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName);
arg = field.Attributes.Single().PositionalArguments.Single();
Assert.AreEqual("System.Int32", arg.GetValueType(ctx).FullName);
Assert.AreEqual(100, arg.GetValue(ctx));
}
[Test]
public void DllImportAttribute()
{
IMethod method = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod");
IAttribute dllImport = method.Attributes.Single();
Assert.AreEqual("System.Runtime.InteropServices.DllImportAttribute", dllImport.AttributeType.Resolve(ctx).FullName);
Assert.AreEqual("unmanaged.dll", dllImport.PositionalArguments[0].GetValue(ctx));
Assert.AreEqual((int)CharSet.Unicode, dllImport.NamedArguments.Single().Value.GetValue(ctx));
}
[Test]
public void InOutParametersOnRefMethod()
{
IParameter p = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single();
Assert.IsTrue(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(2, p.Attributes.Count);
Assert.AreEqual("System.Runtime.InteropServices.InAttribute", p.Attributes[0].AttributeType.Resolve(ctx).FullName);
Assert.AreEqual("System.Runtime.InteropServices.OutAttribute", p.Attributes[1].AttributeType.Resolve(ctx).FullName);
}
[Test]
public void MarshalAsAttributeOnMethod()
{
IMethod method = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod");
IAttribute marshalAs = method.ReturnTypeAttributes.Single();
Assert.AreEqual((int)UnmanagedType.Bool, marshalAs.PositionalArguments.Single().GetValue(ctx));
}
[Test]
public void MethodWithOutParameter()
{
IParameter p = ctx.GetClass(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOutParameter").Parameters.Single();
Assert.IsFalse(p.IsRef);
Assert.IsTrue(p.IsOut);
Assert.AreEqual(0, p.Attributes.Count);
Assert.IsTrue(p.Type is ByReferenceTypeReference);
}
[Test]
public void MethodWithParamsArray()
{
IParameter p = ctx.GetClass(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithParamsArray").Parameters.Single();
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsTrue(p.IsParams);
Assert.AreEqual(0, p.Attributes.Count);
Assert.IsTrue(p.Type is ArrayTypeReference);
}
}
}

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

@ -5,7 +5,10 @@ using System; @@ -5,7 +5,10 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
@ -135,12 +138,25 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -135,12 +138,25 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
Statement rootStatement;
ResolveVisitor resolveVisitor;
List<ControlFlowNode> nodes;
Dictionary<string, ControlFlowNode> labels;
List<ControlFlowNode> gotoStatements;
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement)
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
{
return BuildControlFlowGraph(statement, new ResolveVisitor(
new CSharpResolver(context, cancellationToken),
null, ConstantModeResolveVisitorNavigator.Skip));
}
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor)
{
if (statement == null)
throw new ArgumentNullException("statement");
if (resolveVisitor == null)
throw new ArgumentNullException("resolveVisitor");
NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor();
nodeCreationVisitor.builder = this;
try {
@ -148,6 +164,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -148,6 +164,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.labels = new Dictionary<string, ControlFlowNode>();
this.gotoStatements = new List<ControlFlowNode>();
this.rootStatement = statement;
this.resolveVisitor = resolveVisitor;
ControlFlowNode entryPoint = CreateStartNode(statement);
statement.AcceptVisitor(nodeCreationVisitor, entryPoint);
@ -167,6 +184,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -167,6 +184,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.labels = null;
this.gotoStatements = null;
this.rootStatement = null;
this.resolveVisitor = null;
}
}
@ -204,14 +222,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -204,14 +222,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return node;
}
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type)
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true)
{
ControlFlowNode node = CreateNode(statement, null, type);
nodes.Add(node);
ControlFlowNode node = CreateNode(null, statement, type);
if (addToNodeList)
nodes.Add(node);
return node;
}
ControlFlowNode CreateEndNode(Statement statement)
ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true)
{
Statement nextStatement;
if (statement == rootStatement) {
@ -226,19 +245,29 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -226,19 +245,29 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode;
ControlFlowNode node = CreateNode(statement, nextStatement, type);
nodes.Add(node);
if (addToNodeList)
nodes.Add(node);
return node;
}
#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)
{
return null; // TODO: implement this using the C# resolver
if (EvaluateOnlyPrimitiveConstants) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
/// <summary>
@ -247,16 +276,20 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -247,16 +276,20 @@ 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?;
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
return rr.ConstantValue as bool?;
else
return null;
}
bool AreEqualConstants(ConstantResolveResult c1, ConstantResolveResult c2)
{
return false; // TODO: implement this using the resolver's operator==
if (c1 == null || c2 == null)
return false;
CSharpResolver r = new CSharpResolver(resolveVisitor.TypeResolveContext, resolveVisitor.CancellationToken);
ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2);
return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true;
}
#endregion
@ -307,7 +340,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -307,7 +340,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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);
@ -386,7 +420,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -386,7 +420,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count;
ControlFlowNode end = builder.CreateEndNode(switchStatement);
ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false);
breakTargets.Push(end);
foreach (SwitchSection section in switchStatement.SwitchSections) {
if (constant == null || section == sectionMatchedByConstant) {
@ -407,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -407,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new NotImplementedException();
}
builder.nodes.Add(end);
return end;
}
@ -425,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -425,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data)
{
// <data> <condition> while (cond) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(whileStatement);
ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition);
breakTargets.Push(end);
continueTargets.Push(conditionNode);
@ -443,14 +478,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -443,14 +478,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop();
continueTargets.Pop();
builder.nodes.Add(end);
return end;
}
public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data)
{
// <data> do { <bodyStart> embeddedStmt; <bodyEnd>} <condition> while(cond); <end>
ControlFlowNode end = builder.CreateEndNode(doWhileStatement);
ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition);
ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false);
breakTargets.Push(end);
continueTargets.Push(conditionNode);
@ -467,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -467,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop();
continueTargets.Pop();
builder.nodes.Add(conditionNode);
builder.nodes.Add(end);
return end;
}
@ -474,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -474,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
data = HandleStatementList(forStatement.Initializers, data);
// for (initializers <data>; <condition>cond; <iteratorStart>iterators<iteratorEnd>) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(forStatement);
ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode);
@ -504,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -504,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (cond != true)
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
builder.nodes.Add(end);
return end;
}
@ -520,7 +559,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -520,7 +559,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data)
{
// <data> foreach (<condition>...) { <bodyStart>embeddedStmt<bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(foreachStatement);
ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode);
@ -534,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -534,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
continueTargets.Pop();
Connect(conditionNode, end);
builder.nodes.Add(end);
return end;
}
@ -570,7 +609,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -570,7 +609,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data)
{
ControlFlowNode end = builder.CreateEndNode(tryCatchStatement);
ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false);
var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end);
if (!tryCatchStatement.FinallyBlock.IsNull)
edge.AddJumpOutOfTryFinally(tryCatchStatement);
@ -584,6 +623,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -584,6 +623,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
// Consumers of the CFG will have to special-case try-finally.
HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data);
}
builder.nodes.Add(end);
return end;
}

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

@ -5,7 +5,9 @@ using System; @@ -5,7 +5,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -42,40 +44,112 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -42,40 +44,112 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// </summary>
public class DefiniteAssignmentAnalysis
{
sealed class DefiniteAssignmentNode : ControlFlowNode
{
public int Index;
public DefiniteAssignmentStatus NodeStatus;
public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
: base(previousStatement, nextStatement, type)
{
}
}
sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder
{
protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
{
return new DefiniteAssignmentNode(previousStatement, nextStatement, type);
}
}
readonly DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder();
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>();
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
readonly List<DefiniteAssignmentNode> allNodes = new List<DefiniteAssignmentNode>();
readonly Dictionary<Statement, DefiniteAssignmentNode> beginNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly Dictionary<Statement, DefiniteAssignmentNode> endNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly Dictionary<Statement, DefiniteAssignmentNode> conditionNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly ResolveVisitor resolveVisitor;
readonly CancellationToken cancellationToken;
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
string variableName;
List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>();
int analyzedRangeStart, analyzedRangeEnd;
Queue<DefiniteAssignmentNode> nodesWithModifiedInput = new Queue<DefiniteAssignmentNode>();
public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, null, cancellationToken)
{
}
Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>();
public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, new ResolveVisitor(new CSharpResolver(context ?? MinimalResolveContext.Instance, cancellationToken),
null, ConstantModeResolveVisitorNavigator.Skip))
{
}
public DefiniteAssignmentAnalysis(Statement rootStatement)
public DefiniteAssignmentAnalysis(Statement rootStatement, ResolveVisitor resolveVisitor)
{
if (rootStatement == null)
throw new ArgumentNullException("rootStatement");
if (resolveVisitor == null)
throw new ArgumentNullException("resolveVisitor");
this.resolveVisitor = resolveVisitor;
this.cancellationToken = resolveVisitor.CancellationToken;
visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder();
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement));
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));
LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body));
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
}
// Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
// Now register the nodes in the dictionaries:
foreach (ControlFlowNode node in allNodes) {
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolveVisitor).Cast<DefiniteAssignmentNode>());
for (int i = 0; i < allNodes.Count; i++) {
DefiniteAssignmentNode node = allNodes[i];
node.Index = i; // assign numbers to the nodes
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) {
// Anonymous methods have separate control flow graphs, but we also need to analyze those.
// Iterate backwards so that anonymous methods are inserted in the correct order
for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) {
InsertAnonymousMethods(i + 1, child);
}
}
// Now register the node in the dictionaries:
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
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);
}
// Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
// Verify that we put all nodes into the dictionaries:
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => beginNodeDict.ContainsKey(stmt)));
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => endNodeDict.ContainsKey(stmt)));
this.analyzedRangeStart = 0;
this.analyzedRangeEnd = allNodes.Count - 1;
}
void InsertAnonymousMethods(int insertPos, AstNode node)
{
// Ignore any statements, as those have their own ControlFlowNode and get handled separately
if (node is Statement)
return;
AnonymousMethodExpression ame = node as AnonymousMethodExpression;
if (ame != null) {
allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolveVisitor).Cast<DefiniteAssignmentNode>());
return;
}
LambdaExpression lambda = node as LambdaExpression;
if (lambda != null && lambda.Body is Statement) {
allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor).Cast<DefiniteAssignmentNode>());
return;
}
// Descend into child expressions
// Iterate backwards so that anonymous methods are inserted in the correct order
for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) {
InsertAnonymousMethods(insertPos, child);
}
}
@ -88,21 +162,38 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -88,21 +162,38 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
}
/// <summary>
/// Sets the range of statements to be analyzed.
/// This method can be used to restrict the analysis to only a part of the method.
/// Only the control flow paths that are fully contained within the selected part will be analyzed.
/// </summary>
/// <remarks>Both 'start' and 'end' are inclusive.</remarks>
public void SetAnalyzedRange(Statement start, Statement end)
{
Debug.Assert(beginNodeDict.ContainsKey(start) && endNodeDict.ContainsKey(end));
int startIndex = beginNodeDict[start].Index;
int endIndex = endNodeDict[end].Index;
if (startIndex > endIndex)
throw new ArgumentException("The start statement must be lexically preceding the end statement");
this.analyzedRangeStart = startIndex;
this.analyzedRangeEnd = endIndex;
}
public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned)
{
this.variableName = variable;
// Reset the status:
unassignedVariableUses.Clear();
foreach (ControlFlowNode node in allNodes) {
nodeStatus[node] = DefiniteAssignmentStatus.CodeUnreachable;
foreach (DefiniteAssignmentNode node in allNodes) {
node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Outgoing)
edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
}
ChangeNodeStatus(allNodes[0], initialStatus);
ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus);
// Iterate as long as the input status of some nodes is changing:
while (nodesWithModifiedInput.Count > 0) {
ControlFlowNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Incoming) {
inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
@ -113,12 +204,17 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -113,12 +204,17 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public DefiniteAssignmentStatus GetStatusBefore(Statement statement)
{
return nodeStatus[beginNodeDict[statement]];
return beginNodeDict[statement].NodeStatus;
}
public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
{
return nodeStatus[endNodeDict[statement]];
return endNodeDict[statement].NodeStatus;
}
public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement)
{
return conditionNodeDict[statement].NodeStatus;
}
/// <summary>
@ -129,7 +225,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -129,7 +225,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
GraphVizGraph g = new GraphVizGraph();
g.Title = "DefiniteAssignment - " + variableName;
for (int i = 0; i < allNodes.Count; i++) {
string name = nodeStatus[allNodes[i]].ToString() + Environment.NewLine;
string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine;
switch (allNodes[i].Type) {
case ControlFlowNodeType.StartNode:
case ControlFlowNodeType.BetweenStatements:
@ -147,7 +243,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -147,7 +243,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
g.AddNode(new GraphVizNode(i) { label = name });
foreach (ControlFlowEdge edge in allNodes[i].Outgoing) {
GraphVizEdge ge = new GraphVizEdge(i, allNodes.IndexOf(edge.To));
GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index);
if (edgeStatus.Count > 0)
ge.label = edgeStatus[edge].ToString();
if (edge.IsLeavingTryFinally)
@ -186,11 +282,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -186,11 +282,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return DefiniteAssignmentStatus.PotentiallyAssigned;
}
void ChangeNodeStatus(ControlFlowNode node, DefiniteAssignmentStatus inputStatus)
void ChangeNodeStatus(DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus)
{
if (nodeStatus[node] == inputStatus)
if (node.NodeStatus == inputStatus)
return;
nodeStatus[node] = inputStatus;
node.NodeStatus = inputStatus;
DefiniteAssignmentStatus outputStatus;
switch (node.Type) {
case ControlFlowNodeType.StartNode:
@ -262,25 +358,26 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -262,25 +358,26 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
DefiniteAssignmentStatus oldStatus = edgeStatus[edge];
if (oldStatus == newStatus)
return;
// Ensure that status can change only in one direction:
// CodeUnreachable -> PotentiallyAssigned -> DefinitelyAssigned
// Going against this direction indicates a bug and could cause infinite loops.
switch (oldStatus) {
case DefiniteAssignmentStatus.PotentiallyAssigned:
if (newStatus != DefiniteAssignmentStatus.DefinitelyAssigned)
throw new InvalidOperationException("Invalid state transition");
break;
case DefiniteAssignmentStatus.CodeUnreachable:
if (!(newStatus == DefiniteAssignmentStatus.PotentiallyAssigned || newStatus == DefiniteAssignmentStatus.DefinitelyAssigned))
throw new InvalidOperationException("Invalid state transition");
break;
case DefiniteAssignmentStatus.DefinitelyAssigned:
throw new InvalidOperationException("Invalid state transition");
default:
throw new InvalidOperationException("Invalid value for DefiniteAssignmentStatus");
// Ensure that status can cannot change back to CodeUnreachable after it once was reachable.
// Also, don't ever use AssignedAfter... for statements.
if (newStatus == DefiniteAssignmentStatus.CodeUnreachable
|| newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression
|| newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
{
throw new InvalidOperationException();
}
// Note that the status can change from DefinitelyAssigned
// back to PotentiallyAssigned as unreachable input edges are
// discovered to be reachable.
edgeStatus[edge] = newStatus;
nodesWithModifiedInput.Enqueue(edge.To);
DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To;
if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) {
// TODO: potential optimization: visit previously unreachable nodes with higher priority
// (e.g. use Deque and enqueue previously unreachable nodes at the front, but
// other nodes at the end)
nodesWithModifiedInput.Enqueue(targetNode);
}
}
/// <summary>
@ -289,7 +386,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -289,7 +386,11 @@ 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 null; // TODO: implement this using the C# resolver
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
/// <summary>
@ -298,9 +399,9 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -298,9 +399,9 @@ 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?;
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
return rr.ConstantValue as bool?;
else
return null;
}
@ -462,11 +563,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -462,11 +563,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
IdentifierExpression ident = left as IdentifierExpression;
if (ident != null && ident.Identifier == analysis.variableName) {
right.AcceptVisitor(this, initialStatus);
// right==null is special case when handling 'out' expressions
if (right != null)
right.AcceptVisitor(this, initialStatus);
return DefiniteAssignmentStatus.DefinitelyAssigned;
} else {
DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus);
status = right.AcceptVisitor(this, CleanSpecialValues(status));
if (right != null)
status = right.AcceptVisitor(this, CleanSpecialValues(status));
return CleanSpecialValues(status);
}
}
@ -605,10 +709,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -605,10 +709,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data)
{
BlockStatement body = anonymousMethodExpression.Body;
foreach (ControlFlowNode node in analysis.allNodes) {
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
return data;
}
@ -616,10 +717,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -616,10 +717,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
Statement body = lambdaExpression.Body as Statement;
if (body != null) {
foreach (ControlFlowNode node in analysis.allNodes) {
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
} else {
lambdaExpression.Body.AcceptVisitor(this, data);
}

116
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
}
}
}

57
ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs

@ -129,6 +129,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -129,6 +129,7 @@ namespace ICSharpCode.NRefactory.CSharp
get {
AstNode next;
for (AstNode cur = firstChild; cur != null; cur = next) {
Debug.Assert(cur.parent == this);
// Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list.
next = cur.nextSibling;
@ -537,49 +538,23 @@ namespace ICSharpCode.NRefactory.CSharp @@ -537,49 +538,23 @@ namespace ICSharpCode.NRefactory.CSharp
return null;
}
public IEnumerable<T> Annotations<T>() where T: class
{
object annotations = this.annotations;
AnnotationList list = annotations as AnnotationList;
if (list != null) {
List<T> result = new List<T>();
lock (list) {
foreach (object obj in list) {
T t = obj as T;
if (t != null)
result.Add(t);
}
}
return result;
} else {
T t = annotations as T;
if (t != null)
return new T[] { t };
else
return Enumerable.Empty<T>();
}
}
public IEnumerable<object> Annotations(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
object annotations = this.annotations;
AnnotationList list = annotations as AnnotationList;
if (list != null) {
List<object> result = new List<object>();
lock (list) {
foreach (object obj in list) {
if (type.IsInstanceOfType(obj))
result.Add(obj);
/// <summary>
/// Gets all annotations stored on this AstNode.
/// </summary>
public IEnumerable<object> Annotations {
get {
object annotations = this.annotations;
AnnotationList list = annotations as AnnotationList;
if (list != null) {
lock (list) {
return list.ToArray();
}
} else {
if (annotations != null)
return new object[] { annotations };
else
return Enumerable.Empty<object>();
}
return result;
} else {
if (type.IsInstanceOfType(annotations))
return new object[] { annotations };
else
return Enumerable.Empty<object>();
}
}
#endregion

11
ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs

@ -117,6 +117,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -117,6 +117,7 @@ namespace ICSharpCode.NRefactory.CSharp
{
AstNode next;
for (AstNode cur = node.FirstChild; cur != null; cur = next) {
Debug.Assert(cur.Parent == node);
// Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list.
next = cur.NextSibling;
@ -192,5 +193,15 @@ namespace ICSharpCode.NRefactory.CSharp @@ -192,5 +193,15 @@ namespace ICSharpCode.NRefactory.CSharp
}
return false;
}
public void InsertAfter(T existingItem, T newItem)
{
node.InsertChildAfter(existingItem, newItem, role);
}
public void InsertBefore(T existingItem, T newItem)
{
node.InsertChildBefore(existingItem, newItem, role);
}
}
}

2
ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs

@ -55,6 +55,8 @@ namespace ICSharpCode.NRefactory.CSharp @@ -55,6 +55,8 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildrenByRole(PointerRole).Count();
}
set {
if (value < 0)
throw new ArgumentOutOfRangeException();
int d = this.PointerRank;
while (d > value) {
GetChildByRole(PointerRole).Remove();

32
ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs

@ -38,6 +38,38 @@ namespace ICSharpCode.NRefactory.CSharp @@ -38,6 +38,38 @@ namespace ICSharpCode.NRefactory.CSharp
}
#endregion
/// <summary>
/// Gets the previous statement within the current block.
/// This is usually equivalent to <see cref="PrevSibling"/>, but will skip any non-statements (e.g. comments)
/// </summary>
public Statement PreviousStatement {
get {
AstNode node = this;
while ((node = node.PrevSibling) != null) {
Statement stmt = node as Statement;
if (stmt != null)
return stmt;
}
return null;
}
}
/// <summary>
/// Gets the next statement within the current block.
/// This is usually equivalent to <see cref="NextSibling"/>, but will skip any non-statements (e.g. comments)
/// </summary>
public Statement NextStatement {
get {
AstNode node = this;
while ((node = node.NextSibling) != null) {
Statement stmt = node as Statement;
if (stmt != null)
return stmt;
}
return null;
}
}
public new Statement Clone()
{
return (Statement)base.Clone();

6
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)
@ -1405,9 +1405,13 @@ namespace ICSharpCode.NRefactory.CSharp @@ -1405,9 +1405,13 @@ namespace ICSharpCode.NRefactory.CSharp
{
StartNode(fixedStatement);
WriteKeyword("fixed");
Space(policy.SpaceBeforeUsingParentheses);
LPar();
Space(policy.SpacesWithinUsingParentheses);
fixedStatement.Type.AcceptVisitor(this, data);
Space();
WriteCommaSeparatedList(fixedStatement.Variables);
Space(policy.SpacesWithinUsingParentheses);
RPar();
WriteEmbeddedStatement(fixedStatement.EmbeddedStatement);
return EndNode(fixedStatement);

8
ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs

@ -382,15 +382,13 @@ namespace ICSharpCode.NRefactory.CSharp @@ -382,15 +382,13 @@ namespace ICSharpCode.NRefactory.CSharp
public override void Visit (EnumMember em)
{
EnumMemberDeclaration newField = new EnumMemberDeclaration ();
VariableInitializer variable = new VariableInitializer ();
variable.AddChild (new Identifier (em.Name, Convert (em.Location)), AstNode.Roles.Identifier);
// TODO: attributes, 'new' modifier
newField.AddChild (new Identifier (em.Name, Convert (em.Location)), AstNode.Roles.Identifier);
if (em.Initializer != null) {
variable.AddChild ((Expression)em.Initializer.Accept (this), VariableInitializer.Roles.Expression);
newField.AddChild ((Expression)em.Initializer.Accept (this), EnumMemberDeclaration.InitializerRole);
}
newField.AddChild (variable, AstNode.Roles.Variable);
typeStack.Peek ().AddChild (newField, TypeDeclaration.MemberRole);
}
#endregion

2
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -33,7 +33,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -33,7 +33,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.context = context;
}
#if !DOTNET35
public CSharpResolver(ITypeResolveContext context, CancellationToken cancellationToken)
{
if (context == null)
@ -41,7 +40,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -41,7 +40,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.context = context;
this.cancellationToken = cancellationToken;
}
#endif
#endregion
#region Properties

12
ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs

@ -37,4 +37,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -37,4 +37,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary>
ResolveAll
}
sealed class ConstantModeResolveVisitorNavigator : IResolveVisitorNavigator
{
ResolveVisitorNavigationMode mode;
public static readonly IResolveVisitorNavigator Skip = new ConstantModeResolveVisitorNavigator { mode = ResolveVisitorNavigationMode.Skip };
ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node)
{
return mode;
}
}
}

15
ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs

@ -74,6 +74,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -74,6 +74,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#endregion
/// <summary>
/// Gets the TypeResolveContext used by this ResolveVisitor.
/// </summary>
public ITypeResolveContext TypeResolveContext {
get { return resolver.Context; }
}
/// <summary>
/// Gets the CancellationToken used by this ResolveVisitor.
/// </summary>
public CancellationToken CancellationToken {
get { return resolver.cancellationToken; }
}
#region Scan / Resolve
bool resolverEnabled {
get { return mode != ResolveVisitorNavigationMode.Scan; }
@ -118,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -118,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
mode = ResolveVisitorNavigationMode.Resolve;
ResolveResult result;
if (!cache.TryGetValue(node, out result)) {
resolver.cancellationToken.ThrowIfCancellationRequested();
result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult;
}
if (wasScan)

10
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</ProjectGuid>
@ -53,11 +53,11 @@ @@ -53,11 +53,11 @@
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
</Reference>
<Reference Include="Mono.Cecil, Version=0.9.4.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
</ItemGroup>
<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" />
@ -354,5 +354,11 @@ @@ -354,5 +354,11 @@
<ItemGroup>
<Folder Include="CSharp\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

260
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public bool IncludeInternalMembers { get; set; }
/// <summary>
/// Gets/Sets the documentation provider that is used to retrive the XML documentation for all members.
/// Gets/Sets the documentation provider that is used to retrieve the XML documentation for all members.
/// </summary>
public IDocumentationProvider DocumentationProvider { get; set; }
@ -46,6 +46,10 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -46,6 +46,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
#endregion
#region Load From AssemblyDefinition
/// <summary>
/// Loads the assembly definition into a project content.
/// </summary>
/// <returns>IProjectContent that represents the assembly</returns>
public IProjectContent LoadAssembly(AssemblyDefinition assemblyDefinition)
{
if (assemblyDefinition == null)
@ -144,7 +148,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -144,7 +148,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public void Dispose()
{
// Disposibng the synchronization context has no effect
// Disposing the synchronization context has no effect
}
string IDocumentationProvider.GetDocumentation(IEntity entity)
@ -297,14 +301,13 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -297,14 +301,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
}
const string DynamicAttributeFullName = "System.Runtime.CompilerServices.DynamicAttribute";
static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex)
{
if (attributeProvider == null || !attributeProvider.HasCustomAttributes)
return false;
foreach (CustomAttribute a in attributeProvider.CustomAttributes) {
if (a.Constructor.DeclaringType.FullName == DynamicAttributeFullName) {
TypeReference type = a.AttributeType;
if (type.Name == "DynamicAttribute" && type.Namespace == "System.Runtime.CompilerServices") {
if (a.ConstructorArguments.Count == 1) {
CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[];
if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool)
@ -325,21 +328,150 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -325,21 +328,150 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
}
static readonly IAttribute inAttribute = new DefaultAttribute(typeof(InAttribute).ToTypeReference(), null);
static readonly IAttribute outAttribute = new DefaultAttribute(typeof(OutAttribute).ToTypeReference(), null);
void AddAttributes(ParameterDefinition parameter, DefaultParameter targetParameter)
{
if (!targetParameter.IsOut) {
if (parameter.IsIn)
targetParameter.Attributes.Add(inAttribute);
if (parameter.IsOut)
targetParameter.Attributes.Add(outAttribute);
}
if (parameter.HasCustomAttributes) {
AddCustomAttributes(parameter.CustomAttributes, targetParameter.Attributes);
}
}
void AddAttributes(MethodDefinition accessorMethod, DefaultAccessor targetAccessor)
static readonly ITypeReference dllImportAttributeTypeRef = typeof(DllImportAttribute).ToTypeReference();
static readonly SimpleConstantValue trueValue = new SimpleConstantValue(KnownTypeReference.Boolean, true);
static readonly SimpleConstantValue falseValue = new SimpleConstantValue(KnownTypeReference.Boolean, true);
static readonly ITypeReference callingConventionTypeRef = typeof(CallingConvention).ToTypeReference();
static readonly IAttribute preserveSigAttribute = new DefaultAttribute(typeof(PreserveSigAttribute).ToTypeReference(), null);
static readonly ITypeReference methodImplAttributeTypeRef = typeof(MethodImplAttribute).ToTypeReference();
static readonly ITypeReference methodImplOptionsTypeRef = typeof(MethodImplOptions).ToTypeReference();
bool HasAnyAttributes(MethodDefinition methodDefinition)
{
if (accessorMethod.HasCustomAttributes) {
AddCustomAttributes(accessorMethod.CustomAttributes, targetAccessor.Attributes);
if (methodDefinition.HasPInvokeInfo)
return true;
if ((methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask) != 0)
return true;
if (methodDefinition.MethodReturnType.HasFieldMarshal)
return true;
return methodDefinition.HasCustomAttributes || methodDefinition.MethodReturnType.HasCustomAttributes;
}
void AddAttributes(MethodDefinition methodDefinition, IList<IAttribute> attributes, IList<IAttribute> returnTypeAttributes)
{
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
#region DllImportAttribute
if (methodDefinition.HasPInvokeInfo) {
PInvokeInfo info = methodDefinition.PInvokeInfo;
DefaultAttribute dllImport = new DefaultAttribute(dllImportAttributeTypeRef, new[] { KnownTypeReference.String });
dllImport.PositionalArguments.Add(new SimpleConstantValue(KnownTypeReference.String, info.Module.Name));
if (info.IsBestFitDisabled)
AddNamedArgument(dllImport, "BestFitMapping", falseValue);
if (info.IsBestFitEnabled)
AddNamedArgument(dllImport, "BestFitMapping", trueValue);
CallingConvention callingConvention;
switch (info.Attributes & PInvokeAttributes.CallConvMask) {
case PInvokeAttributes.CallConvCdecl:
callingConvention = CallingConvention.Cdecl;
break;
case PInvokeAttributes.CallConvFastcall:
callingConvention = CallingConvention.FastCall;
break;
case PInvokeAttributes.CallConvStdCall:
callingConvention = CallingConvention.StdCall;
break;
case PInvokeAttributes.CallConvThiscall:
callingConvention = CallingConvention.ThisCall;
break;
case PInvokeAttributes.CallConvWinapi:
callingConvention = CallingConvention.Winapi;
break;
default:
throw new NotSupportedException("unknown calling convention");
}
if (callingConvention != CallingConvention.Winapi)
AddNamedArgument(dllImport, "CallingConvention", new SimpleConstantValue(callingConventionTypeRef, (int)callingConvention));
CharSet charSet = CharSet.None;
switch (info.Attributes & PInvokeAttributes.CharSetMask) {
case PInvokeAttributes.CharSetAnsi:
charSet = CharSet.Ansi;
break;
case PInvokeAttributes.CharSetAuto:
charSet = CharSet.Auto;
break;
case PInvokeAttributes.CharSetUnicode:
charSet = CharSet.Unicode;
break;
}
if (charSet != CharSet.None)
dllImport.NamedArguments.Add(new KeyValuePair<string, IConstantValue>(
"CharSet", new SimpleConstantValue(charSetTypeRef, (int)charSet)));
if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name)
AddNamedArgument(dllImport, "EntryPoint", new SimpleConstantValue(KnownTypeReference.String, info.EntryPoint));
if (info.IsNoMangle)
AddNamedArgument(dllImport, "ExactSpelling", trueValue);
if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig)
implAttributes &= ~MethodImplAttributes.PreserveSig;
else
AddNamedArgument(dllImport, "PreserveSig", falseValue);
if (info.SupportsLastError)
AddNamedArgument(dllImport, "SetLastError", trueValue);
if (info.IsThrowOnUnmappableCharDisabled)
AddNamedArgument(dllImport, "ThrowOnUnmappableChar", falseValue);
if (info.IsThrowOnUnmappableCharEnabled)
AddNamedArgument(dllImport, "ThrowOnUnmappableChar", trueValue);
attributes.Add(dllImport);
}
#endregion
#region PreserveSigAttribute
if (implAttributes == MethodImplAttributes.PreserveSig) {
attributes.Add(preserveSigAttribute);
implAttributes = 0;
}
#endregion
#region MethodImplAttribute
if (implAttributes != 0) {
DefaultAttribute methodImpl = new DefaultAttribute(methodImplAttributeTypeRef, new[] { methodImplOptionsTypeRef });
methodImpl.PositionalArguments.Add(new SimpleConstantValue(methodImplOptionsTypeRef, (int)implAttributes));
attributes.Add(methodImpl);
}
#endregion
if (methodDefinition.HasCustomAttributes) {
AddCustomAttributes(methodDefinition.CustomAttributes, attributes);
}
if (methodDefinition.MethodReturnType.HasMarshalInfo) {
returnTypeAttributes.Add(ConvertMarshalInfo(methodDefinition.MethodReturnType.MarshalInfo));
}
if (methodDefinition.MethodReturnType.HasCustomAttributes) {
AddCustomAttributes(methodDefinition.MethodReturnType.CustomAttributes, returnTypeAttributes);
}
}
static void AddNamedArgument(DefaultAttribute attribute, string name, IConstantValue value)
{
attribute.NamedArguments.Add(new KeyValuePair<string, IConstantValue>(name, value));
}
static readonly DefaultAttribute serializableAttribute = new DefaultAttribute(typeof(SerializableAttribute).ToTypeReference());
static readonly DefaultAttribute serializableAttribute = new DefaultAttribute(typeof(SerializableAttribute).ToTypeReference(), null);
static readonly ITypeReference structLayoutAttributeTypeRef = typeof(StructLayoutAttribute).ToTypeReference();
static readonly ITypeReference layoutKindTypeRef = typeof(LayoutKind).ToTypeReference();
static readonly ITypeReference charSetTypeRef = typeof(CharSet).ToTypeReference();
@ -373,8 +505,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -373,8 +505,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
charSet = CharSet.Unicode;
break;
}
if (layoutKind != LayoutKind.Auto || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) {
DefaultAttribute structLayout = new DefaultAttribute(structLayoutAttributeTypeRef);
LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto;
if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) {
DefaultAttribute structLayout = new DefaultAttribute(structLayoutAttributeTypeRef, new[] { layoutKindTypeRef });
structLayout.PositionalArguments.Add(new SimpleConstantValue(layoutKindTypeRef, (int)layoutKind));
if (charSet != CharSet.Ansi) {
structLayout.NamedArguments.Add(new KeyValuePair<string, IConstantValue>(
@ -400,12 +533,58 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -400,12 +533,58 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
}
static readonly ITypeReference fieldOffsetAttributeTypeRef = typeof(FieldOffsetAttribute).ToTypeReference();
static readonly DefaultAttribute nonSerializedAttribute = new DefaultAttribute(typeof(NonSerializedAttribute).ToTypeReference(), null);
void AddAttributes(FieldDefinition fieldDefinition, IEntity targetEntity)
{
#region FieldOffsetAttribute
if (fieldDefinition.HasLayoutInfo) {
DefaultAttribute fieldOffset = new DefaultAttribute(fieldOffsetAttributeTypeRef, new[] { KnownTypeReference.Int32 });
fieldOffset.PositionalArguments.Add(new SimpleConstantValue(KnownTypeReference.Int32, fieldDefinition.Offset));
targetEntity.Attributes.Add(fieldOffset);
}
#endregion
#region NonSerializedAttribute
if (fieldDefinition.IsNotSerialized) {
targetEntity.Attributes.Add(nonSerializedAttribute);
}
#endregion
if (fieldDefinition.HasMarshalInfo) {
targetEntity.Attributes.Add(ConvertMarshalInfo(fieldDefinition.MarshalInfo));
}
if (fieldDefinition.HasCustomAttributes) {
AddCustomAttributes(fieldDefinition.CustomAttributes, targetEntity.Attributes);
}
}
#region MarshalAsAttribute (ConvertMarshalInfo)
static readonly ITypeReference marshalAsAttributeTypeRef = typeof(MarshalAsAttribute).ToTypeReference();
static readonly ITypeReference unmanagedTypeTypeRef = typeof(UnmanagedType).ToTypeReference();
static IAttribute ConvertMarshalInfo(MarshalInfo marshalInfo)
{
DefaultAttribute attr = new DefaultAttribute(marshalAsAttributeTypeRef, new[] { unmanagedTypeTypeRef });
attr.PositionalArguments.Add(new SimpleConstantValue(unmanagedTypeTypeRef, (int)marshalInfo.NativeType));
// TODO: handle classes derived from MarshalInfo
return attr;
}
#endregion
void AddCustomAttributes(Mono.Collections.Generic.Collection<CustomAttribute> attributes, IList<IAttribute> targetCollection)
{
foreach (var cecilAttribute in attributes) {
if (cecilAttribute.AttributeType.FullName != DynamicAttributeFullName) {
targetCollection.Add(ReadAttribute(cecilAttribute));
TypeReference type = cecilAttribute.AttributeType;
if (type.Namespace == "System.Runtime.CompilerServices") {
if (type.Name == "DynamicAttribute" || type.Name == "ExtensionAttribute")
continue;
} else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") {
continue;
}
targetCollection.Add(ReadAttribute(cecilAttribute));
}
}
@ -413,7 +592,15 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -413,7 +592,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
{
if (attribute == null)
throw new ArgumentNullException("attribute");
DefaultAttribute a = new DefaultAttribute(ReadTypeReference(attribute.AttributeType));
MethodReference ctor = attribute.Constructor;
ITypeReference[] ctorParameters = null;
if (ctor.HasParameters) {
ctorParameters = new ITypeReference[ctor.Parameters.Count];
for (int i = 0; i < ctorParameters.Length; i++) {
ctorParameters[i] = ReadTypeReference(ctor.Parameters[i].ParameterType);
}
}
DefaultAttribute a = new DefaultAttribute(ReadTypeReference(attribute.AttributeType), ctorParameters);
try {
if (attribute.HasConstructorArguments) {
foreach (var arg in attribute.ConstructorArguments) {
@ -442,8 +629,13 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -442,8 +629,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
#region Read Constant Value
public IConstantValue ReadConstantValue(CustomAttributeArgument arg)
{
ITypeReference type = ReadTypeReference(arg.Type);
object value = arg.Value;
if (value is CustomAttributeArgument) {
// Cecil uses this representation for boxed values
arg = (CustomAttributeArgument)value;
value = arg.Value;
}
ITypeReference type = ReadTypeReference(arg.Type);
CustomAttributeArgument[] array = value as CustomAttributeArgument[];
if (array != null) {
// TODO: write unit test for this
@ -500,9 +692,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -500,9 +692,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
InitNestedTypes(loader); // nested types can be initialized only after generic parameters were created
if (typeDefinition.HasCustomAttributes) {
loader.AddAttributes(typeDefinition, this);
}
loader.AddAttributes(typeDefinition, this);
this.HasExtensionMethods = HasExtensionAttribute(typeDefinition);
// set base classes
if (typeDefinition.IsEnum) {
@ -699,7 +891,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -699,7 +891,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
else
m.ReturnType = ReadTypeReference(method.ReturnType, typeAttributes: method.MethodReturnType, entity: m);
AddAttributes(method, m);
if (HasAnyAttributes(method))
AddAttributes(method, m.Attributes, m.ReturnTypeAttributes);
TranslateModifiers(method, m);
if (method.HasParameters) {
@ -708,18 +901,26 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -708,18 +901,26 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
}
// mark as extension method is the attribute is set
if (method.IsStatic && method.HasCustomAttributes) {
foreach (var attr in method.CustomAttributes) {
if (attr.AttributeType.FullName == typeof(ExtensionAttribute).FullName)
m.IsExtensionMethod = true;
}
// mark as extension method if the attribute is set
if (method.IsStatic && HasExtensionAttribute(method)) {
m.IsExtensionMethod = true;
}
FinishReadMember(m);
return m;
}
static bool HasExtensionAttribute(ICustomAttributeProvider provider)
{
if (provider.HasCustomAttributes) {
foreach (var attr in provider.CustomAttributes) {
if (attr.AttributeType.Name == "ExtensionAttribute" && attr.AttributeType.Namespace == "System.Runtime.CompilerServices")
return true;
}
}
return false;
}
bool IsVisible(MethodAttributes att)
{
att &= MethodAttributes.MemberAccessMask;
@ -782,14 +983,13 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -782,14 +983,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
var type = ReadTypeReference(parameter.ParameterType, typeAttributes: parameter, entity: parentMember);
DefaultParameter p = new DefaultParameter(type, parameter.Name);
AddAttributes(parameter, p);
if (parameter.ParameterType is Mono.Cecil.ByReferenceType) {
if (parameter.IsOut)
if (!parameter.IsIn && parameter.IsOut)
p.IsOut = true;
else
p.IsRef = true;
}
AddAttributes(parameter, p);
if (parameter.IsOptional) {
p.DefaultValue = ReadConstantValue(new CustomAttributeArgument(parameter.ParameterType, parameter.Constant));
@ -917,10 +1117,10 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -917,10 +1117,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
{
if (accessorMethod != null && IsVisible(accessorMethod.Attributes)) {
Accessibility accessibility = GetAccessibility(accessorMethod.Attributes);
if (accessorMethod.HasCustomAttributes) {
if (HasAnyAttributes(accessorMethod)) {
DefaultAccessor a = new DefaultAccessor();
a.Accessibility = accessibility;
AddAttributes(accessorMethod, a);
AddAttributes(accessorMethod, a.Attributes, a.ReturnTypeAttributes);
return a;
} else {
return DefaultAccessor.GetFromAccessibility(accessibility);

5
ICSharpCode.NRefactory/TypeSystem/IAccessor.cs

@ -21,6 +21,11 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -21,6 +21,11 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
IList<IAttribute> Attributes { get; }
/// <summary>
/// Gets the attributes defined on the return type of the accessor. (e.g. [return: MarshalAs(...)])
/// </summary>
IList<IAttribute> ReturnTypeAttributes { get; }
/// <summary>
/// Gets the accessibility of this accessor.
/// </summary>

6
ICSharpCode.NRefactory/TypeSystem/IAttribute.cs

@ -35,6 +35,12 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -35,6 +35,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Gets the named arguments passed to the attribute.
/// </summary>
IList<KeyValuePair<string, IConstantValue>> NamedArguments { get; }
/// <summary>
/// Resolves the constructor method used for this attribute invocation.
/// Returns null if the constructor cannot be found.
/// </summary>
IMethod ResolveConstructor(ITypeResolveContext context);
}
#if WITH_CONTRACTS

2
ICSharpCode.NRefactory/TypeSystem/IMethod.cs

@ -16,7 +16,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -16,7 +16,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public interface IMethod : IParameterizedMember
{
/// <summary>
/// Gets the attributes associated with the return type.
/// Gets the attributes associated with the return type. (e.g. [return: MarshalAs(...)])
/// </summary>
IList<IAttribute> ReturnTypeAttributes { get; }

6
ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs

@ -44,6 +44,12 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -44,6 +44,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Gets all members declared in this class. This is the union of Fields,Properties,Methods and Events.
/// </summary>
IEnumerable<IMember> Members { get; }
/// <summary>
/// Gets whether this type contains extension methods.
/// </summary>
/// <remarks>This property is used to speed up the search for extension methods.</remarks>
bool HasExtensionMethods { get; }
}
#if WITH_CONTRACTS

4
ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs

@ -44,7 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -44,7 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <param name="nameComparer">Language-specific rules for how namespace names are compared</param>
/// <returns>List of classes within that namespace.</returns>
/// <remarks>
/// If this method is called within <c>using (pc.Synchronize())</c>, then the returned enumerable is valid
/// If this method is called within <c>using (var spc = pc.Synchronize())</c>, then the returned enumerable is valid
/// only until the end of the synchronize block.
/// </remarks>
IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer);
@ -53,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -53,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Retrieves all namespaces.
/// </summary>
/// <remarks>
/// If this method is called within <c>using (pc.Synchronize())</c>, then the returned enumerable is valid
/// If this method is called within <c>using (var spc = pc.Synchronize())</c>, then the returned enumerable is valid
/// only until the end of the synchronize block.
/// </remarks>
IEnumerable<string> GetNamespaces();

17
ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs

@ -44,6 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -44,6 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
Accessibility accessibility;
DomRegion region;
IList<IAttribute> attributes;
IList<IAttribute> returnTypeAttributes;
protected override void FreezeInternal()
{
@ -75,20 +76,32 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -75,20 +76,32 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
}
}
public IList<IAttribute> ReturnTypeAttributes {
get {
if (returnTypeAttributes == null)
returnTypeAttributes = new List<IAttribute>();
return returnTypeAttributes;
}
}
void ISupportsInterning.PrepareForInterning(IInterningProvider provider)
{
attributes = provider.InternList(attributes);
returnTypeAttributes = provider.InternList(returnTypeAttributes);
}
int ISupportsInterning.GetHashCodeForInterning()
{
return (attributes != null ? attributes.GetHashCode() : 0) ^ region.GetHashCode() ^ (int)accessibility;
return (attributes != null ? attributes.GetHashCode() : 0)
^ (returnTypeAttributes != null ? returnTypeAttributes.GetHashCode() : 0)
^ region.GetHashCode() ^ (int)accessibility;
}
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other)
{
DefaultAccessor a = other as DefaultAccessor;
return a != null && (attributes == a.attributes && accessibility == a.accessibility && region == a.region);
return a != null && (attributes == a.attributes && returnTypeAttributes == a.returnTypeAttributes
&& accessibility == a.accessibility && region == a.region);
}
}
}

60
ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
@ -13,8 +14,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -13,8 +14,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
/// </summary>
public sealed class DefaultAttribute : AbstractFreezable, IAttribute, ISupportsInterning
{
DomRegion region;
ITypeReference attributeType;
readonly ITypeReference[] constructorParameterTypes;
DomRegion region;
IList<IConstantValue> positionalArguments;
IList<KeyValuePair<string, IConstantValue>> namedArguments;
@ -34,11 +36,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -34,11 +36,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
base.FreezeInternal();
}
public DefaultAttribute(ITypeReference attributeType)
public DefaultAttribute(ITypeReference attributeType, IEnumerable<ITypeReference> constructorParameterTypes)
{
if (attributeType == null)
throw new ArgumentNullException("attributeType");
this.attributeType = attributeType;
this.constructorParameterTypes = constructorParameterTypes != null ? constructorParameterTypes.ToArray() : null;
}
public ITypeReference AttributeType {
get { return attributeType; }
}
public ReadOnlyCollection<ITypeReference> ConstructorParameterTypes {
get { return Array.AsReadOnly(constructorParameterTypes); }
}
public DomRegion Region {
@ -49,14 +60,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -49,14 +60,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
}
}
public ITypeReference AttributeType {
get { return attributeType; }
set {
CheckBeforeMutation();
attributeType = value;
}
}
public IList<IConstantValue> PositionalArguments {
get {
if (positionalArguments == null)
@ -73,6 +76,38 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -73,6 +76,38 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
}
}
public IMethod ResolveConstructor(ITypeResolveContext context)
{
IType[] parameterTypes = null;
if (constructorParameterTypes != null && constructorParameterTypes.Length > 0) {
parameterTypes = new IType[constructorParameterTypes.Length];
for (int i = 0; i < parameterTypes.Length; i++) {
parameterTypes[i] = constructorParameterTypes[i].Resolve(context);
}
}
IMethod bestMatch = null;
foreach (IMethod ctor in attributeType.Resolve(context).GetConstructors(context)) {
if (ctor.IsStatic)
continue;
if (parameterTypes == null) {
if (ctor.Parameters.Count == 0)
return ctor;
} else if (ctor.Parameters.Count == parameterTypes.Length) {
bestMatch = ctor;
bool ok = true;
for (int i = 0; i < parameterTypes.Length; i++) {
if (ctor.Parameters[i].Type != parameterTypes[i]) {
ok = false;
break;
}
}
if (ok)
return ctor;
}
}
return bestMatch;
}
public override string ToString()
{
StringBuilder b = new StringBuilder();
@ -100,6 +135,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -100,6 +135,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
void ISupportsInterning.PrepareForInterning(IInterningProvider provider)
{
attributeType = provider.Intern(attributeType);
if (constructorParameterTypes != null) {
for (int i = 0; i < constructorParameterTypes.Length; i++) {
constructorParameterTypes[i] = provider.Intern(constructorParameterTypes[i]);
}
}
positionalArguments = provider.InternList(positionalArguments);
}

9
ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs

@ -40,6 +40,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -40,6 +40,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
const ushort FlagShadowing = 0x0004;
const ushort FlagSynthetic = 0x0008;
const ushort FlagAddDefaultConstructorIfRequired = 0x0010;
const ushort FlagHasExtensionMethods = 0x0020;
protected override void FreezeInternal()
{
@ -312,6 +313,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -312,6 +313,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
}
}
public bool HasExtensionMethods {
get { return flags[FlagHasExtensionMethods]; }
set {
CheckBeforeMutation();
flags[FlagHasExtensionMethods] = value;
}
}
public IProjectContent ProjectContent {
get { return projectContent; }
}

Loading…
Cancel
Save