diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index 1901f7f406..176c78323c 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -288,6 +288,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.AreEqual(1, BetterConversion(typeof(short), typeof(int), typeof(long))); Assert.AreEqual(1, BetterConversion(typeof(short), typeof(int), typeof(uint))); Assert.AreEqual(2, BetterConversion(typeof(ushort), typeof(uint), typeof(int))); + Assert.AreEqual(1, BetterConversion(typeof(char), typeof(short), typeof(int))); + Assert.AreEqual(1, BetterConversion(typeof(char), typeof(ushort), typeof(int))); + Assert.AreEqual(1, BetterConversion(typeof(sbyte), typeof(long), typeof(ulong))); + Assert.AreEqual(2, BetterConversion(typeof(byte), typeof(ushort), typeof(short))); + + Assert.AreEqual(1, BetterConversion(1, typeof(sbyte), typeof(byte))); + Assert.AreEqual(2, BetterConversion(1, typeof(ushort), typeof(sbyte))); } [Test] @@ -302,6 +309,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.AreEqual(2, BetterConversion(typeof(byte), typeof(ulong?), typeof(uint))); Assert.AreEqual(0, BetterConversion(typeof(byte), typeof(ulong?), typeof(int))); Assert.AreEqual(2, BetterConversion(typeof(ushort?), typeof(long?), typeof(int?))); + Assert.AreEqual(0, BetterConversion(typeof(sbyte), typeof(int?), typeof(uint?))); } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs index 72a60d7d8e..1f7c3cc008 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs @@ -20,18 +20,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return argumentTypes.Select(t => new ResolveResult(t.ToTypeReference().Resolve(context))).ToArray(); } - DefaultMethod MakeMethod(params Type[] parameterTypes) + DefaultMethod MakeMethod(params object[] parameterTypesOrDefaultValues) { DefaultMethod m = new DefaultMethod(dummyClass, "Method"); - foreach (var type in parameterTypes) { - m.Parameters.Add(new DefaultParameter(type.ToTypeReference(), string.Empty)); + foreach (var typeOrDefaultValue in parameterTypesOrDefaultValues) { + Type type = typeOrDefaultValue as Type; + if (type != null) + m.Parameters.Add(new DefaultParameter(type.ToTypeReference(), string.Empty)); + else if (Type.GetTypeCode(typeOrDefaultValue.GetType()) > TypeCode.Object) + m.Parameters.Add(new DefaultParameter(typeOrDefaultValue.GetType().ToTypeReference(), string.Empty) { + DefaultValue = new SimpleConstantValue(typeOrDefaultValue.GetType().ToTypeReference(), typeOrDefaultValue) + }); + else + throw new ArgumentException(typeOrDefaultValue.ToString()); } return m; } - DefaultMethod MakeParamsMethod(params Type[] parameterTypes) + DefaultMethod MakeParamsMethod(params object[] parameterTypesOrDefaultValues) { - DefaultMethod m = MakeMethod(parameterTypes); + DefaultMethod m = MakeMethod(parameterTypesOrDefaultValues); ((DefaultParameter)m.Parameters.Last()).IsParams = true; return m; } @@ -102,5 +110,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.AreEqual(OverloadResolutionErrors.ArgumentTypeMismatch, r.AddCandidate(MakeParamsMethod(typeof(int)))); Assert.IsFalse(r.BestCandidateIsExpandedForm); } + + [Test] + public void PreferMethodWithoutOptionalParameters() + { + var m1 = MakeMethod(); + var m2 = MakeMethod(1); + + OverloadResolution r = new OverloadResolution(context, MakeArgumentList()); + Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m1)); + Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m2)); + Assert.IsFalse(r.IsAmbiguous); + Assert.AreSame(m1, r.BestCandidate); + } } } diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 2f3aa3af46..55095c94c5 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -63,8 +63,8 @@ - - + + @@ -75,7 +75,7 @@ - + diff --git a/ICSharpCode.NRefactory.Tests/Util/CSharpPrimitiveCastTests.cs b/ICSharpCode.NRefactory.Tests/Utils/CSharpPrimitiveCastTests.cs similarity index 98% rename from ICSharpCode.NRefactory.Tests/Util/CSharpPrimitiveCastTests.cs rename to ICSharpCode.NRefactory.Tests/Utils/CSharpPrimitiveCastTests.cs index f03531081d..fc73adcd71 100644 --- a/ICSharpCode.NRefactory.Tests/Util/CSharpPrimitiveCastTests.cs +++ b/ICSharpCode.NRefactory.Tests/Utils/CSharpPrimitiveCastTests.cs @@ -4,7 +4,7 @@ using System; using NUnit.Framework; -namespace ICSharpCode.NRefactory.Util +namespace ICSharpCode.NRefactory.Utils { [TestFixture] public class CSharpPrimitiveCastTests diff --git a/ICSharpCode.NRefactory.Tests/Util/TreeTraversalTests.cs b/ICSharpCode.NRefactory.Tests/Utils/TreeTraversalTests.cs similarity index 96% rename from ICSharpCode.NRefactory.Tests/Util/TreeTraversalTests.cs rename to ICSharpCode.NRefactory.Tests/Utils/TreeTraversalTests.cs index 9def9c4a0a..dae0f43c81 100644 --- a/ICSharpCode.NRefactory.Tests/Util/TreeTraversalTests.cs +++ b/ICSharpCode.NRefactory.Tests/Utils/TreeTraversalTests.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -namespace ICSharpCode.NRefactory.Util +namespace ICSharpCode.NRefactory.Utils { [TestFixture] public class TreeTraversalTests diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 4cd75ea666..de945defb2 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -300,7 +300,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver switch (op) { case UnaryOperatorType.Minus: if (code == TypeCode.UInt32) { - IType targetType = context.GetClass(typeof(long)) ?? SharedTypes.UnknownType; + IType targetType = TypeCode.Int64.ToTypeReference().Resolve(context); type = targetType; if (isNullable) targetType = NullableType.Create(targetType, context); return ResolveCast(targetType, expression); @@ -309,7 +309,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver case UnaryOperatorType.Plus: case UnaryOperatorType.BitNot: if (code >= TypeCode.Char && code <= TypeCode.UInt16) { - IType targetType = context.GetClass(typeof(int)) ?? SharedTypes.UnknownType; + IType targetType = TypeCode.Int32.ToTypeReference().Resolve(context); type = targetType; if (isNullable) targetType = NullableType.Create(targetType, context); return ResolveCast(targetType, expression); @@ -676,7 +676,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return rhs; return ResolveBinaryOperator(op, lhs, rhs); } - return new ResolveResult(context.GetClass(typeof(bool)) ?? SharedTypes.UnknownType); + return new ResolveResult(TypeCode.Boolean.ToTypeReference().Resolve(context)); } /// @@ -1306,7 +1306,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver object CSharpPrimitiveCast(TypeCode targetType, object input) { - return Util.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow); + return Utils.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow); } ResolveResult CheckErrorAndResolveCast(IType targetType, ResolveResult expression) diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs index 0ffe159d83..cb48d6d430 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using System.Linq; + using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; -using ICSharpCode.NRefactory.Util; namespace ICSharpCode.NRefactory.CSharp.Resolver { @@ -233,8 +233,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// int BetterFunctionMember(Candidate c1, Candidate c2) { - if (c1.ErrorCount < c2.ErrorCount) return 1; - if (c1.ErrorCount > c2.ErrorCount) return 2; + // prefer applicable members (part of heuristic that produces a best candidate even if none is applicable) + if (c1.ErrorCount == 0 && c2.ErrorCount > 0) + return 1; + if (c1.ErrorCount > 0 && c2.ErrorCount == 0) + return 2; // C# 4.0 spec: §7.5.3.2 Better function member bool c1IsBetter = false; @@ -261,6 +264,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return 1; if (!c1IsBetter && c2IsBetter) return 2; + + // prefer members with less errors (part of heuristic that produces a best candidate even if none is applicable) + if (c1.ErrorCount < c2.ErrorCount) return 1; + if (c1.ErrorCount > c2.ErrorCount) return 2; + if (!c1IsBetter && !c2IsBetter) { // we need the tie-breaking rules diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index a94dc4e16d..eae194c109 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -261,14 +261,15 @@ - - - - + + + + + - + \ No newline at end of file diff --git a/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs b/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs index f4ac157255..8fcbc60b0c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs @@ -49,13 +49,15 @@ namespace ICSharpCode.NRefactory.TypeSystem public override IEnumerable GetBaseTypes(ITypeResolveContext context) { List baseTypes = new List(); - IType t = context.GetClass(typeof(Array)); + // PERF: if profiling shows the GetClass(typeof()) here to be a problem, create + // a static cache for the ITypeDefinitions + ITypeDefinition t = context.GetClass(typeof(Array)); if (t != null) baseTypes.Add(t); if (dimensions == 1) { // single-dimensional arrays implement IList t = context.GetClass(typeof(IList<>)); if (t != null) - baseTypes.Add(t); + baseTypes.Add(new ParameterizedType(t, new[] { elementType })); } return baseTypes; } diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs index a13104e239..fd2eede773 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using ICSharpCode.NRefactory.Util; namespace ICSharpCode.NRefactory.TypeSystem { @@ -90,7 +88,7 @@ namespace ICSharpCode.NRefactory.TypeSystem } #endregion - #region IsEnum / GetEnumUnderlyingType + #region IsEnum / IsDelegate /// /// Gets whether the type is an enumeration type. /// @@ -116,7 +114,7 @@ namespace ICSharpCode.NRefactory.TypeSystem if (def.BaseTypes.Count == 1) return def.BaseTypes[0].Resolve(context); else - return context.GetClass(typeof(int)) ?? SharedTypes.UnknownType; + return TypeCode.Int32.ToTypeReference().Resolve(context); } else { throw new ArgumentException("enumType must be an enum"); } diff --git a/ICSharpCode.NRefactory/TypeSystem/IConstantValue.cs b/ICSharpCode.NRefactory/TypeSystem/IConstantValue.cs index 14e421ea41..d27354dd37 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IConstantValue.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IConstantValue.cs @@ -23,6 +23,8 @@ namespace ICSharpCode.NRefactory.TypeSystem /// - string /// - IType (for typeof-expressions) /// and arrays of these values. Enum values are returned using the underlying primitive integer. + /// + /// TODO: how do we represent errors (value not available?) /// object GetValue(ITypeResolveContext context); } diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs index c2a42172cf..177c6efb60 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using ICSharpCode.NRefactory.Utils; + namespace ICSharpCode.NRefactory.TypeSystem { /// @@ -68,20 +70,12 @@ namespace ICSharpCode.NRefactory.TypeSystem ISynchronizedTypeResolveContext Synchronize(); /// - /// Returns an object if caching information based on this resolve context is allowed, + /// Returns the cache manager associated with this resolve context, /// or null if caching is not allowed. - /// Whenever the resolve context changes in some way, this property must return a new object. + /// Whenever the resolve context changes in some way, this property must return a new object to + /// ensure that old caches are cleared. /// - /// - /// 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 return this;. - /// 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. - /// - object CacheToken { get; } + CacheManager CacheManager { get; } } [ContractClassFor(typeof(ITypeResolveContext))] @@ -121,7 +115,7 @@ namespace ICSharpCode.NRefactory.TypeSystem return null; } - object ITypeResolveContext.CacheToken { + Utils.CacheManager ITypeResolveContext.CacheManager { get { return null; } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs index adc85ab49e..289feb6703 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -using ICSharpCode.NRefactory.Util; +using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/CompositeTypeResolveContext.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/CompositeTypeResolveContext.cs index 25534bb86a..ec6c12386b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/CompositeTypeResolveContext.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/CompositeTypeResolveContext.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using ICSharpCode.NRefactory.Utils; + namespace ICSharpCode.NRefactory.TypeSystem.Implementation { /// @@ -81,10 +83,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// public virtual ISynchronizedTypeResolveContext Synchronize() { - return Synchronize(new object()); + return Synchronize(new CacheManager(), true); } - ISynchronizedTypeResolveContext Synchronize(object cacheToken) + ISynchronizedTypeResolveContext Synchronize(CacheManager cacheManager, bool isTopLevel) { ISynchronizedTypeResolveContext[] sync = new ISynchronizedTypeResolveContext[children.Length]; bool success = false; @@ -94,7 +96,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation if (sync[i] == null) throw new InvalidOperationException(children[i] + ".ToString() returned null"); } - ISynchronizedTypeResolveContext r = new CompositeSynchronizedTypeResolveContext(sync, cacheToken); + ISynchronizedTypeResolveContext r = new CompositeSynchronizedTypeResolveContext(sync, cacheManager, isTopLevel); success = true; return r; } finally { @@ -108,7 +110,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } - public virtual object CacheToken { + public virtual CacheManager CacheManager { // 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; } @@ -116,13 +118,15 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation sealed class CompositeSynchronizedTypeResolveContext : CompositeTypeResolveContext, ISynchronizedTypeResolveContext { - readonly object cacheToken; + readonly CacheManager cacheManager; + readonly bool isTopLevel; - public CompositeSynchronizedTypeResolveContext(ISynchronizedTypeResolveContext[] children, object cacheToken) + public CompositeSynchronizedTypeResolveContext(ISynchronizedTypeResolveContext[] children, CacheManager cacheManager, bool isTopLevel) : base(children) { - Debug.Assert(cacheToken != null); - this.cacheToken = cacheToken; + Debug.Assert(cacheManager != null); + this.cacheManager = cacheManager; + this.isTopLevel = isTopLevel; } public void Dispose() @@ -130,18 +134,23 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation foreach (ISynchronizedTypeResolveContext element in children) { element.Dispose(); } + if (isTopLevel) { + // When the top-level synchronized block is closed, clear any cached data + // (the cache token isn't valid anymore) + cacheManager.Dispose(); + } } - public override object CacheToken { + public override CacheManager CacheManager { // 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; } + // so this is the only place where implementing CacheManager is really important. + get { return cacheManager; } } public override ISynchronizedTypeResolveContext Synchronize() { // re-use the same cache token for nested synchronized contexts - return base.Synchronize(cacheToken); + return base.Synchronize(cacheManager, false); } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs index d886df57e5..ceef58b593 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs @@ -98,6 +98,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public object GetDefaultValue(ITypeResolveContext context) + { + if (defaultValue == null) + throw new InvalidOperationException(); + else + return defaultValue.GetValue(context); + } + public DomRegion Region { get { return region; } set { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs index 4aa8c80cb6..f4a7e74d33 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs @@ -7,7 +7,7 @@ using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; -using ICSharpCode.NRefactory.Util; +using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs index 9d098ebff9..1d137aa494 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; -using ICSharpCode.NRefactory.Util; + +using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/ProxyTypeResolveContext.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/ProxyTypeResolveContext.cs index 8ed811d13a..2f64267ab7 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/ProxyTypeResolveContext.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/ProxyTypeResolveContext.cs @@ -55,7 +55,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } /// - public virtual object CacheToken { + public virtual Utils.CacheManager CacheManager { // Don't forward this by default; we don't know what derived classes are doing; // it might not be cache-safe. get { return null; } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleProjectContent.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleProjectContent.cs index 5b1da950c2..47fe35d53b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleProjectContent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleProjectContent.cs @@ -7,6 +7,8 @@ using System.Collections.ObjectModel; using System.Linq; using System.Threading; +using ICSharpCode.NRefactory.Utils; + namespace ICSharpCode.NRefactory.TypeSystem.Implementation { /// @@ -154,7 +156,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation #endregion #region Synchronization - public object CacheToken { + public CacheManager CacheManager { get { return null; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeStorage.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeStorage.cs index ae712b638d..a7d0d87208 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeStorage.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeStorage.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using ICSharpCode.NRefactory.Utils; + namespace ICSharpCode.NRefactory.TypeSystem.Implementation { /// @@ -147,7 +149,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } /// - public object CacheToken { + public CacheManager CacheManager { // 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. diff --git a/ICSharpCode.NRefactory/Util/BitVector16.cs b/ICSharpCode.NRefactory/Utils/BitVector16.cs similarity index 97% rename from ICSharpCode.NRefactory/Util/BitVector16.cs rename to ICSharpCode.NRefactory/Utils/BitVector16.cs index e6c4ff72e7..82f4124070 100644 --- a/ICSharpCode.NRefactory/Util/BitVector16.cs +++ b/ICSharpCode.NRefactory/Utils/BitVector16.cs @@ -3,7 +3,7 @@ using System; -namespace ICSharpCode.NRefactory.Util +namespace ICSharpCode.NRefactory.Utils { /// /// Holds 16 boolean values. diff --git a/ICSharpCode.NRefactory/Util/BusyManager.cs b/ICSharpCode.NRefactory/Utils/BusyManager.cs similarity index 97% rename from ICSharpCode.NRefactory/Util/BusyManager.cs rename to ICSharpCode.NRefactory/Utils/BusyManager.cs index bf6cb7e6fb..27f9901a00 100644 --- a/ICSharpCode.NRefactory/Util/BusyManager.cs +++ b/ICSharpCode.NRefactory/Utils/BusyManager.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace ICSharpCode.NRefactory.Util +namespace ICSharpCode.NRefactory.Utils { /// /// This class is used to prevent stack overflows by representing a 'busy' flag diff --git a/ICSharpCode.NRefactory/Util/CSharpPrimitiveCast.cs b/ICSharpCode.NRefactory/Utils/CSharpPrimitiveCast.cs similarity index 99% rename from ICSharpCode.NRefactory/Util/CSharpPrimitiveCast.cs rename to ICSharpCode.NRefactory/Utils/CSharpPrimitiveCast.cs index 3f40718f6f..e7cfbdb911 100644 --- a/ICSharpCode.NRefactory/Util/CSharpPrimitiveCast.cs +++ b/ICSharpCode.NRefactory/Utils/CSharpPrimitiveCast.cs @@ -3,7 +3,7 @@ using System; -namespace ICSharpCode.NRefactory.Util +namespace ICSharpCode.NRefactory.Utils { /// /// Static helper method for converting between primitive types. diff --git a/ICSharpCode.NRefactory/Utils/CacheManager.cs b/ICSharpCode.NRefactory/Utils/CacheManager.cs new file mode 100644 index 0000000000..bfb05248e8 --- /dev/null +++ b/ICSharpCode.NRefactory/Utils/CacheManager.cs @@ -0,0 +1,137 @@ +// 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.Threading; + +namespace ICSharpCode.NRefactory.Utils +{ + /// + /// Allows the registration of static "caching types" which can then be used to efficiently retrieve an + /// instance per CacheManager (or even per CacheManager and thread). + /// + /// This class is thread-safe + public sealed class CacheManager : IDisposable + { + /* Lots of code commented out because I don't know if it's useful, clients can usually replicate + * the functionality much more easily and only need the Disposed event to ensure cleanup. + * + * Actually, what I've implemented here looks very much like .NET's internal System.LocalDataStore + * (used for Thread.GetData/SetData) + * + static int nextSharedIndex, nextThreadLocalIndex; + + /// + /// Registers a new cache type. This causes each CacheManager to allocate space for the new cache type. + /// + /// Specifies whether this cache is shared (multi-threaded) or whether + /// there is one instance per thread. + /// Returns a token that can be used to access the cache. + public static CacheToken RegisterType(CacheMode mode) where T : class, new() + { + int index; + switch (mode) { + case CacheMode.Shared: + index = Interlocked.Increment(ref nextSharedIndex); + break; + case CacheMode.ThreadLocal: + index = Interlocked.Increment(ref nextThreadLocalIndex); + break; + default: + throw new ArgumentException("Invalid value for CacheMode", "mode"); + } + return new CacheToken(mode, index); + } + + readonly object lockObj = new object(); + volatile object[] _sharedCaches = new object[nextSharedIndex]; + ThreadLocal threadLocalCaches = new ThreadLocal(() => new object[nextThreadLocalIndex]); + + /// + /// Gets the cache using the specified token. + /// + public T Get(CacheToken token) where T : class, new() + { + switch (token.Mode) { + case CacheMode.Shared: + object[] sharedCaches = this._sharedCaches; + if (token.Index < sharedCaches.Length) { + object c = sharedCaches[token.Index]; + if (c != null) + return (T)c; + } + // it seems like the cache doesn't exist yet, so try to create it: + T newCache = new T(); + lock (lockObj) { + sharedCaches = this._sharedCaches; // fetch fresh value after locking + // use double-checked locking + if (token.Index < sharedCaches.Length) { + object c = sharedCaches[token.Index]; + if (c != null) { + // looks like someone else was faster creating it than this thread + return (T)c; + } + } else { + Array.Resize(ref sharedCaches, nextSharedIndex); + this._sharedCaches = sharedCaches; + } + sharedCaches[token.Index] = newCache; + } + return newCache; + case CacheMode.ThreadLocal: + object[] localCaches = threadLocalCaches.Value; + if (token.Index >= localCaches.Length) { + Array.Resize(ref localCaches, nextThreadLocalIndex); + threadLocalCaches.Value = localCaches; + } + object lc = localCaches[token.Index]; + if (lc != null) { + return (T)lc; + } else { + T newLocalCache = new T(); + localCaches[token.Index] = newLocalCache; + return newLocalCache; + } + default: + throw new ArgumentException("Invalid token"); + } + } + */ + + public event EventHandler Disposed; + + /// + /// Invokes the event. + /// + public void Dispose() + { + //threadLocalCaches.Dispose(); // dispose the ThreadLocal + // TODO: test whether this frees the referenced value on all threads + + // fire Disposed() only once by removing the old event handlers + EventHandler disposed = Interlocked.Exchange(ref Disposed, null); + if (disposed != null) + disposed(this, EventArgs.Empty); + } + } + + /* + public enum CacheMode + { + // don't use 0 so that default(CacheToken<...>) is an invalid mode + Shared = 1, + ThreadLocal = 2 + } + + public struct CacheToken where T : class, new() + { + internal readonly CacheMode Mode; + internal readonly int Index; + + internal CacheToken(CacheMode mode, int index) + { + this.Mode = mode; + this.Index = index; + } + }*/ +} diff --git a/ICSharpCode.NRefactory/Util/TreeTraversal.cs b/ICSharpCode.NRefactory/Utils/TreeTraversal.cs similarity index 98% rename from ICSharpCode.NRefactory/Util/TreeTraversal.cs rename to ICSharpCode.NRefactory/Utils/TreeTraversal.cs index f46090f1cd..f2bf9f3321 100644 --- a/ICSharpCode.NRefactory/Util/TreeTraversal.cs +++ b/ICSharpCode.NRefactory/Utils/TreeTraversal.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace ICSharpCode.NRefactory.Util +namespace ICSharpCode.NRefactory.Utils { /// /// Static helper methods for traversing trees. diff --git a/README b/README index 3a58696e06..73587367d1 100644 --- a/README +++ b/README @@ -135,3 +135,18 @@ Q: What format do the .ToString() methods use? A: They don't use any particular format. They're merely intended as a debugging aid. Currently .ToString() usually matches .ReflectionName, but that may change in the future. + + +Q: Why are there extension methods IType.IsEnum() and IType.IsDelegate(), but no IType.IsStruct() or IType.IsInterface()? + +A: Because if you're asking whether a type is a struct, it's very likely that you're asking the wrong question. + The distinction between class/struct/interface/enum/delegate is important in the world of type definitions, and there's + ITypeDefinition.ClassType to address this. But the distinction isn't so important in the world of types. + + If whatever you are doing works with struct-types, then it likely will also work with enum-types, and also + with type parameters constraint to be a value-type. + So instead of asking IsStruct(), you really should be asking: IType.IsReferenceType == false + + Enums and delegates are special because you can do special things with those types (e.g. subtract them from each other). + If you really need to know, you can do "type.GetDefinition() != null && type.GetDefinition().ClassType == WhatIWant" yourself, + but for the most part you should be fine with IsReferenceType, IsEnum and IsDelegate.