📄 statement.cs
字号:
Parameter par; int idx; for (ToplevelBlock t = this; t != null; t = t.Container) { Parameters pars = t.Parameters; par = pars.GetParameterByName (name, out idx); if (par != null) return new ParameterReference (pars, this, idx, name, loc); } return null; } // // Whether the parameter named `name' is local to this block, // or false, if the parameter belongs to an encompassing block. // public bool IsLocalParameter (string name) { return Parameters.GetParameterByName (name) != null; } // // Whether the `name' is a parameter reference // public bool IsParameterReference (string name) { for (ToplevelBlock t = this; t != null; t = t.Container) { if (t.IsLocalParameter (name)) return true; } return false; } LocalInfo this_variable = null; // <summary> // Returns the "this" instance variable of this block. // See AddThisVariable() for more information. // </summary> public LocalInfo ThisVariable { get { return this_variable; } } // <summary> // This is used by non-static `struct' constructors which do not have an // initializer - in this case, the constructor must initialize all of the // struct's fields. To do this, we add a "this" variable and use the flow // analysis code to ensure that it's been fully initialized before control // leaves the constructor. // </summary> public LocalInfo AddThisVariable (TypeContainer tc, Location l) { if (this_variable == null) { this_variable = new LocalInfo (tc, this, l); this_variable.Used = true; this_variable.IsThis = true; Variables.Add ("this", this_variable); } return this_variable; } public bool IsThisAssigned (EmitContext ec) { return this_variable == null || this_variable.IsThisAssigned (ec, loc); } public bool ResolveMeta (EmitContext ec, InternalParameters ip) { int errors = Report.Errors; if (top_level_branching != null) return true; ResolveMeta (this, ec, ip); top_level_branching = ec.StartFlowBranching (this); return Report.Errors == errors; } } public class SwitchLabel { Expression label; object converted; Location loc; Label il_label; bool il_label_set; Label il_label_code; bool il_label_code_set; public static readonly object NullStringCase = new object (); // // if expr == null, then it is the default case. // public SwitchLabel (Expression expr, Location l) { label = expr; loc = l; } public Expression Label { get { return label; } } public object Converted { get { return converted; } } public Label GetILLabel (EmitContext ec) { if (!il_label_set){ il_label = ec.ig.DefineLabel (); il_label_set = true; } return il_label; } public Label GetILLabelCode (EmitContext ec) { if (!il_label_code_set){ il_label_code = ec.ig.DefineLabel (); il_label_code_set = true; } return il_label_code; } // // Resolves the expression, reduces it to a literal if possible // and then converts it to the requested type. // public bool ResolveAndReduce (EmitContext ec, Type required_type) { Expression e = label.Resolve (ec); if (e == null) return false; Constant c = e as Constant; if (c == null){ Report.Error (150, loc, "A constant value is expected"); return false; } if (required_type == TypeManager.string_type && c.GetValue () == null) { converted = NullStringCase; return true; } c = c.ToType (required_type, loc); if (c == null) return false; converted = c.GetValue (); return true; } public void Erorr_AlreadyOccurs () { string label; if (converted == null) label = "default"; else if (converted == NullStringCase) label = "null"; else label = converted.ToString (); Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label); } } public class SwitchSection { // An array of SwitchLabels. public readonly ArrayList Labels; public readonly Block Block; public SwitchSection (ArrayList labels, Block block) { Labels = labels; Block = block; } } public class Switch : Statement { public readonly ArrayList Sections; public Expression Expr; /// <summary> /// Maps constants whose type type SwitchType to their SwitchLabels. /// </summary> public IDictionary Elements; /// <summary> /// The governing switch type /// </summary> public Type SwitchType; // // Computed // Label default_target; Expression new_expr; bool is_constant; SwitchSection constant_section; SwitchSection default_section; // // The types allowed to be implicitly cast from // on the governing type // static Type [] allowed_types; public Switch (Expression e, ArrayList sects, Location l) { Expr = e; Sections = sects; loc = l; } public bool GotDefault { get { return default_section != null; } } public Label DefaultTarget { get { return default_target; } } // // Determines the governing type for a switch. The returned // expression might be the expression from the switch, or an // expression that includes any potential conversions to the // integral types or to string. // Expression SwitchGoverningType (EmitContext ec, Type t) { if (t == TypeManager.byte_type || t == TypeManager.sbyte_type || t == TypeManager.ushort_type || t == TypeManager.short_type || t == TypeManager.uint32_type || t == TypeManager.int32_type || t == TypeManager.uint64_type || t == TypeManager.int64_type || t == TypeManager.char_type || t == TypeManager.string_type || t == TypeManager.bool_type || t.IsSubclassOf (TypeManager.enum_type)) return Expr; if (allowed_types == null){ allowed_types = new Type [] { TypeManager.sbyte_type, TypeManager.byte_type, TypeManager.short_type, TypeManager.ushort_type, TypeManager.int32_type, TypeManager.uint32_type, TypeManager.int64_type, TypeManager.uint64_type, TypeManager.char_type, TypeManager.string_type, TypeManager.bool_type }; } // // Try to find a *user* defined implicit conversion. // // If there is no implicit conversion, or if there are multiple // conversions, we have to report an error // Expression converted = null; foreach (Type tt in allowed_types){ Expression e; e = Convert.ImplicitUserConversion (ec, Expr, tt, loc); if (e == null) continue; // // Ignore over-worked ImplicitUserConversions that do // an implicit conversion in addition to the user conversion. // if (!(e is UserCast)) continue; if (converted != null){ Report.ExtraInformation ( loc, String.Format ("reason: more than one conversion to an integral type exist for type {0}", TypeManager.CSharpName (Expr.Type))); return null; } converted = e; } return converted; } // // Performs the basic sanity checks on the switch statement // (looks for duplicate keys and non-constant expressions). // // It also returns a hashtable with the keys that we will later // use to compute the switch tables // bool CheckSwitch (EmitContext ec) { bool error = false; Elements = Sections.Count > 10 ? (IDictionary)new Hashtable () : (IDictionary)new ListDictionary (); foreach (SwitchSection ss in Sections){ foreach (SwitchLabel sl in ss.Labels){ if (sl.Label == null){ if (default_section != null){ sl.Erorr_AlreadyOccurs (); error = true; } default_section = ss; continue; } if (!sl.ResolveAndReduce (ec, SwitchType)){ error = true; continue; } object key = sl.Converted; try { Elements.Add (key, sl); } catch (ArgumentException) { sl.Erorr_AlreadyOccurs (); error = true; } } } return !error; } void EmitObjectInteger (ILGenerator ig, object k) { if (k is int) IntConstant.EmitInt (ig, (int) k); else if (k is Constant) { EmitObjectInteger (ig, ((Constant) k).GetValue ()); } else if (k is uint) IntConstant.EmitInt (ig, unchecked ((int) (uint) k)); else if (k is long) { if ((long) k >= int.MinValue && (long) k <= int.MaxValue) { IntConstant.EmitInt (ig, (int) (long) k); ig.Emit (OpCodes.Conv_I8); } else LongConstant.EmitLong (ig, (long) k); } else if (k is ulong) { if ((ulong) k < (1L<<32)) { IntConstant.EmitInt (ig, (int) (long) k); ig.Emit (OpCodes.Conv_U8); } else { LongConstant.EmitLong (ig, unchecked ((long) (ulong) k)); } } else if (k is char) IntConstant.EmitInt (ig, (int) ((char) k)); else if (k is sbyte) IntConstant.EmitInt (ig, (int) ((sbyte) k)); else if (k is byte) IntConstant.EmitInt (ig, (int) ((byte) k)); else if (k is short) IntConstant.EmitInt (ig, (int) ((short) k)); else if (k is ushort) IntConstant.EmitInt (ig, (int) ((ushort) k)); else if (k is bool) IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0); else throw new Exception ("Unhandled case"); } // structure used to hold blocks of keys while calculating table switch class KeyBlock : IComparable { public KeyBlock (long _nFirst) { nFirst = nLast = _nFirst; } public long nFirst; public long nLast; public ArrayList rgKeys = null; // how many items are in the bucket public int Size = 1; public int Length { get { return (int) (nLast - nFirst + 1); } } public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast) { return kbLast.nLast - kbFirst.nFirst + 1; } public int CompareTo (object obj) { KeyBlock kb = (KeyBlock) obj; int nLength = Length; int nLengthOther = kb.Length; if (nLengthOther == nLength) return (int) (kb.nFirst - nFirst); return nLength - nLengthOther; } } /// <summary> /// This method emits code for a lookup-based switch statement (non-string) /// Basically it groups the cases into blocks that are at least half full, /// and then spits out individual lookup opcodes for each block. /// It emits the longest blocks first, and short blocks are just /// handled with direct compares. /// </summary> /// <param name="ec"></param> /// <param name="val"></param> /// <returns></returns> void TableSwitchEmit (EmitContext ec, LocalBuilder val) { int cElements = Elements.Count; object [] rgKeys = new object [cElements]; Elements.Keys.CopyTo (rgKeys, 0); Array.Sort (rgKeys); // initialize the block list with one element per key ArrayList rgKeyBlocks = new ArrayList (); foreach (object key in rgKeys) rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key))); KeyBlock kbCurr; // iteratively merge the blocks while they are at least half full // there's probably a really cool way to do this with a tree... while (rgKeyBlocks.Count > 1) { ArrayList rgKeyBlocksNew = new ArrayList (); kbCurr = (KeyBlock) rgKeyBlocks [0]; for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++) { KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb]; if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb)) { // merge blocks kbCurr.nLast = kb.nLast; kbCurr.Size += kb.Size; } else { // start a new block rgKeyBlocksNew.Add (kbCurr); kbCurr = kb; } } rgKeyBlocksNew.Add (kbCurr); if (rgKeyBlocks.Count == rgKeyBlocksNew.Count) break; rgKeyBlocks = rgKeyBlocksNew; } // initialize the key lists foreach (KeyBlock kb in rgKeyBlocks) kb.rgKeys = new ArrayList (); // fill the key lists int iBlockCurr = 0; if (rgKeyBlocks.Count > 0) { kbCurr = (KeyBlock) rgKeyBlocks [0]; foreach (object key in rgKeys) { bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : System.Convert.ToInt64 (key) > kbCurr.nLast; if (fNextBlock) kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr]; kbCurr.rgKeys.Add (key); } } // sort the blocks so we can tackle the largest ones first rgKeyBlocks.Sort (); // okay now we can start... ILGenerator ig = ec.ig; Label lblEnd = ig.DefineLabel (); // at the end ;-) Label lblDefault = ig.DefineLabel (); Type typeKeys = null; if (rgKeys.Length > 0) typeKeys = rgKeys [0].GetType (); // used for conversions Type compare_type; if (TypeManager.IsEnumType (SwitchType)) compare_type = TypeManager.EnumToUnderlying (SwitchType); else compare_type = SwitchType; for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock) { KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]); lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel (); if (kb.Length <= 2) { foreach (object key in kb.rgKeys) { ig.Emit (OpCodes.Ldloc, val); EmitObjectInteger (ig, key); SwitchLabel sl = (SwitchLabel) Elements [key]; ig.Emit (OpCodes.Beq, sl.GetILLabel (ec)); } } else { // TODO: if all the keys in the block are the same and there are // no gaps/defaults then just use a range-check. if (compare_type == TypeManager.int64_type || compare_type == TypeManager.uint64_type) { // TODO: optimize constant/I4 cases // check block range (could be > 2^31) ig.Emit (OpCodes.Ldloc, val); EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys)); ig.Emit (OpCodes.Blt, lblDefault); ig.Emit (OpCodes.Ldloc, val);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -