PageRenderTime 69ms CodeModel.GetById 54ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://github.com/icsharpcode/ILSpy
C# | 404 lines | 280 code | 41 blank | 83 comment | 50 complexity | 1bdd12a52f877015c98b5723ab1b7956 MD5 | raw file
  1// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
  2// 
  3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4// software and associated documentation files (the "Software"), to deal in the Software
  5// without restriction, including without limitation the rights to use, copy, modify, merge,
  6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7// to whom the Software is furnished to do so, subject to the following conditions:
  8// 
  9// The above copyright notice and this permission notice shall be included in all copies or
 10// substantial portions of the Software.
 11// 
 12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 17// DEALINGS IN THE SOFTWARE.
 18
 19using System;
 20using System.Collections.Generic;
 21using System.Linq;
 22using System.Runtime.Serialization;
 23using System.Threading;
 24using ICSharpCode.NRefactory.Utils;
 25
 26namespace ICSharpCode.NRefactory.TypeSystem.Implementation
 27{
 28	/// <summary>
 29	/// Stores a set of types and allows resolving them.
 30	/// </summary>
 31	/// <remarks>
 32	/// Concurrent read accesses are thread-safe, but a write access concurrent to any other access is not safe.
 33	/// </remarks>
 34	[Serializable]
 35	public sealed class TypeStorage : ITypeResolveContext, ISerializable, IDeserializationCallback
 36	{
 37		#region FullNameAndTypeParameterCount
 38		struct FullNameAndTypeParameterCount
 39		{
 40			public readonly string Namespace;
 41			public readonly string Name;
 42			public readonly int TypeParameterCount;
 43			
 44			public FullNameAndTypeParameterCount(string nameSpace, string name, int typeParameterCount)
 45			{
 46				this.Namespace = nameSpace;
 47				this.Name = name;
 48				this.TypeParameterCount = typeParameterCount;
 49			}
 50		}
 51		
 52		sealed class FullNameAndTypeParameterCountComparer : IEqualityComparer<FullNameAndTypeParameterCount>
 53		{
 54			public static readonly FullNameAndTypeParameterCountComparer Ordinal = new FullNameAndTypeParameterCountComparer(StringComparer.Ordinal);
 55			
 56			public readonly StringComparer NameComparer;
 57			
 58			public FullNameAndTypeParameterCountComparer(StringComparer nameComparer)
 59			{
 60				this.NameComparer = nameComparer;
 61			}
 62			
 63			public bool Equals(FullNameAndTypeParameterCount x, FullNameAndTypeParameterCount y)
 64			{
 65				return x.TypeParameterCount == y.TypeParameterCount
 66					&& NameComparer.Equals(x.Name, y.Name)
 67					&& NameComparer.Equals(x.Namespace, y.Namespace);
 68			}
 69			
 70			public int GetHashCode(FullNameAndTypeParameterCount obj)
 71			{
 72				return NameComparer.GetHashCode(obj.Name) ^ NameComparer.GetHashCode(obj.Namespace) ^ obj.TypeParameterCount;
 73			}
 74		}
 75		#endregion
 76		
 77		#region Type Dictionary Storage
 78		volatile Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[] _typeDicts = {
 79			new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(FullNameAndTypeParameterCountComparer.Ordinal)
 80		};
 81		readonly object dictsLock = new object();
 82		
 83		Dictionary<FullNameAndTypeParameterCount, ITypeDefinition> GetTypeDictionary(StringComparer nameComparer)
 84		{
 85			// Gets the dictionary for the specified comparer, creating it if necessary.
 86			// New dictionaries might be added during read accesses, so this method needs to be thread-safe,
 87			// as we allow concurrent read-accesses.
 88			var typeDicts = this._typeDicts;
 89			foreach (var dict in typeDicts) {
 90				FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
 91				if (comparer.NameComparer == nameComparer)
 92					return dict;
 93			}
 94			
 95			// ensure that no other thread can try to lazy-create this (or another) dict
 96			lock (dictsLock) {
 97				typeDicts = this._typeDicts; // fetch fresh value after locking
 98				// try looking for it again, maybe it was added while we were waiting for a lock
 99				// (double-checked locking pattern)
100				foreach (var dict in typeDicts) {
101					FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
102					if (comparer.NameComparer == nameComparer)
103						return dict;
104				}
105				
106				// now create new dict
107				var oldDict = typeDicts[0]; // Ordinal dict
108				var newDict = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(
109					oldDict.Count,
110					new FullNameAndTypeParameterCountComparer(nameComparer));
111				foreach (var pair in oldDict) {
112					// don't use Add() as there might be conflicts in the target language
113					newDict[pair.Key] = pair.Value;
114				}
115				
116				// add the new dict to the array of dicts
117				var newTypeDicts = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[typeDicts.Length + 1];
118				Array.Copy(typeDicts, 0, newTypeDicts, 0, typeDicts.Length);
119				newTypeDicts[typeDicts.Length] = newDict;
120				this._typeDicts = newTypeDicts;
121				return newDict;
122			}
123		}
124		#endregion
125		
126		#region Namespace Storage
127		class NamespaceEntry
128		{
129			/// <summary>
130			/// Full namespace name
131			/// </summary>
132			public readonly string Name;
133			
134			/// <summary>
135			/// Parent namespace
136			/// </summary>
137			public readonly NamespaceEntry Parent;
138			
139			/// <summary>
140			/// Number of classes in this namespace (not in sub-namespaces).
141			/// Note: this always refers to the number of classes from the ordinal typeDict that map
142			/// to this namespace when compared with the appropriate StringComparer.
143			/// The actual number of classes in the typeDict matching this StringComparer might be lower.
144			/// </summary>
145			public int ClassCount;
146			
147			/// <summary>
148			/// Number of sub-namespaces.
149			/// </summary>
150			public int SubNamespaceCount;
151			
152			public NamespaceEntry(NamespaceEntry parent, string name)
153			{
154				this.Parent = parent;
155				this.Name = name;
156			}
157		}
158		
159		volatile Dictionary<string, NamespaceEntry>[] _namespaceDicts = {
160			new Dictionary<string, NamespaceEntry>(StringComparer.Ordinal)
161		};
162		
163		Dictionary<string, NamespaceEntry> GetNamespaceDictionary(StringComparer nameComparer)
164		{
165			// Gets the dictionary for the specified comparer, creating it if necessary.
166			// New dictionaries might be added during read accesses, so this method needs to be thread-safe,
167			// as we allow concurrent read-accesses.
168			var namespaceDicts = this._namespaceDicts;
169			foreach (var dict in namespaceDicts) {
170				if (dict.Comparer == nameComparer)
171					return dict;
172			}
173			
174			// ensure that no other thread can try to lazy-create this (or another) dict
175			lock (dictsLock) {
176				namespaceDicts = this._namespaceDicts; // fetch fresh value after locking
177				// try looking for it again, maybe it was added while we were waiting for a lock
178				// (double-checked locking pattern)
179				foreach (var dict in namespaceDicts) {
180					if (dict.Comparer == nameComparer)
181						return dict;
182				}
183				
184				// now create new dict
185				var newDict = new Dictionary<string, NamespaceEntry>(nameComparer);
186				foreach (ITypeDefinition type in _typeDicts[0].Values) {
187					NamespaceEntry ne = GetOrCreateNamespaceEntry(newDict, type.Namespace);
188					ne.ClassCount++;
189				}
190				
191				// add the new dict to the array of dicts
192				var newNamespaceDicts = new Dictionary<string, NamespaceEntry>[namespaceDicts.Length + 1];
193				Array.Copy(namespaceDicts, 0, newNamespaceDicts, 0, namespaceDicts.Length);
194				newNamespaceDicts[namespaceDicts.Length] = newDict;
195				this._namespaceDicts = newNamespaceDicts;
196				return newDict;
197			}
198		}
199		
200		NamespaceEntry GetOrCreateNamespaceEntry(Dictionary<string, NamespaceEntry> dict, string ns)
201		{
202			NamespaceEntry ne;
203			if (!dict.TryGetValue(ns, out ne)) {
204				NamespaceEntry parentEntry;
205				if (string.IsNullOrEmpty(ns)) {
206					parentEntry = null;
207				} else {
208					int pos = ns.LastIndexOf('.');
209					string parentNS = pos < 0 ? string.Empty : ns.Substring(0, pos);
210					parentEntry = GetOrCreateNamespaceEntry(dict, parentNS);
211					parentEntry.SubNamespaceCount++;
212				}
213				ne = new NamespaceEntry(parentEntry, ns);
214				dict.Add(ns, ne);
215			}
216			return ne;
217		}
218		#endregion
219		
220		#region ITypeResolveContext implementation
221		/// <inheritdoc/>
222		public ITypeDefinition GetTypeDefinition(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
223		{
224			if (nameSpace == null)
225				throw new ArgumentNullException("nameSpace");
226			if (name == null)
227				throw new ArgumentNullException("name");
228			if (nameComparer == null)
229				throw new ArgumentNullException("nameComparer");
230			
231			var key = new FullNameAndTypeParameterCount(nameSpace, name, typeParameterCount);
232			ITypeDefinition result;
233			if (GetTypeDictionary(nameComparer).TryGetValue(key, out result))
234				return result;
235			else
236				return null;
237		}
238		
239		/// <inheritdoc/>
240		public ITypeDefinition GetKnownTypeDefinition(TypeCode typeCode)
241		{
242			return GetTypeDefinition("System", ReflectionHelper.GetShortNameByTypeCode(typeCode), 0, StringComparer.Ordinal);
243		}
244		
245		/// <inheritdoc/>
246		public IEnumerable<ITypeDefinition> GetTypes()
247		{
248			return _typeDicts[0].Values;
249		}
250		
251		/// <inheritdoc/>
252		public IEnumerable<ITypeDefinition> GetTypes(string nameSpace, StringComparer nameComparer)
253		{
254			if (nameSpace == null)
255				throw new ArgumentNullException("nameSpace");
256			if (nameComparer == null)
257				throw new ArgumentNullException("nameComparer");
258			return GetTypes().Where(c => nameComparer.Equals(nameSpace, c.Namespace));
259		}
260		
261		/// <inheritdoc/>
262		public IEnumerable<string> GetNamespaces()
263		{
264			return _namespaceDicts[0].Keys;
265		}
266		
267		/// <inheritdoc/>
268		public string GetNamespace(string nameSpace, StringComparer nameComparer)
269		{
270			if (nameSpace == null)
271				throw new ArgumentNullException("nameSpace");
272			if (nameComparer == null)
273				throw new ArgumentNullException("nameComparer");
274			NamespaceEntry result;
275			if (GetNamespaceDictionary(nameComparer).TryGetValue(nameSpace, out result))
276				return result.Name;
277			else
278				return null;
279		}
280		#endregion
281		
282		#region Synchronize
283		/// <summary>
284		/// TypeStorage is mutable and does not provide any means for synchronization, so this method
285		/// always throws a <see cref="NotSupportedException"/>.
286		/// </summary>
287		public ISynchronizedTypeResolveContext Synchronize()
288		{
289			throw new NotSupportedException();
290		}
291		
292		/// <inheritdoc/>
293		public CacheManager CacheManager {
294			// TypeStorage is mutable, so caching is a bad idea.
295			// We could provide a CacheToken if we update it on every modication, but
296			// that's not worth the effort as TypeStorage is rarely directly used in resolve operations.
297			get { return null; }
298		}
299		#endregion
300		
301		#region RemoveType
302		/// <summary>
303		/// Removes a type definition from this project content.
304		/// </summary>
305		public bool RemoveType(ITypeDefinition typeDefinition)
306		{
307			if (typeDefinition == null)
308				throw new ArgumentNullException("typeDefinition");
309			var key = new FullNameAndTypeParameterCount(typeDefinition.Namespace, typeDefinition.Name, typeDefinition.TypeParameterCount);
310			bool wasRemoved = false;
311			foreach (var dict in _typeDicts) {
312				ITypeDefinition defInDict;
313				if (dict.TryGetValue(key, out defInDict)) {
314					if (defInDict == typeDefinition) {
315						if (dict.Comparer == FullNameAndTypeParameterCountComparer.Ordinal) {
316							// Set wasRemoved flag only on removal in the ordinal comparison.
317							// This keeps the ClassCount consistent when there are name clashes.
318							wasRemoved = true;
319						}
320						dict.Remove(key);
321					}
322				}
323			}
324			if (wasRemoved) {
325				foreach (var dict in _namespaceDicts) {
326					NamespaceEntry ns;
327					if (dict.TryGetValue(typeDefinition.Namespace, out ns)) {
328						ns.ClassCount--;
329						RemoveNamespaceIfPossible(dict, ns);
330					}
331				}
332			}
333			return wasRemoved;
334		}
335		
336		void RemoveNamespaceIfPossible(Dictionary<string, NamespaceEntry> dict, NamespaceEntry ns)
337		{
338			while (ns.ClassCount == 0 && ns.SubNamespaceCount == 0) {
339				dict.Remove(ns.Name);
340				ns = ns.Parent;
341				if (ns == null)
342					break;
343				ns.SubNamespaceCount--;
344			}
345		}
346		#endregion
347		
348		#region UpdateType
349		/// <summary>
350		/// Adds the type definition to this project content.
351		/// Replaces existing type definitions with the same name.
352		/// </summary>
353		public void UpdateType(ITypeDefinition typeDefinition)
354		{
355			if (typeDefinition == null)
356				throw new ArgumentNullException("typeDefinition");
357			var key = new FullNameAndTypeParameterCount(typeDefinition.Namespace, typeDefinition.Name, typeDefinition.TypeParameterCount);
358			// Set isNew on addition in the ordinal comparison.
359			// This keeps the ClassCount consistent when there are name clashes.
360			bool isNew = !_typeDicts[0].ContainsKey(key);
361			foreach (var dict in _typeDicts) {
362				dict[key] = typeDefinition;
363			}
364			if (isNew) {
365				foreach (var dict in _namespaceDicts) {
366					NamespaceEntry ns = GetOrCreateNamespaceEntry(dict, typeDefinition.Namespace);
367					++ns.ClassCount;
368				}
369			}
370		}
371		#endregion
372		
373		#region Serialization
374		/// <summary>
375		/// Creates a new TypeStorage instance.
376		/// </summary>
377		public TypeStorage()
378		{
379		}
380		
381		SerializationInfo serializationInfo;
382		
383		private TypeStorage(SerializationInfo info, StreamingContext context)
384		{
385			this.serializationInfo = info;
386		}
387		
388		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
389		{
390			info.AddValue("Types", this.GetTypes().ToArray());
391		}
392		
393		void IDeserializationCallback.OnDeserialization(object sender)
394		{
395			if (serializationInfo == null)
396				return;
397			foreach (var typeDef in (ITypeDefinition[])serializationInfo.GetValue("Types", typeof(ITypeDefinition[]))) {
398				UpdateType(typeDef);
399			}
400			serializationInfo = null;
401		}
402		#endregion
403	}
404}