context: //
  // attribute.cs: Attribute Handler
  //
  // Author: Ravi Pratap (ravi@ximian.com)
  //         Marek Safar (marek.safar@seznam.cz)
  //
  // Dual licensed under the terms of the MIT X11 or GNU GPL
  //
  // Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  // Copyright 2003-2008 Novell, Inc.
  //
  
  using System;
  using System.Collections.Generic;
  using System.Runtime.InteropServices;
  using System.Runtime.CompilerServices;
  using System.Security; 
  using System.Security.Permissions;
  using System.Text;
  using System.IO;
  
  #if STATIC
  using SecurityType = System.Collections.Generic.List<IKVM.Reflection.Emit.CustomAttributeBuilder>;
  using IKVM.Reflection;
  using IKVM.Reflection.Emit;
  #else
  using SecurityType = System.Collections.Generic.Dictionary<System.Security.Permissions.SecurityAction, System.Security.PermissionSet>;
  using System.Reflection;
  using System.Reflection.Emit;
  #endif
  
  namespace Mono.CSharp {
  
  	/// <summary>
  	///   Base class for objects that can have Attributes applied to them.
  	/// </summary>
  	public abstract class Attributable {
  		//
  		// Holds all attributes attached to this element
  		//
   		protected Attributes attributes;
  
  		public void AddAttributes (Attributes attrs, IMemberContext context)
  		{
  			if (attrs == null)
  				return;
  
  			if (attributes == null)
  				attributes = attrs;
  			else
  				attributes.AddAttributes (attrs.Attrs);
  
  			attrs.AttachTo (this, context);
  		}
  
  		public Attributes OptAttributes {
  			get {
  				return attributes;
  			}
  			set {
  				attributes = value;
  			}
  		}
  
  		/// <summary>
  		/// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder
  		/// </summary>
  		public abstract void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa);
  
  		/// <summary>
  		/// Returns one AttributeTarget for this element.
  		/// </summary>
  		public abstract AttributeTargets AttributeTargets { get; }
  
  		public abstract bool IsClsComplianceRequired ();
  
  		/// <summary>
  		/// Gets list of valid attribute targets for explicit target declaration.
  		/// The first array item is default target. Don't break this rule.
  		/// </summary>
  		public abstract string[] ValidAttributeTargets { get; }
  	};
  
  	public class Attribute : Expression
  	{
  		public readonly string ExplicitTarget;
  		public AttributeTargets Target;
  		readonly ATypeNameExpression expression;
  
  		public Arguments PosArguments;
  		public Arguments NamedArguments;
  
  		bool resolve_error;
  		bool arg_resolved;
  		readonly bool nameEscaped;
  
  		//
  		// An attribute can be attached to multiple targets (e.g. multiple fields)
  		//
  		Attributable[] targets;
  
  		//
  		// A member context for the attribute, it's much easier to hold it here
  		// than trying to pull it during resolve
  		//
  		IMemberContext context;
  
  		public static readonly AttributeUsageAttribute DefaultUsageAttribute = new AttributeUsageAttribute (AttributeTargets.All);
  		public static readonly object[] EmptyObject = new object [0];
  
  		List<KeyValuePair<MemberExpr, NamedArgument>> named_values;
  
  		public Attribute (string target, ATypeNameExpression expr, Arguments[] args, Location loc, bool nameEscaped)
  		{
  			this.expression = expr;
  			if (args != null) {
  				PosArguments = args[0];
  				NamedArguments = args[1];
  			}
  			this.loc = loc;
  			ExplicitTarget = target;
  			this.nameEscaped = nameEscaped;
  		}
  
  		void AddModuleCharSet (ResolveContext rc)
  		{
  			const string dll_import_char_set = "CharSet";
  
  			//
  			// Only when not customized by user
  			//
  			if (HasField (dll_import_char_set))
  				return;
  
  			if (!rc.Module.PredefinedTypes.CharSet.Define ()) {
  				return;
  			}
  
  			if (NamedArguments == null)
  				NamedArguments = new Arguments (1);
  
  			var value = Constant.CreateConstant (rc.Module.PredefinedTypes.CharSet.TypeSpec, rc.Module.DefaultCharSet, Location);
  			NamedArguments.Add (new NamedArgument (dll_import_char_set, loc, value));
  		}
  
  		public Attribute Clone ()
  		{
  			Attribute a = new Attribute (ExplicitTarget, expression, null, loc, nameEscaped);
  			a.PosArguments = PosArguments;
  			a.NamedArguments = NamedArguments;
  			return a;
  		}
  
  		//
  		// When the same attribute is attached to multiple fiels
  		// we use @target field as a list of targets. The attribute
  		// has to be resolved only once but emitted for each target.
  		//
  		public void AttachTo (Attributable target, IMemberContext context)
  		{
  			if (this.targets == null) {
  				this.targets = new Attributable[] { target };
  				this.context = context;
  				return;
  			}
  
  			// When re-attaching global attributes
  			if (context is NamespaceContainer) {
  				this.targets[0] = target;
  				this.context = context;
  				return;
  			}
  
  			// Resize target array
  			Attributable[] new_array = new Attributable [this.targets.Length + 1];
  			targets.CopyTo (new_array, 0);
  			new_array [targets.Length] = target;
  			this.targets = new_array;
  
  			// No need to update context, different targets cannot have
  			// different contexts, it's enough to remove same attributes
  			// from secondary members.
  
  			target.OptAttributes = null;
  		}
  
  		public ResolveContext CreateResolveContext ()
  		{
  			return new ResolveContext (context, ResolveContext.Options.ConstantScope);
  		}
  
  		static void Error_InvalidNamedArgument (ResolveContext rc, NamedArgument name)
  		{
  			rc.Report.Error (617, name.Location, "`{0}' is not a valid named attribute argument. Named attribute arguments " +
  				      "must be fields which are not readonly, static, const or read-write properties which are " +
  				      "public and not static",
  			      name.Name);
  		}
  
  		static void Error_InvalidNamedArgumentType (ResolveContext rc, NamedArgument name)
  		{
  			rc.Report.Error (655, name.Location,
  				"`{0}' is not a valid named attribute argument because it is not a valid attribute parameter type",
  				name.Name);
  		}
  
  		public static void Error_AttributeArgumentIsDynamic (IMemberContext context, Location loc)
  		{
  			context.Module.Compiler.Report.Error (1982, loc, "An attribute argument cannot be dynamic expression");
  		}
  		
  		public void Error_MissingGuidAttribute ()
  		{
  			Report.Error (596, Location, "The Guid attribute must be specified with the ComImport attribute");
  		}
  
  		public void Error_MisusedExtensionAttribute ()
  		{
  			Report.Error (1112, Location, "Do not use `{0}' directly. Use parameter modifier `this' instead", GetSignatureForError ());
  		}
  
  		public void Error_MisusedDynamicAttribute ()
  		{
  			Report.Error (1970, loc, "Do not use `{0}' directly. Use `dynamic' keyword instead", GetSignatureForError ());
  		}
  
  		/// <summary>
  		/// This is rather hack. We report many emit attribute error with same error to be compatible with
  		/// csc. But because csc has to report them this way because error came from ilasm we needn't.
  		/// </summary>
  		public void Error_AttributeEmitError (string inner)
  		{
  			Report.Error (647, Location, "Error during emitting `{0}' attribute. The reason is `{1}'",
  				      TypeManager.CSharpName (Type), inner);
  		}
  
  		public void Error_InvalidSecurityParent ()
  		{
  			Error_AttributeEmitError ("it is attached to invalid parent");
  		}
  
  		Attributable Owner {
  			get {
  				return targets [0];
  			}
  		}
  
  		TypeSpec ResolvePossibleAttributeType (ATypeNameExpression expr)
  		{
  			TypeExpr te = expr.ResolveAsType (context);
  			if (te == null)
  				return null;
  
  			return te.Type;
  		}
  
  		/// <summary>
  		///   Tries to resolve the type of the attribute. Flags an error if it can't, and complain is true.
  		/// </summary>
  		void ResolveAttributeType ()
  		{
  			SessionReportPrinter resolve_printer = new SessionReportPrinter ();
  			ReportPrinter prev_recorder = context.Module.Compiler.Report.SetPrinter (resolve_printer);
  
  			bool t1_is_attr = false;
  			bool t2_is_attr = false;
  			TypeSpec t1, t2;
  			ATypeNameExpression expanded = null;
  
  			// TODO: Additional warnings such as CS0436 are swallowed because we don't
  			// print on success
  
  			try {
  				t1 = ResolvePossibleAttributeType (expression);
  				if (t1 != null)
  					t1_is_attr = t1.IsAttribute;
  
  				resolve_printer.EndSession ();
  
  				if (nameEscaped) {
  					t2 = null;
  				} else {
  					expanded = (ATypeNameExpression) expression.Clone (null);
  					expanded.Name += "Attribute";
  
  					t2 = ResolvePossibleAttributeType (expanded);
  					if (t2 != null)
  						t2_is_attr = t2.IsAttribute;
  				}
  			} finally {
  				context.Module.Compiler.Report.SetPrinter (prev_recorder);
  			}
  
  			if (t1_is_attr && t2_is_attr && t1 != t2) {
  				Report.Error (1614, Location, "`{0}' is ambiguous between `{1}' and `{2}'. Use either `@{0}' or `{0}Attribute'",
  					GetSignatureForError (), expression.GetSignatureForError (), expanded.GetSignatureForError ());
  				resolve_error = true;
  				return;
  			}
  
  			if (t1_is_attr) {
  				Type = t1;
  				return;
  			}
  
  			if (t2_is_attr) {
  				Type = t2;
  				return;
  			}
  
  			resolve_error = true;
  
  			if (t1 != null) {
  				resolve_printer.Merge (prev_recorder);
  
  				Report.SymbolRelatedToPreviousError (t1);
  				Report.Error (616, Location, "`{0}': is not an attribute class", t1.GetSignatureForError ());
  				return;
  			}
  
  			if (t2 != null) {
  				Report.SymbolRelatedToPreviousError (t2);
  				Report.Error (616, Location, "`{0}': is not an attribute class", t2.GetSignatureForError ());
  				return;
  			}
  
  			resolve_printer.Merge (prev_recorder);
  		}
  
  		public TypeSpec ResolveType ()
  		{
  			if (Type == null && !resolve_error)
  				ResolveAttributeType ();
  			return Type;
  		}
  
  		public override string GetSignatureForError ()
  		{
  			if (Type != null)
  				return TypeManager.CSharpName (Type);
  
  			return expression.GetSignatureForError ();
  		}
  
  		public bool HasSecurityAttribute {
  			get {
  				PredefinedAttribute pa = context.Module.PredefinedAttributes.Security;
  				return pa.IsDefined && TypeSpec.IsBaseClass (type, pa.TypeSpec, false);
  			}
  		}
  
  		public bool IsValidSecurityAttribute ()
  		{
  			return HasSecurityAttribute && IsSecurityActionValid ();
  		}
  
  		static bool IsValidArgumentType (TypeSpec t)
  		{
  			if (t.IsArray) {
  				var ac = (ArrayContainer) t;
  				if (ac.Rank > 1)
  					return false;
  
  				t = ac.Element;
  			}
  
  			switch (t.BuiltinType) {
  			case BuiltinTypeSpec.Type.Int:
  			case BuiltinTypeSpec.Type.UInt:
  			case BuiltinTypeSpec.Type.Long:
  			case BuiltinTypeSpec.Type.ULong:
  			case BuiltinTypeSpec.Type.Float:
  			case BuiltinTypeSpec.Type.Double:
  			case BuiltinTypeSpec.Type.Char:
  			case BuiltinTypeSpec.Type.Short:
  			case BuiltinTypeSpec.Type.Bool:
  			case BuiltinTypeSpec.Type.SByte:
  			case BuiltinTypeSpec.Type.Byte:
  			case BuiltinTypeSpec.Type.UShort:
  
  			case BuiltinTypeSpec.Type.String:
  			case BuiltinTypeSpec.Type.Object:
  			case BuiltinTypeSpec.Type.Dynamic:
  			case BuiltinTypeSpec.Type.Type:
  				return true;
  			}
  
  			return t.IsEnum;
  		}
  
  		// TODO: Don't use this ambiguous value
  		public string Name {
  			get { return expression.Name; }
  		}
  
  		public ATypeNameExpression TypeNameExpression {
  			get {
  				return expression;
  			}
  		}
  
  		public Report Report {
  			get { return context.Module.Compiler.Report; }
  		}
  
  		public MethodSpec Resolve ()
  		{
  			if (resolve_error)
  				return null;
  
  			resolve_error = true;
  			arg_resolved = true;
  
  			if (Type == null) {
  				ResolveAttributeType ();
  				if (Type == null)
  					return null;
  			}
  
  			if (Type.IsAbstract) {
  				Report.Error (653, Location, "Cannot apply attribute class `{0}' because it is abstract", GetSignatureForError ());
  				return null;
  			}
  
  			ObsoleteAttribute obsolete_attr = Type.GetAttributeObsolete ();
  			if (obsolete_attr != null) {
  				AttributeTester.Report_ObsoleteMessage (obsolete_attr, TypeManager.CSharpName (Type), Location, Report);
  			}
  
  			ResolveContext rc = null;
  
  			MethodSpec ctor;
  			// Try if the attribute is simple and has been resolved before
  			if (PosArguments != null || !context.Module.AttributeConstructorCache.TryGetValue (Type, out ctor)) {
  				rc = CreateResolveContext ();
  				ctor = ResolveConstructor (rc);
  				if (ctor == null) {
  					return null;
  				}
  
  				if (PosArguments == null && ctor.Parameters.IsEmpty)
  					context.Module.AttributeConstructorCache.Add (Type, ctor);
  			}
  
  			//
  			// Add [module: DefaultCharSet] to all DllImport import attributes
  			//
  			var module = context.Module;
  			if ((Type == module.PredefinedAttributes.DllImport || Type == module.PredefinedAttributes.UnmanagedFunctionPointer) && module.HasDefaultCharSet) {
  				if (rc == null)
  					rc = CreateResolveContext ();
  
  				AddModuleCharSet (rc);
  			}
  
  			if (NamedArguments != null) {
  				if (rc == null)
  					rc = CreateResolveContext ();
  
  				if (!ResolveNamedArguments (rc))
  					return null;
  			}
  
  			resolve_error = false;
  			return ctor;
  		}
  
  		MethodSpec ResolveConstructor (ResolveContext ec)
  		{
  			if (PosArguments != null) {
  				bool dynamic;
  				PosArguments.Resolve (ec, out dynamic);
  				if (dynamic) {
  					Error_AttributeArgumentIsDynamic (ec.MemberContext, loc);
  					return null;
  				}
  			}
  
  			return ConstructorLookup (ec, Type, ref PosArguments, loc);
  		}
  
  		bool ResolveNamedArguments (ResolveContext ec)
  		{
  			int named_arg_count = NamedArguments.Count;
  			var seen_names = new List<string> (named_arg_count);
  
  			named_values = new List<KeyValuePair<MemberExpr, NamedArgument>> (named_arg_count);
  			
  			foreach (NamedArgument a in NamedArguments) {
  				string name = a.Name;
  				if (seen_names.Contains (name)) {
  					ec.Report.Error (643, a.Location, "Duplicate named attribute `{0}' argument", name);
  					continue;
  				}			
  	
  				seen_names.Add (name);
  
  				a.Resolve (ec);
  
  				Expression member = Expression.MemberLookup (ec, false, Type, name, 0, MemberLookupRestrictions.ExactArity, loc);
  
  				if (member == null) {
  					member = Expression.MemberLookup (ec, true, Type, name, 0, MemberLookupRestrictions.ExactArity, loc);
  
  					if (member != null) {
  						// TODO: ec.Report.SymbolRelatedToPreviousError (member);
  						Expression.ErrorIsInaccesible (ec, member.GetSignatureForError (), loc);
  						return false;
  					}
  				}
  
  				if (member == null){
  					Expression.Error_TypeDoesNotContainDefinition (ec, Location, Type, name);
  					return false;
  				}
  				
  				if (!(member is PropertyExpr || member is FieldExpr)) {
  					Error_InvalidNamedArgument (ec, a);
  					return false;
  				}
  
  				ObsoleteAttribute obsolete_attr;
  
  				if (member is PropertyExpr) {
  					var pi = ((PropertyExpr) member).PropertyInfo;
  
  					if (!pi.HasSet || !pi.HasGet || pi.IsStatic || !pi.Get.IsPublic || !pi.Set.IsPublic) {
  						ec.Report.SymbolRelatedToPreviousError (pi);
  						Error_InvalidNamedArgument (ec, a);
  						return false;
  					}
  
  					if (!IsValidArgumentType (member.Type)) {
  						ec.Report.SymbolRelatedToPreviousError (pi);
  						Error_InvalidNamedArgumentType (ec, a);
  						return false;
  					}
  
  					obsolete_attr = pi.GetAttributeObsolete ();
  					pi.MemberDefinition.SetIsAssigned ();
  				} else {
  					var fi = ((FieldExpr) member).Spec;
  
  					if (fi.IsReadOnly || fi.IsStatic || !fi.IsPublic) {
  						Error_InvalidNamedArgument (ec, a);
  						return false;
  					}
  
  					if (!IsValidArgumentType (member.Type)) {
  						ec.Report.SymbolRelatedToPreviousError (fi);
  						Error_InvalidNamedArgumentType (ec, a);
  						return false;
  					}
  
  					obsolete_attr = fi.GetAttributeObsolete ();
  					fi.MemberDefinition.SetIsAssigned ();
  				}
  
  				if (obsolete_attr != null && !context.IsObsolete)
  					AttributeTester.Report_ObsoleteMessage (obsolete_attr, member.GetSignatureForError (), member.Location, Report);
  
  				if (a.Type != member.Type) {
  					a.Expr = Convert.ImplicitConversionRequired (ec, a.Expr, member.Type, a.Expr.Location);
  				}
  
  				if (a.Expr != null)
  					named_values.Add (new KeyValuePair<MemberExpr, NamedArgument> ((MemberExpr) member, a));
  			}
  
  			return true;
  		}
  
  		/// <summary>
  		///   Get a string containing a list of valid targets for the attribute 'attr'
  		/// </summary>
  		public string GetValidTargets ()
  		{
  			StringBuilder sb = new StringBuilder ();
  			AttributeTargets targets = Type.GetAttributeUsage (context.Module.PredefinedAttributes.AttributeUsage).ValidOn;
  
  			if ((targets & AttributeTargets.Assembly) != 0)
  				sb.Append ("assembly, ");
  
  			if ((targets & AttributeTargets.Module) != 0)
  				sb.Append ("module, ");
  
  			if ((targets & AttributeTargets.Class) != 0)
  				sb.Append ("class, ");
  
  			if ((targets & AttributeTargets.Struct) != 0)
  				sb.Append ("struct, ");
  
  			if ((targets & AttributeTargets.Enum) != 0)
  				sb.Append ("enum, ");
  
  			if ((targets & AttributeTargets.Constructor) != 0)
  				sb.Append ("constructor, ");
  
  			if ((targets & AttributeTargets.Method) != 0)
  				sb.Append ("method, ");
  
  			if ((targets & AttributeTargets.Property) != 0)
  				sb.Append ("property, indexer, ");
  
  			if ((targets & AttributeTargets.Field) != 0)
  				sb.Append ("field, ");
  
  			if ((targets & AttributeTargets.Event) != 0)
  				sb.Append ("event, ");
  
  			if ((targets & AttributeTargets.Interface) != 0)
  
gt: 				sb.Append ("interface, ");