// // interface.cs: Interface handler // // Author: Miguel de Icaza (miguel@gnu.org) // // Licensed under the terms of the GNU GPL // // (C) 2001 Ximian, Inc (http://www.ximian.com) // #define CACHE using System.Collections; using System; using System.IO; using System.Reflection; using System.Reflection.Emit; namespace Mono.CSharp { /// /// Interfaces /// public class Interface : DeclSpace, IMemberContainer { const MethodAttributes interface_method_attributes = MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; const MethodAttributes property_attributes = MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName | MethodAttributes.Virtual; ArrayList bases; ArrayList defined_method; ArrayList defined_indexer; ArrayList defined_events; ArrayList defined_properties; ArrayList method_builders; ArrayList property_builders; ArrayList event_builders; Attributes OptAttributes; public string IndexerName; IMemberContainer parent_container; MemberCache member_cache; bool members_defined; // These will happen after the semantic analysis // Hashtable defined_indexers; // Hashtable defined_methods; /// /// Modifiers allowed in a class declaration /// public const int AllowedModifiers = Modifiers.NEW | Modifiers.PUBLIC | Modifiers.PROTECTED | Modifiers.INTERNAL | Modifiers.UNSAFE | Modifiers.PRIVATE; public Interface (TypeContainer parent, string name, int mod, Attributes attrs, Location l) : base (parent, name, l) { ModFlags = Modifiers.Check (AllowedModifiers, mod, Modifiers.PRIVATE, l); OptAttributes = attrs; method_builders = new ArrayList (); property_builders = new ArrayList (); event_builders = new ArrayList (); } public AdditionResult AddMethod (InterfaceMethod imethod) { string name = imethod.Name; Object value = defined_names [name]; if (value != null){ if (!(value is InterfaceMethod)) return AdditionResult.NameExists; } if (defined_method == null) defined_method = new ArrayList (); defined_method.Add (imethod); if (value == null) DefineName (name, imethod); return AdditionResult.Success; } public AdditionResult AddProperty (InterfaceProperty iprop) { AdditionResult res; string name = iprop.Name; if ((res = IsValid (name)) != AdditionResult.Success) return res; DefineName (name, iprop); if (defined_properties == null) defined_properties = new ArrayList (); defined_properties.Add (iprop); return AdditionResult.Success; } public AdditionResult AddEvent (InterfaceEvent ievent) { string name = ievent.Name; AdditionResult res; if ((res = IsValid (name)) != AdditionResult.Success) return res; DefineName (name, ievent); if (defined_events == null) defined_events = new ArrayList (); defined_events.Add (ievent); return AdditionResult.Success; } public bool AddIndexer (InterfaceIndexer iindexer) { if (defined_indexer == null) defined_indexer = new ArrayList (); defined_indexer.Add (iindexer); return true; } public ArrayList InterfaceMethods { get { return defined_method; } } public ArrayList InterfaceProperties { get { return defined_properties; } } public ArrayList InterfaceEvents { get { return defined_events; } } public ArrayList InterfaceIndexers { get { return defined_indexer; } } public ArrayList Bases { get { return bases; } set { bases = value; } } public virtual TypeAttributes InterfaceAttr { get { TypeAttributes x = TypeAttributes.Interface | TypeAttributes.Abstract; if (IsTopLevel == false) { if ((ModFlags & Modifiers.PROTECTED) != 0 && (ModFlags & Modifiers.INTERNAL) != 0) x |= TypeAttributes.NestedFamORAssem; else if ((ModFlags & Modifiers.PROTECTED) != 0) x |= TypeAttributes.NestedFamily; else if ((ModFlags & Modifiers.INTERNAL) != 0) x |= TypeAttributes.NestedAssembly; else if ((ModFlags & Modifiers.PUBLIC) != 0) x |= TypeAttributes.NestedPublic; else x |= TypeAttributes.NestedPrivate; } else { if ((ModFlags & Modifiers.PUBLIC) != 0) x |= TypeAttributes.Public; else if ((ModFlags & Modifiers.PRIVATE) != 0) x |= TypeAttributes.NotPublic; } if ((ModFlags & Modifiers.ABSTRACT) != 0) x |= TypeAttributes.Abstract; if ((ModFlags & Modifiers.SEALED) != 0) x |= TypeAttributes.Sealed; return x; } } void Error111 (InterfaceMemberBase ib) { Report.Error ( 111, "Interface `" + Name + "' already contains a definition with the " + "same return value and parameter types for member `" + ib.Name + "'"); } bool RegisterMethod (MethodBase mb, InternalParameters ip, Type [] types) { if (!TypeManager.RegisterMethod (mb, ip, types)) return false; method_builders.Add (mb); return true; } // // This might trigger a definition of the methods. This happens only // with Attributes, as Attribute classes are processed before interfaces. // Ideally, we should make everything just define recursively in terms // of its dependencies. // public MethodInfo [] GetMethods (TypeContainer container) { int n = 0; if (!members_defined){ if (DefineMembers (container)) n = method_builders.Count; } else n = method_builders.Count; MethodInfo [] mi = new MethodInfo [n]; method_builders.CopyTo (mi, 0); return mi; } // Hack around System.Reflection as found everywhere else public override MemberList FindMembers (MemberTypes mt, BindingFlags bf, MemberFilter filter, object criteria) { ArrayList members = new ArrayList (); if ((mt & MemberTypes.Method) != 0) { foreach (MethodBuilder mb in method_builders) if (filter (mb, criteria)) members.Add (mb); } if ((mt & MemberTypes.Property) != 0) { foreach (PropertyBuilder pb in property_builders) if (filter (pb, criteria)) members.Add (pb); } if ((mt & MemberTypes.Event) != 0) { foreach (MyEventBuilder eb in event_builders) if (filter (eb, criteria)) members.Add (eb); } if (((bf & BindingFlags.DeclaredOnly) == 0) && (TypeBuilder.BaseType != null)) { MemberList parent_mi; parent_mi = TypeContainer.FindMembers ( TypeBuilder.BaseType, mt, bf, filter, criteria); members.AddRange (parent_mi); } return new MemberList (members); } public override MemberCache MemberCache { get { return member_cache; } } // // Populates the methods in the interface // void PopulateMethod (TypeContainer parent, DeclSpace decl_space, InterfaceMethod im) { Type return_type = this.ResolveType (im.ReturnType, false, im.Location); Type [] arg_types = im.ParameterTypes (this); MethodBuilder mb; Parameter [] p; int i; if (return_type == null) return; if (return_type.IsPointer && !UnsafeOK (this)) return; if (arg_types == null) return; foreach (Type t in arg_types){ if (t == null) return; if (t.IsPointer && !UnsafeOK (this)) return; } // // Create the method // mb = TypeBuilder.DefineMethod ( im.Name, interface_method_attributes, return_type, arg_types); InternalParameters ip = new InternalParameters (arg_types, im.Parameters); if (!RegisterMethod (mb, ip, arg_types)) { Error111 (im); return; } // // Define each type attribute (in/out/ref) and // the argument names. // p = im.Parameters.FixedParameters; if (p != null){ for (i = 0; i < p.Length; i++) mb.DefineParameter (i + 1, p [i].Attributes, p [i].Name); if (i != arg_types.Length) Console.WriteLine ("Implement the type definition for params"); } EmitContext ec = new EmitContext (parent, decl_space, Location, null, return_type, ModFlags, false); if (im.OptAttributes != null) Attribute.ApplyAttributes (ec, mb, im, im.OptAttributes, Location); } // // Populates the properties in the interface // void PopulateProperty (TypeContainer parent, DeclSpace decl_space, InterfaceProperty ip) { PropertyBuilder pb; MethodBuilder get = null, set = null; ip.Type = this.ResolveTypeExpr (ip.Type, false, ip.Location); Type prop_type = ip.Type.Type; Type [] setter_args = new Type [1]; if (prop_type == null) return; if (prop_type.IsPointer && !UnsafeOK (this)) return; setter_args [0] = prop_type; // // FIXME: properties are missing the following // flags: hidebysig newslot specialname // pb = TypeBuilder.DefineProperty ( ip.Name, PropertyAttributes.None, prop_type, null); if (ip.HasGet){ get = TypeBuilder.DefineMethod ( "get_" + ip.Name, property_attributes , prop_type, null); // // HACK because System.Reflection.Emit is lame // Type [] null_types = null; InternalParameters inp = new InternalParameters (null_types, Parameters.EmptyReadOnlyParameters); if (!RegisterMethod (get, inp, null)) { Error111 (ip); return; } pb.SetGetMethod (get); } if (ip.HasSet){ setter_args [0] = prop_type; set = TypeBuilder.DefineMethod ( "set_" + ip.Name, property_attributes, TypeManager.void_type, setter_args); set.DefineParameter (1, ParameterAttributes.None, "value"); pb.SetSetMethod (set); // // HACK because System.Reflection.Emit is lame // Parameter [] parms = new Parameter [1]; parms [0] = new Parameter (ip.Type, "value", Parameter.Modifier.NONE, null); InternalParameters ipp = new InternalParameters ( this, new Parameters (parms, null, Location.Null)); if (!RegisterMethod (set, ipp, setter_args)) { Error111 (ip); return; } } EmitContext ec = new EmitContext (parent, decl_space, Location, null, null, ModFlags, false); if (ip.OptAttributes != null) Attribute.ApplyAttributes (ec, pb, ip, ip.OptAttributes, Location); TypeManager.RegisterProperty (pb, get, set); property_builders.Add (pb); } // // Populates the events in the interface // void PopulateEvent (TypeContainer parent, DeclSpace decl_space, InterfaceEvent ie) { // // FIXME: We need to do this after delegates have been // declared or we declare them recursively. // MyEventBuilder eb; MethodBuilder add = null, remove = null; ie.Type = this.ResolveTypeExpr (ie.Type, false, ie.Location); Type event_type = ie.Type.Type; if (event_type == null) return; if (event_type.IsPointer && !UnsafeOK (this)) return; Type [] parameters = new Type [1]; parameters [0] = event_type; eb = new MyEventBuilder (TypeBuilder, ie.Name, EventAttributes.None, event_type); // // Now define the accessors // string add_name = "add_" + ie.Name; add = TypeBuilder.DefineMethod ( add_name, property_attributes, null, parameters); add.DefineParameter (1, ParameterAttributes.None, "value"); eb.SetAddOnMethod (add); string remove_name = "remove_" + ie.Name; remove = TypeBuilder.DefineMethod ( remove_name, property_attributes, null, parameters); remove.DefineParameter (1, ParameterAttributes.None, "value"); eb.SetRemoveOnMethod (remove); Parameter [] parms = new Parameter [1]; parms [0] = new Parameter (ie.Type, "value", Parameter.Modifier.NONE, null); InternalParameters ip = new InternalParameters ( this, new Parameters (parms, null, Location.Null)); if (!RegisterMethod (add, ip, parameters)) { Error111 (ie); return; } if (!RegisterMethod (remove, ip, parameters)) { Error111 (ie); return; } EmitContext ec = new EmitContext (parent, decl_space, Location, null, null, ModFlags, false); if (ie.OptAttributes != null) Attribute.ApplyAttributes (ec, eb, ie, ie.OptAttributes, Location); TypeManager.RegisterEvent (eb, add, remove); event_builders.Add (eb); } // // Populates the indexers in the interface // void PopulateIndexer (TypeContainer parent, DeclSpace decl_space, InterfaceIndexer ii) { PropertyBuilder pb; ii.Type = this.ResolveTypeExpr (ii.Type, false, ii.Location); Type prop_type = ii.Type.Type; Type [] arg_types = ii.ParameterTypes (this); Type [] value_arg_types; if (prop_type == null) return; if (prop_type.IsPointer && !UnsafeOK (this)) return; // // Sets up the extra invisible `value' argument for setters. // if (arg_types != null){ int count = arg_types.Length; value_arg_types = new Type [count + 1]; arg_types.CopyTo (value_arg_types, 0); value_arg_types [count] = prop_type; foreach (Type t in arg_types){ if (t.IsPointer && !UnsafeOK (this)) return; } } else { value_arg_types = new Type [1]; value_arg_types [1] = prop_type; } EmitContext ec = new EmitContext (parent, decl_space, Location, null, null, ModFlags, false); IndexerName = Attribute.ScanForIndexerName (ec, ii.OptAttributes); if (IndexerName == null) IndexerName = "Item"; pb = TypeBuilder.DefineProperty ( IndexerName, PropertyAttributes.None, prop_type, arg_types); MethodBuilder set_item = null, get_item = null; if (ii.HasGet){ Parameter [] p = ii.Parameters.FixedParameters; get_item = TypeBuilder.DefineMethod ( "get_" + IndexerName, property_attributes, prop_type, arg_types); pb.SetGetMethod (get_item); // // HACK because System.Reflection.Emit is lame // InternalParameters ip = new InternalParameters ( arg_types, ii.Parameters); if (!RegisterMethod (get_item, ip, arg_types)) { Error111 (ii); return; } if (p != null){ for (int i = 0; i < p.Length; i++) get_item.DefineParameter ( i + 1, p [i].Attributes, p [i].Name); } } if (ii.HasSet){ Parameter [] p = ii.Parameters.FixedParameters; Parameter [] pv; int i = 0; pv = new Parameter [p.Length + 1]; p.CopyTo (pv, 0); pv [p.Length] = new Parameter (ii.Type, "value", Parameter.Modifier.NONE, null); Parameters value_params = new Parameters (pv, null, Location.Null); value_params.GetParameterInfo (decl_space); set_item = TypeBuilder.DefineMethod ( "set_" + IndexerName, property_attributes, TypeManager.void_type, value_arg_types); pb.SetSetMethod (set_item); // // HACK because System.Reflection.Emit is lame // InternalParameters ip = new InternalParameters ( value_arg_types, value_params); if (!RegisterMethod (set_item, ip, value_arg_types)) { Error111 (ii); return; } if (p != null){ for (; i < p.Length; i++) set_item.DefineParameter ( i + 1, p [i].Attributes, p [i].Name); } set_item.DefineParameter (i + 1, ParameterAttributes.None, "value"); } if (ii.OptAttributes != null) Attribute.ApplyAttributes (ec, pb, ii, ii.OptAttributes, Location); property_builders.Add (pb); } /// /// Performs the semantic analysis for all the interface members /// that were declared /// bool SemanticAnalysis () { Hashtable methods = new Hashtable (); if (defined_method != null){ foreach (InterfaceMethod im in defined_method){ string sig = im.GetSignature (this); // // If there was an undefined Type on the signatures // if (sig == null) continue; if (methods [sig] != null){ Error111 (im); return false; } } } // // FIXME: Here I should check i // return true; } Type GetInterfaceTypeByName (string name) { Type t = FindType (Location, name); if (t == null) { Report.Error (246, Location, "The type or namespace `" + name + "' could not be found"); return null; } if (t.IsInterface) return t; string cause; if (t.IsValueType) cause = "is a struct"; else if (t.IsClass) cause = "is a class"; else cause = "Should not happen."; Report.Error (527, Location, "`"+name+"' " + cause + ", need an interface instead"); return null; } // // Returns the list of interfaces that this interface implements // Or null if it does not implement any interface. // // Sets the error boolean accoringly. // Type [] GetInterfaceBases (out bool error) { Type [] tbases; int i; error = false; if (Bases == null) return null; tbases = new Type [Bases.Count]; i = 0; foreach (string name in Bases){ Type t; t = GetInterfaceTypeByName (name); if (t == null){ error = true; return null; } if (!Parent.AsAccessible (t, ModFlags)) Report.Error (61, Location, "Inconsistent accessibility: base interface `" + TypeManager.CSharpName (t) + "' is less " + "accessible than interface `" + Name + "'"); tbases [i++] = t; } return TypeManager.ExpandInterfaces (tbases); } // // // Defines the Interface in the appropriate ModuleBuilder or TypeBuilder // // // TODO: // Rework the way we recurse, because for recursive // definitions of interfaces (A:B and B:A) we report the // error twice, rather than once. public override TypeBuilder DefineType () { Type [] ifaces; bool error; if (TypeBuilder != null) return TypeBuilder; if (InTransit) return null; InTransit = true; ifaces = GetInterfaceBases (out error); if (error) return null; if (IsTopLevel) { ModuleBuilder builder = CodeGen.ModuleBuilder; TypeBuilder = builder.DefineType ( Name, InterfaceAttr, (Type)null, // Parent Type ifaces); RootContext.RegisterOrder (this); } else { TypeBuilder builder = Parent.TypeBuilder; TypeBuilder = builder.DefineNestedType ( Basename, InterfaceAttr, (Type) null, //parent type ifaces); TypeContainer tc = TypeManager.LookupTypeContainer (builder); tc.RegisterOrder (this); } TypeManager.AddUserInterface (Name, TypeBuilder, this, ifaces); InTransit = false; return TypeBuilder; } // // Defines the indexers, and also verifies that the IndexerNameAttribute in the // interface is consistent. Either it is `Item' or it is the name defined by all the // indexers with the `IndexerName' attribute. // // Turns out that the IndexerNameAttribute is applied to each indexer, // but it is never emitted, instead a DefaultName attribute is attached // to the interface // void DefineIndexers (TypeContainer parent) { string interface_indexer_name = null; foreach (InterfaceIndexer ii in defined_indexer){ PopulateIndexer (parent, this, ii); if (interface_indexer_name == null){ interface_indexer_name = IndexerName; continue; } if (IndexerName == interface_indexer_name) continue; Report.Error ( 668, "Two indexers have different names, " + " you should use the same name for all your indexers"); } if (interface_indexer_name == null) interface_indexer_name = "Item"; IndexerName = interface_indexer_name; } /// /// Performs semantic analysis, and then generates the IL interfaces /// public override bool DefineMembers (TypeContainer parent) { if (!SemanticAnalysis ()) return false; if (defined_method != null){ foreach (InterfaceMethod im in defined_method) PopulateMethod (parent, this, im); } if (defined_properties != null){ foreach (InterfaceProperty ip in defined_properties) PopulateProperty (parent, this, ip); } if (defined_events != null) foreach (InterfaceEvent ie in defined_events) PopulateEvent (parent, this, ie); if (defined_indexer != null) { DefineIndexers (parent); CustomAttributeBuilder cb = EmitDefaultMemberAttr ( parent, IndexerName, ModFlags, Location); if (cb != null) TypeBuilder.SetCustomAttribute (cb); } #if CACHE if (TypeBuilder.BaseType != null) parent_container = TypeManager.LookupMemberContainer (TypeBuilder.BaseType); member_cache = new MemberCache (this); #endif members_defined = true; return true; } /// /// Applies all the attributes. /// public override bool Define (TypeContainer parent) { if (OptAttributes != null) { EmitContext ec = new EmitContext (parent, this, Location, null, null, ModFlags, false); Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location); } return true; } public static CustomAttributeBuilder EmitDefaultMemberAttr (TypeContainer parent, string name, int flags, Location loc) { EmitContext ec = new EmitContext (parent, loc, null, null, flags); Expression ml = Expression.MemberLookup (ec, TypeManager.default_member_type, ".ctor", MemberTypes.Constructor, BindingFlags.Public | BindingFlags.Instance, Location.Null); if (!(ml is MethodGroupExpr)) { Console.WriteLine ("Internal error !!!!"); return null; } MethodGroupExpr mg = (MethodGroupExpr) ml; MethodBase constructor = mg.Methods [0]; string [] vals = { name }; CustomAttributeBuilder cb = null; try { cb = new CustomAttributeBuilder ((ConstructorInfo) constructor, vals); } catch { Report.Warning (-100, "Can not set the indexer default member attribute"); } return cb; } // // IMemberContainer // string IMemberContainer.Name { get { return Name; } } Type IMemberContainer.Type { get { return TypeBuilder; } } IMemberContainer IMemberContainer.Parent { get { return parent_container; } } MemberCache IMemberContainer.MemberCache { get { return member_cache; } } bool IMemberContainer.IsInterface { get { return true; } } MemberList IMemberContainer.GetMembers (MemberTypes mt, BindingFlags bf) { // Interfaces only contain instance members. if ((bf & BindingFlags.Instance) == 0) return MemberList.Empty; if ((bf & BindingFlags.Public) == 0) return MemberList.Empty; ArrayList members = new ArrayList (); if ((mt & MemberTypes.Method) != 0) members.AddRange (method_builders); if ((mt & MemberTypes.Property) != 0) members.AddRange (property_builders); if ((mt & MemberTypes.Event) != 0) members.AddRange (event_builders); return new MemberList (members); } } public class InterfaceMemberBase { public readonly string Name; public readonly bool IsNew; public Attributes OptAttributes; public InterfaceMemberBase (string name, bool is_new, Attributes attrs) { Name = name; IsNew = is_new; OptAttributes = attrs; } } public class InterfaceProperty : InterfaceMemberBase { public readonly bool HasSet; public readonly bool HasGet; public readonly Location Location; public Expression Type; public InterfaceProperty (Expression type, string name, bool is_new, bool has_get, bool has_set, Attributes attrs, Location loc) : base (name, is_new, attrs) { Type = type; HasGet = has_get; HasSet = has_set; Location = loc; } } public class InterfaceEvent : InterfaceMemberBase { public readonly Location Location; public Expression Type; public InterfaceEvent (Expression type, string name, bool is_new, Attributes attrs, Location loc) : base (name, is_new, attrs) { Type = type; Location = loc; } } public class InterfaceMethod : InterfaceMemberBase { public readonly Expression ReturnType; public readonly Parameters Parameters; public readonly Location Location; public InterfaceMethod (Expression return_type, string name, bool is_new, Parameters args, Attributes attrs, Location l) : base (name, is_new, attrs) { this.ReturnType = return_type; this.Parameters = args; Location = l; } /// /// Returns the signature for this interface method /// public string GetSignature (DeclSpace ds) { Type ret = ds.ResolveType (ReturnType, false, Location); string args = Parameters.GetSignature (ds); if ((ret == null) || (args == null)) return null; return (IsNew ? "new-" : "") + ret.FullName + "(" + args + ")"; } public Type [] ParameterTypes (DeclSpace ds) { return Parameters.GetParameterInfo (ds); } } public class InterfaceIndexer : InterfaceMemberBase { public readonly bool HasGet, HasSet; public readonly Parameters Parameters; public readonly Location Location; public Expression Type; public InterfaceIndexer (Expression type, Parameters args, bool do_get, bool do_set, bool is_new, Attributes attrs, Location loc) : base ("", is_new, attrs) { Type = type; Parameters = args; HasGet = do_get; HasSet = do_set; Location = loc; } public Type [] ParameterTypes (DeclSpace ds) { return Parameters.GetParameterInfo (ds); } } }