📄 anonymous.cs
字号:
//// anonymous.cs: Support for anonymous methods//// Author:// Miguel de Icaza (miguel@ximain.com)//// (C) 2003, 2004 Novell, Inc.//// TODO: Ideally, we should have the helper classes emited as a hierarchy to map// their nesting, and have the visibility set to private, instead of NestedAssembly//////using System;using System.Text;using System.Collections;using System.Reflection;using System.Reflection.Emit;namespace Mono.CSharp { public abstract class AnonymousContainer : Expression { // Used to generate unique method names. protected static int anonymous_method_count; // An array list of AnonymousMethodParameter or null public Parameters Parameters; // // The block that makes up the body for the anonymous mehtod // public ToplevelBlock Block; // // The container block for this anonymous method. // public Block ContainingBlock; // // The implicit method we create // public Method method; protected MethodInfo invoke_mb; // The emit context for the anonymous method public EmitContext aec; public InternalParameters amp; protected bool unreachable; // // The modifiers applied to the method, we aggregate them // protected int method_modifiers = Modifiers.PRIVATE; // // During the resolve stage of the anonymous method body, // we discover the actual scope where we are hosted, or // null to host the method in the same class // public ScopeInfo Scope; // // Points to our container anonymous method if its present // public AnonymousContainer ContainerAnonymousMethod; protected AnonymousContainer (Parameters parameters, ToplevelBlock container, ToplevelBlock block, Location l) { Parameters = parameters; Block = block; loc = l; // // The order is important: this setups the CaptureContext tree hierarchy. // if (container == null) { Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration"); return; } container.SetHaveAnonymousMethods (l, this); block.SetHaveAnonymousMethods (l, this); } protected AnonymousContainer (Parameters parameters, ToplevelBlock container, Location l): this (parameters, container, new ToplevelBlock (container, parameters, l), l) { } public override Expression DoResolve (EmitContext ec) { // // Set class type, set type // eclass = ExprClass.Value; // // This hack means `The type is not accessible // anywhere', we depend on special conversion // rules. // type = TypeManager.anonymous_method_type; return this; } protected abstract bool CreateMethodHost (EmitContext ec); public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope); public abstract bool IsIterator { get; } } public class AnonymousMethod : AnonymousContainer { public AnonymousMethod (Parameters parameters, ToplevelBlock container, ToplevelBlock block, Location l) : base (parameters, container, block, l) { } public override bool IsIterator { get { return false; } } public override void Emit (EmitContext ec) { // nothing, as we only exist to not do anything. } // // Creates the host for the anonymous method // protected override bool CreateMethodHost (EmitContext ec) { // // Crude hack follows: we replace the TypeBuilder during the // definition to get the method hosted in the right class // TypeBuilder current_type = ec.TypeContainer.TypeBuilder; TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null) ? current_type : Scope.ScopeTypeBuilder; if (current_type == null) throw new Exception ("The current_type is null"); if (type_host == null) throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null")); if (current_type != type_host) method_modifiers = Modifiers.INTERNAL; if (current_type == type_host && ec.IsStatic){ method_modifiers |= Modifiers.STATIC; current_type = null; } method = new Method ( (TypeContainer) ec.TypeContainer, new TypeExpression (invoke_mb.ReturnType, loc), method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc), Parameters, null); method.Block = Block; // // Swap the TypeBuilder while we define the method, then restore // if (current_type != null) ec.TypeContainer.TypeBuilder = type_host; bool res = method.Define (); if (current_type != null) ec.TypeContainer.TypeBuilder = current_type; return res; } void Error_ParameterMismatch (Type t) { Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" + "{0}' since there is a parameter mismatch", t); } // // Returns true if this anonymous method can be implicitly // converted to the delegate type `delegate_type' // public Expression Compatible (EmitContext ec, Type delegate_type, bool probe) { // // At this point its the first time we know the return type that is // needed for the anonymous method. We create the method here. // invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc); ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb); if (Parameters == null){ int i, j; // // We provide a set of inaccessible parameters // int params_idx = -1; for (i = 0; i < invoke_pd.Count; i++){ if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS) params_idx = i; } int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0); Parameter [] fixedpars = new Parameter [n]; for (i = j = 0; i < invoke_pd.Count; i++){ if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS) continue; fixedpars [j] = new Parameter ( new TypeExpression (invoke_pd.ParameterType (i), loc), "+" + j, invoke_pd.ParameterModifier (i), null, loc); j++; } Parameter variable = null; if (params_idx != -1){ variable = new Parameter ( new TypeExpression (invoke_pd.ParameterType (params_idx), loc), "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc); } Parameters = new Parameters (fixedpars, variable); } // // First, parameter types of `delegate_type' must be compatible // with the anonymous method. // amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters); if (amp.Count != invoke_pd.Count){ if (!probe){ Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments", TypeManager.CSharpName (delegate_type), amp.Count); Error_ParameterMismatch (delegate_type); } return null; } for (int i = 0; i < amp.Count; i++){ Parameter.Modifier amp_mod = amp.ParameterModifier (i); if (!probe) { if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){ Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword", i+1, amp.ModifierDesc (i)); Error_ParameterMismatch (delegate_type); return null; } if (amp_mod != invoke_pd.ParameterModifier (i)){ Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword", i+1, Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i))); Error_ParameterMismatch (delegate_type); return null; } if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){ Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'", i+1, TypeManager.CSharpName (amp.ParameterType (i)), TypeManager.CSharpName (invoke_pd.ParameterType (i))); Error_ParameterMismatch (delegate_type); return null; } } } // // If we are only probing, return ourselves // if (probe) return this; // // Second: the return type of the delegate must be compatible with // the anonymous type. Instead of doing a pass to examine the block // we satisfy the rule by setting the return type on the EmitContext // to be the delegate type return type. // //MethodBuilder builder = method_data.MethodBuilder; //ILGenerator ig = builder.GetILGenerator (); aec = new EmitContext ( ec.TypeContainer, ec.DeclSpace, loc, null, invoke_mb.ReturnType, /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) | (ec.InUnsafe ? Modifiers.UNSAFE : 0) | (ec.IsStatic ? Modifiers.STATIC : 0), /* No constructor */ false); aec.CurrentAnonymousMethod = this; ContainerAnonymousMethod = ec.CurrentAnonymousMethod; ContainingBlock = ec.CurrentBlock; if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable)) return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec); return null; } public override string ExprClassName { get { return "anonymous method"; } } public MethodBuilder GetMethodBuilder () { return method.MethodData.MethodBuilder; } public override string GetSignatureForError () { string s = TypeManager.CSharpSignature (invoke_mb); return s.Substring (0, s.IndexOf (".Invoke(")); } public bool EmitMethod (EmitContext ec) { if (!CreateMethodHost (ec)) return false; MethodBuilder builder = GetMethodBuilder (); ILGenerator ig = builder.GetILGenerator (); aec.ig = ig; Parameters.LabelParameters (aec, builder); // // Adjust based on the computed state of the // method from CreateMethodHost aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0; aec.EmitMeta (Block, amp); aec.EmitResolvedTopBlock (Block, unreachable); return true; } public override void CreateScopeType (EmitContext ec, ScopeInfo scope) { TypeBuilder container = ec.TypeContainer.TypeBuilder; string name = String.Format ("<>AnonHelp<{0}>", scope.id); scope.ScopeTypeBuilder = container.DefineNestedType ( name, TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.NestedAssembly, TypeManager.object_type, null); Type [] constructor_types = TypeManager.NoTypes; Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters; scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor ( MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.HasThis, constructor_types); InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters); TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types); ILGenerator cig = scope.ScopeConstructor.GetILGenerator (); cig.Emit (OpCodes.Ldarg_0); cig.Emit (OpCodes.Call, TypeManager.object_ctor); cig.Emit (OpCodes.Ret); } public static void Error_AddressOfCapturedVar (string name, Location loc) { Report.Error (1686, loc, "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block", name); } } // // This will emit the code for the delegate, as well delegate creation on the host // public class AnonymousDelegate : DelegateCreation { AnonymousMethod am; public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l) { type = target_type; loc = l; this.am = am; } public override Expression DoResolve (EmitContext ec) { eclass = ExprClass.Value; return this; } public override void Emit (EmitContext ec) { if (!am.EmitMethod (ec)) return; // // Now emit the delegate creation. // if ((am.method.ModFlags & Modifiers.STATIC) == 0) delegate_instance_expression = new AnonymousInstance (am); Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc); constructor_method = ((MethodGroupExpr) ml).Methods [0]; delegate_method = am.GetMethodBuilder (); base.Emit (ec); } class AnonymousInstance : Expression { AnonymousMethod am; public AnonymousInstance (AnonymousMethod am) { this.am = am; eclass = ExprClass.Value; } public override Expression DoResolve (EmitContext ec) { return this;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -