2 // flowanalyis.cs: The control flow analysis code
5 // Martin Baulig (martin@ximian.com)
7 // (C) 2001, 2002, 2003 Ximian, Inc.
12 using System.Collections;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Diagnostics;
19 public enum TriState : byte {
20 // Never < Sometimes < Always
27 // A new instance of this class is created every time a new block is resolved
28 // and if there's branching in the block's control flow.
30 public abstract class FlowBranching
33 // The type of a FlowBranching.
35 public enum BranchingType : byte {
36 // Normal (conditional or toplevel) block.
45 // The statement embedded inside a loop
48 // part of a block headed by a jump target
60 // The toplevel block of a function
65 // The type of one sibling of a branching.
67 public enum SiblingType : byte {
76 public sealed class Reachability
78 TriState returns, throws, barrier;
80 public TriState Returns {
81 get { return returns; }
83 public TriState Throws {
84 get { return throws; }
86 public TriState Barrier {
87 get { return barrier; }
90 Reachability (TriState returns, TriState throws, TriState barrier)
92 this.returns = returns;
94 this.barrier = barrier;
97 public Reachability Clone ()
99 return new Reachability (returns, throws, barrier);
102 public static TriState TriState_Meet (TriState a, TriState b)
104 // (1) if both are Never, return Never
105 // (2) if both are Always, return Always
106 // (3) otherwise, return Sometimes
107 // note that (3) => (3') if both are Sometimes, return Sometimes
108 return a == b ? a : TriState.Sometimes;
111 public static TriState TriState_Max (TriState a, TriState b)
113 return ((byte) a > (byte) b) ? a : b;
116 public void Meet (Reachability b)
118 if ((AlwaysReturns && b.AlwaysHasBarrier) || (AlwaysHasBarrier && b.AlwaysReturns))
119 returns = TriState.Always;
121 returns = TriState_Meet (returns, b.returns);
123 throws = TriState_Meet (throws, b.throws);
124 barrier = TriState_Meet (barrier, b.barrier);
127 public void Or (Reachability b)
129 returns = TriState_Max (returns, b.returns);
130 throws = TriState_Max (throws, b.throws);
131 barrier = TriState_Max (barrier, b.barrier);
134 public static Reachability Always ()
136 return new Reachability (TriState.Never, TriState.Never, TriState.Never);
139 TriState Unreachable {
140 get { return TriState_Max (returns, TriState_Max (throws, barrier)); }
145 TriState unreachable = Unreachable;
146 if (unreachable == TriState.Sometimes)
147 return TriState.Sometimes;
148 return unreachable == TriState.Always ? TriState.Never : TriState.Always;
152 public bool AlwaysReturns {
153 get { return returns == TriState.Always; }
156 public bool AlwaysThrows {
157 get { return throws == TriState.Always; }
160 public bool AlwaysHasBarrier {
161 get { return barrier == TriState.Always; }
164 public bool IsUnreachable {
165 get { return Unreachable == TriState.Always; }
168 public void SetReturns ()
170 returns = TriState.Always;
173 public void SetThrows ()
175 throws = TriState.Always;
178 public void SetBarrier ()
180 barrier = TriState.Always;
183 static string ShortName (TriState returns)
188 case TriState.Sometimes:
195 public override string ToString ()
197 return String.Format ("[{0}:{1}:{2}:{3}]",
198 ShortName (returns), ShortName (throws), ShortName (barrier),
199 ShortName (Reachable));
203 public static FlowBranching CreateBranching (FlowBranching parent, BranchingType type, Block block, Location loc)
206 case BranchingType.Exception:
207 case BranchingType.Labeled:
208 case BranchingType.Toplevel:
209 throw new InvalidOperationException ();
211 case BranchingType.Switch:
212 return new FlowBranchingBreakable (parent, type, SiblingType.SwitchSection, block, loc);
214 case BranchingType.SwitchSection:
215 return new FlowBranchingBlock (parent, type, SiblingType.Block, block, loc);
217 case BranchingType.Block:
218 return new FlowBranchingBlock (parent, type, SiblingType.Block, block, loc);
220 case BranchingType.Loop:
221 return new FlowBranchingBreakable (parent, type, SiblingType.Conditional, block, loc);
223 case BranchingType.Embedded:
224 return new FlowBranchingContinuable (parent, type, SiblingType.Conditional, block, loc);
227 return new FlowBranchingBlock (parent, type, SiblingType.Conditional, block, loc);
232 // The type of this flow branching.
234 public readonly BranchingType Type;
237 // The block this branching is contained in. This may be null if it's not
238 // a top-level block and it doesn't declare any local variables.
240 public readonly Block Block;
243 // The parent of this branching or null if this is the top-block.
245 public readonly FlowBranching Parent;
248 // Start-Location of this flow branching.
250 public readonly Location Location;
252 protected VariableMap param_map, local_map;
254 static int next_id = 0;
258 // The vector contains a BitArray with information about which local variables
259 // and parameters are already initialized at the current code position.
261 public class UsageVector {
263 // The type of this branching.
265 public readonly SiblingType Type;
268 // Start location of this branching.
270 public Location Location;
273 // This is only valid for SwitchSection, Try, Catch and Finally.
275 public readonly Block Block;
278 // The number of parameters in this block.
280 public readonly int CountParameters;
283 // The number of locals in this block.
285 public readonly int CountLocals;
288 // If not null, then we inherit our state from this vector and do a
289 // copy-on-write. If null, then we're the first sibling in a top-level
290 // block and inherit from the empty vector.
292 public readonly UsageVector InheritsFrom;
295 // This is used to construct a list of UsageVector's.
297 public UsageVector Next;
302 MyBitVector locals, parameters;
303 Reachability reachability;
305 static int next_id = 0;
309 // Normally, you should not use any of these constructors.
311 public UsageVector (SiblingType type, UsageVector parent, Block block, Location loc, int num_params, int num_locals)
316 this.InheritsFrom = parent;
317 this.CountParameters = num_params;
318 this.CountLocals = num_locals;
320 locals = num_locals == 0
322 : new MyBitVector (parent == null ? MyBitVector.Empty : parent.locals, num_locals);
324 parameters = num_params == 0
326 : new MyBitVector (parent == null ? MyBitVector.Empty : parent.parameters, num_params);
328 reachability = parent == null ? Reachability.Always () : parent.Reachability.Clone ();
333 public UsageVector (SiblingType type, UsageVector parent, Block block, Location loc)
334 : this (type, parent, block, loc, parent.CountParameters, parent.CountLocals)
337 public UsageVector (MyBitVector parameters, MyBitVector locals, Reachability reachability, Block block, Location loc)
339 this.Type = SiblingType.Block;
343 this.reachability = reachability;
344 this.parameters = parameters;
345 this.locals = locals;
351 // This does a deep copy of the usage vector.
353 public UsageVector Clone ()
355 UsageVector retval = new UsageVector (Type, null, Block, Location, CountParameters, CountLocals);
357 retval.locals = locals.Clone ();
358 retval.parameters = parameters.Clone ();
359 retval.reachability = reachability.Clone ();
364 public bool IsAssigned (VariableInfo var, bool ignoreReachability)
366 if (!ignoreReachability && !var.IsParameter && Reachability.IsUnreachable)
369 return var.IsAssigned (var.IsParameter ? parameters : locals);
372 public void SetAssigned (VariableInfo var)
374 if (!var.IsParameter && Reachability.IsUnreachable)
377 var.SetAssigned (var.IsParameter ? parameters : locals);
380 public bool IsFieldAssigned (VariableInfo var, string name)
382 if (!var.IsParameter && Reachability.IsUnreachable)
385 return var.IsFieldAssigned (var.IsParameter ? parameters : locals, name);
388 public void SetFieldAssigned (VariableInfo var, string name)
390 if (!var.IsParameter && Reachability.IsUnreachable)
393 var.SetFieldAssigned (var.IsParameter ? parameters : locals, name);
396 public Reachability Reachability {
397 get { return reachability; }
400 public void Return ()
402 if (!reachability.IsUnreachable)
403 reachability.SetReturns ();
408 if (!reachability.IsUnreachable) {
409 reachability.SetThrows ();
410 reachability.SetBarrier ();
416 if (!reachability.IsUnreachable)
417 reachability.SetBarrier ();
420 public static UsageVector MergeSiblings (UsageVector sibling_list, Location loc)
422 if (sibling_list.Next == null)
425 MyBitVector locals = null;
426 MyBitVector parameters = null;
427 Reachability reachability = null;
429 for (UsageVector child = sibling_list; child != null; child = child.Next) {
430 Report.Debug (2, " MERGING SIBLING ", reachability, child);
432 if (reachability == null)
433 reachability = child.Reachability.Clone ();
435 reachability.Meet (child.Reachability);
437 // A local variable is initialized after a flow branching if it
438 // has been initialized in all its branches which do neither
439 // always return or always throw an exception.
441 // If a branch may return, but does not always return, then we
442 // can treat it like a never-returning branch here: control will
443 // only reach the code position after the branching if we did not
446 // It's important to distinguish between always and sometimes
447 // returning branches here:
450 // 2 if (something) {
454 // 6 Console.WriteLine (a);
456 // The if block in lines 3-4 always returns, so we must not look
457 // at the initialization of `a' in line 4 - thus it'll still be
458 // uninitialized in line 6.
460 // On the other hand, the following is allowed:
467 // 6 Console.WriteLine (a);
469 // Here, `a' is initialized in line 3 and we must not look at
470 // line 5 since it always returns.
472 bool unreachable = child.Reachability.IsUnreachable;
474 Report.Debug (2, " MERGING SIBLING #1", reachability,
475 child.Type, child.Reachability.IsUnreachable, unreachable);
478 MyBitVector.And (ref locals, child.locals);
480 // An `out' parameter must be assigned in all branches which do
481 // not always throw an exception.
482 if (!child.Reachability.AlwaysThrows)
483 MyBitVector.And (ref parameters, child.parameters);
485 Report.Debug (2, " MERGING SIBLING #2", parameters, locals);
488 if (reachability == null)
489 throw new InternalErrorException ("Cannot happen: the loop above runs at least twice");
491 return new UsageVector (parameters, locals, reachability, null, loc);
495 // Merges a child branching.
497 public UsageVector MergeChild (UsageVector child, bool implicit_block)
499 Report.Debug (2, " MERGING CHILD EFFECTS", this, child, reachability, Type);
501 Reachability new_r = child.Reachability;
504 // We've now either reached the point after the branching or we will
505 // never get there since we always return or always throw an exception.
507 // If we can reach the point after the branching, mark all locals and
508 // parameters as initialized which have been initialized in all branches
509 // we need to look at (see above).
512 if ((Type == SiblingType.SwitchSection) && !new_r.IsUnreachable) {
513 Report.Error (163, Location,
514 "Control cannot fall through from one " +
515 "case label to another");
519 MyBitVector.Or (ref locals, child.locals);
520 MyBitVector.Or (ref parameters, child.parameters);
523 reachability = new_r.Clone ();
525 reachability.Or (new_r);
531 // Tells control flow analysis that the current code position may be reached with
532 // a forward jump from any of the origins listed in `origin_vectors' which is a
533 // list of UsageVectors.
535 // This is used when resolving forward gotos - in the following example, the
536 // variable `a' is uninitialized in line 8 becase this line may be reached via
537 // the goto in line 4:
547 // 8 Console.WriteLine (a);
550 public void MergeJumpOrigins (UsageVector o_vectors)
552 Report.Debug (1, " MERGING JUMP ORIGINS", this);
554 if (o_vectors == null)
557 UsageVector vector = o_vectors;
558 if (reachability.IsUnreachable) {
559 Report.Debug (1, " MERGING JUMP ORIGIN INTO UNREACHABLE", this, vector);
560 MyBitVector.Or (ref locals, vector.locals);
561 MyBitVector.Or (ref parameters, vector.parameters);
562 reachability.Meet (vector.Reachability);
563 vector = vector.Next;
566 for (; vector != null; vector = vector.Next) {
567 Report.Debug (1, " MERGING JUMP ORIGIN", this, vector);
568 MyBitVector.And (ref locals, vector.locals);
569 MyBitVector.And (ref parameters, vector.parameters);
570 reachability.Meet (vector.Reachability);
572 Report.Debug (1, " MERGING JUMP ORIGIN #1", vector);
575 Report.Debug (1, " MERGING JUMP ORIGINS DONE", this);
578 public void MergeOrigins (UsageVector o_vectors)
580 Report.Debug (1, " MERGING BREAK ORIGINS", this);
582 if (o_vectors == null)
585 if (reachability.IsUnreachable) {
590 for (UsageVector vector = o_vectors; vector != null; vector = vector.Next) {
591 Report.Debug (1, " MERGING BREAK ORIGIN", vector);
592 MyBitVector.And (ref locals, vector.locals);
593 MyBitVector.And (ref parameters, vector.parameters);
594 reachability.Meet (vector.Reachability);
597 Report.Debug (1, " MERGING BREAK ORIGINS DONE", this);
604 public override string ToString ()
606 return String.Format ("Vector ({0},{1},{2}-{3}-{4})", Type, id, reachability, parameters, locals);
611 // Creates a new flow branching which is contained in `parent'.
612 // You should only pass non-null for the `block' argument if this block
613 // introduces any new variables - in this case, we need to create a new
614 // usage vector with a different size than our parent's one.
616 protected FlowBranching (FlowBranching parent, BranchingType type, SiblingType stype,
617 Block block, Location loc)
627 param_map = Block.ParameterMap;
628 local_map = Block.LocalMap;
630 UsageVector parent_vector = parent != null ? parent.CurrentUsageVector : null;
631 vector = new UsageVector (
632 stype, parent_vector, Block, loc,
633 param_map.Length, local_map.Length);
635 param_map = Parent.param_map;
636 local_map = Parent.local_map;
637 vector = new UsageVector (
638 stype, Parent.CurrentUsageVector, null, loc);
644 public abstract UsageVector CurrentUsageVector {
649 // Creates a sibling of the current usage vector.
651 public virtual void CreateSibling (Block block, SiblingType type)
653 UsageVector vector = new UsageVector (
654 type, Parent.CurrentUsageVector, block, Location);
657 Report.Debug (1, " CREATED SIBLING", CurrentUsageVector);
660 public void CreateSibling ()
662 CreateSibling (null, SiblingType.Conditional);
665 protected abstract void AddSibling (UsageVector uv);
667 public virtual LabeledStatement LookupLabel (string name, Location loc)
670 return Parent.LookupLabel (name, loc);
674 "No such label `" + name + "' in this scope");
678 public abstract void Label (UsageVector origin_vectors);
680 protected abstract UsageVector Merge ();
683 // Merge a child branching.
685 public UsageVector MergeChild (FlowBranching child)
687 bool implicit_block = child.Type == BranchingType.Block && child.Block.Implicit;
688 Report.Debug (2, " MERGING CHILD", this, child);
689 UsageVector result = CurrentUsageVector.MergeChild (child.Merge (), implicit_block);
690 Report.Debug (2, " MERGING CHILD DONE", this, result);
694 public virtual bool InTryWithCatch ()
696 return Parent.InTryWithCatch ();
699 // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
700 public virtual bool AddBreakOrigin (UsageVector vector, Location loc)
702 return Parent.AddBreakOrigin (vector, loc);
705 // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
706 public virtual bool AddContinueOrigin (UsageVector vector, Location loc)
708 return Parent.AddContinueOrigin (vector, loc);
711 // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
712 public virtual bool AddReturnOrigin (UsageVector vector, Location loc)
714 return Parent.AddReturnOrigin (vector, loc);
717 public virtual void StealFinallyClauses (ref ArrayList list)
719 Parent.StealFinallyClauses (ref list);
722 public bool IsAssigned (VariableInfo vi)
724 return CurrentUsageVector.IsAssigned (vi, false);
727 public bool IsFieldAssigned (VariableInfo vi, string field_name)
729 return CurrentUsageVector.IsAssigned (vi, false) || CurrentUsageVector.IsFieldAssigned (vi, field_name);
732 public void SetAssigned (VariableInfo vi)
734 CurrentUsageVector.SetAssigned (vi);
737 public void SetFieldAssigned (VariableInfo vi, string name)
739 CurrentUsageVector.SetFieldAssigned (vi, name);
742 public override string ToString ()
744 StringBuilder sb = new StringBuilder ();
745 sb.Append (GetType ());
753 sb.Append (Block.ID);
755 sb.Append (Block.StartLocation);
758 // sb.Append (Siblings.Length);
759 // sb.Append (" - ");
760 sb.Append (CurrentUsageVector);
762 return sb.ToString ();
766 get { return String.Format ("{0} ({1}:{2}:{3})", GetType (), id, Type, Location); }
770 public class FlowBranchingBlock : FlowBranching
772 UsageVector sibling_list = null;
774 public FlowBranchingBlock (FlowBranching parent, BranchingType type,
775 SiblingType stype, Block block, Location loc)
776 : base (parent, type, stype, block, loc)
779 public override UsageVector CurrentUsageVector {
780 get { return sibling_list; }
783 protected override void AddSibling (UsageVector sibling)
785 sibling.Next = sibling_list;
786 sibling_list = sibling;
789 public override LabeledStatement LookupLabel (string name, Location loc)
792 return base.LookupLabel (name, loc);
794 LabeledStatement s = Block.LookupLabel (name);
798 return base.LookupLabel (name, loc);
801 public override void Label (UsageVector origin_vectors)
803 if (!CurrentUsageVector.Reachability.IsUnreachable) {
804 UsageVector vector = CurrentUsageVector.Clone ();
805 vector.Next = origin_vectors;
806 origin_vectors = vector;
809 CurrentUsageVector.MergeJumpOrigins (origin_vectors);
812 protected override UsageVector Merge ()
814 Report.Debug (2, " MERGING SIBLINGS", Name);
815 UsageVector vector = UsageVector.MergeSiblings (sibling_list, Location);
816 Report.Debug (2, " MERGING SIBLINGS DONE", Name, vector);
821 public class FlowBranchingBreakable : FlowBranchingBlock
823 UsageVector break_origins;
825 public FlowBranchingBreakable (FlowBranching parent, BranchingType type, SiblingType stype, Block block, Location loc)
826 : base (parent, type, stype, block, loc)
829 public override bool AddBreakOrigin (UsageVector vector, Location loc)
831 vector = vector.Clone ();
832 vector.Next = break_origins;
833 break_origins = vector;
837 protected override UsageVector Merge ()
839 UsageVector vector = base.Merge ();
840 vector.MergeOrigins (break_origins);
845 public class FlowBranchingContinuable : FlowBranchingBlock
847 UsageVector continue_origins;
849 public FlowBranchingContinuable (FlowBranching parent, BranchingType type, SiblingType stype, Block block, Location loc)
850 : base (parent, type, stype, block, loc)
853 public override bool AddContinueOrigin (UsageVector vector, Location loc)
855 vector = vector.Clone ();
856 vector.Next = continue_origins;
857 continue_origins = vector;
861 protected override UsageVector Merge ()
863 UsageVector vector = base.Merge ();
864 vector.MergeOrigins (continue_origins);
869 public class FlowBranchingLabeled : FlowBranchingBlock
871 LabeledStatement stmt;
872 public FlowBranchingLabeled (FlowBranching parent, LabeledStatement stmt)
873 : base (parent, BranchingType.Labeled, SiblingType.Conditional, null, stmt.loc)
879 public class FlowBranchingToplevel : FlowBranchingBlock
881 public FlowBranchingToplevel (FlowBranching parent, ToplevelBlock stmt)
882 : base (parent, BranchingType.Toplevel, SiblingType.Conditional, stmt, stmt.loc)
887 // Check whether all `out' parameters have been assigned.
889 void CheckOutParameters (UsageVector vector, Location loc)
891 for (int i = 0; i < param_map.Count; i++) {
892 VariableInfo var = param_map [i];
897 if (vector.IsAssigned (var, false))
900 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
905 public override bool InTryWithCatch ()
910 public override bool AddBreakOrigin (UsageVector vector, Location loc)
912 Report.Error (139, loc, "No enclosing loop out of which to break or continue");
916 public override bool AddContinueOrigin (UsageVector vector, Location loc)
918 Report.Error (139, loc, "No enclosing loop out of which to break or continue");
922 public override bool AddReturnOrigin (UsageVector vector, Location loc)
924 CheckOutParameters (vector, loc);
928 public override void StealFinallyClauses (ref ArrayList list)
933 public Reachability End ()
935 UsageVector result = Merge ();
937 Report.Debug (4, "MERGE TOP BLOCK", Location, result);
939 if (!result.Reachability.AlwaysThrows && !result.Reachability.AlwaysHasBarrier)
940 CheckOutParameters (result, Location);
942 return result.Reachability;
946 public class FlowBranchingException : FlowBranching
948 ExceptionStatement stmt;
949 UsageVector current_vector;
950 UsageVector catch_vectors;
951 UsageVector finally_vector;
953 UsageVector break_origins;
954 UsageVector continue_origins;
955 UsageVector return_origins;
959 public FlowBranchingException (FlowBranching parent,
960 ExceptionStatement stmt)
961 : base (parent, BranchingType.Exception, SiblingType.Try,
965 this.emit_finally = true;
968 protected override void AddSibling (UsageVector sibling)
970 switch (sibling.Type) {
971 case SiblingType.Try:
972 case SiblingType.Catch:
973 sibling.Next = catch_vectors;
974 catch_vectors = sibling;
976 case SiblingType.Finally:
977 finally_vector = sibling;
980 throw new InvalidOperationException ();
982 current_vector = sibling;
985 public override UsageVector CurrentUsageVector {
986 get { return current_vector; }
989 public override bool InTryWithCatch ()
991 if (finally_vector == null) {
993 if (t != null && t.HasCatch)
997 return base.InTryWithCatch ();
1000 public override bool AddBreakOrigin (UsageVector vector, Location loc)
1002 if (finally_vector != null) {
1003 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
1005 vector = vector.Clone ();
1006 vector.Location = loc;
1007 vector.Next = break_origins;
1008 break_origins = vector;
1013 public override bool AddContinueOrigin (UsageVector vector, Location loc)
1015 if (finally_vector != null) {
1016 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
1018 vector = vector.Clone ();
1019 vector.Location = loc;
1020 vector.Next = continue_origins;
1021 continue_origins = vector;
1026 public override bool AddReturnOrigin (UsageVector vector, Location loc)
1028 if (finally_vector != null) {
1029 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
1031 vector = vector.Clone ();
1032 vector.Location = loc;
1033 vector.Next = return_origins;
1034 return_origins = vector;
1039 public override void StealFinallyClauses (ref ArrayList list)
1042 list = new ArrayList ();
1044 emit_finally = false;
1045 base.StealFinallyClauses (ref list);
1048 public bool EmitFinally {
1049 get { return emit_finally; }
1052 public override LabeledStatement LookupLabel (string name, Location loc)
1054 if (current_vector.Block == null)
1055 return base.LookupLabel (name, loc);
1057 LabeledStatement s = current_vector.Block.LookupLabel (name);
1061 if (finally_vector != null) {
1062 Report.Error (157, loc,
1063 "Control cannot leave the body of a finally clause");
1067 return base.LookupLabel (name, loc);
1070 public override void Label (UsageVector origin_vectors)
1072 CurrentUsageVector.MergeJumpOrigins (origin_vectors);
1075 protected override UsageVector Merge ()
1077 Report.Debug (2, " MERGING TRY/CATCH", Name);
1078 UsageVector vector = UsageVector.MergeSiblings (catch_vectors, Location);
1079 Report.Debug (2, " MERGING TRY/CATCH DONE", vector);
1081 if (finally_vector != null)
1082 vector.MergeChild (finally_vector, false);
1084 for (UsageVector origin = break_origins; origin != null; origin = origin.Next) {
1085 if (finally_vector != null)
1086 origin.MergeChild (finally_vector, false);
1087 if (!origin.Reachability.IsUnreachable)
1088 Parent.AddBreakOrigin (origin, origin.Location);
1091 for (UsageVector origin = continue_origins; origin != null; origin = origin.Next) {
1092 if (finally_vector != null)
1093 origin.MergeChild (finally_vector, false);
1094 if (!origin.Reachability.IsUnreachable)
1095 Parent.AddContinueOrigin (origin, origin.Location);
1098 for (UsageVector origin = return_origins; origin != null; origin = origin.Next) {
1099 if (finally_vector != null)
1100 origin.MergeChild (finally_vector, false);
1101 if (!origin.Reachability.IsUnreachable)
1102 Parent.AddReturnOrigin (origin, origin.Location);
1110 // This is used by the flow analysis code to keep track of the type of local variables
1113 // The flow code uses a BitVector to keep track of whether a variable has been assigned
1114 // or not. This is easy for fundamental types (int, char etc.) or reference types since
1115 // you can only assign the whole variable as such.
1117 // For structs, we also need to keep track of all its fields. To do this, we allocate one
1118 // bit for the struct itself (it's used if you assign/access the whole struct) followed by
1119 // one bit for each of its fields.
1121 // This class computes this `layout' for each type.
1123 public class TypeInfo
1125 public readonly Type Type;
1128 // Total number of bits a variable of this type consumes in the flow vector.
1130 public readonly int TotalLength;
1133 // Number of bits the simple fields of a variable of this type consume
1134 // in the flow vector.
1136 public readonly int Length;
1139 // This is only used by sub-structs.
1141 public readonly int Offset;
1144 // If this is a struct.
1146 public readonly bool IsStruct;
1149 // If this is a struct, all fields which are structs theirselves.
1151 public TypeInfo[] SubStructInfo;
1153 protected readonly StructInfo struct_info;
1154 private static Hashtable type_hash = new Hashtable ();
1156 public static TypeInfo GetTypeInfo (Type type)
1158 TypeInfo info = (TypeInfo) type_hash [type];
1162 info = new TypeInfo (type);
1163 type_hash.Add (type, info);
1167 public static TypeInfo GetTypeInfo (TypeContainer tc)
1169 TypeInfo info = (TypeInfo) type_hash [tc.TypeBuilder];
1173 info = new TypeInfo (tc);
1174 type_hash.Add (tc.TypeBuilder, info);
1178 private TypeInfo (Type type)
1182 struct_info = StructInfo.GetStructInfo (type);
1183 if (struct_info != null) {
1184 Length = struct_info.Length;
1185 TotalLength = struct_info.TotalLength;
1186 SubStructInfo = struct_info.StructFields;
1195 private TypeInfo (TypeContainer tc)
1197 this.Type = tc.TypeBuilder;
1199 struct_info = StructInfo.GetStructInfo (tc);
1200 if (struct_info != null) {
1201 Length = struct_info.Length;
1202 TotalLength = struct_info.TotalLength;
1203 SubStructInfo = struct_info.StructFields;
1212 protected TypeInfo (StructInfo struct_info, int offset)
1214 this.struct_info = struct_info;
1215 this.Offset = offset;
1216 this.Length = struct_info.Length;
1217 this.TotalLength = struct_info.TotalLength;
1218 this.SubStructInfo = struct_info.StructFields;
1219 this.Type = struct_info.Type;
1220 this.IsStruct = true;
1223 public int GetFieldIndex (string name)
1225 if (struct_info == null)
1228 return struct_info [name];
1231 public TypeInfo GetSubStruct (string name)
1233 if (struct_info == null)
1236 return struct_info.GetStructField (name);
1240 // A struct's constructor must always assign all fields.
1241 // This method checks whether it actually does so.
1243 public bool IsFullyInitialized (FlowBranching branching, VariableInfo vi, Location loc)
1245 if (struct_info == null)
1249 for (int i = 0; i < struct_info.Count; i++) {
1250 FieldInfo field = struct_info.Fields [i];
1252 if (!branching.IsFieldAssigned (vi, field.Name)) {
1253 Report.Error (171, loc,
1254 "Field `{0}' must be fully assigned before control leaves the constructor",
1255 TypeManager.GetFullNameSignature (field));
1263 public override string ToString ()
1265 return String.Format ("TypeInfo ({0}:{1}:{2}:{3})",
1266 Type, Offset, Length, TotalLength);
1269 protected class StructInfo {
1270 public readonly Type Type;
1271 public readonly FieldInfo[] Fields;
1272 public readonly TypeInfo[] StructFields;
1273 public readonly int Count;
1274 public readonly int CountPublic;
1275 public readonly int CountNonPublic;
1276 public readonly int Length;
1277 public readonly int TotalLength;
1278 public readonly bool HasStructFields;
1280 private static Hashtable field_type_hash = new Hashtable ();
1281 private Hashtable struct_field_hash;
1282 private Hashtable field_hash;
1284 protected bool InTransit = false;
1286 // Private constructor. To save memory usage, we only need to create one instance
1287 // of this class per struct type.
1288 private StructInfo (Type type)
1292 field_type_hash.Add (type, this);
1294 if (type is TypeBuilder) {
1295 TypeContainer tc = TypeManager.LookupTypeContainer (type);
1297 ArrayList fields = null;
1301 ArrayList public_fields = new ArrayList ();
1302 ArrayList non_public_fields = new ArrayList ();
1304 if (fields != null) {
1305 foreach (FieldMember field in fields) {
1306 if ((field.ModFlags & Modifiers.STATIC) != 0)
1308 if ((field.ModFlags & Modifiers.PUBLIC) != 0)
1309 public_fields.Add (field.FieldBuilder);
1311 non_public_fields.Add (field.FieldBuilder);
1315 CountPublic = public_fields.Count;
1316 CountNonPublic = non_public_fields.Count;
1317 Count = CountPublic + CountNonPublic;
1319 Fields = new FieldInfo [Count];
1320 public_fields.CopyTo (Fields, 0);
1321 non_public_fields.CopyTo (Fields, CountPublic);
1323 FieldInfo[] public_fields = type.GetFields (
1324 BindingFlags.Instance|BindingFlags.Public);
1325 FieldInfo[] non_public_fields = type.GetFields (
1326 BindingFlags.Instance|BindingFlags.NonPublic);
1328 CountPublic = public_fields.Length;
1329 CountNonPublic = non_public_fields.Length;
1330 Count = CountPublic + CountNonPublic;
1332 Fields = new FieldInfo [Count];
1333 public_fields.CopyTo (Fields, 0);
1334 non_public_fields.CopyTo (Fields, CountPublic);
1337 struct_field_hash = new Hashtable ();
1338 field_hash = new Hashtable ();
1341 StructFields = new TypeInfo [Count];
1342 StructInfo[] sinfo = new StructInfo [Count];
1346 for (int i = 0; i < Count; i++) {
1347 FieldInfo field = (FieldInfo) Fields [i];
1349 sinfo [i] = GetStructInfo (field.FieldType);
1350 if (sinfo [i] == null)
1351 field_hash.Add (field.Name, ++Length);
1352 else if (sinfo [i].InTransit) {
1353 Report.Error (523, String.Format (
1354 "Struct member `{0}.{1}' of type `{2}' causes " +
1355 "a cycle in the structure layout",
1356 type, field.Name, sinfo [i].Type));
1364 TotalLength = Length + 1;
1365 for (int i = 0; i < Count; i++) {
1366 FieldInfo field = (FieldInfo) Fields [i];
1368 if (sinfo [i] == null)
1371 field_hash.Add (field.Name, TotalLength);
1373 HasStructFields = true;
1374 StructFields [i] = new TypeInfo (sinfo [i], TotalLength);
1375 struct_field_hash.Add (field.Name, StructFields [i]);
1376 TotalLength += sinfo [i].TotalLength;
1380 public int this [string name] {
1382 if (field_hash.Contains (name))
1383 return (int) field_hash [name];
1389 public TypeInfo GetStructField (string name)
1391 return (TypeInfo) struct_field_hash [name];
1394 public static StructInfo GetStructInfo (Type type)
1396 if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type) ||
1397 TypeManager.IsBuiltinType (type))
1400 StructInfo info = (StructInfo) field_type_hash [type];
1404 return new StructInfo (type);
1407 public static StructInfo GetStructInfo (TypeContainer tc)
1409 StructInfo info = (StructInfo) field_type_hash [tc.TypeBuilder];
1413 return new StructInfo (tc.TypeBuilder);
1419 // This is used by the flow analysis code to store information about a single local variable
1420 // or parameter. Depending on the variable's type, we need to allocate one or more elements
1421 // in the BitVector - if it's a fundamental or reference type, we just need to know whether
1422 // it has been assigned or not, but for structs, we need this information for each of its fields.
1424 public class VariableInfo {
1425 public readonly string Name;
1426 public readonly TypeInfo TypeInfo;
1429 // The bit offset of this variable in the flow vector.
1431 public readonly int Offset;
1434 // The number of bits this variable needs in the flow vector.
1435 // The first bit always specifies whether the variable as such has been assigned while
1436 // the remaining bits contain this information for each of a struct's fields.
1438 public readonly int Length;
1441 // If this is a parameter of local variable.
1443 public readonly bool IsParameter;
1445 public readonly LocalInfo LocalInfo;
1446 public readonly int ParameterIndex;
1448 readonly VariableInfo Parent;
1449 VariableInfo[] sub_info;
1451 protected VariableInfo (string name, Type type, int offset)
1454 this.Offset = offset;
1455 this.TypeInfo = TypeInfo.GetTypeInfo (type);
1457 Length = TypeInfo.TotalLength;
1462 protected VariableInfo (VariableInfo parent, TypeInfo type)
1464 this.Name = parent.Name;
1465 this.TypeInfo = type;
1466 this.Offset = parent.Offset + type.Offset;
1467 this.Parent = parent;
1468 this.Length = type.TotalLength;
1470 this.IsParameter = parent.IsParameter;
1471 this.LocalInfo = parent.LocalInfo;
1472 this.ParameterIndex = parent.ParameterIndex;
1477 protected void Initialize ()
1479 TypeInfo[] sub_fields = TypeInfo.SubStructInfo;
1480 if (sub_fields != null) {
1481 sub_info = new VariableInfo [sub_fields.Length];
1482 for (int i = 0; i < sub_fields.Length; i++) {
1483 if (sub_fields [i] != null)
1484 sub_info [i] = new VariableInfo (this, sub_fields [i]);
1487 sub_info = new VariableInfo [0];
1490 public VariableInfo (LocalInfo local_info, int offset)
1491 : this (local_info.Name, local_info.VariableType, offset)
1493 this.LocalInfo = local_info;
1494 this.IsParameter = false;
1497 public VariableInfo (string name, Type type, int param_idx, int offset)
1498 : this (name, type, offset)
1500 this.ParameterIndex = param_idx;
1501 this.IsParameter = true;
1504 public bool IsAssigned (EmitContext ec)
1506 return !ec.DoFlowAnalysis ||
1507 ec.OmitStructFlowAnalysis && TypeInfo.IsStruct ||
1508 ec.CurrentBranching.IsAssigned (this);
1511 public bool IsAssigned (EmitContext ec, Location loc)
1513 if (IsAssigned (ec))
1516 Report.Error (165, loc,
1517 "Use of unassigned local variable `" + Name + "'");
1518 ec.CurrentBranching.SetAssigned (this);
1522 public bool IsAssigned (MyBitVector vector)
1527 if (vector [Offset])
1530 for (VariableInfo parent = Parent; parent != null; parent = parent.Parent)
1531 if (vector [parent.Offset])
1534 // Return unless this is a struct.
1535 if (!TypeInfo.IsStruct)
1538 // Ok, so each field must be assigned.
1539 for (int i = 0; i < TypeInfo.Length; i++) {
1540 if (!vector [Offset + i + 1])
1544 // Ok, now check all fields which are structs.
1545 for (int i = 0; i < sub_info.Length; i++) {
1546 VariableInfo sinfo = sub_info [i];
1550 if (!sinfo.IsAssigned (vector))
1554 vector [Offset] = true;
1558 public void SetAssigned (EmitContext ec)
1560 if (ec.DoFlowAnalysis)
1561 ec.CurrentBranching.SetAssigned (this);
1564 public void SetAssigned (MyBitVector vector)
1566 vector [Offset] = true;
1569 public bool IsFieldAssigned (EmitContext ec, string name, Location loc)
1571 if (!ec.DoFlowAnalysis ||
1572 ec.OmitStructFlowAnalysis && TypeInfo.IsStruct ||
1573 ec.CurrentBranching.IsFieldAssigned (this, name))
1576 Report.Error (170, loc,
1577 "Use of possibly unassigned field `" + name + "'");
1578 ec.CurrentBranching.SetFieldAssigned (this, name);
1582 public bool IsFieldAssigned (MyBitVector vector, string field_name)
1584 int field_idx = TypeInfo.GetFieldIndex (field_name);
1589 return vector [Offset + field_idx];
1592 public void SetFieldAssigned (EmitContext ec, string name)
1594 if (ec.DoFlowAnalysis)
1595 ec.CurrentBranching.SetFieldAssigned (this, name);
1598 public void SetFieldAssigned (MyBitVector vector, string field_name)
1600 int field_idx = TypeInfo.GetFieldIndex (field_name);
1605 vector [Offset + field_idx] = true;
1608 public VariableInfo GetSubStruct (string name)
1610 TypeInfo type = TypeInfo.GetSubStruct (name);
1615 return new VariableInfo (this, type);
1618 public override string ToString ()
1620 return String.Format ("VariableInfo ({0}:{1}:{2}:{3}:{4})",
1621 Name, TypeInfo, Offset, Length, IsParameter);
1626 // This is used by the flow code to hold the `layout' of the flow vector for
1627 // all locals and all parameters (ie. we create one instance of this class for the
1628 // locals and another one for the params).
1630 public class VariableMap {
1632 // The number of variables in the map.
1634 public readonly int Count;
1637 // Total length of the flow vector for this map.
1639 public readonly int Length;
1643 public VariableMap (Parameters ip)
1645 Count = ip != null ? ip.Count : 0;
1647 // Dont bother allocating anything!
1653 for (int i = 0; i < Count; i++) {
1654 Parameter.Modifier mod = ip.ParameterModifier (i);
1656 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
1659 // Dont allocate till we find an out var.
1661 map = new VariableInfo [Count];
1663 map [i] = new VariableInfo (ip.ParameterName (i),
1664 TypeManager.GetElementType (ip.ParameterType (i)), i, Length);
1666 Length += map [i].Length;
1670 public VariableMap (LocalInfo[] locals)
1671 : this (null, locals)
1674 public VariableMap (VariableMap parent, LocalInfo[] locals)
1676 int offset = 0, start = 0;
1677 if (parent != null && parent.map != null) {
1678 offset = parent.Length;
1679 start = parent.Count;
1682 Count = locals.Length + start;
1687 map = new VariableInfo [Count];
1690 if (parent != null && parent.map != null) {
1691 parent.map.CopyTo (map, 0);
1694 for (int i = start; i < Count; i++) {
1695 LocalInfo li = locals [i-start];
1697 if (li.VariableType == null)
1700 map [i] = li.VariableInfo = new VariableInfo (li, Length);
1701 Length += map [i].Length;
1706 // Returns the VariableInfo for variable @index or null if we don't need to
1707 // compute assignment info for this variable.
1709 public VariableInfo this [int index] {
1718 public override string ToString ()
1720 return String.Format ("VariableMap ({0}:{1})", Count, Length);
1725 // This is a special bit vector which can inherit from another bit vector doing a
1726 // copy-on-write strategy. The inherited vector may have a smaller size than the
1729 public class MyBitVector {
1730 public readonly int Count;
1731 public MyBitVector InheritsFrom;
1732 public static readonly MyBitVector Empty = new MyBitVector ();
1738 InheritsFrom = null;
1742 public MyBitVector (MyBitVector InheritsFrom, int Count)
1744 if (InheritsFrom != null) {
1745 while (InheritsFrom.InheritsFrom != null)
1746 InheritsFrom = InheritsFrom.InheritsFrom;
1747 if (InheritsFrom.Count >= Count && InheritsFrom.vector == null)
1748 InheritsFrom = null;
1751 this.InheritsFrom = InheritsFrom;
1756 // Get/set bit `index' in the bit vector.
1758 public bool this [int index]
1762 throw new ArgumentOutOfRangeException ();
1764 // We're doing a "copy-on-write" strategy here; as long
1765 // as nobody writes to the array, we can use our parent's
1766 // copy instead of duplicating the vector.
1769 return vector [index];
1770 if (InheritsFrom == null)
1772 if (index < InheritsFrom.Count)
1773 return InheritsFrom [index];
1778 // Only copy the vector if we're actually modifying it.
1779 if (this [index] != value) {
1781 initialize_vector ();
1782 vector [index] = value;
1788 // Performs an `or' operation on the bit vector. The `new_vector' may have a
1789 // different size than the current one.
1791 private void Or (MyBitVector new_vector)
1793 int min = new_vector.Count;
1797 for (int i = 0; i < min; i++)
1798 this [i] |= new_vector [i];
1802 // Perfonrms an `and' operation on the bit vector. The `new_vector' may have
1803 // a different size than the current one.
1805 private void And (MyBitVector new_vector)
1807 int min = new_vector.Count;
1811 for (int i = 0; i < min; i++)
1812 this [i] &= new_vector [i];
1814 for (int i = min; i < Count; i++)
1818 public static void And (ref MyBitVector target, MyBitVector vector)
1823 target = vector.Clone ();
1825 target.And (vector);
1828 public static void Or (ref MyBitVector target, MyBitVector vector)
1839 // This does a deep copy of the bit vector.
1841 public MyBitVector Clone ()
1845 MyBitVector retval = new MyBitVector (this, Count);
1846 retval.initialize_vector ();
1850 void initialize_vector ()
1852 if (InheritsFrom == null) {
1853 vector = new BitArray (Count, true);
1857 vector = new BitArray (Count, false);
1859 int min = InheritsFrom.Count;
1863 for (int i = 0; i < min; i++)
1864 vector [i] = InheritsFrom [i];
1866 InheritsFrom = null;
1869 StringBuilder Dump (StringBuilder sb)
1872 return InheritsFrom == null ? sb.Append ("/") : InheritsFrom.Dump (sb.Append ("="));
1873 for (int i = 0; i < Count; i++)
1874 sb.Append (this [i] ? "1" : "0");
1878 public override string ToString ()
1880 return Dump (new StringBuilder ("{")).Append ("}").ToString ();