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. 156
      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 @@ -58,7 +58,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
{
ITypeDefinition c = Mscorlib.GetClass(typeof(IntPtr));
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.AreEqual("System.Void", ((PointerType)toPointer.ReturnType.Resolve(ctx)).ElementType.FullName);
}
@ -107,7 +107,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -107,7 +107,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
ITypeDefinition c = Mscorlib.GetClass(typeof(Environment.SpecialFolder));
Assert.IsNotNull(c, "c is null");
Assert.AreEqual("System.Environment.SpecialFolder", c.FullName);
Assert.AreEqual("System.Environment+SpecialFolder", c.DotNetName);
Assert.AreEqual("System.Environment+SpecialFolder", c.ReflectionName);
}
[Test]
@ -135,7 +135,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -135,7 +135,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
var dictionaryRT = new ParameterizedType(dictionary, new[] { Mscorlib.GetClass(typeof(string)), Mscorlib.GetClass(typeof(int)) });
IProperty valueProperty = dictionaryRT.GetProperties(ctx).Single(p => p.Name == "Values");
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());
}

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

@ -18,12 +18,12 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -18,12 +18,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
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)
{
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]
@ -85,7 +85,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -85,7 +85,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
mscorlib.GetClass(typeof(object))
};
Assert.AreEqual(expected,
c.GetAllBaseTypes(context).OrderBy(t => t.DotNetName).ToArray());
c.GetAllBaseTypes(context).OrderBy(t => t.ReflectionName).ToArray());
}
[Test]
@ -103,7 +103,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -103,7 +103,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
mscorlib.GetClass(typeof(ValueType))
};
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 @@ -12,11 +12,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
[TestFixture]
public class ReflectionHelperTests
{
ITypeResolveContext context = CecilLoaderTests.Mscorlib;
void TestGetClass(Type type)
{
ITypeDefinition t = CecilLoaderTests.Mscorlib.GetClass(type);
Assert.NotNull(t, type.FullName);
Assert.AreEqual(type.FullName, t.DotNetName);
Assert.AreEqual(type.FullName, t.ReflectionName);
}
[Test]
@ -53,25 +55,25 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -53,25 +55,25 @@ namespace ICSharpCode.NRefactory.TypeSystem
public void TestToTypeReferenceInnerClass()
{
Assert.AreEqual("System.Environment+SpecialFolder",
typeof(Environment.SpecialFolder).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(Environment.SpecialFolder).ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
public void TestToTypeReferenceUnboundGenericClass()
{
Assert.AreEqual("System.Action`1",
typeof(Action<>).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(Action<>).ToTypeReference().Resolve(context).ReflectionName);
Assert.AreEqual("System.Action`2",
typeof(Action<,>).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(Action<,>).ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
public void TestToTypeReferenceBoundGenericClass()
{
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]]",
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 @@ -79,56 +81,56 @@ namespace ICSharpCode.NRefactory.TypeSystem
public void TestToTypeReferenceNullableType()
{
Assert.AreEqual("System.Nullable`1[[System.Int32]]",
typeof(int?).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(int?).ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
public void TestToTypeReferenceInnerClassInUnboundGenericType()
{
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]
public void TestToTypeReferenceInnerClassInBoundGenericType()
{
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]
public void TestToTypeReferenceArrayType()
{
Assert.AreEqual(typeof(int[]).FullName,
typeof(int[]).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(int[]).ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
public void TestToTypeReferenceMultidimensionalArrayType()
{
Assert.AreEqual(typeof(int[,]).FullName,
typeof(int[,]).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(int[,]).ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
public void TestToTypeReferenceJaggedMultidimensionalArrayType()
{
Assert.AreEqual(typeof(int[,][,,]).FullName,
typeof(int[,][,,]).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(int[,][,,]).ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
public void TestToTypeReferencePointerType()
{
Assert.AreEqual(typeof(int*).FullName,
typeof(int*).ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(int*).ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
public void TestToTypeReferenceByReferenceType()
{
Assert.AreEqual(typeof(int).MakeByRefType().FullName,
typeof(int).MakeByRefType().ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
typeof(int).MakeByRefType().ToTypeReference().Resolve(context).ReflectionName);
}
[Test]
@ -138,11 +140,23 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -138,11 +140,23 @@ namespace ICSharpCode.NRefactory.TypeSystem
Type parameterType = convertAllInfo.GetParameters()[0].ParameterType; // Converter[[`0],[``0]]
// cannot resolve generic types without knowing the parent entity:
Assert.AreEqual("System.Converter`2[[?],[?]]",
parameterType.ToTypeReference().Resolve(CecilLoaderTests.Mscorlib).DotNetName);
parameterType.ToTypeReference().Resolve(context).ReflectionName);
// 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]]",
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 @@ -22,7 +22,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
[SetUpAttribute]
public void SetUp()
{
ctx = AggregateTypeResolveContext.Combine(testCasePC, CecilLoaderTests.Mscorlib);
ctx = CompositeTypeResolveContext.Combine(testCasePC, CecilLoaderTests.Mscorlib);
}
ITypeDefinition GetClass(Type type)
@ -37,7 +37,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -37,7 +37,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual(typeof(SimplePublicClass).Name, c.Name);
Assert.AreEqual(typeof(SimplePublicClass).FullName, c.FullName);
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.IsFalse(c.IsAbstract);
@ -77,26 +77,26 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -77,26 +77,26 @@ namespace ICSharpCode.NRefactory.TypeSystem
ITypeDefinition testClass = testCasePC.GetClass(typeof(DynamicTest));
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.Action`3[[System.Object],[dynamic[]],[System.Object]]", m1.Parameters[0].Type.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).ReflectionName);
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");
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");
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");
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");
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");
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]
@ -117,7 +117,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -117,7 +117,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual("System.Collections.Generic.IDictionary", crt.FullName);
Assert.AreEqual("System.String", crt.TypeArguments[0].FullName);
// ? 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]
@ -140,7 +140,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -140,7 +140,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreSame(m, m.TypeParameters[0].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));
}
@ -202,8 +202,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -202,8 +202,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
var e = testCasePC.GetClass(typeof(MyEnum));
Assert.AreEqual(ClassType.Enum, e.ClassType);
Assert.AreEqual(false, e.IsReferenceType);
Assert.AreEqual("System.Int16", e.BaseTypes[0].Resolve(ctx).DotNetName);
Assert.AreEqual(new[] { "System.Enum" }, e.GetBaseTypes(ctx).Select(t => t.DotNetName).ToArray());
Assert.AreEqual("System.Int16", e.BaseTypes[0].Resolve(ctx).ReflectionName);
Assert.AreEqual(new[] { "System.Enum" }, e.GetBaseTypes(ctx).Select(t => t.ReflectionName).ToArray());
}
[Test]

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

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

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

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

3
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -179,7 +179,7 @@ @@ -179,7 +179,7 @@
<Compile Include="TypeSystem\Implementation\DefaultProperty.cs" />
<Compile Include="TypeSystem\Implementation\DefaultTypeParameter.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\ProxyTypeResolveContext.cs" />
<Compile Include="TypeSystem\Implementation\DefaultTypeDefinition.cs" />
@ -206,6 +206,7 @@ @@ -206,6 +206,7 @@
<Compile Include="TypeSystem\ITypeResolveContext.cs" />
<Compile Include="TypeSystem\NullableType.cs" />
<Compile Include="TypeSystem\ParameterizedType.cs" />
<Compile Include="TypeSystem\ReflectionNameParseException.cs" />
<Compile Include="TypeSystem\TypeVisitor.cs" />
<Compile Include="TypeSystem\IVariable.cs" />
<Compile Include="TypeSystem\PointerType.cs" />

2
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -48,7 +48,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -48,7 +48,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
TypeStorage typeStorage = new TypeStorage();
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>();
foreach (ModuleDefinition module in assemblyDefinition.Modules) {
foreach (TypeDefinition td in module.Types) {

4
ICSharpCode.NRefactory/TypeSystem/INamedElement.cs

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

3
ICSharpCode.NRefactory/TypeSystem/ISynchronizedTypeResolveContext.cs

@ -17,6 +17,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -17,6 +17,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// A simple implementation might enter a ReaderWriterLock when the synchronized context
/// is created, and releases the lock when Dispose() is called.
/// 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>
public interface ISynchronizedTypeResolveContext : ITypeResolveContext, IDisposable
{

2
ICSharpCode.NRefactory/TypeSystem/IType.cs

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

20
ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs

@ -66,6 +66,22 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -66,6 +66,22 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// However, implementations based on immutable data structures are also possible.
/// </remarks>
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))]
@ -104,5 +120,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -104,5 +120,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
Contract.Ensures(Contract.Result<IEnumerable<ITypeDefinition>>() != 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 @@ -237,13 +237,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return declaringTypeDefinition.Namespace; }
}
public virtual string DotNetName {
get { return this.DeclaringType.DotNetName + "." + this.Name; }
public virtual string ReflectionName {
get { return this.DeclaringType.ReflectionName + "." + this.Name; }
}
public override string ToString()
{
return "[" + EntityType + " " + DotNetName + ":" + ReturnType + "]";
return "[" + EntityType + " " + ReflectionName + ":" + ReturnType + "]";
}
public virtual void PrepareForInterning(IInterningProvider provider)

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

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

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

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

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

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

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

@ -25,6 +25,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -25,6 +25,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
if (context == null)
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;
}

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

@ -53,5 +53,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -53,5 +53,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
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; }
}
}
}

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

@ -5,60 +5,53 @@ using System; @@ -5,60 +5,53 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
/// <summary>
/// Simple <see cref="IProjectContent"/> implementation that stores the list of classes/namespaces.
/// Synchronization is implemented using a <see cref="ReaderWriterLockSlim"/>.
/// </summary>
/// <remarks>
/// This is a decorator around <see cref="TypeStorage"/> that adds support for the IProjectContent interface
/// and for partial classes.
/// Compared with <see cref="TypeStorage"/>, this class adds support for the IProjectContent interface,
/// for partial classes, and for multi-threading.
/// </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
/// <summary>
/// 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
readonly TypeStorage types = new TypeStorage();
readonly ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim();
#region AssemblyAttributes
readonly List<IAttribute> assemblyAttributes;
readonly ReadOnlyCollection<IAttribute> readOnlyAssemblyAttributes;
readonly List<IAttribute> assemblyAttributes = new List<IAttribute>(); // mutable assembly attribute storage
volatile IAttribute[] readOnlyAssemblyAttributes = {}; // volatile field with copy for reading threads
/// <inheritdoc/>
public virtual IList<IAttribute> AssemblyAttributes {
public IList<IAttribute> AssemblyAttributes {
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
// the list inside the lock (this method is called inside the ReaderWriterLock)
assemblyAttributes.AddRange(attributes);
}
void RemoveAssemblyAttributes(ICollection<IAttribute> attributes)
{
assemblyAttributes.RemoveAll(attributes.Contains);
// the list inside the lock (this method is called inside the write lock)
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;
}
if (hasChanges)
readOnlyAssemblyAttributes = assemblyAttributes.ToArray();
}
#endregion
@ -96,8 +89,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -96,8 +89,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
ICollection<IAttribute> oldAssemblyAttributes = null,
ICollection<IAttribute> newAssemblyAttributes = null)
{
readerWriterLock.EnterWriteLock();
try {
types.ReadWriteLock.EnterWriteLock();
if (oldTypes != null) {
foreach (var element in oldTypes) {
RemoveType(element);
@ -108,12 +101,93 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -108,12 +101,93 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
AddType(element);
}
}
if (oldAssemblyAttributes != null)
RemoveAssemblyAttributes(oldAssemblyAttributes);
if (newAssemblyAttributes != null)
AddAssemblyAttributes(newAssemblyAttributes);
AddRemoveAssemblyAttributes(oldAssemblyAttributes, newAssemblyAttributes);
} finally {
readerWriterLock.ExitWriteLock();
}
}
#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 {
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 {
types.ReadWriteLock.ExitWriteLock();
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

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

@ -10,16 +10,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -10,16 +10,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
/// <summary>
/// Stores a set of types and allows resolving them.
/// Synchronization is implemented using a <see cref="ReaderWriterLockSlim"/>.
/// </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
{
ReaderWriterLockSlim readWriteLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public ReaderWriterLockSlim ReadWriteLock {
get { return readWriteLock; }
}
#region FullNameAndTypeParameterCount
struct FullNameAndTypeParameterCount
{
@ -57,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -57,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
#endregion
#region Type Dictionary Storage
Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[] _typeDicts = {
volatile Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[] _typeDicts = {
new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(FullNameAndTypeParameterCountComparer.Ordinal)
};
readonly object typeDictsLock = new object();
@ -66,7 +62,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -66,7 +62,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
// 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,
// as we allow concurrent read-accesses without locking.
// as we allow concurrent read-accesses.
var typeDicts = this._typeDicts;
foreach (var dict in typeDicts) {
FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
@ -74,38 +70,33 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -74,38 +70,33 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
return dict;
}
try {
// ensure we have a read-lock on the PC so that no concurrent modifications are attempted
readWriteLock.EnterReadLock();
// ensure that no other thread can try to lazy-create this (or another) dict
lock (typeDictsLock) {
typeDicts = this._typeDicts; // fetch fresh value after locking
// try looking for it again, maybe it was added while we were waiting for a lock
foreach (var dict in typeDicts) {
FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
if (comparer.NameComparer == nameComparer)
return dict;
}
// now create new dict
var oldDict = typeDicts[0]; // Ordinal dict
var newDict = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(
oldDict.Count,
new FullNameAndTypeParameterCountComparer(nameComparer));
foreach (var pair in oldDict) {
// 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;
// ensure that no other thread can try to lazy-create this (or another) dict
lock (typeDictsLock) {
typeDicts = this._typeDicts; // fetch fresh value after locking
// try looking for it again, maybe it was added while we were waiting for a lock
// (double-checked locking pattern)
foreach (var dict in typeDicts) {
FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
if (comparer.NameComparer == nameComparer)
return dict;
}
} finally {
readWriteLock.ExitReadLock();
// now create new dict
var oldDict = typeDicts[0]; // Ordinal dict
var newDict = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(
oldDict.Count,
new FullNameAndTypeParameterCountComparer(nameComparer));
foreach (var pair in oldDict) {
// 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;
}
}
#endregion
@ -146,31 +137,21 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -146,31 +137,21 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
#endregion
#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()
{
readWriteLock.EnterReadLock();
return new ReadWriteSynchronizedTypeResolveContext(this, readWriteLock);
throw new NotSupportedException();
}
sealed class ReadWriteSynchronizedTypeResolveContext : ProxyTypeResolveContext, ISynchronizedTypeResolveContext
{
readonly ReaderWriterLockSlim readWriteLock;
bool disposed;
public ReadWriteSynchronizedTypeResolveContext(ITypeResolveContext context, ReaderWriterLockSlim readWriteLock)
: base(context)
{
this.readWriteLock = readWriteLock;
}
public void Dispose()
{
if (!disposed) {
disposed = true;
readWriteLock.ExitReadLock();
}
}
/// <inheritdoc/>
public object CacheToken {
// TypeStorage is mutable, so caching is a bad idea.
// 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.
get { return null; }
}
#endregion
@ -182,14 +163,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -182,14 +163,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
if (fullName == null)
throw new ArgumentNullException("fullName");
try {
readWriteLock.EnterWriteLock();
var key = new FullNameAndTypeParameterCount(fullName, typeParameterCount);
foreach (var dict in _typeDicts) {
dict.Remove(key);
}
} finally {
readWriteLock.ExitWriteLock();
var key = new FullNameAndTypeParameterCount(fullName, typeParameterCount);
foreach (var dict in _typeDicts) {
dict.Remove(key);
}
}
#endregion
@ -203,14 +179,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -203,14 +179,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
if (typeDefinition == null)
throw new ArgumentNullException("typeDefinition");
try {
readWriteLock.EnterWriteLock();
var key = new FullNameAndTypeParameterCount(typeDefinition.FullName, typeDefinition.TypeParameterCount);
foreach (var dict in _typeDicts) {
dict[key] = typeDefinition;
}
} finally {
readWriteLock.ExitWriteLock();
var key = new FullNameAndTypeParameterCount(typeDefinition.FullName, typeDefinition.TypeParameterCount);
foreach (var dict in _typeDicts) {
dict[key] = typeDefinition;
}
}
#endregion

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

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

8
ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

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

187
ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.TypeSystem
@ -22,6 +23,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -22,6 +23,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public sealed class Dynamic {}
#region ITypeResolveContext.GetClass(Type)
/// <summary>
/// Retrieves a class.
/// </summary>
@ -55,7 +57,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -55,7 +57,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
return context.GetClass(name, typeParameterCount, StringComparer.Ordinal);
}
}
#endregion
#region Type.ToTypeReference()
/// <summary>
/// Creates a reference to the specified type.
/// </summary>
@ -90,7 +94,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -90,7 +94,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
return SharedTypes.UnknownType;
} 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.TypeParameters[type.GenericParameterPosition].Name == type.Name) {
return c.TypeParameters[type.GenericParameterPosition];
@ -113,7 +117,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -113,7 +117,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
return new GetClassTypeReference(name, typeParameterCount);
}
}
#endregion
#region SplitTypeParameterCountFromReflectionName
/// <summary>
/// Removes the ` with type parameter count from the reflection name.
/// </summary>
@ -146,7 +152,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -146,7 +152,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
return reflectionName;
}
}
#endregion
#region TypeCode.ToTypeReference()
static readonly ITypeReference[] primitiveTypeReferences = {
SharedTypes.UnknownType, // TypeCode.Empty
new GetClassTypeReference("System.Object", 0),
@ -178,7 +186,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -178,7 +186,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
{
return primitiveTypeReferences[(int)typeCode];
}
#endregion
#region GetTypeCode
static readonly Dictionary<string, TypeCode> typeNameToCodeDict = new Dictionary<string, TypeCode> {
{ "System.Object", TypeCode.Object },
{ "System.DBNull", TypeCode.DBNull },
@ -211,5 +221,180 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -211,5 +221,180 @@ namespace ICSharpCode.NRefactory.TypeSystem
else
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 @@ @@ -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: @@ -26,3 +26,101 @@ Null-Object pattern:
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,
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