New tests.
[mono.git] / mcs / class / corlib / System.Runtime.CompilerServices / ConditionalWeakTable.cs
1 // -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 // -----------------------------------------------------------------------
4 //
5 // This code comes from the Managed Extension Frameworks:
6 //  http://mef.codeplex.com
7 //
8 // And is licensed under the MS-PL license
9 //
10         // Glenn said on IRC:
11         //   "I think our table is weak, but does not do proper compacting"
12         //
13
14 #if NET_4_0 || BOOTSTRAP_NET_4_0 || MOONLIGHT
15 using System;
16 using System.Collections;
17 using System.Collections.Generic;
18 using System.Linq;
19
20 namespace System.Runtime.CompilerServices
21 {
22         
23     // This is a broken implementation of ConditionalWeakTable that allows us
24     // to compile and work on versions of .Net eariler then 4.0. This class is
25     // broken when there are circular dependencies between keys and values, which
26     // can only be fixed by using some specific CLR 4.0 features.
27     // For code samples of the broken behavior see ConditionalWeakTableTests.cs.
28     public class ConditionalWeakTable<TKey, TValue> 
29         where TKey : class
30         where TValue : class
31     {
32         private readonly Dictionary<object, TValue> _table;
33         private int _capacity = 4;
34
35         public ConditionalWeakTable()
36         {
37             this._table = new Dictionary<object, TValue>();
38         }
39
40         public void Add(TKey key, TValue value)
41         {
42             CleanupDeadReferences();
43             this._table.Add(CreateWeakKey(key), value);
44         }
45
46         public bool Remove(TKey key)
47         {
48             return this._table.Remove(key);
49         }
50
51         public bool TryGetValue(TKey key, out TValue value)
52         {
53             return this._table.TryGetValue(key, out value);
54         }
55
56         private void CleanupDeadReferences()
57         {
58             if (this._table.Count < _capacity)
59             {
60                 return;
61             }
62
63             ArrayList deadKeys = new ArrayList ();
64             foreach (var weakRef in _table.Keys){
65                     if (!((EquivalentWeakReference)weakRef).IsAlive)
66                             deadKeys.Add (weakRef);
67             }
68
69             foreach (var deadKey in deadKeys)
70             {
71                 this._table.Remove(deadKey);
72             }
73
74             if (this._table.Count >= _capacity)
75             {
76                 _capacity *= 2;
77             }
78         }
79
80         private static object CreateWeakKey(TKey key)
81         {
82             return new EquivalentWeakReference(key);
83         }
84
85         private class EquivalentWeakReference
86         {
87             private readonly WeakReference _weakReference;
88             private readonly int _hashCode;
89
90             public EquivalentWeakReference(object obj)
91             {
92                 this._hashCode = obj.GetHashCode();
93                 this._weakReference = new WeakReference(obj);
94             }
95
96             public bool IsAlive
97             {
98                 get
99                 {
100                     return this._weakReference.IsAlive;
101                 }
102             }
103
104             public override bool Equals(object obj)
105             {
106                 EquivalentWeakReference weakRef = obj as EquivalentWeakReference;
107
108                 if (weakRef != null)
109                 {
110                     obj = weakRef._weakReference.Target;
111                 }
112
113                 if (obj == null)
114                 {
115                     return base.Equals(weakRef);
116                 }
117                 
118                 return object.Equals(this._weakReference.Target, obj);
119             }
120
121             public override int GetHashCode()
122             {
123                 return this._hashCode;
124             }
125         }
126     }
127 }
128 #endif