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

Eyal Alaluf eyala at mainsoft.com
Thu Oct 5 12:33:06 EDT 2006


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.
-------------- next part --------------
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);
 		}
 	}
 }
-------------- next part --------------
A non-text attachment was scrubbed...
Name: mytests.zip
Type: application/zip
Size: 1637 bytes
Desc: 
Url : http://lists.ximian.com/pipermail/mono-devel-list/attachments/20061005/79f5502a/attachment.zip 


More information about the Mono-devel-list mailing list