[Mono-dev] Parch for Enum support in Custom attributes

Jb Evain mono at evain.net
Thu Oct 5 13:16:47 EDT 2006


Hi Eyal,

Thanks for working on this. I don't want to commit it as it is, but I'll 
surely use part of it. I don't want to load the assembly referenced only 
for reading a custom attribute body. Instead, I'll create an interface 
that CustomAttribute and SecurityDeclaration will share, and will allow 
one to load the content of something that needs to load a reference.

Something like:

CustomAttribute ca = ...;
if (!ca.Read) {
	ca.ForceRead ();
}

Otherwise, for a lot of assemblies, Cecil will have to load the corelib 
while the user don't necessary need to read the content of the custom 
attribute.

Jb

Eyal Alaluf wrote:
> Hi, JB.
> 
> Attached is patch for supporting enums in cutom attributes. Support is 
> added
> for enums as ctor parameters as fields and as properties.
> 
> The main problem with Enums is to identify their underlying integral type.
> Without this integral type the custom attribute cannot be read. The patch
> uses the assembly resolver for this purpose.
> 
> I have attached a simple test scenraio with 3 C# files.
>     * Test1.cs is a DLL defining enums and an attribute that has enums as
>       field properties and ctor params.
>     * Test2.cs is another DLL that uses the attribute and enums from Test1.
>       This exercise the new code that resolves enum types from another DLL.
>     * ReadTest2.cs is an EXE written using Cecil that parses test2.dll and
>       prints the custom attributes of its types. It gets as argument the 
> path
>       to the dll it parses.
> Note that Test1 uses ClassUsageAttaribute from mscorlib. For some reason 
> the
> assembly resolver didn't find mscorlib.dll from the GAC when I ran 
> ReadTest2
> on Test2 until I put mscorlib.dll in the same dir as Test2 & ReadTest2.
> 
> Eyal.
> 
> 
> ------------------------------------------------------------------------
> 
> Index: Mono.Cecil/ReflectionReader.cs
> ===================================================================
> --- Mono.Cecil/ReflectionReader.cs	(revision 66216)
> +++ Mono.Cecil/ReflectionReader.cs	(working copy)
> @@ -65,7 +65,24 @@
>  		protected CodeReader m_codeReader;
>  		protected ISymbolReader m_symbolReader;
>  
> -		public ModuleDefinition Module {
> +		internal AssemblyNameReference Corlib
> +		{
> +			get 
> +			{
> +				if (m_corlib == null) {
> +					foreach (AssemblyNameReference ar in m_module.AssemblyReferences) {
> +						if (ar.Name == Constants.Corlib) {
> +							m_corlib = ar;
> +							break;
> +						}
> +					}
> +				}
> +				return m_corlib;
> +			}			
> +		}
> +
> +		public ModuleDefinition Module 
> +		{
>  			get { return m_module; }
>  		}
>  
> @@ -295,19 +312,11 @@
>  
>  			TypeReference coreType =  m_module.TypeReferences [fullName];
>  			if (coreType == null) {
> -				if (m_corlib == null) {
> -					foreach (AssemblyNameReference ar in m_module.AssemblyReferences) {
> -						if (ar.Name == Constants.Corlib) {
> -							m_corlib = ar;
> -							break;
> -						}
> -					}
> -				}
>  
>  				string [] parts = fullName.Split ('.');
>  				if (parts.Length != 2)
>  					throw new ReflectionException ("Unvalid core type name");
> -				coreType = new TypeReference (parts [1], parts [0], m_corlib);
> +				coreType = new TypeReference (parts [1], parts [0], Corlib);
>  				m_module.TypeReferences.Add (coreType);
>  			}
>  			if (!coreType.IsValueType) {
> Index: ChangeLog
> ===================================================================
> --- ChangeLog	(revision 66226)
> +++ ChangeLog	(working copy)
> @@ -1,3 +1,11 @@
> +2006-10-05  Eyal Alaluf  <eyala at mainsoft.com>
> +
> +	Mono.Cecil/ReflectionReader.cs:
> +		Expose Corlib assembly refereice so SignatureReader can ise it.
> +	Mono.Cecil.Signatures/SignatureReader.cs:
> +		Added support for enums in custom attributes ctors, properties and
> +		fields.
> +		
>  2006-10-04  Eyal Alaluf  <eyala at mainsoft.com>
>  
>  	* Mono.Cecil/StructureReader.cs:
> @@ -2,3 +10,2 @@
>  		Visit the module we load when a DLL has more then 1 module.
> -			Visit the module we load when a DLL has more then 1 module.
>  	  Mono.Cecil/AssemblyNameReference.cs:
> Index: Mono.Cecil.Signatures/SignatureReader.cs
> ===================================================================
> --- Mono.Cecil.Signatures/SignatureReader.cs	(revision 66216)
> +++ Mono.Cecil.Signatures/SignatureReader.cs	(working copy)
> @@ -587,7 +587,7 @@
>  		}
>  
>  		CustomAttrib.FixedArg ReadFixedArg (byte [] data, BinaryReader br,
> -			bool array, object param, ref bool read)
> +			bool array, TypeReference param, ref bool read)
>  		{
>  			CustomAttrib.FixedArg fa = new CustomAttrib.FixedArg ();
>  			if (array) {
> @@ -596,6 +596,7 @@
>  
>  				if (fa.NumElem == 0 || fa.NumElem == 0xffffffff) {
>  					fa.Elems = new CustomAttrib.Elem [0];
> +					fa.NumElem = 0;
>  					return fa;
>  				}
>  
> @@ -611,6 +612,95 @@
>  			return fa;
>  		}
>  
> +		TypeReference CreateEnumTypeReference (string enumName)
> +		{
> +			string asmName = null;
> +			int asmStart = enumName.IndexOf (',');
> +			if (asmStart != -1) {
> +				asmName = enumName.Substring (asmStart + 1);
> +				enumName = enumName.Substring (0, asmStart);
> +			}
> +			// Inner class style is reflection style.
> +			enumName = enumName.Replace ('+', '/');
> +			AssemblyNameReference asm = null;
> +			if (asmName == null) {
> +				// If no assembly is given then the ECMA standard says the
> +				// assembly is either the current one or mscorlib.
> +				if (m_reflectReader.Module.Types[enumName] != null)
> +					return m_reflectReader.Module.Types[enumName];
> +				asm = m_reflectReader.Corlib;
> +			}
> +			else
> +				asm = AssemblyNameReference.Parse (asmName);
> +
> +			string[] outers = enumName.Split ('/');
> +			string outerfullname = outers[0];
> +			string ns = null;
> +			int nsIndex = outerfullname.LastIndexOf ('.');
> +			if (nsIndex != -1)
> +				ns = outerfullname.Substring(0, nsIndex);
> +			string name = outerfullname.Substring (nsIndex + 1);
> +			TypeReference decType = new TypeReference (name, ns, asm);
> +			for (int i = 1; i < outers.Length; i++)
> +			{
> +				TypeReference t = new TypeReference (outers[i], null, asm);
> +				t.DeclaringType = decType;
> +				decType = t;
> +			}
> +			decType.IsValueType = true;
> +
> +			return decType;
> +		}
> +
> +		TypeReference ReadTypeReference (byte[] data, BinaryReader br, out ElementType elemType, out bool array)
> +		{
> +			array = false;
> +			elemType = (ElementType) br.ReadByte ();
> +			if (elemType == ElementType.SzArray) 
> +			{
> +				elemType = (ElementType) br.ReadByte ();
> +				array = true;
> +			}
> +
> +			switch (elemType) {
> +			case ElementType.Enum :
> +				return CreateEnumTypeReference (ReadUTF8String (data, br));
> +			case ElementType.Boxed :
> +				return m_reflectReader.SearchCoreType (Constants.Object);
> +			case ElementType.String :
> +				return m_reflectReader.SearchCoreType (Constants.String);
> +			case ElementType.Type :
> +				return m_reflectReader.SearchCoreType (Constants.Type);
> +			case ElementType.Boolean :
> +				return m_reflectReader.SearchCoreType (Constants.Boolean);
> +			case ElementType.Char :
> +				return m_reflectReader.SearchCoreType (Constants.Char);
> +			case ElementType.R4 :
> +				return m_reflectReader.SearchCoreType (Constants.Single);
> +			case ElementType.R8 :
> +				return m_reflectReader.SearchCoreType (Constants.Double);
> +			case ElementType.I1 :
> +				return m_reflectReader.SearchCoreType (Constants.SByte);
> +			case ElementType.I2 :
> +				return m_reflectReader.SearchCoreType (Constants.Int16);
> +			case ElementType.I4 :
> +				return m_reflectReader.SearchCoreType (Constants.Int32);
> +			case ElementType.I8 :
> +				return m_reflectReader.SearchCoreType (Constants.Int64);
> +			case ElementType.U1 :
> +				return m_reflectReader.SearchCoreType (Constants.Byte);
> +			case ElementType.U2 :
> +				return m_reflectReader.SearchCoreType (Constants.UInt16);
> +			case ElementType.U4 :
> +				return m_reflectReader.SearchCoreType (Constants.UInt32);
> +			case ElementType.U8 :
> +				return m_reflectReader.SearchCoreType (Constants.UInt64);
> +			default :
> +				throw new MetadataFormatException ("Non valid type in CustomAttrib.Elem: 0x{0}",
> +					((byte) elemType).ToString("x2"));
> +			}
> +		}
> +
>  		internal CustomAttrib.NamedArg ReadNamedArg (byte [] data, BinaryReader br, ref bool read)
>  		{
>  			CustomAttrib.NamedArg na = new CustomAttrib.NamedArg ();
> @@ -625,42 +715,15 @@
>  				throw new MetadataFormatException ("Wrong kind of namedarg found: 0x" + kind.ToString("x2"));
>  
>  			bool array = false;
> -			na.FieldOrPropType = (ElementType) br.ReadByte ();
> -			if (na.FieldOrPropType == ElementType.SzArray) {
> -				na.FieldOrPropType = (ElementType) br.ReadByte ();
> -				array = true;
> -			}
>  
> -			int next, length;
> +			TypeReference elemType = ReadTypeReference (data, br, out na.FieldOrPropType, out array);
>  
> -			if (na.FieldOrPropType == ElementType.Enum) {
> -				read = false;
> -				return na;
> -			}
> +			na.FieldOrPropName = ReadUTF8String (data, br);
> +			na.FixedArg = ReadFixedArg (data, br, array, elemType, ref read);
>  
> -			length = Utilities.ReadCompressedInteger (data, (int) br.BaseStream.Position, out next);
> -			br.BaseStream.Position = next;
> -
> -			// COMPACT FRAMEWORK NOTE: Encoding.GetString(byte[]) is not supported.
> -			byte [] bytes = br.ReadBytes (length);
> -			na.FieldOrPropName = Encoding.UTF8.GetString (bytes, 0, bytes.Length);
> -
> -			na.FixedArg = ReadFixedArg (data, br, array, na.FieldOrPropType, ref read);
> -
>  			return na;
>  		}
>  
> -		// i hate this construction, should find something better
> -		CustomAttrib.Elem ReadElem (byte [] data, BinaryReader br, object param, ref bool read)
> -		{
> -			if (param is TypeReference)
> -				return ReadElem (data, br, param as TypeReference, ref read);
> -			else if (param is ElementType)
> -				return ReadElem (data, br, (ElementType) param, ref read);
> -			else
> -				throw new MetadataFormatException ("Wrong parameter for ReadElem: " + param.GetType ().FullName);
> -		}
> -
>  		CustomAttrib.Elem ReadElem (byte [] data, BinaryReader br, TypeReference elemType, ref bool read)
>  		{
>  			CustomAttrib.Elem elem = new CustomAttrib.Elem ();
> @@ -668,11 +731,20 @@
>  			string elemName = elemType.FullName;
>  
>  			if (elemName == Constants.Object) {
> -				ElementType elementType = (ElementType) br.ReadByte ();
> -				elem = ReadElem (data, br, elementType, ref read);
> +				bool array;
> +				elemType = ReadTypeReference (data, br, out elem.FieldOrPropType, out array);
> +
> +				if (array) {
> +					read = false; // Don't know how to represent arrays as an object value.
> +					return elem;
> +				}
> +				else if (elemType.FullName == Constants.Object)
> +					throw new MetadataFormatException ("Non valid type in CustomAttrib.Elem after boxed prefix: 0x{0}",
> +						((byte) elem.FieldOrPropType).ToString("x2"));
> +
> +				elem = ReadElem (data, br, elemType, ref read);
>  				elem.String = elem.Simple = elem.Type = false;
>  				elem.BoxedValueType = true;
> -				elem.FieldOrPropType = elementType;
>  				return elem;
>  			}
>  
> @@ -694,19 +766,45 @@
>  					elem.Value = null;
>  					br.BaseStream.Position++;
>  				} else {
> -					int next, length = Utilities.ReadCompressedInteger (data, (int) br.BaseStream.Position, out next);
> -					br.BaseStream.Position = next;
> -					// COMPACT FRAMEWORK NOTE: Encoding.GetString(byte[]) is not supported.
> -					byte [] bytes = br.ReadBytes (length);
> -					elem.Value = Encoding.UTF8.GetString (bytes, 0, bytes.Length);
> +					elem.Value = ReadUTF8String (data, br);
>  				}
> -
>  				return elem;
>  			}
>  
>  			elem.String = elem.Type = elem.BoxedValueType = false;
> +			if (!readSimpleValue (br, ref elem, elem.ElemType)) {
> +				TypeReference typeRef = GetEnumUnderlyingType (elem.ElemType);
> +				if (typeRef == null || !readSimpleValue (br, ref elem, typeRef))
> +					read = false;
> +			}
>  
> -			switch (elemName) {
> +			return elem;
> +		}
> +
> +		private IAssemblyResolver AssemblyResolver
> +		{
> +			get 
> +			{ 
> +				return m_reflectReader.Module.Assembly.Resolver;
> +			}
> +		}
> +
> +		private TypeReference GetEnumUnderlyingType (TypeReference enumType)
> +		{
> +			TypeDefinition type = enumType as TypeDefinition;
> +			if (type == null && AssemblyResolver != null) 
> +			{
> +				AssemblyDefinition asm = AssemblyResolver.Resolve (enumType.Scope.Name);
> +				type = asm.MainModule.Types[enumType.FullName];
> +			}
> +			if (type != null && type.IsEnum)
> +				return type.Fields.GetField ("value__").FieldType;
> +			return null;
> +		}
> +
> +		bool readSimpleValue (BinaryReader br, ref CustomAttrib.Elem elem, TypeReference type)
> +		{
> +			switch (type.FullName) {
>  			case Constants.Boolean :
>  				elem.Value = br.ReadByte () == 1;
>  				break;
> @@ -744,120 +842,12 @@
>  				elem.Value = br.ReadUInt64 ();
>  				break;
>  			default : // enum
> -				read = false;
> -				return elem;
> +				return false;
>  			}
> -
>  			elem.Simple = true;
> -			return elem;
> +			return true;
>  		}
>  
> -		// elem in named args, only have an ElementType
> -		CustomAttrib.Elem ReadElem (byte [] data, BinaryReader br, ElementType elemType, ref bool read)
> -		{
> -			CustomAttrib.Elem elem = new CustomAttrib.Elem ();
> -
> -			if (elemType == ElementType.Boxed) {
> -				ElementType elementType = (ElementType) br.ReadByte ();
> -				elem = ReadElem (data, br, elementType, ref read);
> -				elem.String = elem.Simple = elem.Type = false;
> -				elem.BoxedValueType = true;
> -				elem.FieldOrPropType = elementType;
> -				return elem;
> -			}
> -
> -			if (elemType == ElementType.Type || elemType == ElementType.String) { // type or string
> -				switch (elemType) {
> -				case ElementType.String :
> -					elem.String = true;
> -					elem.BoxedValueType = elem.Simple = elem.Type = false;
> -					elem.ElemType = m_reflectReader.SearchCoreType (Constants.String);
> -					break;
> -				case ElementType.Type :
> -					elem.Type = true;
> -					elem.BoxedValueType = elem.Simple = elem.String = false;
> -					elem.ElemType = m_reflectReader.SearchCoreType (Constants.Type);
> -					break;
> -				}
> -
> -				if (data [br.BaseStream.Position] == 0xff) { // null
> -					elem.Value = null;
> -					br.BaseStream.Position++;
> -				} else {
> -					int next, length = Utilities.ReadCompressedInteger (data, (int) br.BaseStream.Position, out next);
> -					br.BaseStream.Position = next;
> -					// COMPACT FRAMEWORK NOTE: Encoding.GetString(byte[]) is not supported.
> -					byte [] bytes = br.ReadBytes (length);
> -					elem.Value = Encoding.UTF8.GetString (bytes, 0, bytes.Length);
> -				}
> -
> -				return elem;
> -			}
> -
> -			elem.String = elem.Type = elem.BoxedValueType = false;
> -
> -			switch (elemType) {
> -			case ElementType.Boolean :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Boolean);
> -				elem.Value = br.ReadByte () == 1;
> -				break;
> -			case ElementType.Char :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Char);
> -				elem.Value = (char) br.ReadUInt16 ();
> -				break;
> -			case ElementType.R4 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Single);
> -				elem.Value = br.ReadSingle ();
> -				break;
> -			case ElementType.R8 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Double);
> -				elem.Value = br.ReadDouble ();
> -				break;
> -			case ElementType.I1 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.SByte);
> -				elem.Value = br.ReadSByte ();
> -				break;
> -			case ElementType.I2 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Int16);
> -				elem.Value = br.ReadInt16 ();
> -				break;
> -			case ElementType.I4 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Int32);
> -				elem.Value = br.ReadInt32 ();
> -				break;
> -			case ElementType.I8 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Int64);
> -				elem.Value = br.ReadInt64 ();
> -				break;
> -			case ElementType.U1 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.Byte);
> -				elem.Value = br.ReadByte ();
> -				break;
> -			case ElementType.U2 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.UInt16);
> -				elem.Value = br.ReadUInt16 ();
> -				break;
> -			case ElementType.U4 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.UInt32);
> -				elem.Value = br.ReadUInt32 ();
> -				break;
> -			case ElementType.U8 :
> -				elem.ElemType = m_reflectReader.SearchCoreType (Constants.UInt64);
> -				elem.Value = br.ReadUInt64 ();
> -				break;
> -			case ElementType.Enum :
> -				read = false;
> -				return elem;
> -			default :
> -				throw new MetadataFormatException ("Non valid type in CustomAttrib.Elem: 0x{0}",
> -					((byte) elemType).ToString("x2"));
> -			}
> -
> -			read = true;
> -			elem.Simple = true;
> -			return elem;
> -		}
> -
>  		MarshalSig ReadMarshalSig (byte [] data)
>  		{
>  			int start;
> @@ -905,14 +895,21 @@
>  			return ms;
>  		}
>  
> +		static internal string ReadUTF8String (byte [] data, BinaryReader br)
> +		{
> +			int start = (int)br.BaseStream.Position;
> +			string val = ReadUTF8String (data, start, out start);
> +			br.BaseStream.Position = start;
> +			return val;
> +		}
> +
>  		static internal string ReadUTF8String (byte [] data, int pos, out int start)
>  		{
>  			int length = Utilities.ReadCompressedInteger (data, pos, out start);
> -			byte [] str = new byte [length];
> -			Buffer.BlockCopy (data, start, str, 0, length);
> +			pos = start;
>  			start += length;
> -			// COMPACT FRAMEWORK NOTE: Encoding.GetString(byte[]) is not supported.
> -			return Encoding.UTF8.GetString (str, 0, str.Length);
> +			// COMPACT FRAMEWORK NOTE: Encoding.GetString (byte[]) is not supported.
> +			return Encoding.UTF8.GetString (data, pos, length);
>  		}
>  	}
>  }
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> Mono-devel-list mailing list
> Mono-devel-list at lists.ximian.com
> http://lists.ximian.com/mailman/listinfo/mono-devel-list



More information about the Mono-devel-list mailing list