From 2be1569cc719621b48e65c5f32c8c220a39e4ebd Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 26 Aug 2011 11:59:06 +0200 Subject: [PATCH] Add cache for implicit conversions for 25% performance boost. Also did some improvements to interning. --- .../CSharp/Parser/TypeSystemConvertVisitor.cs | 10 +++- .../CSharp/Resolver/CSharpResolver.cs | 16 +++--- .../CSharp/Resolver/Conversions.cs | 50 +++++++++++++++++-- .../Resolver/MethodGroupResolveResult.cs | 6 +-- .../CSharp/Resolver/OverloadResolution.cs | 4 +- .../CSharp/Resolver/ResolveVisitor.cs | 4 +- .../TypeSystem/ByReferenceType.cs | 38 ++++++++++++-- .../Implementation/CompoundTypeDefinition.cs | 1 + .../DefaultExplicitInterfaceImplementation.cs | 1 + .../TypeSystem/PointerType.cs | 38 ++++++++++++-- 10 files changed, 140 insertions(+), 28 deletions(-) diff --git a/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs b/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs index a557a21578..294243a2cf 100644 --- a/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs @@ -130,16 +130,22 @@ namespace ICSharpCode.NRefactory.CSharp public override IEntity VisitUsingDeclaration(UsingDeclaration usingDeclaration, object data) { ITypeOrNamespaceReference u = ConvertType(usingDeclaration.Import, SimpleNameLookupMode.TypeInUsingDeclaration) as ITypeOrNamespaceReference; - if (u != null) + if (u != null) { + if (interningProvider != null) + u = interningProvider.Intern(u); usingScope.Usings.Add(u); + } return null; } public override IEntity VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration, object data) { ITypeOrNamespaceReference u = ConvertType(usingDeclaration.Import, SimpleNameLookupMode.TypeInUsingDeclaration) as ITypeOrNamespaceReference; - if (u != null) + if (u != null) { + if (interningProvider != null) + u = interningProvider.Intern(u); usingScope.UsingAliases.Add(new KeyValuePair(usingDeclaration.Alias, u)); + } return null; } #endregion diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index d3b50fee60..32ceb91ebc 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -539,7 +539,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver bool isNullable = NullableType.IsNullable(expression.Type); // the operator is overloadable: - OverloadResolution userDefinedOperatorOR = new OverloadResolution(context, new[] { expression }); + OverloadResolution userDefinedOperatorOR = new OverloadResolution(context, new[] { expression }, conversions: conversions); foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) { userDefinedOperatorOR.AddCandidate(candidate); } @@ -587,7 +587,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver default: throw new InvalidOperationException(); } - OverloadResolution builtinOperatorOR = new OverloadResolution(context, new[] { expression }); + OverloadResolution builtinOperatorOR = new OverloadResolution(context, new[] { expression }, conversions: conversions); foreach (var candidate in methodGroup) { builtinOperatorOR.AddCandidate(candidate); } @@ -800,7 +800,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver IType rhsType = NullableType.GetUnderlyingType(rhs.Type); // the operator is overloadable: - OverloadResolution userDefinedOperatorOR = new OverloadResolution(context, new[] { lhs, rhs }); + OverloadResolution userDefinedOperatorOR = new OverloadResolution(context, new[] { lhs, rhs }, conversions: conversions); HashSet userOperatorCandidates = new HashSet(); userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName)); userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName)); @@ -1016,7 +1016,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver default: throw new InvalidOperationException(); } - OverloadResolution builtinOperatorOR = new OverloadResolution(context, new[] { lhs, rhs }); + OverloadResolution builtinOperatorOR = new OverloadResolution(context, new[] { lhs, rhs }, conversions: conversions); foreach (var candidate in methodGroup) { builtinOperatorOR.AddCandidate(candidate); } @@ -2240,7 +2240,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; if (mgrr != null) { - OverloadResolution or = mgrr.PerformOverloadResolution(context, arguments, argumentNames); + OverloadResolution or = mgrr.PerformOverloadResolution(context, arguments, argumentNames, conversions: conversions); if (or.BestCandidate != null) { return new InvocationResolveResult(mgrr.TargetResult, or, context); } else { @@ -2260,7 +2260,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } IMethod invokeMethod = target.Type.GetDelegateInvokeMethod(); if (invokeMethod != null) { - OverloadResolution or = new OverloadResolution(context, arguments, argumentNames); + OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, conversions: conversions); or.AddCandidate(invokeMethod); return new InvocationResolveResult( target, invokeMethod, invokeMethod.ReturnType.Resolve(context), @@ -2386,7 +2386,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } // §7.6.6.2 Indexer access - OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, new IType[0]); + OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, conversions: conversions); MemberLookup lookup = CreateMemberLookup(); var indexers = lookup.LookupIndexers(target.Type); or.AddMethodLists(indexers); @@ -2435,7 +2435,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (type.Kind == TypeKind.Delegate && arguments.Length == 1) { return Convert(arguments[0], type); } - OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, new IType[0]); + OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, conversions: conversions); MemberLookup lookup = CreateMemberLookup(); bool allowProtectedAccess = lookup.IsProtectedAccessAllowed(type); var constructors = type.GetConstructors(context, m => lookup.IsAccessible(m, allowProtectedAccess)); diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs index b74c1a4114..2e724e496c 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs @@ -235,6 +235,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// public class Conversions { + readonly Dictionary implicitConversionCache = new Dictionary(); readonly ITypeResolveContext context; readonly IType objectType; @@ -247,6 +248,37 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.dynamicErasure = new DynamicErasure(this); } + #region TypePair (for caching) + struct TypePair : IEquatable + { + public readonly IType FromType; + public readonly IType ToType; + + public TypePair(IType fromType, IType toType) + { + this.FromType = fromType; + this.ToType = toType; + } + + public override bool Equals(object obj) + { + return (obj is TypePair) && Equals((TypePair)obj); + } + + public bool Equals(TypePair other) + { + return this.FromType.Equals(other.FromType) && this.ToType.Equals(other.ToType); + } + + public override int GetHashCode() + { + unchecked { + return 1000000007 * FromType.GetHashCode() + 1000000009 * ToType.GetHashCode(); + } + } + } + #endregion + #region ImplicitConversion public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType) { @@ -273,11 +305,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver throw new ArgumentNullException("fromType"); if (toType == null) throw new ArgumentNullException("toType"); - // C# 4.0 spec: §6.1 - Conversion c = StandardImplicitConversion(fromType, toType); - if (c) return c; - return UserDefinedImplicitConversion(fromType, toType); + TypePair pair = new TypePair(fromType, toType); + Conversion c; + if (implicitConversionCache.TryGetValue(pair, out c)) + return c; + + // C# 4.0 spec: §6.1 + c = StandardImplicitConversion(fromType, toType); + if (!c) { + c = UserDefinedImplicitConversion(fromType, toType); + } + implicitConversionCache[pair] = c; + return c; } public Conversion StandardImplicitConversion(IType fromType, IType toType) @@ -928,7 +968,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver args[i] = new ResolveResult(parameterType); } } - var or = rr.PerformOverloadResolution(context, args, allowExpandingParams: false); + var or = rr.PerformOverloadResolution(context, args, allowExpandingParams: false, conversions: this); if (or.FoundApplicableCandidate) return Conversion.MethodGroupConversion((IMethod)or.BestCandidate); else diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs index a34830d91d..fcecd03e35 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs @@ -156,13 +156,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return string.Format("[{0} with {1} method(s)]", GetType().Name, this.Methods.Count()); } - public OverloadResolution PerformOverloadResolution(ITypeResolveContext context, ResolveResult[] arguments, string[] argumentNames = null, bool allowExtensionMethods = true, bool allowExpandingParams = true) + public OverloadResolution PerformOverloadResolution(ITypeResolveContext context, ResolveResult[] arguments, string[] argumentNames = null, bool allowExtensionMethods = true, bool allowExpandingParams = true, Conversions conversions = null) { Log.WriteLine("Performing overload resolution for " + this); Log.WriteCollection(" Arguments: ", arguments); var typeArgumentArray = this.TypeArguments.ToArray(); - OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, typeArgumentArray); + OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, typeArgumentArray, conversions); or.AllowExpandingParams = allowExpandingParams; or.AddMethodLists(methodLists); @@ -182,7 +182,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver extArgumentNames = new string[argumentNames.Length + 1]; argumentNames.CopyTo(extArgumentNames, 1); } - var extOr = new OverloadResolution(context, extArguments, extArgumentNames, typeArgumentArray); + var extOr = new OverloadResolution(context, extArguments, extArgumentNames, typeArgumentArray, conversions); extOr.AllowExpandingParams = allowExpandingParams; extOr.IsExtensionMethodInvocation = true; diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs index 9771168bc4..d624f0dff3 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs @@ -119,7 +119,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver IType[] explicitlyGivenTypeArguments; #region Constructor - public OverloadResolution(ITypeResolveContext context, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null) + public OverloadResolution(ITypeResolveContext context, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null, Conversions conversions = null) { if (context == null) throw new ArgumentNullException("context"); @@ -137,7 +137,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (typeArguments != null && typeArguments.Length > 0) this.explicitlyGivenTypeArguments = typeArguments; - this.conversions = new Conversions(context); + this.conversions = conversions ?? new Conversions(context); this.AllowExpandingParams = true; } #endregion diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index 3f82c0115c..59ef83614e 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -1078,7 +1078,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver var addRR = memberLookup.Lookup(targetResult, "Add", EmptyList.Instance, true); var mgrr = addRR as MethodGroupResolveResult; if (mgrr != null) { - OverloadResolution or = mgrr.PerformOverloadResolution(resolver.Context, addArguments, null, false, false); + OverloadResolution or = mgrr.PerformOverloadResolution(resolver.Context, addArguments, null, false, false, resolver.conversions); var invocationRR = new InvocationResolveResult(targetResult, or, resolver.Context); StoreResult(aie, invocationRR); ProcessConversionsInResult(invocationRR); @@ -1923,7 +1923,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver for (int i = 0; i < returnValues.Count; i++) { returnValues[i] = resolveResultCache[alv.ReturnExpressions[i]]; } - TypeInference ti = new TypeInference(resolver.Context); + TypeInference ti = new TypeInference(resolver.Context, resolver.conversions); bool tiSuccess; inferredReturnType = ti.GetBestCommonType(returnValues, out tiSuccess); // Failure to infer a return type does not make the lambda invalid, diff --git a/ICSharpCode.NRefactory/TypeSystem/ByReferenceType.cs b/ICSharpCode.NRefactory/TypeSystem/ByReferenceType.cs index 553341f2f3..3e12f549e8 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ByReferenceType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ByReferenceType.cs @@ -22,7 +22,7 @@ using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.TypeSystem { [Serializable] - public sealed class ByReferenceType : TypeWithElementType + public sealed class ByReferenceType : TypeWithElementType, ISupportsInterning { public ByReferenceType(IType elementType) : base(elementType) { @@ -67,12 +67,28 @@ namespace ICSharpCode.NRefactory.TypeSystem else return new ByReferenceType(e); } + + void ISupportsInterning.PrepareForInterning(IInterningProvider provider) + { + elementType = provider.Intern(elementType); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + return GetHashCode(); + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + ByReferenceType brt = other as ByReferenceType; + return brt != null && this.elementType == brt.elementType; + } } [Serializable] - public class ByReferenceTypeReference : ITypeReference + public class ByReferenceTypeReference : ITypeReference, ISupportsInterning { - readonly ITypeReference elementType; + ITypeReference elementType; public ByReferenceTypeReference(ITypeReference elementType) { @@ -102,5 +118,21 @@ namespace ICSharpCode.NRefactory.TypeSystem else return new ByReferenceTypeReference(elementType); } + + void ISupportsInterning.PrepareForInterning(IInterningProvider provider) + { + elementType = provider.Intern(elementType); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + return elementType.GetHashCode() ^ 91725814; + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + ByReferenceTypeReference brt = other as ByReferenceTypeReference; + return brt != null && this.elementType == brt.elementType; + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/CompoundTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/CompoundTypeDefinition.cs index 61e824b0cd..e2cf3a95ed 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/CompoundTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/CompoundTypeDefinition.cs @@ -25,6 +25,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// Type definition that represents a partial class with multiple parts. /// + [Serializable] public class CompoundTypeDefinition : DefaultTypeDefinition { IList parts; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultExplicitInterfaceImplementation.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultExplicitInterfaceImplementation.cs index d2081203b8..aa857f000f 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultExplicitInterfaceImplementation.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultExplicitInterfaceImplementation.cs @@ -23,6 +23,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// Default implementation for IExplicitInterfaceImplementation. /// + [Serializable] public sealed class DefaultExplicitInterfaceImplementation : Immutable, IExplicitInterfaceImplementation, ISupportsInterning { public ITypeReference InterfaceType { get; private set; } diff --git a/ICSharpCode.NRefactory/TypeSystem/PointerType.cs b/ICSharpCode.NRefactory/TypeSystem/PointerType.cs index 07dc29c1a2..ad96faa155 100644 --- a/ICSharpCode.NRefactory/TypeSystem/PointerType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/PointerType.cs @@ -23,7 +23,7 @@ using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.TypeSystem { [Serializable] - public sealed class PointerType : TypeWithElementType + public sealed class PointerType : TypeWithElementType, ISupportsInterning { public PointerType(IType elementType) : base(elementType) { @@ -68,12 +68,28 @@ namespace ICSharpCode.NRefactory.TypeSystem else return new PointerType(e); } + + void ISupportsInterning.PrepareForInterning(IInterningProvider provider) + { + elementType = provider.Intern(elementType); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + return elementType.GetHashCode() ^ 91725811; + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + PointerType o = other as PointerType; + return o != null && this.elementType == o.elementType; + } } [Serializable] - public class PointerTypeReference : ITypeReference + public class PointerTypeReference : ITypeReference, ISupportsInterning { - readonly ITypeReference elementType; + ITypeReference elementType; public PointerTypeReference(ITypeReference elementType) { @@ -103,5 +119,21 @@ namespace ICSharpCode.NRefactory.TypeSystem else return new PointerTypeReference(elementType); } + + void ISupportsInterning.PrepareForInterning(IInterningProvider provider) + { + elementType = provider.Intern(elementType); + } + + int ISupportsInterning.GetHashCodeForInterning() + { + return elementType.GetHashCode() ^ 91725812; + } + + bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) + { + PointerTypeReference o = other as PointerTypeReference; + return o != null && this.elementType == o.elementType; + } } }