Merge pull request #1624 from esdrubal/getprocesstimes
[mono.git] / mcs / mcs / flowanalysis.cs
1 //
2 // flowanalyis.cs: The control flow analysis code
3 //
4 // Authors:
5 //   Martin Baulig (martin@ximian.com)
6 //   Raja R Harinath (rharinath@novell.com)
7 //   Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003-2008 Novell, Inc.
11 // Copyright 2011 Xamarin, Inc.
12 //
13
14 using System;
15 using System.Text;
16 using System.Collections.Generic;
17
18 namespace Mono.CSharp
19 {
20         // <summary>
21         //   This is used by the flow analysis code to keep track of the type of local variables.
22         //
23         //   The flow code uses a BitVector to keep track of whether a variable has been assigned
24         //   or not.  This is easy for fundamental types (int, char etc.) or reference types since
25         //   you can only assign the whole variable as such.
26         //
27         //   For structs, we also need to keep track of all its fields.  To do this, we allocate one
28         //   bit for the struct itself (it's used if you assign/access the whole struct) followed by
29         //   one bit for each of its fields.
30         //
31         //   This class computes this `layout' for each type.
32         // </summary>
33         public class TypeInfo
34         {
35                 // <summary>
36                 //   Total number of bits a variable of this type consumes in the flow vector.
37                 // </summary>
38                 public readonly int TotalLength;
39
40                 // <summary>
41                 //   Number of bits the simple fields of a variable of this type consume
42                 //   in the flow vector.
43                 // </summary>
44                 public readonly int Length;
45
46                 // <summary>
47                 //   This is only used by sub-structs.
48                 // </summary>
49                 public readonly int Offset;
50
51                 // <summary>
52                 //   If this is a struct.
53                 // </summary>
54                 public readonly bool IsStruct;
55
56                 // <summary>
57                 //   If this is a struct, all fields which are structs theirselves.
58                 // </summary>
59                 public TypeInfo[] SubStructInfo;
60
61                 readonly StructInfo struct_info;
62
63                 static readonly TypeInfo simple_type = new TypeInfo (1);
64
65                 static TypeInfo ()
66                 {
67                         Reset ();
68                 }
69                 
70                 public static void Reset ()
71                 {
72                         StructInfo.field_type_hash = new Dictionary<TypeSpec, StructInfo> ();
73                 }
74
75                 TypeInfo (int totalLength)
76                 {
77                         this.TotalLength = totalLength;
78                 }
79                 
80                 TypeInfo (StructInfo struct_info, int offset)
81                 {
82                         this.struct_info = struct_info;
83                         this.Offset = offset;
84                         this.Length = struct_info.Length;
85                         this.TotalLength = struct_info.TotalLength;
86                         this.SubStructInfo = struct_info.StructFields;
87                         this.IsStruct = true;
88                 }
89                 
90                 public int GetFieldIndex (string name)
91                 {
92                         if (struct_info == null)
93                                 return 0;
94
95                         return struct_info [name];
96                 }
97
98                 public TypeInfo GetStructField (string name)
99                 {
100                         if (struct_info == null)
101                                 return null;
102
103                         return struct_info.GetStructField (name);
104                 }
105
106                 public static TypeInfo GetTypeInfo (TypeSpec type, IMemberContext context)
107                 {
108                         if (!type.IsStruct)
109                                 return simple_type;
110
111                         TypeInfo info;
112                         Dictionary<TypeSpec, TypeInfo> type_hash;
113                         if (type.BuiltinType > 0) {
114                                 // Don't cache built-in types, they are null in most cases except for
115                                 // corlib compilation when we need to distinguish between declaration
116                                 // and referencing
117                                 type_hash = null;
118                         } else {
119                                 type_hash = context.Module.TypeInfoCache;
120                                 if (type_hash.TryGetValue (type, out info))
121                                         return info;
122                         }
123
124                         var struct_info = StructInfo.GetStructInfo (type, context);
125                         if (struct_info != null) {
126                                 info = new TypeInfo (struct_info, 0);
127                         } else {
128                                 info = simple_type;
129                         }
130
131                         if (type_hash != null)
132                                 type_hash.Add (type, info);
133
134                         return info;
135                 }
136
137                 // <summary>
138                 //   A struct's constructor must always assign all fields.
139                 //   This method checks whether it actually does so.
140                 // </summary>
141                 public bool IsFullyInitialized (FlowAnalysisContext fc, VariableInfo vi, Location loc)
142                 {
143                         if (struct_info == null)
144                                 return true;
145
146                         bool ok = true;
147                         for (int i = 0; i < struct_info.Count; i++) {
148                                 var field = struct_info.Fields[i];
149
150                                 if (!fc.IsStructFieldDefinitelyAssigned (vi, field.Name)) {
151                                         var bf = field.MemberDefinition as Property.BackingFieldDeclaration;
152                                         if (bf != null) {
153                                                 if (bf.Initializer != null)
154                                                         continue;
155
156                                                 fc.Report.Error (843, loc,
157                                                         "An automatically implemented property `{0}' must be fully assigned before control leaves the constructor. Consider calling the default struct contructor from a constructor initializer",
158                                                         field.GetSignatureForError ());
159
160                                                 ok = false;
161                                                 continue;
162                                         }
163
164                                         fc.Report.Error (171, loc,
165                                                 "Field `{0}' must be fully assigned before control leaves the constructor",
166                                                 field.GetSignatureForError ());
167                                         ok = false;
168                                 }
169                         }
170
171                         return ok;
172                 }
173
174                 public override string ToString ()
175                 {
176                         return String.Format ("TypeInfo ({0}:{1}:{2})",
177                                               Offset, Length, TotalLength);
178                 }
179
180                 class StructInfo
181                 {
182                         readonly List<FieldSpec> fields;
183                         public readonly TypeInfo[] StructFields;
184                         public readonly int Length;
185                         public readonly int TotalLength;
186
187                         public static Dictionary<TypeSpec, StructInfo> field_type_hash;
188                         private Dictionary<string, TypeInfo> struct_field_hash;
189                         private Dictionary<string, int> field_hash;
190
191                         bool InTransit;
192
193                         //
194                         // We only need one instance per type
195                         //
196                         StructInfo (TypeSpec type, IMemberContext context)
197                         {
198                                 field_type_hash.Add (type, this);
199
200                                 fields = MemberCache.GetAllFieldsForDefiniteAssignment (type, context);
201
202                                 struct_field_hash = new Dictionary<string, TypeInfo> ();
203                                 field_hash = new Dictionary<string, int> (fields.Count);
204
205                                 StructFields = new TypeInfo[fields.Count];
206                                 StructInfo[] sinfo = new StructInfo[fields.Count];
207
208                                 InTransit = true;
209
210                                 for (int i = 0; i < fields.Count; i++) {
211                                         var field = fields [i];
212
213                                         if (field.MemberType.IsStruct)
214                                                 sinfo [i] = GetStructInfo (field.MemberType, context);
215
216                                         if (sinfo [i] == null)
217                                                 field_hash.Add (field.Name, ++Length);
218                                         else if (sinfo [i].InTransit) {
219                                                 sinfo [i] = null;
220                                                 return;
221                                         }
222                                 }
223
224                                 InTransit = false;
225
226                                 TotalLength = Length + 1;
227                                 for (int i = 0; i < fields.Count; i++) {
228                                         var field = fields [i];
229
230                                         if (sinfo [i] == null)
231                                                 continue;
232
233                                         field_hash.Add (field.Name, TotalLength);
234
235                                         StructFields [i] = new TypeInfo (sinfo [i], TotalLength);
236                                         struct_field_hash.Add (field.Name, StructFields [i]);
237                                         TotalLength += sinfo [i].TotalLength;
238                                 }
239                         }
240
241                         public int Count {
242                                 get {
243                                         return fields.Count;
244                                 }
245                         }
246
247                         public List<FieldSpec> Fields {
248                                 get {
249                                         return fields;
250                                 }
251                         }
252
253                         public int this [string name] {
254                                 get {
255                                         int val;
256                                         if (!field_hash.TryGetValue (name, out val))
257                                                 return 0;
258
259                                         return val;
260                                 }
261                         }
262
263                         public TypeInfo GetStructField (string name)
264                         {
265                                 TypeInfo ti;
266                                 if (struct_field_hash.TryGetValue (name, out ti))
267                                         return ti;
268
269                                 return null;
270                         }
271
272                         public static StructInfo GetStructInfo (TypeSpec type, IMemberContext context)
273                         {
274                                 if (type.BuiltinType > 0 && type != context.CurrentType)
275                                         return null;
276
277                                 StructInfo info;
278                                 if (field_type_hash.TryGetValue (type, out info))
279                                         return info;
280
281                                 return new StructInfo (type, context);
282                         }
283                 }
284         }
285
286         //
287         // This is used by definite assignment analysis code to store information about a local variable
288         // or parameter.  Depending on the variable's type, we need to allocate one or more elements
289         // in the BitVector - if it's a fundamental or reference type, we just need to know whether
290         // it has been assigned or not, but for structs, we need this information for each of its fields.
291         //
292         public class VariableInfo
293         {
294                 readonly string Name;
295
296                 readonly TypeInfo TypeInfo;
297
298                 // <summary>
299                 //   The bit offset of this variable in the flow vector.
300                 // </summary>
301                 readonly int Offset;
302
303                 // <summary>
304                 //   The number of bits this variable needs in the flow vector.
305                 //   The first bit always specifies whether the variable as such has been assigned while
306                 //   the remaining bits contain this information for each of a struct's fields.
307                 // </summary>
308                 readonly int Length;
309
310                 // <summary>
311                 //   If this is a parameter of local variable.
312                 // </summary>
313                 public bool IsParameter;
314
315                 VariableInfo[] sub_info;
316
317                 VariableInfo (string name, TypeSpec type, int offset, IMemberContext context)
318                 {
319                         this.Name = name;
320                         this.Offset = offset;
321                         this.TypeInfo = TypeInfo.GetTypeInfo (type, context);
322
323                         Length = TypeInfo.TotalLength;
324
325                         Initialize ();
326                 }
327
328                 VariableInfo (VariableInfo parent, TypeInfo type)
329                 {
330                         this.Name = parent.Name;
331                         this.TypeInfo = type;
332                         this.Offset = parent.Offset + type.Offset;
333                         this.Length = type.TotalLength;
334
335                         this.IsParameter = parent.IsParameter;
336
337                         Initialize ();
338                 }
339
340                 void Initialize ()
341                 {
342                         TypeInfo[] sub_fields = TypeInfo.SubStructInfo;
343                         if (sub_fields != null) {
344                                 sub_info = new VariableInfo [sub_fields.Length];
345                                 for (int i = 0; i < sub_fields.Length; i++) {
346                                         if (sub_fields [i] != null)
347                                                 sub_info [i] = new VariableInfo (this, sub_fields [i]);
348                                 }
349                         } else
350                                 sub_info = new VariableInfo [0];
351                 }
352
353                 public static VariableInfo Create (BlockContext bc, LocalVariable variable)
354                 {
355                         var info = new VariableInfo (variable.Name, variable.Type, bc.AssignmentInfoOffset, bc);
356                         bc.AssignmentInfoOffset += info.Length;
357                         return info;
358                 }
359
360                 public static VariableInfo Create (BlockContext bc, Parameter parameter)
361                 {
362                         var info = new VariableInfo (parameter.Name, parameter.Type, bc.AssignmentInfoOffset, bc) {
363                                 IsParameter = true
364                         };
365
366                         bc.AssignmentInfoOffset += info.Length;
367                         return info;
368                 }
369
370                 public bool IsAssigned (DefiniteAssignmentBitSet vector)
371                 {
372                         if (vector == null)
373                                 return true;
374
375                         if (vector [Offset])
376                                 return true;
377
378                         // Unless this is a struct
379                         if (!TypeInfo.IsStruct)
380                                 return false;
381
382                         //
383                         // Following case cannot be handled fully by SetStructFieldAssigned
384                         // because we may encounter following case
385                         // 
386                         // struct A { B b }
387                         // struct B { int value; }
388                         //
389                         // setting a.b.value is propagated only to B's vector and not upwards to possible parents
390                         //
391                         //
392                         // Each field must be assigned
393                         //
394                         for (int i = Offset + 1; i <= TypeInfo.Length + Offset; i++) {
395                                 if (!vector[i])
396                                         return false;
397                         }
398
399                         // Ok, now check all fields which are structs.
400                         for (int i = 0; i < sub_info.Length; i++) {
401                                 VariableInfo sinfo = sub_info[i];
402                                 if (sinfo == null)
403                                         continue;
404
405                                 if (!sinfo.IsAssigned (vector))
406                                         return false;
407                         }
408                         
409                         vector.Set (Offset);
410                         return true;
411                 }
412
413                 public bool IsEverAssigned { get; set; }
414
415                 public bool IsFullyInitialized (FlowAnalysisContext fc, Location loc)
416                 {
417                         return TypeInfo.IsFullyInitialized (fc, this, loc);
418                 }
419
420                 public bool IsStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name)
421                 {
422                         int field_idx = TypeInfo.GetFieldIndex (field_name);
423
424                         if (field_idx == 0)
425                                 return true;
426
427                         return vector [Offset + field_idx];
428                 }
429
430                 public void SetAssigned (DefiniteAssignmentBitSet vector, bool generatedAssignment)
431                 {
432                         if (Length == 1)
433                                 vector.Set (Offset);
434                         else
435                                 vector.Set (Offset, Length);
436
437                         if (!generatedAssignment)
438                                 IsEverAssigned = true;
439                 }
440
441                 public void SetStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name)
442                 {
443                         if (vector [Offset])
444                                 return;
445
446                         int field_idx = TypeInfo.GetFieldIndex (field_name);
447
448                         if (field_idx == 0)
449                                 return;
450
451                         var complex_field = TypeInfo.GetStructField (field_name);
452                         if (complex_field != null) {
453                                 vector.Set (Offset + complex_field.Offset, complex_field.TotalLength);
454                         } else {
455                                 vector.Set (Offset + field_idx);
456                         }
457
458                         IsEverAssigned = true;
459
460                         //
461                         // Each field must be assigned before setting master bit
462                         //
463                         for (int i = Offset + 1; i < TypeInfo.TotalLength + Offset; i++) {
464                                 if (!vector[i])
465                                         return;
466                         }
467
468                         //
469                         // Set master struct flag to assigned when all tested struct
470                         // fields have been assigned
471                         //
472                         vector.Set (Offset);
473                 }
474
475                 public VariableInfo GetStructFieldInfo (string fieldName)
476                 {
477                         TypeInfo type = TypeInfo.GetStructField (fieldName);
478
479                         if (type == null)
480                                 return null;
481
482                         return new VariableInfo (this, type);
483                 }
484
485                 public override string ToString ()
486                 {
487                         return String.Format ("Name={0} Offset={1} Length={2} {3})", Name, Offset, Length, TypeInfo);
488                 }
489         }
490
491         public struct Reachability
492         {
493                 readonly bool unreachable;
494
495                 Reachability (bool unreachable)
496                 {
497                         this.unreachable = unreachable;
498                 }
499
500                 public bool IsUnreachable {
501                         get {
502                                 return unreachable;
503                         }
504                 }
505
506                 public static Reachability CreateUnreachable ()
507                 {
508                         return new Reachability (true);
509                 }
510
511                 public static Reachability operator & (Reachability a, Reachability b)
512                 {
513                     return new Reachability (a.unreachable && b.unreachable);
514                 }
515
516                 public static Reachability operator | (Reachability a, Reachability b)
517                 {
518                         return new Reachability (a.unreachable | b.unreachable);
519                 }
520         }
521
522         //
523         // Special version of bit array. Many operations can be simplified because
524         // we are always dealing with arrays of same sizes
525         //
526         public class DefiniteAssignmentBitSet
527         {
528                 const uint copy_on_write_flag = 1u << 31;
529
530                 uint bits;
531
532                 // Used when bits overflows
533                 int[] large_bits;
534
535                 public static readonly DefiniteAssignmentBitSet Empty = new DefiniteAssignmentBitSet (0);
536
537                 public DefiniteAssignmentBitSet (int length)
538                 {
539                         if (length > 31)
540                                 large_bits = new int[(length + 31) / 32];
541                 }
542
543                 public DefiniteAssignmentBitSet (DefiniteAssignmentBitSet source)
544                 {
545                         if (source.large_bits != null) {
546                                 large_bits = source.large_bits;
547                                 bits = source.bits | copy_on_write_flag;
548                         } else {
549                                 bits = source.bits & ~copy_on_write_flag;
550                         }
551                 }
552
553                 public static DefiniteAssignmentBitSet operator & (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
554                 {
555                         if (AreEqual (a, b))
556                                 return a;
557
558                         DefiniteAssignmentBitSet res;
559                         if (a.large_bits == null) {
560                                 res = new DefiniteAssignmentBitSet (a);
561                                 res.bits &= (b.bits & ~copy_on_write_flag);
562                                 return res;
563                         }
564
565                         res = new DefiniteAssignmentBitSet (a);
566                         res.Clone ();
567                         var dest = res.large_bits;
568                         var src = b.large_bits;
569                         for (int i = 0; i < dest.Length; ++i) {
570                                 dest[i] &= src[i];
571                         }
572
573                         return res;
574                 }
575
576                 public static DefiniteAssignmentBitSet operator | (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
577                 {
578                         if (AreEqual (a, b))
579                                 return a;
580
581                         DefiniteAssignmentBitSet res;
582                         if (a.large_bits == null) {
583                                 res = new DefiniteAssignmentBitSet (a);
584                                 res.bits |= b.bits;
585                                 res.bits &= ~copy_on_write_flag;
586                                 return res;
587                         }
588
589                         res = new DefiniteAssignmentBitSet (a);
590                         res.Clone ();
591                         var dest = res.large_bits;
592                         var src = b.large_bits;
593
594                         for (int i = 0; i < dest.Length; ++i) {
595                                 dest[i] |= src[i];
596                         }
597
598                         return res;
599                 }
600
601                 public static DefiniteAssignmentBitSet And (List<DefiniteAssignmentBitSet> das)
602                 {
603                         if (das.Count == 0)
604                                 throw new ArgumentException ("Empty das");
605
606                         DefiniteAssignmentBitSet res = das[0];
607                         for (int i = 1; i < das.Count; ++i) {
608                                 res &= das[i];
609                         }
610
611                         return res;
612                 }
613
614                 bool CopyOnWrite {
615                         get {
616                                 return (bits & copy_on_write_flag) != 0;
617                         }
618                 }
619
620                 int Length {
621                         get {
622                                 return large_bits == null ? 31 : large_bits.Length * 32;
623                         }
624                 }
625
626                 public void Set (int index)
627                 {
628                         if (CopyOnWrite && !this[index])
629                                 Clone ();
630
631                         SetBit (index);
632                 }
633
634                 public void Set (int index, int length)
635                 {
636                         for (int i = 0; i < length; ++i) {
637                                 if (CopyOnWrite && !this[index + i])
638                                         Clone ();
639
640                                 SetBit (index + i);
641                         }
642                 }
643
644                 public bool this[int index] {
645                         get {
646                                 return GetBit (index);
647                         }
648                 }
649
650                 public override string ToString ()
651                 {
652                         var length = Length;
653                         StringBuilder sb = new StringBuilder (length);
654                         for (int i = 0; i < length; ++i) {
655                                 sb.Append (this[i] ? '1' : '0');
656                         }
657
658                         return sb.ToString ();
659                 }
660
661                 void Clone ()
662                 {
663                         large_bits = (int[]) large_bits.Clone ();
664                 }
665
666                 bool GetBit (int index)
667                 {
668                         return large_bits == null ?
669                                 (bits & (1 << index)) != 0 :
670                                 (large_bits[index >> 5] & (1 << (index & 31))) != 0;
671                 }
672
673                 void SetBit (int index)
674                 {
675                         if (large_bits == null)
676                                 bits = (uint) ((int) bits | (1 << index));
677                         else
678                                 large_bits[index >> 5] |= (1 << (index & 31));
679                 }
680
681                 public static bool AreEqual (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
682                 {
683                         if (a.large_bits == null)
684                                 return (a.bits & ~copy_on_write_flag) == (b.bits & ~copy_on_write_flag);
685
686                         for (int i = 0; i < a.large_bits.Length; ++i) {
687                                 if (a.large_bits[i] != b.large_bits[i])
688                                         return false;
689                         }
690
691                         return true;
692                 }
693         }
694 }