Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/flowanalysis.cs @ 12515

Last change on this file since 12515 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

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