📄 statement.cs
字号:
throw new Exception ("Variables have not been initialized yet"); return param_map; } } public VariableMap LocalMap { get { if ((flags & Flags.VariablesInitialized) == 0) throw new Exception ("Variables have not been initialized yet"); return local_map; } } /// <summary> /// Emits the variable declarations and labels. /// </summary> /// <remarks> /// tc: is our typecontainer (to resolve type references) /// ig: is the code generator: /// </remarks> public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip) { bool old_unsafe = ec.InUnsafe; // If some parent block was unsafe, we remain unsafe even if this block // isn't explicitly marked as such. ec.InUnsafe |= Unsafe; // // Compute the VariableMap's. // // Unfortunately, we don't know the type when adding variables with // AddVariable(), so we need to compute this info here. // LocalInfo[] locals; if (variables != null) { foreach (LocalInfo li in variables.Values) li.Resolve (ec); locals = new LocalInfo [variables.Count]; variables.Values.CopyTo (locals, 0); } else locals = new LocalInfo [0]; if (Parent != null) local_map = new VariableMap (Parent.LocalMap, locals); else local_map = new VariableMap (locals); param_map = new VariableMap (ip); flags |= Flags.VariablesInitialized; bool old_check_state = ec.ConstantCheckState; ec.ConstantCheckState = (flags & Flags.Unchecked) == 0; // // Process this block variables // if (variables != null){ foreach (DictionaryEntry de in variables){ string name = (string) de.Key; LocalInfo vi = (LocalInfo) de.Value; if (vi.VariableType == null) continue; Type variable_type = vi.VariableType; if (variable_type.IsPointer){ // // Am not really convinced that this test is required (Microsoft does it) // but the fact is that you would not be able to use the pointer variable // *anyways* // if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type), vi.Location)) continue; } if (constants == null) continue; Expression cv = (Expression) constants [name]; if (cv == null) continue; // Don't let 'const int Foo = Foo;' succeed. // Removing the name from 'constants' ensures that we get a LocalVariableReference below, // which in turn causes the 'must be constant' error to be triggered. constants.Remove (name); ec.CurrentBlock = this; Expression e = cv.Resolve (ec); if (e == null) continue; Constant ce = e as Constant; if (ce == null){ Const.Error_ExpressionMustBeConstant (vi.Location, name); continue; } e = ce.ToType (variable_type, vi.Location); if (e == null) continue; constants.Add (name, e); } } ec.ConstantCheckState = old_check_state; // // Now, handle the children // if (children != null){ foreach (Block b in children) b.ResolveMeta (toplevel, ec, ip); } ec.InUnsafe = old_unsafe; } // // Emits the local variable declarations for a block // public void EmitMeta (EmitContext ec) { ILGenerator ig = ec.ig; if (variables != null){ bool have_captured_vars = ec.HaveCapturedVariables (); foreach (DictionaryEntry de in variables){ LocalInfo vi = (LocalInfo) de.Value; if (have_captured_vars && ec.IsCaptured (vi)) continue; if (vi.Pinned) // // This is needed to compile on both .NET 1.x and .NET 2.x // the later introduced `DeclareLocal (Type t, bool pinned)' // vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType); else if (!vi.IsThis) vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); } } if (temporary_variables != null) { AnonymousContainer am = ec.CurrentAnonymousMethod; TypeBuilder scope = null; if ((am != null) && am.IsIterator) { scope = am.Scope.ScopeTypeBuilder; if (scope == null) throw new InternalErrorException (); } foreach (LocalInfo vi in temporary_variables) { if (scope != null) { if (vi.FieldBuilder == null) vi.FieldBuilder = scope.DefineField ( vi.Name, vi.VariableType, FieldAttributes.Assembly); } else vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); } } if (children != null){ foreach (Block b in children) b.EmitMeta (ec); } } void UsageWarning (FlowBranching.UsageVector vector) { string name; if ((variables != null) && (RootContext.WarningLevel >= 3)) { foreach (DictionaryEntry de in variables){ LocalInfo vi = (LocalInfo) de.Value; if (vi.Used) continue; name = (string) de.Key; if (vector.IsAssigned (vi.VariableInfo)){ Report.Warning (219, vi.Location, "The variable `{0}' is assigned but its value is never used", name); } else { Report.Warning (168, vi.Location, "The variable `{0}' is declared but never used", name); } } } } bool unreachable_shown; bool unreachable; private void CheckPossibleMistakenEmptyStatement (Statement s) { Statement body; // Some statements are wrapped by a Block. Since // others' internal could be changed, here I treat // them as possibly wrapped by Block equally. Block b = s as Block; if (b != null && b.statements.Count == 1) s = (Statement) b.statements [0]; if (s is Lock) body = ((Lock) s).Statement; else if (s is For) body = ((For) s).Statement; else if (s is Foreach) body = ((Foreach) s).Statement; else if (s is While) body = ((While) s).Statement; else if (s is Using) body = ((Using) s).Statement; else if (s is Fixed) body = ((Fixed) s).Statement; else return; if (body == null || body is EmptyStatement) Report.Warning (642, 3, s.loc, "Possible mistaken empty statement"); } public override bool Resolve (EmitContext ec) { Block prev_block = ec.CurrentBlock; bool ok = true; int errors = Report.Errors; ec.CurrentBlock = this; ec.StartFlowBranching (this); Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching); int statement_count = statements.Count; for (int ix = 0; ix < statement_count; ix++){ Statement s = (Statement) statements [ix]; // Check possible empty statement (CS0642) if (RootContext.WarningLevel >= 3 && ix + 1 < statement_count && statements [ix + 1] is Block) CheckPossibleMistakenEmptyStatement (s); // // Warn if we detect unreachable code. // if (unreachable) { if (s is Block) ((Block) s).unreachable = true; if (!unreachable_shown && (RootContext.WarningLevel >= 2)) { Report.Warning ( 162, s.loc, "Unreachable code detected"); unreachable_shown = true; } } // // Note that we're not using ResolveUnreachable() for unreachable // statements here. ResolveUnreachable() creates a temporary // flow branching and kills it afterwards. This leads to problems // if you have two unreachable statements where the first one // assigns a variable and the second one tries to access it. // if (!s.Resolve (ec)) { ok = false; statements [ix] = EmptyStatement.Value; continue; } if (unreachable && !(s is LabeledStatement) && !(s is Block)) statements [ix] = EmptyStatement.Value; num_statements = ix + 1; if (s is LabeledStatement) unreachable = false; else unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable; } Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching, statement_count, num_statements); FlowBranching.UsageVector vector = ec.DoEndFlowBranching (); ec.CurrentBlock = prev_block; // If we're a non-static `struct' constructor which doesn't have an // initializer, then we must initialize all of the struct's fields. if ((flags & Flags.IsToplevel) != 0 && !Toplevel.IsThisAssigned (ec) && vector.Reachability.Throws != FlowBranching.FlowReturns.Always) ok = false; if ((labels != null) && (RootContext.WarningLevel >= 2)) { foreach (LabeledStatement label in labels.Values) if (!label.HasBeenReferenced) Report.Warning (164, label.loc, "This label has not been referenced"); } Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector); if ((vector.Reachability.Returns == FlowBranching.FlowReturns.Always) || (vector.Reachability.Throws == FlowBranching.FlowReturns.Always) || (vector.Reachability.Reachable == FlowBranching.FlowReturns.Never)) flags |= Flags.HasRet; if (ok && (errors == Report.Errors)) { if (RootContext.WarningLevel >= 3) UsageWarning (vector); } return ok; } public override bool ResolveUnreachable (EmitContext ec, bool warn) { unreachable_shown = true; unreachable = true; if (warn && (RootContext.WarningLevel >= 2)) Report.Warning (162, loc, "Unreachable code detected"); ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc); bool ok = Resolve (ec); ec.KillFlowBranching (); return ok; } protected override void DoEmit (EmitContext ec) { for (int ix = 0; ix < num_statements; ix++){ Statement s = (Statement) statements [ix]; // Check whether we are the last statement in a // top-level block. if (((Parent == null) || Implicit) && (ix+1 == num_statements) && !(s is Block)) ec.IsLastStatement = true; else ec.IsLastStatement = false; s.Emit (ec); } } public override void Emit (EmitContext ec) { Block prev_block = ec.CurrentBlock; ec.CurrentBlock = this; bool emit_debug_info = (CodeGen.SymbolWriter != null); bool is_lexical_block = !Implicit && (Parent != null); if (emit_debug_info) { if (is_lexical_block) ec.BeginScope (); if (variables != null) { foreach (DictionaryEntry de in variables) { string name = (string) de.Key; LocalInfo vi = (LocalInfo) de.Value; if (vi.LocalBuilder == null) continue; ec.DefineLocalVariable (name, vi.LocalBuilder); } } } ec.Mark (StartLocation, true); DoEmit (ec); ec.Mark (EndLocation, true); if (emit_debug_info && is_lexical_block) ec.EndScope (); ec.CurrentBlock = prev_block; } // // Returns true if we ar ea child of `b'. // public bool IsChildOf (Block b) { Block current = this; do { if (current.Parent == b) return true; current = current.Parent; } while (current != null); return false; } public override string ToString () { return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation); } } // // A toplevel block contains extra information, the split is done // only to separate information that would otherwise bloat the more // lightweight Block. // // In particular, this was introduced when the support for Anonymous // Methods was implemented. // public class ToplevelBlock : Block { // // Pointer to the host of this anonymous method, or null // if we are the topmost block // ToplevelBlock container; CaptureContext capture_context; FlowBranching top_level_branching; Hashtable capture_contexts; ArrayList children; public bool HasVarargs { get { return (flags & Flags.HasVarargs) != 0; } set { flags |= Flags.HasVarargs; } } // // The parameters for the block. // public readonly Parameters Parameters; public void RegisterCaptureContext (CaptureContext cc) { if (capture_contexts == null) capture_contexts = new Hashtable (); capture_contexts [cc] = cc; } public void CompleteContexts () { if (capture_contexts == null) return; foreach (CaptureContext cc in capture_contexts.Keys){ cc.AdjustScopes (); } } public CaptureContext ToplevelBlockCaptureContext { get { return capture_context; } } public ToplevelBlock Container { get { return container; } } protected void AddChild (ToplevelBlock block) { if (children == null) children = new ArrayList (); children.Add (block); } // // Parent is only used by anonymous blocks to link back to their // parents // public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) : this (container, (Flags) 0, parameters, start) { } public ToplevelBlock (Parameters parameters, Location start) : this (null, (Flags) 0, parameters, start) { } public ToplevelBlock (Flags flags, Parameters parameters, Location start) : this (null, flags, parameters, start) { } public ToplevelBlock (ToplevelBlock container, Flags flags, Parameters parameters, Location start) : base (null, flags | Flags.IsToplevel, start, Location.Null) { Parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters; this.container = container; if (container != null) container.AddChild (this); } public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc) { } public void SetHaveAnonymousMethods (Location loc, AnonymousContainer host) { if (capture_context == null) capture_context = new CaptureContext (this, loc, host); } public CaptureContext CaptureContext { get { return capture_context; } } public FlowBranching TopLevelBranching { get { return top_level_branching; } } // // This is used if anonymous methods are used inside an iterator // (see 2test-22.cs for an example). // // The AnonymousMethod is created while parsing - at a time when we don't // know yet that we're inside an iterator, so it's `Container' is initially // null. Later on, when resolving the iterator, we need to move the // anonymous method into that iterator. // public void ReParent (ToplevelBlock new_parent, AnonymousContainer new_host) { foreach (ToplevelBlock block in children) { if (block.CaptureContext == null) continue; block.container = new_parent; block.CaptureContext.ReParent (new_parent, new_host); } } // // Returns a `ParameterReference' for the given name, or null if there // is no such parameter // public ParameterReference GetParameterReference (string name, Location loc) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -