Browse Source

Rename AggregateTypeResolveContext to CompositeTypeResolveContext.

Rename DotNetName to ReflectionName.
Added ReflectionName-parser to ReflectionHelper.
Move the ReaderWriterLock synchronization from TypeStorage to SimpleProjectContent.
Added some documentation to the README.
newNRvisualizers
Daniel Grunwald 16 years ago
parent
commit
9efef610b1
  1. 6
      ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs
  2. 8
      ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs
  3. 48
      ICSharpCode.NRefactory.Tests/TypeSystem/ReflectionHelperTests.cs
  4. 28
      ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
  5. 2
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  6. 2
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  7. 3
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  8. 2
      ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
  9. 4
      ICSharpCode.NRefactory/TypeSystem/INamedElement.cs
  10. 3
      ICSharpCode.NRefactory/TypeSystem/ISynchronizedTypeResolveContext.cs
  11. 2
      ICSharpCode.NRefactory/TypeSystem/IType.cs
  12. 20
      ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs
  13. 6
      ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs
  14. 4
      ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs
  15. 98
      ICSharpCode.NRefactory/TypeSystem/Implementation/AggregateTypeResolveContext.cs
  16. 148
      ICSharpCode.NRefactory/TypeSystem/Implementation/CompositeTypeResolveContext.cs
  17. 8
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs
  18. 2
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs
  19. 12
      ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs
  20. 7
      ICSharpCode.NRefactory/TypeSystem/Implementation/ProxyTypeResolveContext.cs
  21. 154
      ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleProjectContent.cs
  22. 125
      ICSharpCode.NRefactory/TypeSystem/Implementation/TypeStorage.cs
  23. 4
      ICSharpCode.NRefactory/TypeSystem/Implementation/TypeWithElementType.cs
  24. 8
      ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs
  25. 187
      ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs
  26. 45
      ICSharpCode.NRefactory/TypeSystem/ReflectionNameParseException.cs
  27. 98
      README

6
ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs

@ -58,7 +58,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
{ {
ITypeDefinition c = Mscorlib.GetClass(typeof(IntPtr)); ITypeDefinition c = Mscorlib.GetClass(typeof(IntPtr));
IMethod toPointer = c.Methods.Single(p => p.Name == "ToPointer"); IMethod toPointer = c.Methods.Single(p => p.Name == "ToPointer");
Assert.AreEqual("System.Void*", toPointer.ReturnType.Resolve(ctx).DotNetName); Assert.AreEqual("System.Void*", toPointer.ReturnType.Resolve(ctx).ReflectionName);
Assert.IsInstanceOf(typeof(PointerType), toPointer.ReturnType.Resolve(ctx)); Assert.IsInstanceOf(typeof(PointerType), toPointer.ReturnType.Resolve(ctx));
Assert.AreEqual("System.Void", ((PointerType)toPointer.ReturnType.Resolve(ctx)).ElementType.FullName); Assert.AreEqual("System.Void", ((PointerType)toPointer.ReturnType.Resolve(ctx)).ElementType.FullName);
} }
@ -107,7 +107,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
ITypeDefinition c = Mscorlib.GetClass(typeof(Environment.SpecialFolder)); ITypeDefinition c = Mscorlib.GetClass(typeof(Environment.SpecialFolder));
Assert.IsNotNull(c, "c is null"); Assert.IsNotNull(c, "c is null");
Assert.AreEqual("System.Environment.SpecialFolder", c.FullName); Assert.AreEqual("System.Environment.SpecialFolder", c.FullName);
Assert.AreEqual("System.Environment+SpecialFolder", c.DotNetName); Assert.AreEqual("System.Environment+SpecialFolder", c.ReflectionName);
} }
[Test] [Test]
@ -135,7 +135,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
var dictionaryRT = new ParameterizedType(dictionary, new[] { Mscorlib.GetClass(typeof(string)), Mscorlib.GetClass(typeof(int)) }); var dictionaryRT = new ParameterizedType(dictionary, new[] { Mscorlib.GetClass(typeof(string)), Mscorlib.GetClass(typeof(int)) });
IProperty valueProperty = dictionaryRT.GetProperties(ctx).Single(p => p.Name == "Values"); IProperty valueProperty = dictionaryRT.GetProperties(ctx).Single(p => p.Name == "Values");
IType parameterizedValueCollection = valueProperty.ReturnType.Resolve(ctx); IType parameterizedValueCollection = valueProperty.ReturnType.Resolve(ctx);
Assert.AreEqual("System.Collections.Generic.Dictionary`2+ValueCollection[[System.String],[System.Int32]]", parameterizedValueCollection.DotNetName); Assert.AreEqual("System.Collections.Generic.Dictionary`2+ValueCollection[[System.String],[System.Int32]]", parameterizedValueCollection.ReflectionName);
Assert.AreSame(valueCollection, parameterizedValueCollection.GetDefinition()); Assert.AreSame(valueCollection, parameterizedValueCollection.GetDefinition());
} }

8
ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs

@ -18,12 +18,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
IType[] GetAllBaseTypes(Type type) IType[] GetAllBaseTypes(Type type)
{ {
return type.ToTypeReference().Resolve(context).GetAllBaseTypes(context).OrderBy(t => t.DotNetName).ToArray(); return type.ToTypeReference().Resolve(context).GetAllBaseTypes(context).OrderBy(t => t.ReflectionName).ToArray();
} }
IType[] GetTypes(params Type[] types) IType[] GetTypes(params Type[] types)
{ {
return types.Select(t => t.ToTypeReference().Resolve(context)).OrderBy(t => t.DotNetName).ToArray();; return types.Select(t => t.ToTypeReference().Resolve(context)).OrderBy(t => t.ReflectionName).ToArray();;
} }
[Test] [Test]
@ -85,7 +85,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
mscorlib.GetClass(typeof(object)) mscorlib.GetClass(typeof(object))
}; };
Assert.AreEqual(expected, Assert.AreEqual(expected,
c.GetAllBaseTypes(context).OrderBy(t => t.DotNetName).ToArray()); c.GetAllBaseTypes(context).OrderBy(t => t.ReflectionName).ToArray());
} }
[Test] [Test]
@ -103,7 +103,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
mscorlib.GetClass(typeof(ValueType)) mscorlib.GetClass(typeof(ValueType))
}; };
Assert.AreEqual(expected, Assert.AreEqual(expected,
s.GetAllBaseTypes(context).OrderBy(t => t.DotNetName).ToArray()); s.GetAllBaseTypes(context).OrderBy(t => t.ReflectionName).ToArray());
} }
} }
} }

48
ICSharpCode.NRefactory.Tests/TypeSystem/ReflectionHelperTests.cs

