Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[mono.git] / mcs / mcs / support.cs
1 //
2 // support.cs: Support routines to work around the fact that System.Reflection.Emit
3 // can not introspect types that are being constructed
4 //
5 // Author:
6 //   Miguel de Icaza (miguel@ximian.com)
7 //   Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2003-2009 Novell, Inc
11 // Copyright 2011 Xamarin Inc
12 //
13
14 using System;
15 using System.IO;
16 using System.Text;
17 using System.Collections.Generic;
18
19 namespace Mono.CSharp {
20
21         sealed class ReferenceEquality<T> : IEqualityComparer<T> where T : class
22         {
23                 public static readonly IEqualityComparer<T> Default = new ReferenceEquality<T> ();
24
25                 private ReferenceEquality ()
26                 {
27                 }
28
29                 public bool Equals (T x, T y)
30                 {
31                         return ReferenceEquals (x, y);
32                 }
33
34                 public int GetHashCode (T obj)
35                 {
36                         return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode (obj);
37                 }
38         }
39 #if !NET_4_0 && !MONODROID
40         public class Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>
41         {
42                 public Tuple (T1 item1, T2 item2)
43                 {
44                         Item1 = item1;
45                         Item2 = item2;
46                 }
47
48                 public T1 Item1 { get; private set; }
49                 public T2 Item2 { get; private set; }
50
51                 public override int GetHashCode ()
52                 {
53                         return Item1.GetHashCode () ^ Item2.GetHashCode ();
54                 }
55
56                 #region IEquatable<Tuple<T1,T2>> Members
57
58                 public bool Equals (Tuple<T1, T2> other)
59                 {
60                         return EqualityComparer<T1>.Default.Equals (Item1, other.Item1) &&
61                                 EqualityComparer<T2>.Default.Equals (Item2, other.Item2);
62                 }
63
64                 #endregion
65         }
66
67         public class Tuple<T1, T2, T3> : IEquatable<Tuple<T1, T2, T3>>
68         {
69                 public Tuple (T1 item1, T2 item2, T3 item3)
70                 {
71                         Item1 = item1;
72                         Item2 = item2;
73                         Item3 = item3;
74                 }
75
76                 public T1 Item1 { get; private set; }
77                 public T2 Item2 { get; private set; }
78                 public T3 Item3 { get; private set; }
79
80                 public override int GetHashCode ()
81                 {
82                         return Item1.GetHashCode () ^ Item2.GetHashCode () ^ Item3.GetHashCode ();
83                 }
84
85                 #region IEquatable<Tuple<T1,T2>> Members
86
87                 public bool Equals (Tuple<T1, T2, T3> other)
88                 {
89                         return EqualityComparer<T1>.Default.Equals (Item1, other.Item1) &&
90                                 EqualityComparer<T2>.Default.Equals (Item2, other.Item2) &&
91                                 EqualityComparer<T3>.Default.Equals (Item3, other.Item3);
92                 }
93
94                 #endregion
95         }
96
97         static class Tuple
98         {
99                 public static Tuple<T1, T2> Create<T1, T2> (T1 item1, T2 item2)
100                 {
101                         return new Tuple<T1, T2> (item1, item2);
102                 }
103
104                 public static Tuple<T1, T2, T3> Create<T1, T2, T3> (T1 item1, T2 item2, T3 item3)
105                 {
106                         return new Tuple<T1, T2, T3> (item1, item2, item3);
107                 }
108         }
109 #endif
110
111         static class ArrayComparer
112         {
113                 public static bool IsEqual<T> (T[] array1, T[] array2)
114                 {
115                         if (array1 == null || array2 == null)
116                                 return array1 == array2;
117
118                         var eq = EqualityComparer<T>.Default;
119
120                         for (int i = 0; i < array1.Length; ++i) {
121                                 if (!eq.Equals (array1[i], array2[i])) {
122                                         return false;
123                                 }
124                         }
125
126                         return true;
127                 }
128         }
129
130         /// <summary>
131         ///   This is an arbitrarily seekable StreamReader wrapper.
132         ///
133         ///   It uses a self-tuning buffer to cache the seekable data,
134         ///   but if the seek is too far, it may read the underly
135         ///   stream all over from the beginning.
136         /// </summary>
137         public class SeekableStreamReader : IDisposable
138         {
139                 public const int DefaultReadAheadSize =
140 #if FULL_AST
141                         65536 / 2; // Large buffer because of ReadChars of large literal string
142 #else
143                         4096 / 2;
144 #endif
145
146                 StreamReader reader;
147                 Stream stream;
148
149                 char[] buffer;
150                 int read_ahead_length;  // the length of read buffer
151                 int buffer_start;       // in chars
152                 int char_count;         // count of filled characters in buffer[]
153                 int pos;                // index into buffer[]
154
155                 public SeekableStreamReader (Stream stream, Encoding encoding, char[] sharedBuffer = null)
156                 {
157                         this.stream = stream;
158                         this.buffer = sharedBuffer;
159
160                         InitializeStream (DefaultReadAheadSize);
161                         reader = new StreamReader (stream, encoding, true);
162                 }
163
164                 public void Dispose ()
165                 {
166                         // Needed to release stream reader buffers
167                         reader.Dispose ();
168                 }
169
170                 void InitializeStream (int read_length_inc)
171                 {
172                         read_ahead_length += read_length_inc;
173
174                         int required_buffer_size = read_ahead_length * 2;
175
176                         if (buffer == null || buffer.Length < required_buffer_size)
177                                 buffer = new char [required_buffer_size];
178
179                         stream.Position = 0;
180                         buffer_start = char_count = pos = 0;
181                 }
182
183                 /// <remarks>
184                 ///   This value corresponds to the current position in a stream of characters.
185                 ///   The StreamReader hides its manipulation of the underlying byte stream and all
186                 ///   character set/decoding issues.  Thus, we cannot use this position to guess at
187                 ///   the corresponding position in the underlying byte stream even though there is
188                 ///   a correlation between them.
189                 /// </remarks>
190                 public int Position {
191                         get {
192                                 return buffer_start + pos;
193                         }
194
195                         set {
196                                 //
197                                 // If the lookahead was too small, re-read from the beginning. Increase the buffer size while we're at it
198                                 // This should never happen until we are parsing some weird source code
199                                 //
200                                 if (value < buffer_start) {
201                                         InitializeStream (read_ahead_length);
202
203                                         //
204                                         // Discard buffer data after underlying stream changed position
205                                         // Cannot use handy reader.DiscardBufferedData () because it for
206                                         // some strange reason resets encoding as well
207                                         //
208                                         reader = new StreamReader (stream, reader.CurrentEncoding, true);
209                                 }
210
211                                 while (value > buffer_start + char_count) {
212                                         pos = char_count;
213                                         if (!ReadBuffer ())
214                                                 throw new InternalErrorException ("Seek beyond end of file: " + (buffer_start + char_count - value));
215                                 }
216
217                                 pos = value - buffer_start;
218                         }
219                 }
220
221                 bool ReadBuffer ()
222                 {
223                         int slack = buffer.Length - char_count;
224
225                         //
226                         // read_ahead_length is only half of the buffer to deal with
227                         // reads ahead and moves back without re-reading whole buffer
228                         //
229                         if (slack <= read_ahead_length) {
230                                 //
231                                 // shift the buffer to make room for read_ahead_length number of characters
232                                 //
233                                 int shift = read_ahead_length - slack;
234                                 Array.Copy (buffer, shift, buffer, 0, char_count - shift);
235
236                                 // Update all counters
237                                 pos -= shift;
238                                 char_count -= shift;
239                                 buffer_start += shift;
240                                 slack += shift;
241                         }
242
243                         char_count += reader.Read (buffer, char_count, slack);
244
245                         return pos < char_count;
246                 }
247
248                 public char[] ReadChars (int fromPosition, int toPosition)
249                 {
250                         char[] chars = new char[toPosition - fromPosition];
251                         if (buffer_start <= fromPosition && toPosition <= buffer_start + buffer.Length) {
252                                 Array.Copy (buffer, fromPosition - buffer_start, chars, 0, chars.Length);
253                         } else {
254                                 throw new NotImplementedException ();
255                         }
256
257                         return chars;
258                 }
259
260                 public int Peek ()
261                 {
262                         if ((pos >= char_count) && !ReadBuffer ())
263                                 return -1;
264
265                         return buffer [pos];
266                 }
267
268                 public int Read ()
269                 {
270                         if ((pos >= char_count) && !ReadBuffer ())
271                                 return -1;
272
273                         return buffer [pos++];
274                 }
275         }
276
277         public class UnixUtils {
278                 [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
279                 extern static int _isatty (int fd);
280                         
281                 public static bool isatty (int fd)
282                 {
283                         try {
284                                 return _isatty (fd) == 1;
285                         } catch {
286                                 return false;
287                         }
288                 }
289         }
290
291         /// <summary>
292         ///   An exception used to terminate the compiler resolution phase and provide completions
293         /// </summary>
294         /// <remarks>
295         ///   This is thrown when we want to return the completions or
296         ///   terminate the completion process by AST nodes used in
297         ///   the completion process.
298         /// </remarks>
299         public class CompletionResult : Exception {
300                 string [] result;
301                 string base_text;
302                 
303                 public CompletionResult (string base_text, string [] res)
304                 {
305                         if (base_text == null)
306                                 throw new ArgumentNullException ("base_text");
307                         this.base_text = base_text;
308
309                         result = res;
310                         Array.Sort (result);
311                 }
312
313                 public string [] Result {
314                         get {
315                                 return result;
316                         }
317                 }
318
319                 public string BaseText {
320                         get {
321                                 return base_text;
322                         }
323                 }
324         }
325
326         struct TypeNameParser
327         {
328                 internal static string Escape(string name)
329                 {
330                         if (name == null) {
331                                 return null;
332                         }
333                         StringBuilder sb = null;
334                         for (int pos = 0; pos < name.Length; pos++) {
335                                 char c = name[pos];
336                                 switch (c) {
337                                         case '\\':
338                                         case '+':
339                                         case ',':
340                                         case '[':
341                                         case ']':
342                                         case '*':
343                                         case '&':
344                                                 if (sb == null) {
345                                                         sb = new StringBuilder(name, 0, pos, name.Length + 3);
346                                                 }
347                                                 sb.Append("\\").Append(c);
348                                                 break;
349                                         default:
350                                                 if (sb != null) {
351                                                         sb.Append(c);
352                                                 }
353                                                 break;
354                                 }
355                         }
356                         return sb != null ? sb.ToString() : name;
357                 }
358         }
359 }