/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. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using System.Runtime.Serialization;
  22. using System.Threading;
  23. using ICSharpCode.NRefactory.Utils;
  24. namespace ICSharpCode.NRefactory.TypeSystem.Implementation
  25. {
  26. /// <summary>
  27. /// Stores a set of types and allows resolving them.
  28. /// </summary>
  29. /// <remarks>
  30. /// Concurrent read accesses are thread-safe, but a write access concurrent to any other access is not safe.
  31. /// </remarks>
  32. [Serializable]
  33. public sealed class TypeStorage : ITypeResolveContext, ISerializable, IDeserializationCallback
  34. {
  35. #region FullNameAndTypeParameterCount
  36. struct FullNameAndTypeParameterCount
  37. {
  38. public readonly string Namespace;
  39. public readonly string Name;
  40. public readonly int TypeParameterCount;
  41. public FullNameAndTypeParameterCount(string nameSpace, string name, int typeParameterCount)
  42. {
  43. this.Namespace = nameSpace;
  44. this.Name = name;
  45. this.TypeParameterCount = typeParameterCount;
  46. }
  47. }
  48. sealed class FullNameAndTypeParameterCountComparer : IEqualityComparer<FullNameAndTypeParameterCount>
  49. {
  50. public static readonly FullNameAndTypeParameterCountComparer Ordinal = new FullNameAndTypeParameterCountComparer(StringComparer.Ordinal);
  51. public readonly StringComparer NameComparer;
  52. public FullNameAndTypeParameterCountComparer(StringComparer nameComparer)
  53. {
  54. this.NameComparer = nameComparer;
  55. }
  56. public bool Equals(FullNameAndTypeParameterCount x, FullNameAndTypeParameterCount y)
  57. {
  58. return x.TypeParameterCount == y.TypeParameterCount
  59. && NameComparer.Equals(x.Name, y.Name)
  60. && NameComparer.Equals(x.Namespace, y.Namespace);
  61. }
  62. public int GetHashCode(FullNameAndTypeParameterCount obj)
  63. {
  64. return NameComparer.GetHashCode(obj.Name) ^ NameComparer.GetHashCode(obj.Namespace) ^ obj.TypeParameterCount;
  65. }
  66. }
  67. #endregion
  68. #region Type Dictionary Storage
  69. volatile Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[] _typeDicts = {
  70. new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(FullNameAndTypeParameterCountComparer.Ordinal)
  71. };
  72. readonly object dictsLock = new object();
  73. Dictionary<FullNameAndTypeParameterCount, ITypeDefinition> GetTypeDictionary(StringComparer nameComparer)
  74. {
  75. // Gets the dictionary for the specified comparer, creating it if necessary.
  76. // New dictionaries might be added during read accesses, so this method needs to be thread-safe,
  77. // as we allow concurrent read-accesses.
  78. var typeDicts = this._typeDicts;
  79. foreach (var dict in typeDicts) {
  80. FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
  81. if (comparer.NameComparer == nameComparer)
  82. return dict;
  83. }
  84. // ensure that no other thread can try to lazy-create this (or another) dict
  85. lock (dictsLock) {
  86. typeDicts = this._typeDicts; // fetch fresh value after locking
  87. // try looking for it again, maybe it was added while we were waiting for a lock
  88. // (double-checked locking pattern)
  89. foreach (var dict in typeDicts) {
  90. FullNameAndTypeParameterCountComparer comparer = (FullNameAndTypeParameterCountComparer)dict.Comparer;
  91. if (comparer.NameComparer == nameComparer)
  92. return dict;
  93. }
  94. // now create new dict
  95. var oldDict = typeDicts[0]; // Ordinal dict
  96. var newDict = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>(
  97. oldDict.Count,
  98. new FullNameAndTypeParameterCountComparer(nameComparer));
  99. foreach (var pair in oldDict) {
  100. // don't use Add() as there might be conflicts in the target language
  101. newDict[pair.Key] = pair.Value;
  102. }
  103. // add the new dict to the array of dicts
  104. var newTypeDicts = new Dictionary<FullNameAndTypeParameterCount, ITypeDefinition>[typeDicts.Length + 1];
  105. Array.Copy(typeDicts, 0, newTypeDicts, 0, typeDicts.Length);
  106. newTypeDicts[typeDicts.Length] = newDict;
  107. this._typeDicts = newTypeDicts;
  108. return newDict;
  109. }
  110. }
  111. #endregion
  112. #region Namespace Storage
  113. class NamespaceEntry
  114. {
  115. /// <summary>
  116. /// Full namespace name
  117. /// </summary>
  118. public readonly string Name;
  119. /// <summary>
  120. /// Parent namespace
  121. /// </summary>
  122. public readonly NamespaceEntry Parent;
  123. /// <summary>
  124. /// Number of classes in this namespace (not in sub-namespaces).
  125. /// Note: this always refers to the number of classes from the ordinal typeDict that map
  126. /// to this namespace when compared with the appropriate StringComparer.
  127. /// The actual number of classes in the typeDict matching this StringComparer might be lower.
  128. /// </summary>
  129. public int ClassCount;
  130. /// <summary>
  131. /// Number of sub-namespaces.
  132. /// </summary>
  133. public int SubNamespaceCount;
  134. public NamespaceEntry(NamespaceEntry parent, string name)
  135. {
  136. this.Parent = parent;
  137. this.Name = name;
  138. }
  139. }
  140. volatile Dictionary<string, NamespaceEntry>[] _namespaceDicts = {
  141. new Dictionary<string, NamespaceEntry>(StringComparer.Ordinal)
  142. };
  143. Dictionary<string, NamespaceEntry> GetNamespaceDictionary(StringComparer nameComparer)
  144. {
  145. // Gets the dictionary for the specified comparer, creating it if necessary.
  146. // New dictionaries might be added during read accesses, so this method needs to be thread-safe,
  147. // as we allow concurrent read-accesses.
  148. var namespaceDicts = this._namespaceDicts;
  149. foreach (var dict in namespaceDicts) {
  150. if (dict.Comparer == nameComparer)
  151. return dict;
  152. }
  153. // ensure that no other thread can try to lazy-create this (or another) dict
  154. lock (dictsLock) {
  155. namespaceDicts = this._namespaceDicts; // fetch fresh value after locking
  156. // try looking for it again, maybe it was added while we were waiting for a lock
  157. // (double-checked locking pattern)
  158. foreach (var dict in namespaceDicts) {
  159. if (dict.Comparer == nameComparer)
  160. return dict;
  161. }
  162. // now create new dict
  163. var newDict = new Dictionary<string, NamespaceEntry>(nameComparer);
  164. foreach (ITypeDefinition type in _typeDicts[0].Values) {
  165. NamespaceEntry ne = GetOrCreateNamespaceEntry(newDict, type.Namespace);
  166. ne.ClassCount++;
  167. }
  168. // add the new dict to the array of dicts
  169. var newNamespaceDicts = new Dictionary<string, NamespaceEntry>[namespaceDicts.Length + 1];
  170. Array.Copy(namespaceDicts, 0, newNamespaceDicts, 0, namespaceDicts.Length);
  171. newNamespaceDicts[namespaceDicts.Length] = newDict;
  172. this._namespaceDicts = newNamespaceDicts;
  173. return newDict;
  174. }
  175. }
  176. NamespaceEntry GetOrCreateNamespaceEntry(Dictionary<string, NamespaceEntry> dict, string ns)
  177. {
  178. NamespaceEntry ne;
  179. if (!dict.TryGetValue(ns, out ne)) {
  180. NamespaceEntry parentEntry;
  181. if (string.IsNullOrEmpty(ns)) {
  182. parentEntry = null;
  183. } else {
  184. int pos = ns.LastIndexOf('.');
  185. string parentNS = pos < 0 ? string.Empty : ns.Substring(0, pos);
  186. parentEntry = GetOrCreateNamespaceEntry(dict, parentNS);
  187. parentEntry.SubNamespaceCount++;
  188. }
  189. ne = new NamespaceEntry(parentEntry, ns);
  190. dict.Add(ns, ne);
  191. }
  192. return ne;
  193. }
  194. #endregion
  195. #region ITypeResolveContext implementation
  196. /// <inheritdoc/>
  197. public ITypeDefinition GetTypeDefinition(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
  198. {
  199. if (nameSpace == null)
  200. throw new ArgumentNullException("nameSpace");
  201. if (name == null)
  202. throw new ArgumentNullException("name");
  203. if (nameComparer == null)
  204. throw new ArgumentNullException("nameComparer");
  205. var key = new FullNameAndTypeParameterCount(nameSpace, name, typeParameterCount);
  206. ITypeDefinition result;
  207. if (GetTypeDictionary(nameComparer).TryGetValue(key, out result))
  208. return result;
  209. else
  210. return null;
  211. }
  212. /// <inheritdoc/>
  213. public ITypeDefinition GetKnownTypeDefinition(TypeCode typeCode)
  214. {
  215. return GetTypeDefinition("System", ReflectionHelper.GetShortNameByTypeCode(typeCode), 0, StringComparer.Ordinal);
  216. }
  217. /// <inheritdoc/>
  218. public IEnumerable<ITypeDefinition> GetTypes()
  219. {
  220. return _typeDicts[0].Values;
  221. }
  222. /// <inheritdoc/>
  223. public IEnumerable<ITypeDefinition> GetTypes(string nameSpace, StringComparer nameComparer)
  224. {
  225. if (nameSpace == null)
  226. throw new ArgumentNullException("nameSpace");
  227. if (nameComparer == null)
  228. throw new ArgumentNullException("nameComparer");
  229. return GetTypes().Where(c => nameComparer.Equals(nameSpace, c.Namespace));
  230. }
  231. /// <inheritdoc/>
  232. public IEnumerable<string> GetNamespaces()
  233. {
  234. return _namespaceDicts[0].Keys;
  235. }
  236. /// <inheritdoc/>
  237. public string GetNamespace(string nameSpace, StringComparer nameComparer)
  238. {
  239. if (nameSpace == null)
  240. throw new ArgumentNullException("nameSpace");
  241. if (nameComparer == null)
  242. throw new ArgumentNullException("nameComparer");
  243. NamespaceEntry result;
  244. if (GetNamespaceDictionary(nameComparer).TryGetValue(nameSpace, out result))
  245. return result.Name;
  246. else
  247. return null;
  248. }
  249. #endregion
  250. #region Synchronize
  251. /// <summary>
  252. /// TypeStorage is mutable and does not provide any means for synchronization, so this method
  253. /// always throws a <see cref="NotSupportedException"/>.
  254. /// </summary>
  255. public ISynchronizedTypeResolveContext Synchronize()
  256. {
  257. throw new NotSupportedException();
  258. }
  259. /// <inheritdoc/>
  260. public CacheManager CacheManager {
  261. // TypeStorage is mutable, so caching is a bad idea.
  262. // We could provide a CacheToken if we update it on every modication, but
  263. // that's not worth the effort as TypeStorage is rarely directly used in resolve operations.
  264. get { return null; }
  265. }
  266. #endregion
  267. #region RemoveType
  268. /// <summary>
  269. /// Removes a type definition from this project content.
  270. /// </summary>
  271. public bool RemoveType(ITypeDefinition typeDefinition)
  272. {
  273. if (typeDefinition == null)
  274. throw new ArgumentNullException("typeDefinition");
  275. var key = new FullNameAndTypeParameterCount(typeDefinition.Namespace, typeDefinition.Name, typeDefinition.TypeParameterCount);
  276. bool wasRemoved = false;
  277. foreach (var dict in _typeDicts) {
  278. ITypeDefinition defInDict;
  279. if (dict.TryGetValue(key, out defInDict)) {
  280. if (defInDict == typeDefinition) {
  281. if (dict.Comparer == FullNameAndTypeParameterCountComparer.Ordinal) {
  282. // Set wasRemoved flag only on removal in the ordinal comparison.
  283. // This keeps the ClassCount consistent when there are name clashes.
  284. wasRemoved = true;
  285. }
  286. dict.Remove(key);
  287. }
  288. }
  289. }
  290. if (wasRemoved) {
  291. foreach (var dict in _namespaceDicts) {
  292. NamespaceEntry ns;
  293. if (dict.TryGetValue(typeDefinition.Namespace, out ns)) {
  294. ns.ClassCount--;
  295. RemoveNamespaceIfPossible(dict, ns);
  296. }
  297. }
  298. }
  299. return wasRemoved;
  300. }
  301. void RemoveNamespaceIfPossible(Dictionary<string, NamespaceEntry> dict, NamespaceEntry ns)
  302. {
  303. while (ns.ClassCount == 0 && ns.SubNamespaceCount == 0) {
  304. dict.Remove(ns.Name);
  305. ns = ns.Parent;
  306. if (ns == null)
  307. break;
  308. ns.SubNamespaceCount--;
  309. }
  310. }
  311. #endregion
  312. #region UpdateType
  313. /// <summary>
  314. /// Adds the type definition to this project content.
  315. /// Replaces existing type definitions with the same name.
  316. /// </summary>
  317. public void UpdateType(ITypeDefinition typeDefinition)
  318. {
  319. if (typeDefinition == null)
  320. throw new ArgumentNullException("typeDefinition");
  321. var key = new FullNameAndTypeParameterCount(typeDefinition.Namespace, typeDefinition.Name, typeDefinition.TypeParameterCount);
  322. // Set isNew on addition in the ordinal comparison.
  323. // This keeps the ClassCount consistent when there are name clashes.
  324. bool isNew = !_typeDicts[0].ContainsKey(key);
  325. foreach (var dict in _typeDicts) {
  326. dict[key] = typeDefinition;
  327. }
  328. if (isNew) {
  329. foreach (var dict in _namespaceDicts) {
  330. NamespaceEntry ns = GetOrCreateNamespaceEntry(dict, typeDefinition.Namespace);
  331. ++ns.ClassCount;
  332. }
  333. }
  334. }
  335. #endregion
  336. #region Serialization
  337. /// <summary>
  338. /// Creates a new TypeStorage instance.
  339. /// </summary>
  340. public TypeStorage()
  341. {
  342. }
  343. SerializationInfo serializationInfo;
  344. private TypeStorage(SerializationInfo info, StreamingContext context)
  345. {
  346. this.serializationInfo = info;
  347. }
  348. void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  349. {
  350. info.AddValue("Types", this.GetTypes().ToArray());
  351. }
  352. void IDeserializationCallback.OnDeserialization(object sender)
  353. {
  354. if (serializationInfo == null)
  355. return;
  356. foreach (var typeDef in (ITypeDefinition[])serializationInfo.GetValue("Types", typeof(ITypeDefinition[]))) {
  357. UpdateType(typeDef);
  358. }
  359. serializationInfo = null;
  360. }
  361. #endregion
  362. }
  363. }