@ -12,11 +12,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
[TestFixture] [TestFixture]
public class ReflectionHelperTests public class ReflectionHelperTests
{ {
ITypeResolveContext context = CecilLoaderTests.Mscorlib;
void TestGetClass(Type type) void TestGetClass(Type type)
{ {
ITypeDefinition t = CecilLoaderTests.Mscorlib.GetClass(type); ITypeDefinition t = CecilLoaderTests.Mscorlib.GetClass(type);
Assert.NotNull(t, type.FullName); Assert.NotNull(t, type.FullName);
Assert.AreEqual(type.FullName, t.DotNetName); Assert.AreEqual(type.FullName, t.ReflectionName);
} }
[Test] [Test]
@ -53,25 +55,25 @@ namespace ICSharpCode.NRefactory.TypeSystem
public void TestToTypeReferenceInnerClass() public void TestToTypeReferenceInnerClass()
{ {
Assert.AreEqual("System.Environment+SpecialFolder", Assert.AreEqual("System.Environment+SpecialFolder",
typeof(Environment.SpecialFolder).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(Environment.SpecialFolder).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceUnboundGenericClass() public void TestToTypeReferenceUnboundGenericClass()
{ {
Assert.AreEqual("System.Action`1", Assert.AreEqual("System.Action`1",
typeof(Action<>).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(Action<>).ToTypeReference().Resolve(context).ReflectionName);
Assert.AreEqual("System.Action`2", Assert.AreEqual("System.Action`2",
typeof(Action<,>).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(Action<,>).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceBoundGenericClass() public void TestToTypeReferenceBoundGenericClass()
{ {
Assert.AreEqual("System.Action`1[[System.String]]", Assert.AreEqual("System.Action`1[[System.String]]",
typeof(Action<string>).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(Action<string>).ToTypeReference().Resolve(context).ReflectionName);
Assert.AreEqual("System.Action`2[[System.Int32],[System.Int16]]", Assert.AreEqual("System.Action`2[[System.Int32],[System.Int16]]",
typeof(Action<int, short>).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(Action<int, short>).ToTypeReference().Resolve(context).ReflectionName);
} }
@ -79,56 +81,56 @@ namespace ICSharpCode.NRefactory.TypeSystem
public void TestToTypeReferenceNullableType() public void TestToTypeReferenceNullableType()
{ {
Assert.AreEqual("System.Nullable`1[[System.Int32]]", Assert.AreEqual("System.Nullable`1[[System.Int32]]",
typeof(int?).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(int?).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceInnerClassInUnboundGenericType() public void TestToTypeReferenceInnerClassInUnboundGenericType()
{ {
Assert.AreEqual("System.Collections.Generic.Dictionary`2+ValueCollection[[`0],[`1]]", Assert.AreEqual("System.Collections.Generic.Dictionary`2+ValueCollection[[`0],[`1]]",
typeof(Dictionary<,>.ValueCollection).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(Dictionary<,>.ValueCollection).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceInnerClassInBoundGenericType() public void TestToTypeReferenceInnerClassInBoundGenericType()
{ {
Assert.AreEqual("System.Collections.Generic.Dictionary`2+KeyCollection[[System.String],[System.Int32]]", Assert.AreEqual("System.Collections.Generic.Dictionary`2+KeyCollection[[System.String],[System.Int32]]",
typeof(Dictionary<string, int>.KeyCollection).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(Dictionary<string, int>.KeyCollection).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceArrayType() public void TestToTypeReferenceArrayType()
{ {
Assert.AreEqual(typeof(int[]).FullName, Assert.AreEqual(typeof(int[]).FullName,
typeof(int[]).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(int[]).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceMultidimensionalArrayType() public void TestToTypeReferenceMultidimensionalArrayType()
{ {
Assert.AreEqual(typeof(int[,]).FullName, Assert.AreEqual(typeof(int[,]).FullName,
typeof(int[,]).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(int[,]).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceJaggedMultidimensionalArrayType() public void TestToTypeReferenceJaggedMultidimensionalArrayType()
{ {
Assert.AreEqual(typeof(int[,][,,]).FullName, Assert.AreEqual(typeof(int[,][,,]).FullName,
typeof(int[,][,,]).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(int[,][,,]).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferencePointerType() public void TestToTypeReferencePointerType()
{ {
Assert.AreEqual(typeof(int*).FullName, Assert.AreEqual(typeof(int*).FullName,
typeof(int*).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(int*).ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
public void TestToTypeReferenceByReferenceType() public void TestToTypeReferenceByReferenceType()
{ {
Assert.AreEqual(typeof(int).MakeByRefType().FullName, Assert.AreEqual(typeof(int).MakeByRefType().FullName,
typeof(int).MakeByRefType().ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); typeof(int).MakeByRefType().ToTypeReference().Resolve(context).ReflectionName);
} }
[Test] [Test]
@ -138,11 +140,23 @@ namespace ICSharpCode.NRefactory.TypeSystem
Type parameterType = convertAllInfo.GetParameters()[0].ParameterType; // Converter[[`0],[``0]] Type parameterType = convertAllInfo.GetParameters()[0].ParameterType; // Converter[[`0],[``0]]
// cannot resolve generic types without knowing the parent entity: // cannot resolve generic types without knowing the parent entity:
Assert.AreEqual("System.Converter`2[[?],[?]]", Assert.AreEqual("System.Converter`2[[?],[?]]",
parameterType.ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName); parameterType.ToTypeReference().Resolve(context).ReflectionName);
// now try with parent entity: // now try with parent entity:
IMethod convertAll = CecilLoaderTests.Mscorlib.GetClass(typeof(List<>)).Methods.Single(m => m.Name == "ConvertAll"); IMethod convertAll = context.GetClass(typeof(List<>)).Methods.Single(m => m.Name == "ConvertAll");
Assert.AreEqual("System.Converter`2[[`0],[``0]]", Assert.AreEqual("System.Converter`2[[`0],[``0]]",
parameterType.ToTypeReference(entity: convertAll).Resolve(CecilLoaderTests.Mscorlib).DotNetName); parameterType.ToTypeReference(entity: convertAll).Resolve(context).ReflectionName);
}
[Test]
public void ParseReflectionNameSimpleTypes()
{
Assert.AreEqual("System.Int32", ReflectionHelper.ParseReflectionName("System.Int32").Resolve(context).ReflectionName);
Assert.AreEqual("System.Int32&", ReflectionHelper.ParseReflectionName("System.Int32&").Resolve(context).ReflectionName);
Assert.AreEqual("System.Int32*&", ReflectionHelper.ParseReflectionName("System.Int32*&").Resolve(context).ReflectionName);
Assert.AreEqual("System.Int32", ReflectionHelper.ParseReflectionName(typeof(int).AssemblyQualifiedName).Resolve(context).ReflectionName);
Assert.AreEqual("System.Action`1[[System.String]]", ReflectionHelper.ParseReflectionName("System.Action`1[[System.String]]").Resolve(context).ReflectionName);
Assert.AreEqual("System.Action`1[[System.String]]", ReflectionHelper.ParseReflectionName("System.Action`1[[System.String, mscorlib]]").Resolve(context).ReflectionName);
Assert.AreEqual("System.Int32[,,][,]", ReflectionHelper.ParseReflectionName(typeof(int[,][,,]).AssemblyQualifiedName).Resolve(context).ReflectionName);
} }
} }
} }

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

@ -22,7 +22,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
[SetUpAttribute] [SetUpAttribute]
public void SetUp() public void SetUp()
{ {
ctx = AggregateTypeResolveContext.Combine(testCasePC, CecilLoaderTests.Mscorlib); ctx = CompositeTypeResolveContext.Combine(testCasePC, CecilLoaderTests.Mscorlib);
} }
ITypeDefinition GetClass(Type type) ITypeDefinition GetClass(Type type)
@ -37,7 +37,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual(typeof(SimplePublicClass).Name, c.Name); Assert.AreEqual(typeof(SimplePublicClass).Name, c.Name);
Assert.AreEqual(typeof(SimplePublicClass).FullName, c.FullName); Assert.AreEqual(typeof(SimplePublicClass).FullName, c.FullName);
Assert.AreEqual(typeof(SimplePublicClass).Namespace, c.Namespace); Assert.AreEqual(typeof(SimplePublicClass).Namespace, c.Namespace);
Assert.AreEqual(typeof(SimplePublicClass).FullName, c.DotNetName); Assert.AreEqual(typeof(SimplePublicClass).FullName, c.ReflectionName);
Assert.AreEqual(Accessibility.Public, c.Accessibility); Assert.AreEqual(Accessibility.Public, c.Accessibility);
Assert.IsFalse(c.IsAbstract); Assert.IsFalse(c.IsAbstract);
@ -77,26 +77,26 @@ namespace ICSharpCode.NRefactory.TypeSystem
ITypeDefinition testClass = testCasePC.GetClass(typeof(DynamicTest)); ITypeDefinition testClass = testCasePC.GetClass(typeof(DynamicTest));
IMethod m1 = testClass.Methods.Single(me => me.Name == "DynamicGenerics1"); IMethod m1 = testClass.Methods.Single(me => me.Name == "DynamicGenerics1");
Assert.AreEqual("System.Collections.Generic.List`1[[dynamic]]", m1.ReturnType.Resolve(ctx).DotNetName); Assert.AreEqual("System.Collections.Generic.List`1[[dynamic]]", m1.ReturnType.Resolve(ctx).ReflectionName);
Assert.AreEqual("System.Action`3[[System.Object],[dynamic[]],[System.Object]]", m1.Parameters[0].Type.Resolve(ctx).DotNetName); Assert.AreEqual("System.Action`3[[System.Object],[dynamic[]],[System.Object]]", m1.Parameters[0].Type.Resolve(ctx).ReflectionName);
IMethod m2 = testClass.Methods.Single(me => me.Name == "DynamicGenerics2"); IMethod m2 = testClass.Methods.Single(me => me.Name == "DynamicGenerics2");
Assert.AreEqual("System.Action`3[[System.Object],[dynamic],[System.Object]]", m2.Parameters[0].Type.Resolve(ctx).DotNetName); Assert.AreEqual("System.Action`3[[System.Object],[dynamic],[System.Object]]", m2.Parameters[0].Type.Resolve(ctx).ReflectionName);
IMethod m3 = testClass.Methods.Single(me => me.Name == "DynamicGenerics3"); IMethod m3 = testClass.Methods.Single(me => me.Name == "DynamicGenerics3");
Assert.AreEqual("System.Action`3[[System.Int32],[dynamic],[System.Object]]", m3.Parameters[0].Type.Resolve(ctx).DotNetName); Assert.AreEqual("System.Action`3[[System.Int32],[dynamic],[System.Object]]", m3.Parameters[0].Type.Resolve(ctx).ReflectionName);
IMethod m4 = testClass.Methods.Single(me => me.Name == "DynamicGenerics4"); IMethod m4 = testClass.Methods.Single(me => me.Name == "DynamicGenerics4");
Assert.AreEqual("System.Action`3[[System.Int32[]],[dynamic],[System.Object]]", m4.Parameters[0].Type.Resolve(ctx).DotNetName); Assert.AreEqual("System.Action`3[[System.Int32[]],[dynamic],[System.Object]]", m4.Parameters[0].Type.Resolve(ctx).ReflectionName);
IMethod m5 = testClass.Methods.Single(me => me.Name == "DynamicGenerics5"); IMethod m5 = testClass.Methods.Single(me => me.Name == "DynamicGenerics5");
Assert.AreEqual("System.Action`3[[System.Int32*[]],[dynamic],[System.Object]]", m5.Parameters[0].Type.Resolve(ctx).DotNetName); Assert.AreEqual("System.Action`3[[System.Int32*[]],[dynamic],[System.Object]]", m5.Parameters[0].Type.Resolve(ctx).ReflectionName);
IMethod m6 = testClass.Methods.Single(me => me.Name == "DynamicGenerics6"); IMethod m6 = testClass.Methods.Single(me => me.Name == "DynamicGenerics6");
Assert.AreEqual("System.Action`3[[System.Object],[dynamic],[System.Object]]&", m6.Parameters[0].Type.Resolve(ctx).DotNetName); Assert.AreEqual("System.Action`3[[System.Object],[dynamic],[System.Object]]&", m6.Parameters[0].Type.Resolve(ctx).ReflectionName);
IMethod m7 = testClass.Methods.Single(me => me.Name == "DynamicGenerics7"); IMethod m7 = testClass.Methods.Single(me => me.Name == "DynamicGenerics7");
Assert.AreEqual("System.Action`3[[System.Int32[][,]],[dynamic],[System.Object]]", m7.Parameters[0].Type.Resolve(ctx).DotNetName); Assert.AreEqual("System.Action`3[[System.Int32[][,]],[dynamic],[System.Object]]", m7.Parameters[0].Type.Resolve(ctx).ReflectionName);
} }
[Test] [Test]
@ -117,7 +117,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual("System.Collections.Generic.IDictionary", crt.FullName); Assert.AreEqual("System.Collections.Generic.IDictionary", crt.FullName);
Assert.AreEqual("System.String", crt.TypeArguments[0].FullName); Assert.AreEqual("System.String", crt.TypeArguments[0].FullName);
// ? for NUnit.TestAttribute (because that assembly isn't in ctx) // ? for NUnit.TestAttribute (because that assembly isn't in ctx)
Assert.AreEqual("System.Collections.Generic.IList`1[[?]]", crt.TypeArguments[1].DotNetName); Assert.AreEqual("System.Collections.Generic.IList`1[[?]]", crt.TypeArguments[1].ReflectionName);
} }
[Test] [Test]
@ -140,7 +140,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreSame(m, m.TypeParameters[0].ParentMethod); Assert.AreSame(m, m.TypeParameters[0].ParentMethod);
Assert.AreSame(m, m.TypeParameters[1].ParentMethod); Assert.AreSame(m, m.TypeParameters[1].ParentMethod);
Assert.AreEqual("System.IComparable`1[[``1]]", m.TypeParameters[0].Constraints[0].Resolve(ctx).DotNetName); Assert.AreEqual("System.IComparable`1[[``1]]", m.TypeParameters[0].Constraints[0].Resolve(ctx).ReflectionName);
Assert.AreSame(m.TypeParameters[0], m.TypeParameters[1].Constraints[0].Resolve(ctx)); Assert.AreSame(m.TypeParameters[0], m.TypeParameters[1].Constraints[0].Resolve(ctx));
} }
@ -202,8 +202,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
var e = testCasePC.GetClass(typeof(MyEnum)); var e = testCasePC.GetClass(typeof(MyEnum));
Assert.AreEqual(ClassType.Enum, e.ClassType); Assert.AreEqual(ClassType.Enum, e.ClassType);
Assert.AreEqual(false, e.IsReferenceType); Assert.AreEqual(false, e.IsReferenceType);
Assert.AreEqual("System.Int16", e.BaseTypes[0].Resolve(ctx).DotNetName); Assert.AreEqual("System.Int16", e.BaseTypes[0].Resolve(ctx).ReflectionName);
Assert.AreEqual(new[] { "System.Enum" }, e.GetBaseTypes(ctx).Select(t => t.DotNetName).ToArray()); Assert.AreEqual(new[] { "System.Enum" }, e.GetBaseTypes(ctx).Select(t => t.ReflectionName).ToArray());
} }
[Test] [Test]

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

@ -178,7 +178,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
get { return string.Empty; } get { return string.Empty; }
} }
string INamedElement.DotNetName { string INamedElement.ReflectionName {
get { return "operator"; } get { return "operator"; }
} }

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

@ -21,7 +21,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (context == null) if (context == null)
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
this.context = context; this.context = context;
this.objectType = context.GetClass(typeof(object)) ?? SharedTypes.UnknownType; this.objectType = TypeCode.Object.ToTypeReference().Resolve(context);
this.dynamicErasure = new DynamicErasure(this); this.dynamicErasure = new DynamicErasure(this);
} }

3
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -179,7 +179,7 @@
<Compile Include="TypeSystem\Implementation\DefaultProperty.cs" /> <Compile Include="TypeSystem\Implementation\DefaultProperty.cs" />
<Compile Include="TypeSystem\Implementation\DefaultTypeParameter.cs" /> <Compile Include="TypeSystem\Implementation\DefaultTypeParameter.cs" />
<Compile Include="TypeSystem\Implementation\GetClassTypeReference.cs" /> <Compile Include="TypeSystem\Implementation\GetClassTypeReference.cs" />
<Compile Include="TypeSystem\Implementation\AggregateTypeResolveContext.cs" /> <Compile Include="TypeSystem\Implementation\CompositeTypeResolveContext.cs" />
<Compile Include="TypeSystem\Implementation\NestedTypeReference.cs" /> <Compile Include="TypeSystem\Implementation\NestedTypeReference.cs" />
<Compile Include="TypeSystem\Implementation\ProxyTypeResolveContext.cs" /> <Compile Include="TypeSystem\Implementation\ProxyTypeResolveContext.cs" />
<Compile Include="TypeSystem\Implementation\DefaultTypeDefinition.cs" /> <Compile Include="TypeSystem\Implementation\DefaultTypeDefinition.cs" />
@ -206,6 +206,7 @@
<Compile Include="TypeSystem\ITypeResolveContext.cs" /> <Compile Include="TypeSystem\ITypeResolveContext.cs" />
<Compile Include="TypeSystem\NullableType.cs" /> <Compile Include="TypeSystem\NullableType.cs" />
<Compile Include="TypeSystem\ParameterizedType.cs" /> <Compile Include="TypeSystem\ParameterizedType.cs" />
<Compile Include="TypeSystem\ReflectionNameParseException.cs" />
<Compile Include="TypeSystem\TypeVisitor.cs" /> <Compile Include="TypeSystem\TypeVisitor.cs" />
<Compile Include="TypeSystem\IVariable.cs" /> <Compile Include="TypeSystem\IVariable.cs" />
<Compile Include="TypeSystem\PointerType.cs" /> <Compile Include="TypeSystem\PointerType.cs" />

2
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -48,7 +48,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
TypeStorage typeStorage = new TypeStorage(); TypeStorage typeStorage = new TypeStorage();
CecilProjectContent pc = new CecilProjectContent(typeStorage, assemblyDefinition.Name.FullName, assemblyAttributes.AsReadOnly()); CecilProjectContent pc = new CecilProjectContent(typeStorage, assemblyDefinition.Name.FullName, assemblyAttributes.AsReadOnly());
this.EarlyBindContext = AggregateTypeResolveContext.Combine(pc, this.EarlyBindContext); this.EarlyBindContext = CompositeTypeResolveContext.Combine(pc, this.EarlyBindContext);
List<CecilTypeDefinition> types = new List<CecilTypeDefinition>(); List<CecilTypeDefinition> types = new List<CecilTypeDefinition>();
foreach (ModuleDefinition module in assemblyDefinition.Modules) { foreach (ModuleDefinition module in assemblyDefinition.Modules) {
foreach (TypeDefinition td in module.Types) { foreach (TypeDefinition td in module.Types) {

4
ICSharpCode.NRefactory/TypeSystem/INamedElement.cs

@ -54,7 +54,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// "System.Collections.Generic.List`1[[System.String]]" for List&lt;string&gt; /// "System.Collections.Generic.List`1[[System.String]]" for List&lt;string&gt;
/// "System.Environment+SpecialFolder" for Environment.SpecialFolder /// "System.Environment+SpecialFolder" for Environment.SpecialFolder
/// </returns> /// </returns>
string DotNetName { string ReflectionName {
get; get;
} }
} }
@ -83,7 +83,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
} }
string INamedElement.DotNetName { string INamedElement.ReflectionName {
get { get {
Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
return null; return null;

3
ICSharpCode.NRefactory/TypeSystem/ISynchronizedTypeResolveContext.cs

@ -17,6 +17,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// A simple implementation might enter a ReaderWriterLock when the synchronized context /// A simple implementation might enter a ReaderWriterLock when the synchronized context
/// is created, and releases the lock when Dispose() is called. /// is created, and releases the lock when Dispose() is called.
/// However, implementations based on immutable data structures are also possible. /// However, implementations based on immutable data structures are also possible.
///
/// Calling Synchronize() on an already synchronized context is possible, but has no effect.
/// Only disposing the outermost ISynchronizedTypeResolveContext releases the lock.
/// </remarks> /// </remarks>
public interface ISynchronizedTypeResolveContext : ITypeResolveContext, IDisposable public interface ISynchronizedTypeResolveContext : ITypeResolveContext, IDisposable
{ {

2
ICSharpCode.NRefactory/TypeSystem/IType.cs

@ -178,7 +178,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
} }
string INamedElement.DotNetName { string INamedElement.ReflectionName {
get { get {
Contract.Ensures(Contract.Result<string>() != null); Contract.Ensures(Contract.Result<string>() != null);
return null; return null;

20
ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs

@ -66,6 +66,22 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// However, implementations based on immutable data structures are also possible. /// However, implementations based on immutable data structures are also possible.
/// </remarks> /// </remarks>
ISynchronizedTypeResolveContext Synchronize(); ISynchronizedTypeResolveContext Synchronize();
/// <summary>
/// Returns an object if caching information based on this resolve context is allowed,
/// or null if caching is not allowed.
/// Whenever the resolve context changes in some way, this property must return a new object.
/// </summary>
/// <remarks>
/// This allows consumers to see whether their cache is still valid by comparing the current
/// CacheToken with the one they saw before.
///
/// For ISynchronizedTypeResolveContext, this property could be implemented as <c>return this;</c>.
/// However, it is a bad idea to use an object is large or that references a large object graph
/// -- consumers may store a reference to the cache token indefinately, possible extending the
/// lifetime of the ITypeResolveContext.
/// </remarks>
object CacheToken { get; }
} }
[ContractClassFor(typeof(ITypeResolveContext))] [ContractClassFor(typeof(ITypeResolveContext))]
@ -104,5 +120,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
Contract.Ensures(Contract.Result<IEnumerable<ITypeDefinition>>() != null); Contract.Ensures(Contract.Result<IEnumerable<ITypeDefinition>>() != null);
return null; return null;
} }
object ITypeResolveContext.CacheToken {
get { return null; }
}
} }
} }

6
ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs

@ -237,13 +237,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return declaringTypeDefinition.Namespace; } get { return declaringTypeDefinition.Namespace; }
} }
public virtual string DotNetName { public virtual string ReflectionName {
get { return this.DeclaringType.DotNetName + "." + this.Name; } get { return this.DeclaringType.ReflectionName + "." + this.Name; }
} }
public override string ToString() public override string ToString()
{ {
return "[" + EntityType + " " + DotNetName + ":" + ReturnType + "]"; return "[" + EntityType + " " + ReflectionName + ":" + ReturnType + "]";
} }
public virtual void PrepareForInterning(IInterningProvider provider) public virtual void PrepareForInterning(IInterningProvider provider)

4
ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs

@ -32,7 +32,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return string.Empty; } get { return string.Empty; }
} }
public virtual string DotNetName { public virtual string ReflectionName {
get { return this.FullName; } get { return this.FullName; }
} }
@ -101,7 +101,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
public override string ToString() public override string ToString()
{ {
return this.DotNetName; return this.ReflectionName;
} }
public virtual IType AcceptVisitor(TypeVisitor visitor) public virtual IType AcceptVisitor(TypeVisitor visitor)

98
ICSharpCode.NRefactory/TypeSystem/Implementation/AggregateTypeResolveContext.cs

@ -1,98 +0,0 @@
// Copyright (c) 2010 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.Linq;
namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
/// <summary>
/// Represents multiple type resolve contexts.
/// </summary>
public class AggregateTypeResolveContext : ITypeResolveContext
{
/// <summary>
/// Creates a <see cref="MultiTypeResolveContext"/> that combines the given resolve contexts.
/// If one of the input parameters is null, the other input parameter is returned directly.
/// If both input parameters are null, the function returns null.
/// </summary>
public static ITypeResolveContext Combine(ITypeResolveContext a, ITypeResolveContext b)
{
if (a == null)
return b;
if (b == null)
return a;
return new AggregateTypeResolveContext(new [] { a, b });
}
readonly ITypeResolveContext[] contexts;
/// <summary>
/// Creates a new <see cref="MultiTypeResolveContext"/>
/// </summary>
public AggregateTypeResolveContext(IEnumerable<ITypeResolveContext> contexts)
{
if (contexts == null)
throw new ArgumentNullException("contexts");
this.contexts = contexts.ToArray();
}
/// <inheritdoc/>
public ITypeDefinition GetClass(string fullTypeName, int typeParameterCount, StringComparer nameComparer)
{
foreach (ITypeResolveContext context in contexts) {
ITypeDefinition d = context.GetClass(fullTypeName, typeParameterCount, nameComparer);
if (d != null)
return d;
}
return null;
}
/// <inheritdoc/>
public IEnumerable<ITypeDefinition> GetClasses()
{
return contexts.SelectMany(c => c.GetClasses());
}
/// <inheritdoc/>
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer)
{
return contexts.SelectMany(c => c.GetClasses(nameSpace, nameComparer));
}
/// <inheritdoc/>
public IEnumerable<string> GetNamespaces()
{
return contexts.SelectMany(c => c.GetNamespaces()).Distinct();
}
/// <inheritdoc/>
public ISynchronizedTypeResolveContext Synchronize()
{
ISynchronizedTypeResolveContext[] sync = new ISynchronizedTypeResolveContext[contexts.Length];
for (int i = 0; i < sync.Length; i++) {
sync[i] = contexts[i].Synchronize();
}
return new AggregateSynchronizedTypeResolveContext(sync);
}
sealed class AggregateSynchronizedTypeResolveContext : AggregateTypeResolveContext, ISynchronizedTypeResolveContext
{
readonly ISynchronizedTypeResolveContext[] sync;
public AggregateSynchronizedTypeResolveContext(ISynchronizedTypeResolveContext[] sync)
: base(sync)
{
this.sync = sync;
}
public void Dispose()
{
foreach (var element in sync) {
element.Dispose();
}
}
}
}
}

148
ICSharpCode.NRefactory/TypeSystem/Implementation/CompositeTypeResolveContext.cs

@ -0,0 +1,148 @@
// Copyright (c) 2010 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.Diagnostics;
using System.Linq;
namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
/// <summary>
/// Represents multiple type resolve contexts.
/// </summary>
public class CompositeTypeResolveContext : ITypeResolveContext
{
/// <summary>
/// Creates a <see cref="CompositeTypeResolveContext"/> that combines the given resolve contexts.
/// If one of the input parameters is null, the other input parameter is returned directly.
/// If both input parameters are null, the function returns null.
/// </summary>
public static ITypeResolveContext Combine(ITypeResolveContext a, ITypeResolveContext b)
{
if (a == null)
return b;
if (b == null)
return a;
return new CompositeTypeResolveContext(new [] { a, b });
}
readonly ITypeResolveContext[] children;
/// <summary>
/// Creates a new <see cref="CompositeTypeResolveContext"/>
/// </summary>
public CompositeTypeResolveContext(IEnumerable<ITypeResolveContext> children)
{
if (children == null)
throw new ArgumentNullException("children");
this.children = children.ToArray();
foreach (ITypeResolveContext c in this.children) {
if (c == null)
throw new ArgumentException("children enumeration contains nulls");
}
}
private CompositeTypeResolveContext(ITypeResolveContext[] children)
{
Debug.Assert(children != null);
this.children = children;
}
/// <inheritdoc/>
public ITypeDefinition GetClass(string fullTypeName, int typeParameterCount, StringComparer nameComparer)
{
foreach (ITypeResolveContext context in children) {
ITypeDefinition d = context.GetClass(fullTypeName, typeParameterCount, nameComparer);
if (d != null)
return d;
}
return null;
}
/// <inheritdoc/>
public IEnumerable<ITypeDefinition> GetClasses()
{
return children.SelectMany(c => c.GetClasses());
}
/// <inheritdoc/>
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer)
{
return children.SelectMany(c => c.GetClasses(nameSpace, nameComparer));
}
/// <inheritdoc/>
public IEnumerable<string> GetNamespaces()
{
return children.SelectMany(c => c.GetNamespaces()).Distinct();
}
/// <inheritdoc/>
public virtual ISynchronizedTypeResolveContext Synchronize()
{
return Synchronize(new object());
}
ISynchronizedTypeResolveContext Synchronize(object cacheToken)
{
ISynchronizedTypeResolveContext[] sync = new ISynchronizedTypeResolveContext[children.Length];
bool success = false;
try {
for (int i = 0; i < sync.Length; i++) {
sync[i] = children[i].Synchronize();
if (sync[i] == null)
throw new InvalidOperationException(children[i] + ".ToString() returned null");
}
ISynchronizedTypeResolveContext r = new CompositeSynchronizedTypeResolveContext(sync, cacheToken);
success = true;
return r;
} finally {
if (!success) {
// something went wrong, so immediately dispose the contexts we acquired
for (int i = 0; i < sync.Length; i++) {
if (sync[i] != null)
sync[i].Dispose();
}
}
}
}
public virtual object CacheToken {
// We don't know if our input contexts are mutable, so, to be on the safe side,
// we don't implement caching here.
get { return null; }
}
sealed class CompositeSynchronizedTypeResolveContext : CompositeTypeResolveContext, ISynchronizedTypeResolveContext
{
readonly object cacheToken;
public CompositeSynchronizedTypeResolveContext(ISynchronizedTypeResolveContext[] children, object cacheToken)
: base(children)
{
Debug.Assert(cacheToken != null);
this.cacheToken = cacheToken;
}
public void Dispose()
{
foreach (ISynchronizedTypeResolveContext element in children) {
element.Dispose();
}
}
public override object CacheToken {
// I expect CompositeTypeResolveContext to be used for almost all resolver operations,
// so this is the only place where implementing cacheToken is really important.
get { return cacheToken; }
}
public override ISynchronizedTypeResolveContext Synchronize()
{
// re-use the same cache token for nested synchronized contexts
return base.Synchronize(cacheToken);
}
}
}
}

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

@ -197,15 +197,15 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return this.ns; } get { return this.ns; }
} }
public string DotNetName { public string ReflectionName {
get { get {
if (declaringTypeDefinition != null) { if (declaringTypeDefinition != null) {
int tpCount = this.TypeParameterCount - declaringTypeDefinition.TypeParameterCount; int tpCount = this.TypeParameterCount - declaringTypeDefinition.TypeParameterCount;
string combinedName; string combinedName;
if (tpCount > 0) if (tpCount > 0)
combinedName = declaringTypeDefinition.DotNetName + "+" + this.Name + "`" + tpCount.ToString(CultureInfo.InvariantCulture); combinedName = declaringTypeDefinition.ReflectionName + "+" + this.Name + "`" + tpCount.ToString(CultureInfo.InvariantCulture);
else else
combinedName = declaringTypeDefinition.DotNetName + "+" + this.Name; combinedName = declaringTypeDefinition.ReflectionName + "+" + this.Name;
return combinedName; return combinedName;
} else { } else {
int tpCount = this.TypeParameterCount; int tpCount = this.TypeParameterCount;
@ -523,7 +523,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
public override string ToString() public override string ToString()
{ {
return DotNetName; return ReflectionName;
} }
/// <summary> /// <summary>

2
ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs

@ -71,7 +71,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return string.Empty; } get { return string.Empty; }
} }
public string DotNetName { public string ReflectionName {
get { get {
if (parent is IMethod) if (parent is IMethod)
return "``" + index.ToString(); return "``" + index.ToString();

12
ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs

@ -25,6 +25,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
if (context == null) if (context == null)
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
// TODO: caching idea: if these lookups are a performance problem and the same GetClassTypeReference
// is asked to resolve lots of times in a row, try the following:
// Give ITypeResolveContext a property "object CacheToken { get; }" which is non-null if the
// context supports caching, and returns the same object only as long as the context is unchanged.
// Then store a thread-local array KeyValuePair<GetClassTypeReference, IType> with the last 5 (?) resolved
// types, and a thread-local reference to the cache token. Subsequent calls with the same cache token
// do a quick (reference equality) lookup in the array first.
// This should be faster than any ServiceContainer-based caches.
// It is worth an idea to make CacheToken implement IServiceContainer, so that other (more expensive)
// caches can be registered there, but I think it's troublesome as one ITypeResolveContext should be usable
// on multiple threads.
return context.GetClass(fullTypeName, typeParameterCount, StringComparer.Ordinal) ?? SharedTypes.UnknownType; return context.GetClass(fullTypeName, typeParameterCount, StringComparer.Ordinal) ?? SharedTypes.UnknownType;
} }

7
ICSharpCode.NRefactory/TypeSystem/Implementation/ProxyTypeResolveContext.cs

@ -53,5 +53,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
return target.Synchronize(); return target.Synchronize();
} }
/// <inheritdoc/>
public virtual object CacheToken {
// Don't forward this by default; we don't know what derived classes are doing;
// it might not be cache-safe.
get { return null; }
}
} }
} }

154
ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleProjectContent.cs

@ -5,60 +5,53 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading;
namespace ICSharpCode.NRefactory.TypeSystem.Implementation namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
/// <summary> /// <summary>
/// Simple <see cref="IProjectContent"/> implementation that stores the list of classes/namespaces. /// Simple <see cref="IProjectContent"/> implementation that stores the list of classes/namespaces.
/// Synchronization is implemented using a <see cref="ReaderWriterLockSlim"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is a decorator around <see cref="TypeStorage"/> that adds support for the IProjectContent interface /// Compared with <see cref="TypeStorage"/>, this class adds support for the IProjectContent interface,
/// and for partial classes. /// for partial classes, and for multi-threading.
/// </remarks> /// </remarks>
public class SimpleProjectContent : ProxyTypeResolveContext, IProjectContent public sealed class SimpleProjectContent : IProjectContent
{ {
readonly TypeStorage types; // This class is sealed by design:
// the synchronization story doesn't mix well with someone trying to extend this class.
// If you wanted to derive from this: use delegation, not inheritance.
#region Constructors readonly TypeStorage types = new TypeStorage();
/// <summary> readonly ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim();
/// Creates a new SimpleProjectContent.
/// </summary>
public SimpleProjectContent()
: this(new TypeStorage())
{
}
/// <summary>
/// Creates a new SimpleProjectContent that reuses the specified type storage.
/// </summary>
protected SimpleProjectContent(TypeStorage types)
: base(types)
{
this.types = types;
this.assemblyAttributes = new List<IAttribute>();
this.readOnlyAssemblyAttributes = assemblyAttributes.AsReadOnly();
}
#endregion
#region AssemblyAttributes #region AssemblyAttributes
readonly List<IAttribute> assemblyAttributes; readonly List<IAttribute> assemblyAttributes = new List<IAttribute>(); // mutable assembly attribute storage
readonly ReadOnlyCollection<IAttribute> readOnlyAssemblyAttributes;
volatile IAttribute[] readOnlyAssemblyAttributes = {}; // volatile field with copy for reading threads
/// <inheritdoc/> /// <inheritdoc/>
public virtual IList<IAttribute> AssemblyAttributes { public IList<IAttribute> AssemblyAttributes {
get { return readOnlyAssemblyAttributes; } get { return readOnlyAssemblyAttributes; }
} }
void AddAssemblyAttributes(ICollection<IAttribute> attributes) void AddRemoveAssemblyAttributes(ICollection<IAttribute> addedAttributes, ICollection<IAttribute> removedAttributes)
{ {
// API uses ICollection instead of IEnumerable to discourage users from evaluating // API uses ICollection instead of IEnumerable to discourage users from evaluating
// the list inside the lock (this method is called inside the ReaderWriterLock) // the list inside the lock (this method is called inside the write lock)
assemblyAttributes.AddRange(attributes); bool hasChanges = false;
} if (removedAttributes != null && removedAttributes.Count > 0) {
if (assemblyAttributes.RemoveAll(removedAttributes.Contains) > 0)
hasChanges = true;
}
if (addedAttributes != null) {
assemblyAttributes.AddRange(addedAttributes);
hasChanges = true;
}
void RemoveAssemblyAttributes(ICollection<IAttribute> attributes) if (hasChanges)
{ readOnlyAssemblyAttributes = assemblyAttributes.ToArray();
assemblyAttributes.RemoveAll(attributes.Contains);
} }
#endregion #endregion
@ -96,8 +89,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
ICollection<IAttribute> oldAssemblyAttributes = null, ICollection<IAttribute> oldAssemblyAttributes = null,
ICollection<IAttribute> newAssemblyAttributes = null) ICollection<IAttribute> newAssemblyAttributes = null)
{ {
readerWriterLock.EnterWriteLock();
try { try {
types.ReadWriteLock.EnterWriteLock();
if (oldTypes != null) { if (oldTypes != null) {
foreach (var element in oldTypes) { foreach (var element in oldTypes) {
RemoveType(element); RemoveType(element);
@ -108,12 +101,93 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
AddType(element); AddType(element);
} }
} }
if (oldAssemblyAttributes != null) AddRemoveAssemblyAttributes(oldAssemblyAttributes, newAssemblyAttributes);
RemoveAssemblyAttributes(oldAssemblyAttributes); } finally {
if (newAssemblyAttributes != null) readerWriterLock.ExitWriteLock();
AddAssemblyAttributes(newAssemblyAttributes); }
}
#endregion
#region IProjectContent implementation
public ITypeDefinition GetClass(string fullTypeName, int typeParameterCount, StringComparer nameComparer)
{
readerWriterLock.EnterReadLock();
try {
return types.GetClass(fullTypeName, typeParameterCount, nameComparer);
} finally {
readerWriterLock.ExitReadLock();
}
}
public IEnumerable<ITypeDefinition> GetClasses()
{
readerWriterLock.EnterReadLock();
try {
// make a copy with ToArray() for thread-safe access
return types.GetClasses().ToArray();
} finally { } finally {
types.ReadWriteLock.ExitWriteLock(); readerWriterLock.ExitReadLock();
}
}
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer)
{
readerWriterLock.EnterReadLock();
try {
// make a copy with ToArray() for thread-safe access
return types.GetClasses(nameSpace, nameComparer).ToArray();
} finally {
readerWriterLock.ExitReadLock();
}
}
public IEnumerable<string> GetNamespaces()
{
readerWriterLock.EnterReadLock();
try {
// make a copy with ToArray() for thread-safe access
return types.GetNamespaces().ToArray();
} finally {
readerWriterLock.ExitReadLock();
}
}
#endregion
#region Synchronization
public object CacheToken {
get { return null; }
}
public ISynchronizedTypeResolveContext Synchronize()
{
// don't acquire the lock on OutOfMemoryException etc.
ISynchronizedTypeResolveContext sync = new ReadWriteSynchronizedTypeResolveContext(types, readerWriterLock);
readerWriterLock.EnterReadLock();
return sync;
}
sealed class ReadWriteSynchronizedTypeResolveContext : ProxyTypeResolveContext, ISynchronizedTypeResolveContext
{
ReaderWriterLockSlim readerWriterLock;
public ReadWriteSynchronizedTypeResolveContext(ITypeResolveContext target, ReaderWriterLockSlim readerWriterLock)
: base(target)
{
this.readerWriterLock = readerWriterLock;
}
public void Dispose()
{
if (readerWriterLock != null) {
readerWriterLock.ExitReadLock();
readerWriterLock = null;
}
}
public override ISynchronizedTypeResolveContext Synchronize()
{
// nested Synchronize() calls don't need any locking
return new ReadWriteSynchronizedTypeResolveContext(target, null);
} }
} }
#endregion #endregion

125
ICSharpCode.NRefactory/TypeSystem/Implementation/TypeStorage.cs

@ -10,16 +10,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
/// <summary> /// <summary>
/// Stores a set of types and allows resolving them. /// Stores a set of types and allows resolving them.
/// Synchronization is implemented using a <see cref="ReaderWriterLockSlim"/>.
/// </summary> /// </summary>
/// <remarks>
/// Concurrent read accesses are thread-safe, but a write access concurrent to any other access is not safe.
/// </remarks>
public sealed class TypeStorage : ITypeResolveContext public sealed class TypeStorage : ITypeResolveContext
{ {
ReaderWriterLockSlim readWriteLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public ReaderWriterLockSlim ReadWriteLock {
get { return readWriteLock; }
}
#region FullNameAndTypeParameterCount #region FullNameAndTypeParameterCount
struct FullNameAndTypeParameterCount struct FullNameAndTypeParameterCount
{ {
@ -57,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
#endregion #endregion
#region Type Dictionary Storage #region Type Dictionary Storage
Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[] _typeDicts = { volatile Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[] _typeDicts = {
new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(FullNameAndTypeParameterCountComparer.Ordinal) new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(FullNameAndTypeParameterCountComparer.Ordinal)
}; };
readonly object typeDictsLock = new object(); readonly object typeDictsLock = new object();
@ -66,7 +62,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
// Gets the dictionary for the specified comparer, creating it if necessary. // Gets the dictionary for the specified comparer, creating it if necessary.
// New dictionaries might be added during read accesses, so this method needs to be thread-safe, // New dictionaries might be added during read accesses, so this method needs to be thread-safe,
// as we allow concurrent read-accesses without locking. // as we allow concurrent read-accesses.
var typeDicts = this._typeDicts; var typeDicts = this._typeDicts;
foreach (var dict in typeDicts) { foreach (var dict in typeDicts) {
FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer; FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
@ -74,38 +70,33 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
return dict; return dict;
} }
try { // ensure that no other thread can try to lazy-create this (or another) dict
// ensure we have a read-lock on the PC so that no concurrent modifications are attempted lock (typeDictsLock) {
readWriteLock.EnterReadLock(); typeDicts = this._typeDicts; // fetch fresh value after locking
// ensure that no other thread can try to lazy-create this (or another) dict // try looking for it again, maybe it was added while we were waiting for a lock
lock (typeDictsLock) { // (double-checked locking pattern)
typeDicts = this._typeDicts; // fetch fresh value after locking foreach (var dict in typeDicts) {
// try looking for it again, maybe it was added while we were waiting for a lock FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
foreach (var dict in typeDicts) { if (comparer.NameComparer == nameComparer)
FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer; return dict;
if (comparer.NameComparer == nameComparer) }
return dict;
} // now create new dict
var oldDict = typeDicts[0]; // Ordinal dict
// now create new dict var newDict = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(
var oldDict = typeDicts[0]; // Ordinal dict oldDict.Count,
var newDict = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>( new FullNameAndTypeParameterCountComparer(nameComparer));
oldDict.Count, foreach (var pair in oldDict) {
new FullNameAndTypeParameterCountComparer(nameComparer)); // don't use Add() as there might be conflicts in the target language
foreach (var pair in oldDict) { newDict[pair.Key] = pair.Value;
// don't use Add() as there might be conflicts in the target language
newDict[pair.Key] = pair.Value;
}
// add the new dict to the array of dicts
var newTypeDicts = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[typeDicts.Length + 1];
Array.Copy(typeDicts, 0, newTypeDicts, 0, typeDicts.Length);
newTypeDicts[typeDicts.Length] = newDict;
this._typeDicts = newTypeDicts;
return newDict;
} }
} finally {
readWriteLock.ExitReadLock(); // add the new dict to the array of dicts
var newTypeDicts = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[typeDicts.Length + 1];
Array.Copy(typeDicts, 0, newTypeDicts, 0, typeDicts.Length);
newTypeDicts[typeDicts.Length] = newDict;
this._typeDicts = newTypeDicts;
return newDict;
} }
} }
#endregion #endregion
@ -146,31 +137,21 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
#endregion #endregion
#region Synchronize #region Synchronize
/// <inheritdoc/> /// <summary>
/// TypeStorage is mutable and does not provide any means for synchronization, so this method
/// always throws a <see cref="NotSupportedException"/>.
/// </summary>
public ISynchronizedTypeResolveContext Synchronize() public ISynchronizedTypeResolveContext Synchronize()
{ {
readWriteLock.EnterReadLock(); throw new NotSupportedException();
return new ReadWriteSynchronizedTypeResolveContext(this, readWriteLock);
} }
sealed class ReadWriteSynchronizedTypeResolveContext : ProxyTypeResolveContext, ISynchronizedTypeResolveContext /// <inheritdoc/>
{ public object CacheToken {
readonly ReaderWriterLockSlim readWriteLock; // TypeStorage is mutable, so caching is a bad idea.
bool disposed; // We could provide a CacheToken if we update it on every modication, but
// that's not worth the effort as TypeStorage is rarely directly used in resolve operations.
public ReadWriteSynchronizedTypeResolveContext(ITypeResolveContext context, ReaderWriterLockSlim readWriteLock) get { return null; }
: base(context)
{
this.readWriteLock = readWriteLock;
}
public void Dispose()
{
if (!disposed) {
disposed = true;
readWriteLock.ExitReadLock();
}
}
} }
#endregion #endregion
@ -182,14 +163,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
if (fullName == null) if (fullName == null)
throw new ArgumentNullException("fullName"); throw new ArgumentNullException("fullName");
try { var key = new FullNameAndTypeParameterCount(fullName, typeParameterCount);
readWriteLock.EnterWriteLock(); foreach (var dict in _typeDicts) {
var key = new FullNameAndTypeParameterCount(fullName, typeParameterCount); dict.Remove(key);
foreach (var dict in _typeDicts) {
dict.Remove(key);
}
} finally {
readWriteLock.ExitWriteLock();
} }
} }
#endregion #endregion
@ -203,14 +179,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
if (typeDefinition == null) if (typeDefinition == null)
throw new ArgumentNullException("typeDefinition"); throw new ArgumentNullException("typeDefinition");
try { var key = new FullNameAndTypeParameterCount(typeDefinition.FullName, typeDefinition.TypeParameterCount);
readWriteLock.EnterWriteLock(); foreach (var dict in _typeDicts) {
var key = new FullNameAndTypeParameterCount(typeDefinition.FullName, typeDefinition.TypeParameterCount); dict[key] = typeDefinition;
foreach (var dict in _typeDicts) {
dict[key] = typeDefinition;
}
} finally {
readWriteLock.ExitWriteLock();
} }
} }
#endregion #endregion

4
ICSharpCode.NRefactory/TypeSystem/Implementation/TypeWithElementType.cs

@ -28,8 +28,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return elementType.FullName + NameSuffix; } get { return elementType.FullName + NameSuffix; }
} }
public override string DotNetName { public override string ReflectionName {
get { return elementType.DotNetName + NameSuffix; } get { return elementType.ReflectionName + NameSuffix; }
} }
public abstract string NameSuffix { get; } public abstract string NameSuffix { get; }

8
ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

@ -115,15 +115,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
get { return genericType.Namespace;} get { return genericType.Namespace;}
} }
public string DotNetName { public string ReflectionName {
get { get {
StringBuilder b = new StringBuilder(genericType.DotNetName); StringBuilder b = new StringBuilder(genericType.ReflectionName);
b.Append('['); b.Append('[');
for (int i = 0; i < typeArguments.Length; i++) { for (int i = 0; i < typeArguments.Length; i++) {
if (i > 0) if (i > 0)
b.Append(','); b.Append(',');
b.Append('['); b.Append('[');
b.Append(typeArguments[i].DotNetName); b.Append(typeArguments[i].ReflectionName);
b.Append(']'); b.Append(']');
} }
b.Append(']'); b.Append(']');
@ -133,7 +133,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public override string ToString() public override string ToString()
{ {
return DotNetName; return ReflectionName;
} }
public ReadOnlyCollection<IType> TypeArguments { public ReadOnlyCollection<IType> TypeArguments {

187
ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.TypeSystem namespace ICSharpCode.NRefactory.TypeSystem
@ -22,6 +23,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary> /// </summary>
public sealed class Dynamic {} public sealed class Dynamic {}
#region ITypeResolveContext.GetClass(Type)
/// <summary> /// <summary>
/// Retrieves a class. /// Retrieves a class.
/// </summary> /// </summary>
@ -55,7 +57,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
return context.GetClass(name, typeParameterCount, StringComparer.Ordinal); return context.GetClass(name, typeParameterCount, StringComparer.Ordinal);
} }
} }
#endregion
#region Type.ToTypeReference()
/// <summary> /// <summary>
/// Creates a reference to the specified type. /// Creates a reference to the specified type.
/// </summary> /// </summary>
@ -90,7 +94,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
return SharedTypes.UnknownType; return SharedTypes.UnknownType;
} else { } else {
ITypeDefinition c = (entity as ITypeDefinition) ?? (entity is IMember ? ((IMember)entity).DeclaringTypeDefinition : null); ITypeDefinition c = (entity as ITypeDefinition) ?? (entity != null ? entity.DeclaringTypeDefinition : null);
if (c != null && type.GenericParameterPosition < c.TypeParameters.Count) { if (c != null && type.GenericParameterPosition < c.TypeParameters.Count) {
if (c.TypeParameters[type.GenericParameterPosition].Name == type.Name) { if (c.TypeParameters[type.GenericParameterPosition].Name == type.Name) {
return c.TypeParameters[type.GenericParameterPosition]; return c.TypeParameters[type.GenericParameterPosition];
@ -113,7 +117,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
return new GetClassTypeReference(name, typeParameterCount); return new GetClassTypeReference(name, typeParameterCount);
} }
} }
#endregion
#region SplitTypeParameterCountFromReflectionName
/// <summary> /// <summary>
/// Removes the ` with type parameter count from the reflection name. /// Removes the ` with type parameter count from the reflection name.
/// </summary> /// </summary>
@ -146,7 +152,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
return reflectionName; return reflectionName;
} }
} }
#endregion
#region TypeCode.ToTypeReference()
static readonly ITypeReference[] primitiveTypeReferences = { static readonly ITypeReference[] primitiveTypeReferences = {
SharedTypes.UnknownType, // TypeCode.Empty SharedTypes.UnknownType, // TypeCode.Empty
new GetClassTypeReference("System.Object", 0), new GetClassTypeReference("System.Object", 0),
@ -178,7 +186,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
{ {
return primitiveTypeReferences[(int)typeCode]; return primitiveTypeReferences[(int)typeCode];
} }
#endregion
#region GetTypeCode
static readonly Dictionary<string, TypeCode> typeNameToCodeDict = new Dictionary<string, TypeCode> { static readonly Dictionary<string, TypeCode> typeNameToCodeDict = new Dictionary<string, TypeCode> {
{ "System.Object", TypeCode.Object }, { "System.Object", TypeCode.Object },
{ "System.DBNull", TypeCode.DBNull }, { "System.DBNull", TypeCode.DBNull },
@ -211,5 +221,180 @@ namespace ICSharpCode.NRefactory.TypeSystem
else else
return TypeCode.Empty; return TypeCode.Empty;
} }
#endregion
#region ParseReflectionName
/// <summary>
/// Parses a reflection name into a type reference.
/// </summary>
public static ITypeReference ParseReflectionName(string reflectionTypeName, IEntity parentEntity = null)
{
int pos = 0;
ITypeReference r = ParseReflectionName(reflectionTypeName, ref pos, parentEntity);
if (pos < reflectionTypeName.Length)
throw new ReflectionNameParseException(pos, "Expected end of type name");
return r;
}
static bool IsReflectionNameSpecialCharacter(char c)
{
switch (c) {
case '+':
case '`':
case '[':
case ']':
case ',':
case '*':
case '&':
return true;
default:
return false;
}
}
static ITypeReference ParseReflectionName(string reflectionTypeName, ref int pos, IEntity entity)
{
if (pos == reflectionTypeName.Length)
throw new ReflectionNameParseException(pos, "Unexpected end");
if (reflectionTypeName[pos] == '`') {
// type parameter reference
pos++;
if (pos == reflectionTypeName.Length)
throw new ReflectionNameParseException(pos, "Unexpected end");
if (reflectionTypeName[pos] == '`') {
// method type parameter reference
pos++;
int index = ReadTypeParameterCount(reflectionTypeName, ref pos);
IMethod method = entity as IMethod;
if (method != null && index >= 0 && index < method.TypeParameters.Count)
return method.TypeParameters[index];
else
return SharedTypes.UnknownType;
} else {
// class type parameter reference
int index = ReadTypeParameterCount(reflectionTypeName, ref pos);
ITypeDefinition c = (entity as ITypeDefinition) ?? (entity != null ? entity.DeclaringTypeDefinition : null);
if (c != null && index >= 0 && index < c.TypeParameters.Count)
return c.TypeParameters[index];
else
return SharedTypes.UnknownType;
}
}
// not a type parameter reference: read the actual type name
int tpc;
string typeName = ReadTypeName(reflectionTypeName, ref pos, out tpc);
ITypeReference reference = new GetClassTypeReference(typeName, tpc);
// read type suffixes
while (pos < reflectionTypeName.Length) {
switch (reflectionTypeName[pos++]) {
case '+':
typeName = ReadTypeName(reflectionTypeName, ref pos, out tpc);
reference = new NestedTypeReference(reference, typeName, tpc);
break;
case '*':
reference = new PointerTypeReference(reference);
break;
case '&':
reference = new ByReferenceTypeReference(reference);
break;
case '[':
// this might be an array or a generic type
if (pos == reflectionTypeName.Length)
throw new ReflectionNameParseException(pos, "Unexpected end");
if (reflectionTypeName[pos] == '[') {
// it's a generic type
List<ITypeReference> typeArguments = new List<ITypeReference>(tpc);
pos++;
typeArguments.Add(ParseReflectionName(reflectionTypeName, ref pos, entity));
if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']')
pos++;
else
throw new ReflectionNameParseException(pos, "Expected end of type argument");
while (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ',') {
pos++;
if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == '[')
pos++;
else
throw new ReflectionNameParseException(pos, "Expected another type argument");
typeArguments.Add(ParseReflectionName(reflectionTypeName, ref pos, entity));
if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']')
pos++;
else
throw new ReflectionNameParseException(pos, "Expected end of type argument");
}
if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']') {
pos++;
reference = new ParameterizedTypeReference(reference, typeArguments);
} else {
throw new ReflectionNameParseException(pos, "Expected end of generic type");
}
} else {
// it's an array
int dimensions = 1;
while (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ',') {
dimensions++;
pos++;
}
if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']') {
pos++; // end of array
reference = new ArrayTypeReference(reference, dimensions);
} else {
throw new ReflectionNameParseException(pos, "Expected array modifier");
}
}
break;
case ',':
// assembly qualified name, ignore everything up to the end/next ']'
while (pos < reflectionTypeName.Length && reflectionTypeName[pos] != ']')
pos++;
break;
default:
pos--; // reset pos to the character we couldn't read
if (reflectionTypeName[pos] == ']')
return reference; // return from a nested generic
else
throw new ReflectionNameParseException(pos, "Unexpected character: '" + reflectionTypeName[pos] + "'");
}
}
return reference;
}
static string ReadTypeName(string reflectionTypeName, ref int pos, out int tpc)
{
int startPos = pos;
// skip the simple name portion:
while (pos < reflectionTypeName.Length && !IsReflectionNameSpecialCharacter(reflectionTypeName[pos]))
pos++;
if (pos == startPos)
throw new ReflectionNameParseException(pos, "Expected type name");
string typeName = reflectionTypeName.Substring(startPos, pos - startPos);
if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == '`') {
pos++;
tpc = ReadTypeParameterCount(reflectionTypeName, ref pos);
} else {
tpc = 0;
}
return typeName;
}
static int ReadTypeParameterCount(string reflectionTypeName, ref int pos)
{
int startPos = pos;
while (pos < reflectionTypeName.Length) {
char c = reflectionTypeName[pos];
if (c < '0' || c > '9')
break;
pos++;
}
int tpc;
if (!int.TryParse(reflectionTypeName.Substring(startPos, pos - startPos), out tpc))
throw new ReflectionNameParseException(pos, "Expected type parameter count");
return tpc;
}
#endregion
} }
} }

45
ICSharpCode.NRefactory/TypeSystem/ReflectionNameParseException.cs

@ -0,0 +1,45 @@

using System;
using System.Runtime.Serialization;
namespace ICSharpCode.NRefactory.TypeSystem
{
/// <summary>
/// Represents an error while parsing a reflection name.
/// </summary>
public class ReflectionNameParseException : Exception
{
int position;
public int Position {
get { return position; }
}
public ReflectionNameParseException(int position)
{
this.position = position;
}
public ReflectionNameParseException(int position, string message) : base(message)
{
this.position = position;
}
public ReflectionNameParseException(int position, string message, Exception innerException) : base(message, innerException)
{
this.position = position;
}
// This constructor is needed for serialization.
protected ReflectionNameParseException(SerializationInfo info, StreamingContext context) : base(info, context)
{
position = info.GetInt32("position");
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("position", position);
}
}
}

98
README

@ -26,3 +26,101 @@ Null-Object pattern:
The pattern also extends to the C# resolver, which always produces a ResolveResult, even in error cases. The pattern also extends to the C# resolver, which always produces a ResolveResult, even in error cases.
Use ResolveResult.IsError to detect resolver errors. Also note that many resolver errors still have a meaningful type attached, Use ResolveResult.IsError to detect resolver errors. Also note that many resolver errors still have a meaningful type attached,
this allows code completion to work in the presence of minor semantic errors. this allows code completion to work in the presence of minor semantic errors.
FAQ:
Q: What is the difference between a type and a type definition?
A: Basically, a type (IType) is any type in the .NET type system:
- an array (ArrayType)
- a pointer (PointerType)
- a managed reference (ByReferenceType)
- a parameterized type (ParameterizedType, e.g. List<int>)
- a type parameter (ITypeParameter, e.g. T)
- or a type definition (ITypeDefiniton)
Type definitions are only classes, structs, enums and delegates.
Every type definition is a type, but not every type is a type definition.
NRefactory's ITypeDefinition derives from IType, so you can directly use any type definition as a type.
In the other direction, you could try to cast a type to ITypeDefinition, or you can call the GetDefinition()
method. The GetDefinition() method will also return the underlying ITypeDefinition if given a parameterized type,
so "List<int>".GetDefinition() is "List<T>".
Q: What is the difference betweent type references and types?
I've seen lots of duplicated classes (ArrayType vs. ArrayTypeReference, etc.)
NRefactory has the concept of the "project content": every assembly/project is stored independently from other assemblies/projects.
It is possible to load some source code into a project which contains the type reference "int[]" without having to load
mscorlib into NRefactory.
So inside the entities stored for the project, the array type is only referenced using an ITypeReference.
This interface has a single method:
interface ITypeReference {
IType Resolve(ITypeResolutionContext context);
}
By calling the Resolve()-method, you will get back the actual ArrayType.
At this point, you have to provide the type resolution context:
Note that every type can also be used as type reference - the IType interface derives from ITypeReference.
Every IType simply returns itself when the Resolve()-method is called.
Types are often directly used as references when source and target of the reference are within the same assembly.
A: If you've previously used the .NET Reflection API, the concept of type references is new to you.
Q: How do I get the IType or ITypeReference for a primitive type such as string or int?
A: Please use:
TypeCode.Int32.ToTypeReference().Resolve(context)
Skip the Resolve() call if you only need the type reference.
ReflectionHelper.ToTypeReference is very fast if given a TypeCode (it simply looks up an existing type reference in an array),
and your code will benefit from caching of the resolve result (once that gets implemented for these primitive type references).
Avoid using "context.GetClass(typeof(int))" - this call involves Reflection on the System.Type being passed,
cannot benefit from any caching implemented in the future, and most importantly: it may return null.
And do you always test your code in a scenario where mscorlib isn't contained in the resolve context?
The approach suggested above will return SharedTypes.UnknownType when the type cannot be resolved, so you
don't run into the risk of getting NullReferenceExceptions.
Q: Is it thread-safe?
A: This question is a bit difficult to answer.
NRefactory was designed to be usable in a multi-threaded IDE.
But of course, this does not mean that everything is thread-safe.
First off, there's no hidden static state, so any two operations working on independent data can be executed concurrently.
TODO: what about the C# parser? gmcs is full of static state...
Some instance methods may use hidden instance state, so it is not safe to e.g use an instance of the CSharp.Resolver.Conversions class
concurrently. Instead, you need to create an instance on every thread.
In the case of project contents, it is desirable to be able to use them, and all the classes in that project content,
on multiple threads - for example to provide code completion in an IDE while a background thread parses more files and adds
them to the project content.
For this reason, the entity interfaces (ITypeDefinition, IMember, etc.) are designed to be freezable. Once the Freeze() method is
called, an entity instance becomes immutable and thread-safe.
Whether an ITypeResolveContext is thread-safe depends on the implementation:
TypeStorage: thread-safe for concurrent reads, but only if it's not written to (see XML documentation on TypeStorage)
CecilProjectContent: immutable and thread-safe
SimpleProjectContent: fully thread-safe
CompositeTypeResolveContext: depends on the child contexts
Usually, you'll work with a set of loaded projects (SimpleProjectContents)
and loaded external assemblies (CecilProjectContent).
A CompositeTypeResolveContext representing such a set is thread-safe.
Hoever, some algorithms can become confused if two GetClass() calls with same arguments produce different
results (e.g. because another thread updated a class definition).
Also, there's a performance problem: if you have a composite of 15 SimpleProjectContents and the resolve algorithm
requests 100 types, that's 1500 times entering and leaving the read-lock.
Moreoever, the ITypeResolveContext methods that return collections need to create a copy of the collection.
The solution is to make the read lock more coarse-grained:
using (var syncContext = compositeTypeResolveContext.Synchronize()) {
resolver.ResolveStuff(syncContext);
}
On the call to Synchronize(), all 15 SimpleProjectContents are locked for reading.
The return value "syncContext" can then be used to access the type resolve context without further synchronization overhead.
Once the return value is disposed, the read-locks are released.

Loading…
Cancel
Save