+
+ public UsageVector (UsageVector parent)
+ : this (parent, parent.CountParameters, parent.CountLocals)
+ { }
+
+ // <summary>
+ // This does a deep copy of the usage vector.
+ // </summary>
+ public UsageVector Clone ()
+ {
+ UsageVector retval = new UsageVector (null, CountParameters, CountLocals);
+
+ retval.locals = locals.Clone ();
+ if (parameters != null)
+ retval.parameters = parameters.Clone ();
+ retval.real_returns = real_returns;
+ retval.real_breaks = real_breaks;
+
+ return retval;
+ }
+
+ //
+ // State of parameter `number'.
+ //
+ public bool this [int number]
+ {
+ get {
+ if (number == -1)
+ return true;
+ else if (number == 0)
+ throw new ArgumentException ();
+
+ return parameters [number - 1];
+ }
+
+ set {
+ if (number == -1)
+ return;
+ else if (number == 0)
+ throw new ArgumentException ();
+
+ parameters [number - 1] = value;
+ }
+ }
+
+ //
+ // State of the local variable `vi'.
+ // If the local variable is a struct, use a non-zero `field_idx'
+ // to check an individual field in it.
+ //
+ public bool this [VariableInfo vi, int field_idx]
+ {
+ get {
+ if (vi.Number == -1)
+ return true;
+ else if (vi.Number == 0)
+ throw new ArgumentException ();
+
+ return locals [vi.Number + field_idx - 1];
+ }
+
+ set {
+ if (vi.Number == -1)
+ return;
+ else if (vi.Number == 0)
+ throw new ArgumentException ();
+
+ locals [vi.Number + field_idx - 1] = value;
+ }
+ }
+
+ // <summary>
+ // Specifies when the current block returns.
+ // If this is FlowReturns.UNREACHABLE, then control can never reach the
+ // end of the method (so that we don't need to emit a return statement).
+ // The same applies for FlowReturns.EXCEPTION, but in this case the return
+ // value will never be used.
+ // </summary>
+ public FlowReturns Returns {
+ get {
+ return real_returns;
+ }
+
+ set {
+ real_returns = value;
+ }
+ }
+
+ // <summary>
+ // Specifies whether control may return to our containing block
+ // before reaching the end of this block. This happens if there
+ // is a break/continue/goto/return in it.
+ // This can also be used to find out whether the statement immediately
+ // following the current block may be reached or not.
+ // </summary>
+ public FlowReturns Breaks {
+ get {
+ return real_breaks;
+ }
+
+ set {
+ real_breaks = value;
+ }
+ }
+
+ public bool AlwaysBreaks {
+ get {
+ return (Breaks == FlowReturns.ALWAYS) ||
+ (Breaks == FlowReturns.EXCEPTION) ||
+ (Breaks == FlowReturns.UNREACHABLE);
+ }
+ }
+
+ public bool MayBreak {
+ get {
+ return Breaks != FlowReturns.NEVER;
+ }
+ }
+
+ public bool AlwaysReturns {
+ get {
+ return (Returns == FlowReturns.ALWAYS) ||
+ (Returns == FlowReturns.EXCEPTION);
+ }
+ }
+
+ public bool MayReturn {
+ get {
+ return (Returns == FlowReturns.SOMETIMES) ||
+ (Returns == FlowReturns.ALWAYS);
+ }
+ }
+
+ // <summary>
+ // Merge a child branching.
+ // </summary>
+ public FlowReturns MergeChildren (FlowBranching branching, ICollection children)
+ {
+ MyBitVector new_locals = null;
+ MyBitVector new_params = null;
+
+ FlowReturns new_returns = FlowReturns.NEVER;
+ FlowReturns new_breaks = FlowReturns.NEVER;
+ bool new_returns_set = false, new_breaks_set = false;
+
+ Report.Debug (2, "MERGING CHILDREN", branching, branching.Type,
+ this, children.Count);
+
+ foreach (UsageVector child in children) {
+ Report.Debug (2, " MERGING CHILD", child, child.is_finally);
+
+ if (!child.is_finally) {
+ if (child.Breaks != FlowReturns.UNREACHABLE) {
+ // If Returns is already set, perform an
+ // `And' operation on it, otherwise just set just.
+ if (!new_returns_set) {
+ new_returns = child.Returns;
+ new_returns_set = true;
+ } else
+ new_returns = AndFlowReturns (
+ new_returns, child.Returns);
+ }
+
+ // If Breaks is already set, perform an
+ // `And' operation on it, otherwise just set just.
+ if (!new_breaks_set) {
+ new_breaks = child.Breaks;
+ new_breaks_set = true;
+ } else
+ new_breaks = AndFlowReturns (
+ new_breaks, child.Breaks);
+ }
+
+ // Ignore unreachable children.
+ if (child.Returns == FlowReturns.UNREACHABLE)
+ continue;
+
+ // A local variable is initialized after a flow branching if it
+ // has been initialized in all its branches which do neither
+ // always return or always throw an exception.
+ //
+ // If a branch may return, but does not always return, then we
+ // can treat it like a never-returning branch here: control will
+ // only reach the code position after the branching if we did not
+ // return here.
+ //
+ // It's important to distinguish between always and sometimes
+ // returning branches here:
+ //
+ // 1 int a;
+ // 2 if (something) {
+ // 3 return;
+ // 4 a = 5;
+ // 5 }
+ // 6 Console.WriteLine (a);
+ //
+ // The if block in lines 3-4 always returns, so we must not look
+ // at the initialization of `a' in line 4 - thus it'll still be
+ // uninitialized in line 6.
+ //
+ // On the other hand, the following is allowed:
+ //
+ // 1 int a;
+ // 2 if (something)
+ // 3 a = 5;
+ // 4 else
+ // 5 return;
+ // 6 Console.WriteLine (a);
+ //
+ // Here, `a' is initialized in line 3 and we must not look at
+ // line 5 since it always returns.
+ //
+ if (child.is_finally) {
+ if (new_locals == null)
+ new_locals = locals.Clone ();
+ new_locals.Or (child.locals);
+
+ if (parameters != null) {
+ if (new_params == null)
+ new_params = parameters.Clone ();
+ new_params.Or (child.parameters);
+ }
+
+ } else {
+ if (!child.AlwaysReturns && !child.AlwaysBreaks) {
+ if (new_locals != null)
+ new_locals.And (child.locals);
+ else {
+ new_locals = locals.Clone ();
+ new_locals.Or (child.locals);
+ }
+ } else if (children.Count == 1) {
+ new_locals = locals.Clone ();
+ new_locals.Or (child.locals);
+ }
+
+ // An `out' parameter must be assigned in all branches which do
+ // not always throw an exception.
+ if (parameters != null) {
+ if (child.Breaks != FlowReturns.EXCEPTION) {
+ if (new_params != null)
+ new_params.And (child.parameters);
+ else {
+ new_params = parameters.Clone ();
+ new_params.Or (child.parameters);
+ }
+ } else if (children.Count == 1) {
+ new_params = parameters.Clone ();
+ new_params.Or (child.parameters);
+ }
+ }
+ }
+ }
+
+ Returns = new_returns;
+ if ((branching.Type == FlowBranchingType.BLOCK) ||
+ (branching.Type == FlowBranchingType.EXCEPTION) ||
+ (new_breaks == FlowReturns.UNREACHABLE) ||
+ (new_breaks == FlowReturns.EXCEPTION))
+ Breaks = new_breaks;
+ else if (branching.Type == FlowBranchingType.SWITCH_SECTION)
+ Breaks = new_returns;
+ else if (branching.Type == FlowBranchingType.SWITCH){
+ if (new_breaks == FlowReturns.ALWAYS)
+ Breaks = FlowReturns.ALWAYS;
+ }
+
+ //
+ // We've now either reached the point after the branching or we will
+ // never get there since we always return or always throw an exception.
+ //
+ // If we can reach the point after the branching, mark all locals and
+ // parameters as initialized which have been initialized in all branches
+ // we need to look at (see above).
+ //
+
+ if (((new_breaks != FlowReturns.ALWAYS) &&
+ (new_breaks != FlowReturns.EXCEPTION) &&
+ (new_breaks != FlowReturns.UNREACHABLE)) ||
+ (children.Count == 1)) {
+ if (new_locals != null)
+ locals.Or (new_locals);
+
+ if (new_params != null)
+ parameters.Or (new_params);
+ }
+
+ Report.Debug (2, "MERGING CHILDREN DONE", branching.Type,
+ new_params, new_locals, new_returns, new_breaks,
+ branching.Infinite, branching.MayLeaveLoop, this);
+
+ if (branching.Type == FlowBranchingType.SWITCH_SECTION) {
+ if ((new_breaks != FlowReturns.ALWAYS) &&
+ (new_breaks != FlowReturns.EXCEPTION) &&
+ (new_breaks != FlowReturns.UNREACHABLE))
+ Report.Error (163, branching.Location,
+ "Control cannot fall through from one " +
+ "case label to another");
+ }
+
+ if (branching.Infinite && !branching.MayLeaveLoop) {
+ Report.Debug (1, "INFINITE", new_returns, new_breaks,
+ Returns, Breaks, this);
+
+ // We're actually infinite.
+ if (new_returns == FlowReturns.NEVER) {
+ Breaks = FlowReturns.UNREACHABLE;
+ return FlowReturns.UNREACHABLE;
+ }
+
+ // If we're an infinite loop and do not break, the code after
+ // the loop can never be reached. However, if we may return
+ // from the loop, then we do always return (or stay in the loop
+ // forever).
+ if ((new_returns == FlowReturns.SOMETIMES) ||
+ (new_returns == FlowReturns.ALWAYS)) {
+ Returns = FlowReturns.ALWAYS;
+ return FlowReturns.ALWAYS;
+ }
+ }
+
+ return new_returns;
+ }
+
+ // <summary>
+ // Tells control flow analysis that the current code position may be reached with
+ // a forward jump from any of the origins listed in `origin_vectors' which is a
+ // list of UsageVectors.
+ //
+ // This is used when resolving forward gotos - in the following example, the
+ // variable `a' is uninitialized in line 8 becase this line may be reached via
+ // the goto in line 4:
+ //
+ // 1 int a;
+ //
+ // 3 if (something)
+ // 4 goto World;
+ //
+ // 6 a = 5;
+ //
+ // 7 World:
+ // 8 Console.WriteLine (a);
+ //
+ // </summary>
+ public void MergeJumpOrigins (ICollection origin_vectors)
+ {
+ Report.Debug (1, "MERGING JUMP ORIGIN", this);
+
+ real_breaks = FlowReturns.NEVER;
+ real_returns = FlowReturns.NEVER;
+
+ foreach (UsageVector vector in origin_vectors) {
+ Report.Debug (1, " MERGING JUMP ORIGIN", vector);
+
+ locals.And (vector.locals);
+ if (parameters != null)
+ parameters.And (vector.parameters);
+ Breaks = AndFlowReturns (Breaks, vector.Breaks);
+ Returns = AndFlowReturns (Returns, vector.Returns);
+ }
+
+ Report.Debug (1, "MERGING JUMP ORIGIN DONE", this);
+ }
+
+ // <summary>
+ // This is used at the beginning of a finally block if there were
+ // any return statements in the try block or one of the catch blocks.
+ // </summary>
+ public void MergeFinallyOrigins (ICollection finally_vectors)
+ {
+ Report.Debug (1, "MERGING FINALLY ORIGIN", this);
+
+ real_breaks = FlowReturns.NEVER;
+
+ foreach (UsageVector vector in finally_vectors) {
+ Report.Debug (1, " MERGING FINALLY ORIGIN", vector);
+
+ if (parameters != null)
+ parameters.And (vector.parameters);
+ Breaks = AndFlowReturns (Breaks, vector.Breaks);
+ }
+
+ is_finally = true;
+
+ Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this);
+ }
+
+ public void CheckOutParameters (FlowBranching branching)
+ {
+ if (parameters != null)
+ branching.CheckOutParameters (parameters, branching.Location);
+ }
+
+ // <summary>
+ // Performs an `or' operation on the locals and the parameters.
+ // </summary>
+ public void Or (UsageVector new_vector)
+ {
+ locals.Or (new_vector.locals);
+ if (parameters != null)
+ parameters.Or (new_vector.parameters);
+ }
+
+ // <summary>
+ // Performs an `and' operation on the locals.
+ // </summary>
+ public void AndLocals (UsageVector new_vector)
+ {
+ locals.And (new_vector.locals);
+ }
+
+ // <summary>
+ // Returns a deep copy of the parameters.
+ // </summary>
+ public MyBitVector Parameters {
+ get {
+ if (parameters != null)
+ return parameters.Clone ();
+ else
+ return null;
+ }
+ }
+
+ // <summary>
+ // Returns a deep copy of the locals.
+ // </summary>
+ public MyBitVector Locals {
+ get {
+ return locals.Clone ();
+ }
+ }
+
+ //
+ // Debugging stuff.
+ //
+
+ public override string ToString ()
+ {
+ StringBuilder sb = new StringBuilder ();
+
+ sb.Append ("Vector (");
+ sb.Append (id);
+ sb.Append (",");
+ sb.Append (Returns);
+ sb.Append (",");
+ sb.Append (Breaks);
+ if (parameters != null) {
+ sb.Append (" - ");
+ sb.Append (parameters);
+ }
+ sb.Append (" - ");
+ sb.Append (locals);
+ sb.Append (")");
+
+ return sb.ToString ();
+ }
+ }
+
+ FlowBranching (FlowBranchingType type, Location loc)
+ {
+ this.Siblings = new ArrayList ();
+ this.Block = null;
+ this.Location = loc;
+ this.Type = type;
+ id = ++next_id;
+ }
+
+ // <summary>
+ // Creates a new flow branching for `block'.
+ // This is used from Block.Resolve to create the top-level branching of
+ // the block.
+ // </summary>
+ public FlowBranching (Block block, InternalParameters ip, Location loc)
+ : this (FlowBranchingType.BLOCK, loc)
+ {
+ Block = block;
+ Parent = null;
+
+ int count = (ip != null) ? ip.Count : 0;
+
+ param_info = ip;
+ param_map = new int [count];
+ struct_params = new MyStructInfo [count];
+ num_params = 0;
+
+ for (int i = 0; i < count; i++) {
+ Parameter.Modifier mod = param_info.ParameterModifier (i);
+
+ if ((mod & Parameter.Modifier.OUT) == 0)
+ continue;
+
+ param_map [i] = ++num_params;
+
+ Type param_type = param_info.ParameterType (i);
+
+ struct_params [i] = MyStructInfo.GetStructInfo (param_type);
+ if (struct_params [i] != null)
+ num_params += struct_params [i].Count;
+ }
+
+ Siblings = new ArrayList ();
+ Siblings.Add (new UsageVector (null, num_params, block.CountVariables));
+ }
+
+ // <summary>
+ // Creates a new flow branching which is contained in `parent'.
+ // You should only pass non-null for the `block' argument if this block
+ // introduces any new variables - in this case, we need to create a new
+ // usage vector with a different size than our parent's one.
+ // </summary>
+ public FlowBranching (FlowBranching parent, FlowBranchingType type,
+ Block block, Location loc)
+ : this (type, loc)
+ {
+ Parent = parent;
+ Block = block;
+
+ if (parent != null) {
+ param_info = parent.param_info;
+ param_map = parent.param_map;
+ struct_params = parent.struct_params;
+ num_params = parent.num_params;
+ }
+
+ UsageVector vector;
+ if (Block != null)
+ vector = new UsageVector (parent.CurrentUsageVector, num_params,
+ Block.CountVariables);
+ else
+ vector = new UsageVector (Parent.CurrentUsageVector);
+
+ Siblings.Add (vector);
+
+ switch (Type) {
+ case FlowBranchingType.EXCEPTION:
+ finally_vectors = new ArrayList ();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // <summary>
+ // Returns the branching's current usage vector.
+ // </summary>
+ public UsageVector CurrentUsageVector
+ {
+ get {
+ return (UsageVector) Siblings [Siblings.Count - 1];
+ }
+ }
+
+ // <summary>
+ // Creates a sibling of the current usage vector.
+ // </summary>
+ public void CreateSibling ()
+ {
+ Siblings.Add (new UsageVector (Parent.CurrentUsageVector));
+
+ Report.Debug (1, "CREATED SIBLING", CurrentUsageVector);
+ }
+
+ // <summary>
+ // Creates a sibling for a `finally' block.
+ // </summary>
+ public void CreateSiblingForFinally ()
+ {
+ if (Type != FlowBranchingType.EXCEPTION)
+ throw new NotSupportedException ();
+
+ CreateSibling ();
+
+ CurrentUsageVector.MergeFinallyOrigins (finally_vectors);
+ }
+
+ // <summary>
+ // Check whether all `out' parameters have been assigned.
+ // </summary>
+ public void CheckOutParameters (MyBitVector parameters, Location loc)
+ {
+ if (InTryBlock ())
+ return;
+
+ for (int i = 0; i < param_map.Length; i++) {
+ int index = param_map [i];
+
+ if (index == 0)
+ continue;
+
+ if (parameters [index - 1])
+ continue;
+
+ // If it's a struct, we must ensure that all its fields have
+ // been assigned. If the struct has any non-public fields, this
+ // can only be done by assigning the whole struct.
+
+ MyStructInfo struct_info = struct_params [index - 1];
+ if ((struct_info == null) || struct_info.HasNonPublicFields) {
+ Report.Error (
+ 177, loc, "The out parameter `" +
+ param_info.ParameterName (i) + "' must be " +
+ "assigned before control leave the current method.");
+ param_map [i] = 0;
+ continue;
+ }
+
+
+ for (int j = 0; j < struct_info.Count; j++) {
+ if (!parameters [index + j]) {
+ Report.Error (
+ 177, loc, "The out parameter `" +
+ param_info.ParameterName (i) + "' must be " +
+ "assigned before control leave the current method.");
+ param_map [i] = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ // <summary>
+ // Merge a child branching.
+ // </summary>
+ public FlowReturns MergeChild (FlowBranching child)
+ {
+ FlowReturns returns = CurrentUsageVector.MergeChildren (child, child.Siblings);
+
+ if (child.Type != FlowBranchingType.LOOP_BLOCK)
+ MayLeaveLoop |= child.MayLeaveLoop;
+ else
+ MayLeaveLoop = false;
+
+ return returns;
+ }
+
+ // <summary>
+ // Does the toplevel merging.
+ // </summary>
+ public FlowReturns MergeTopBlock ()
+ {
+ if ((Type != FlowBranchingType.BLOCK) || (Block == null))
+ throw new NotSupportedException ();
+
+ UsageVector vector = new UsageVector (null, num_params, Block.CountVariables);
+
+ Report.Debug (1, "MERGING TOP BLOCK", Location, vector);
+
+ vector.MergeChildren (this, Siblings);
+
+ Siblings.Clear ();
+ Siblings.Add (vector);
+
+ Report.Debug (1, "MERGING TOP BLOCK DONE", Location, vector);
+
+ if (vector.Breaks != FlowReturns.EXCEPTION) {
+ if (!vector.AlwaysBreaks)
+ CheckOutParameters (CurrentUsageVector.Parameters, Location);
+ return vector.AlwaysBreaks ? FlowReturns.ALWAYS : vector.Returns;
+ } else
+ return FlowReturns.EXCEPTION;
+ }
+
+ public bool InTryBlock ()
+ {
+ if (finally_vectors != null)
+ return true;
+ else if (Parent != null)
+ return Parent.InTryBlock ();
+ else
+ return false;
+ }
+
+ public void AddFinallyVector (UsageVector vector)
+ {
+ if (finally_vectors != null) {
+ finally_vectors.Add (vector.Clone ());
+ return;
+ }
+
+ if (Parent != null)
+ Parent.AddFinallyVector (vector);
+ else
+ throw new NotSupportedException ();
+ }
+
+ public bool IsVariableAssigned (VariableInfo vi)
+ {
+ if (CurrentUsageVector.AlwaysBreaks)
+ return true;
+ else
+ return CurrentUsageVector [vi, 0];
+ }
+
+ public bool IsVariableAssigned (VariableInfo vi, int field_idx)
+ {
+ if (CurrentUsageVector.AlwaysBreaks)
+ return true;
+ else
+ return CurrentUsageVector [vi, field_idx];
+ }
+
+ public void SetVariableAssigned (VariableInfo vi)
+ {
+ if (CurrentUsageVector.AlwaysBreaks)
+ return;
+
+ CurrentUsageVector [vi, 0] = true;
+ }
+
+ public void SetVariableAssigned (VariableInfo vi, int field_idx)
+ {
+ if (CurrentUsageVector.AlwaysBreaks)
+ return;
+
+ CurrentUsageVector [vi, field_idx] = true;
+ }
+
+ public bool IsParameterAssigned (int number)
+ {
+ int index = param_map [number];
+
+ if (index == 0)
+ return true;
+
+ if (CurrentUsageVector [index])
+ return true;
+
+ // Parameter is not assigned, so check whether it's a struct.
+ // If it's either not a struct or a struct which non-public
+ // fields, return false.
+ MyStructInfo struct_info = struct_params [number];
+ if ((struct_info == null) || struct_info.HasNonPublicFields)
+ return false;
+
+ // Ok, so each field must be assigned.
+ for (int i = 0; i < struct_info.Count; i++)
+ if (!CurrentUsageVector [index + i])
+ return false;
+
+ return true;
+ }
+
+ public bool IsParameterAssigned (int number, string field_name)
+ {
+ int index = param_map [number];
+
+ if (index == 0)
+ return true;
+
+ MyStructInfo info = (MyStructInfo) struct_params [number];
+ if (info == null)
+ return true;
+
+ int field_idx = info [field_name];
+
+ return CurrentUsageVector [index + field_idx];
+ }
+
+ public void SetParameterAssigned (int number)
+ {
+ if (param_map [number] == 0)
+ return;
+
+ if (!CurrentUsageVector.AlwaysBreaks)
+ CurrentUsageVector [param_map [number]] = true;
+ }
+
+ public void SetParameterAssigned (int number, string field_name)
+ {
+ int index = param_map [number];
+
+ if (index == 0)
+ return;
+
+ MyStructInfo info = (MyStructInfo) struct_params [number];
+ if (info == null)
+ return;
+
+ int field_idx = info [field_name];
+
+ if (!CurrentUsageVector.AlwaysBreaks)
+ CurrentUsageVector [index + field_idx] = true;
+ }
+
+ public bool IsReachable ()
+ {
+ bool reachable;
+
+ switch (Type) {
+ case FlowBranchingType.SWITCH_SECTION:
+ // The code following a switch block is reachable unless the switch
+ // block always returns.
+ reachable = !CurrentUsageVector.AlwaysReturns;
+ break;
+
+ case FlowBranchingType.LOOP_BLOCK:
+ // The code following a loop is reachable unless the loop always
+ // returns or it's an infinite loop without any `break's in it.
+ reachable = !CurrentUsageVector.AlwaysReturns &&
+ (CurrentUsageVector.Breaks != FlowReturns.UNREACHABLE);
+ break;
+
+ default:
+ // The code following a block or exception is reachable unless the
+ // block either always returns or always breaks.
+ reachable = !CurrentUsageVector.AlwaysBreaks &&
+ !CurrentUsageVector.AlwaysReturns;
+ break;
+ }
+
+ Report.Debug (1, "REACHABLE", Type, CurrentUsageVector.Returns,
+ CurrentUsageVector.Breaks, CurrentUsageVector, reachable);
+
+ return reachable;
+ }
+
+ public override string ToString ()
+ {
+ StringBuilder sb = new StringBuilder ("FlowBranching (");
+
+ sb.Append (id);
+ sb.Append (",");
+ sb.Append (Type);
+ if (Block != null) {
+ sb.Append (" - ");
+ sb.Append (Block.ID);
+ sb.Append (" - ");
+ sb.Append (Block.StartLocation);
+ }
+ sb.Append (" - ");
+ sb.Append (Siblings.Count);
+ sb.Append (" - ");
+ sb.Append (CurrentUsageVector);
+ sb.Append (")");
+ return sb.ToString ();
+ }
+ }
+
+ public class MyStructInfo {
+ public readonly Type Type;
+ public readonly FieldInfo[] Fields;
+ public readonly FieldInfo[] NonPublicFields;
+ public readonly int Count;
+ public readonly int CountNonPublic;
+ public readonly bool HasNonPublicFields;
+
+ private static Hashtable field_type_hash = new Hashtable ();
+ private Hashtable field_hash;
+
+ // Private constructor. To save memory usage, we only need to create one instance
+ // of this class per struct type.
+ private MyStructInfo (Type type)
+ {
+ this.Type = type;
+
+ if (type is TypeBuilder) {
+ TypeContainer tc = TypeManager.LookupTypeContainer (type);
+
+ ArrayList fields = tc.Fields;
+ if (fields != null) {
+ foreach (Field field in fields) {
+ if ((field.ModFlags & Modifiers.STATIC) != 0)
+ continue;
+ if ((field.ModFlags & Modifiers.PUBLIC) != 0)
+ ++Count;
+ else
+ ++CountNonPublic;
+ }
+ }
+
+ Fields = new FieldInfo [Count];
+ NonPublicFields = new FieldInfo [CountNonPublic];
+
+ Count = CountNonPublic = 0;
+ if (fields != null) {
+ foreach (Field field in fields) {
+ if ((field.ModFlags & Modifiers.STATIC) != 0)
+ continue;
+ if ((field.ModFlags & Modifiers.PUBLIC) != 0)
+ Fields [Count++] = field.FieldBuilder;
+ else
+ NonPublicFields [CountNonPublic++] =
+ field.FieldBuilder;
+ }
+ }
+
+ } else {
+ Fields = type.GetFields (BindingFlags.Instance|BindingFlags.Public);
+ Count = Fields.Length;
+
+ NonPublicFields = type.GetFields (BindingFlags.Instance|BindingFlags.NonPublic);
+ CountNonPublic = NonPublicFields.Length;
+ }
+
+ Count += NonPublicFields.Length;
+
+ int number = 0;
+ field_hash = new Hashtable ();
+ foreach (FieldInfo field in Fields)
+ field_hash.Add (field.Name, ++number);
+
+ if (NonPublicFields.Length != 0)
+ HasNonPublicFields = true;
+
+ foreach (FieldInfo field in NonPublicFields)
+ field_hash.Add (field.Name, ++number);
+ }
+
+ public int this [string name] {
+ get {
+ if (field_hash.Contains (name))
+ return (int) field_hash [name];
+ else
+ return 0;
+ }
+ }
+
+ public FieldInfo this [int index] {
+ get {
+ if (index >= Fields.Length)
+ return NonPublicFields [index - Fields.Length];
+ else
+ return Fields [index];
+ }
+ }
+
+ public static MyStructInfo GetStructInfo (Type type)
+ {
+ if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type))
+ return null;
+
+ if (!(type is TypeBuilder) && TypeManager.IsBuiltinType (type))
+ return null;
+
+ MyStructInfo info = (MyStructInfo) field_type_hash [type];
+ if (info != null)
+ return info;
+
+ info = new MyStructInfo (type);
+ field_type_hash.Add (type, info);
+ return info;
+ }
+
+ public static MyStructInfo GetStructInfo (TypeContainer tc)
+ {
+ MyStructInfo info = (MyStructInfo) field_type_hash [tc.TypeBuilder];
+ if (info != null)
+ return info;
+
+ info = new MyStructInfo (tc.TypeBuilder);
+ field_type_hash.Add (tc.TypeBuilder, info);
+ return info;
+ }
+ }
+
+ public class VariableInfo : IVariable {
+ public Expression Type;
+ public LocalBuilder LocalBuilder;
+ public Type VariableType;
+ public readonly string Name;
+ public readonly Location Location;
+ public readonly int Block;
+
+ public int Number;
+
+ public bool Used;
+ public bool Assigned;
+ public bool ReadOnly;
+
+ public VariableInfo (Expression type, string name, int block, Location l)
+ {
+ Type = type;
+ Name = name;
+ Block = block;
+ LocalBuilder = null;
+ Location = l;
+ }
+
+ public VariableInfo (TypeContainer tc, int block, Location l)
+ {
+ VariableType = tc.TypeBuilder;
+ struct_info = MyStructInfo.GetStructInfo (tc);
+ Block = block;
+ LocalBuilder = null;
+ Location = l;
+ }
+
+ MyStructInfo struct_info;
+ public MyStructInfo StructInfo {
+ get {
+ return struct_info;
+ }
+ }
+
+ public bool IsAssigned (EmitContext ec, Location loc)
+ {
+ if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this))
+ return true;
+
+ MyStructInfo struct_info = StructInfo;
+ if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) {
+ Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'");
+ ec.CurrentBranching.SetVariableAssigned (this);
+ return false;
+ }
+
+ int count = struct_info.Count;
+
+ for (int i = 0; i < count; i++) {
+ if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) {
+ if (Name != null) {
+ Report.Error (165, loc,
+ "Use of unassigned local variable `" +
+ Name + "'");
+ ec.CurrentBranching.SetVariableAssigned (this);
+ return false;
+ }
+
+ FieldInfo field = struct_info [i];
+ Report.Error (171, loc,
+ "Field `" + TypeManager.CSharpName (VariableType) +
+ "." + field.Name + "' must be fully initialized " +
+ "before control leaves the constructor");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public bool IsFieldAssigned (EmitContext ec, string name, Location loc)
+ {
+ if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this) ||
+ (struct_info == null))
+ return true;
+
+ int field_idx = StructInfo [name];
+ if (field_idx == 0)
+ return true;
+
+ if (!ec.CurrentBranching.IsVariableAssigned (this, field_idx)) {
+ Report.Error (170, loc,
+ "Use of possibly unassigned field `" + name + "'");
+ ec.CurrentBranching.SetVariableAssigned (this, field_idx);
+ return false;
+ }
+
+ return true;
+ }
+
+ public void SetAssigned (EmitContext ec)
+ {
+ if (ec.DoFlowAnalysis)
+ ec.CurrentBranching.SetVariableAssigned (this);
+ }
+
+ public void SetFieldAssigned (EmitContext ec, string name)
+ {
+ if (ec.DoFlowAnalysis && (struct_info != null))
+ ec.CurrentBranching.SetVariableAssigned (this, StructInfo [name]);
+ }
+
+ public bool Resolve (DeclSpace decl)
+ {
+ if (struct_info != null)
+ return true;
+
+ if (VariableType == null)
+ VariableType = decl.ResolveType (Type, false, Location);
+
+ if (VariableType == null)
+ return false;
+
+ struct_info = MyStructInfo.GetStructInfo (VariableType);
+
+ return true;
+ }
+
+ public void MakePinned ()
+ {
+ TypeManager.MakePinned (LocalBuilder);
+ }
+
+ public override string ToString ()
+ {
+ return "VariableInfo (" + Number + "," + Type + "," + Location + ")";