From 782887812f7a7defb2d906d4ea01dcc38fbe9a05 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 17 Dec 2010 17:37:09 +0100 Subject: [PATCH] Add interning support to CecilLoader. --- .../ICSharpCode.NRefactory.csproj | 1 + .../TypeSystem/CecilLoader.cs | 25 +++- .../Implementation/SimpleInterningProvider.cs | 122 ++++++++++++++++++ 3 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleInterningProvider.cs diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 8d3905e595..30d194fa34 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -221,6 +221,7 @@ + diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index ba64f7f3b7..4b27679d57 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -38,6 +38,11 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets/Sets the documentation provider that is used to retrive the XML documentation for all members. /// public IDocumentationProvider DocumentationProvider { get; set; } + + /// + /// Gets/Sets the interning provider. + /// + public IInterningProvider InterningProvider { get; set; } #endregion #region Load From AssemblyDefinition @@ -47,12 +52,16 @@ namespace ICSharpCode.NRefactory.TypeSystem throw new ArgumentNullException("assemblyDefinition"); ITypeResolveContext oldEarlyBindContext = this.EarlyBindContext; try { - List assemblyAttributes = new List(); + IList assemblyAttributes = new List(); foreach (var attr in assemblyDefinition.CustomAttributes) { assemblyAttributes.Add(ReadAttribute(attr)); } + if (this.InterningProvider != null) + assemblyAttributes = this.InterningProvider.InternList(assemblyAttributes); + else + assemblyAttributes = new ReadOnlyCollection(assemblyAttributes); TypeStorage typeStorage = new TypeStorage(); - CecilProjectContent pc = new CecilProjectContent(typeStorage, assemblyDefinition.Name.FullName, assemblyAttributes.AsReadOnly(), this.DocumentationProvider); + CecilProjectContent pc = new CecilProjectContent(typeStorage, assemblyDefinition.Name.FullName, assemblyAttributes, this.DocumentationProvider); this.EarlyBindContext = CompositeTypeResolveContext.Combine(pc, this.EarlyBindContext); List types = new List(); @@ -105,10 +114,10 @@ namespace ICSharpCode.NRefactory.TypeSystem sealed class CecilProjectContent : ProxyTypeResolveContext, IProjectContent, ISynchronizedTypeResolveContext, IDocumentationProvider { readonly string assemblyName; - readonly ReadOnlyCollection assemblyAttributes; + readonly IList assemblyAttributes; readonly IDocumentationProvider documentationProvider; - public CecilProjectContent(TypeStorage types, string assemblyName, ReadOnlyCollection assemblyAttributes, IDocumentationProvider documentationProvider) + public CecilProjectContent(TypeStorage types, string assemblyName, IList assemblyAttributes, IDocumentationProvider documentationProvider) : base(types) { Debug.Assert(assemblyName != null); @@ -710,6 +719,8 @@ namespace ICSharpCode.NRefactory.TypeSystem } } + if (this.InterningProvider != null) + m = this.InterningProvider.Intern(m); return m; } @@ -830,6 +841,8 @@ namespace ICSharpCode.NRefactory.TypeSystem f.IsVolatile = true; } + if (this.InterningProvider != null) + f = this.InterningProvider.Intern(f); return f; } @@ -897,6 +910,8 @@ namespace ICSharpCode.NRefactory.TypeSystem } AddAttributes(property, p); + if (this.InterningProvider != null) + p = this.InterningProvider.Intern(p); return p; } @@ -936,6 +951,8 @@ namespace ICSharpCode.NRefactory.TypeSystem AddAttributes(ev, e); + if (this.InterningProvider != null) + e = this.InterningProvider.Intern(e); return e; } #endregion diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleInterningProvider.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleInterningProvider.cs new file mode 100644 index 0000000000..66f6aeb172 --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleInterningProvider.cs @@ -0,0 +1,122 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Runtime.CompilerServices; + +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.TypeSystem.Implementation +{ + /// + /// Simple interning provider. + /// + public sealed class SimpleInterningProvider : IInterningProvider + { + sealed class ReferenceComparer : IEqualityComparer + { + public readonly static ReferenceComparer Instance = new ReferenceComparer(); + + public new bool Equals(object a, object b) + { + return ReferenceEquals(a, b); + } + + public int GetHashCode(object obj) + { + return RuntimeHelpers.GetHashCode(obj); + } + } + + sealed class InterningComparer : IEqualityComparer + { + public bool Equals(ISupportsInterning x, ISupportsInterning y) + { + return x.EqualsForInterning(y); + } + + public int GetHashCode(ISupportsInterning obj) + { + return obj.GetHashCodeForInterning(); + } + } + + sealed class ListComparer : IEqualityComparer> + { + public bool Equals(IEnumerable a, IEnumerable b) + { + if (a.GetType() != b.GetType()) + return false; + return Enumerable.SequenceEqual(a, b, ReferenceComparer.Instance); + } + + public int GetHashCode(IEnumerable obj) + { + int hashCode = obj.GetType().GetHashCode(); + unchecked { + foreach (object o in obj) { + hashCode *= 27; + hashCode += RuntimeHelpers.GetHashCode(o); + } + } + return hashCode; + } + } + + Dictionary byValueDict = new Dictionary(); + Dictionary supportsInternDict = new Dictionary(new InterningComparer()); + Dictionary, IEnumerable> listDict = new Dictionary, IEnumerable>(new ListComparer()); + + public T Intern(T obj) where T : class + { + if (obj == null) + return null; + ISupportsInterning s = obj as ISupportsInterning; + if (s != null) { + ISupportsInterning output; + if (supportsInternDict.TryGetValue(s, out output)) { + obj = (T)output; + } else { + s.PrepareForInterning(this); + if (supportsInternDict.TryGetValue(s, out output)) + obj = (T)output; + else + supportsInternDict.Add(s, s); + } + } else if (obj is IType || Type.GetTypeCode(obj.GetType()) >= TypeCode.Boolean) { + object output; + if (byValueDict.TryGetValue(obj, out output)) + obj = (T)output; + else + byValueDict.Add(obj, obj); + } + return obj; + } + + public IList InternList(IList list) where T : class + { + if (list == null) + return null; + for (int i = 0; i < list.Count; i++) { + T oldItem = list[i]; + T newItem = Intern(oldItem); + if (oldItem != newItem) { + if (list.IsReadOnly) + list = new T[list.Count]; + list[i] = newItem; + } + } + if (!list.IsReadOnly) + list = new ReadOnlyCollection(list); + IEnumerable output; + if (listDict.TryGetValue(list, out output)) + list = (IList)output; + else + listDict.Add(list, list); + return list; + } + } +}