// // Copyright (c) Microsoft Corporation. All rights reserved. // using System; namespace Bartok.MSIL { using System.IO; public class MetaDataCustomAttribute: MetaDataObject { // Constructor Methods internal MetaDataCustomAttribute(int parentIndex, MetaDataObject type, byte[] buffer) { this.parentIndex = parentIndex; this.type = type; this.buffer = buffer; } // This is technically not a constructor method, but it is meant to // be used to set up the object internal void resolveReferences(MetaDataLoader loader) { this.parent = loader.getHasCustomAttribute(this.parentIndex); this.parent.AddCustomAttribute(this); } // Resolve all the custom attributes found in the assemblies registered // in 'resolver', using all the type information found in 'resolver' and // 'resolvers'. internal void resolveReferences(MetaDataResolver resolver, MetaDataResolver[] resolvers) { if (this.buffer[0] != 0x01 || this.buffer[1] != 0x00) { throw new MetaDataLoader.IllegalMetaDataFormatException("Custom Attribute doesn't start with 0x0001!"); } SignatureMethod signature; if (this.type is MetaDataMethod) { MetaDataMethod method = (MetaDataMethod) this.type; signature = (SignatureMethod) method.Signature; if (!method.Name.Equals(".ctor")) { throw new MetaDataLoader.IllegalMetaDataFormatException("Custom attribute with unexpected method name: "+method.Name); } this.typeDefOrRef = method.Parent; this.name = method.Parent.FullName; } else if (this.type is MetaDataMemberRef) { MetaDataMemberRef memberRef = (MetaDataMemberRef) this.type; signature = (SignatureMethod) memberRef.Signature; if (!memberRef.Name.Equals(".ctor")) { throw new MetaDataLoader.IllegalMetaDataFormatException("Custom attribute with unexpected memberRef name: "+memberRef.Name); } MetaDataObject methodParent = memberRef.Class; this.typeDefOrRef = methodParent; if (methodParent is MetaDataTypeDefinition) { this.name = ((MetaDataTypeDefinition) methodParent).FullName; } else if (methodParent is MetaDataTypeReference) { this.name = ((MetaDataTypeReference) methodParent).FullName; } else { throw new MetaDataLoader.IllegalMetaDataFormatException("Custom attribute with unexpected class type: "+methodParent); } } else { throw new MetaDataLoader.IllegalMetaDataFormatException("Custom attribute with unexpected type: "+this.type); } Signature.Param[] parameters = signature.Parameters; int fixedCount = parameters.Length; this.fixedArgs = new Object[fixedCount]; MemoryStream stream = new MemoryStream(this.buffer, 2, this.buffer.Length - 2, false); BinaryReader reader = new BinaryReader(stream); for (int i = 0; i < fixedCount; i++) { Signature.Param parameter = parameters[i]; Signature.Type paramType = parameter.Type; Object value = ExtractParameter(paramType, reader, resolver, resolvers); fixedArgs[i] = value; } short namedCount = ((reader.PeekChar() == -1) ? (short) 0 : reader.ReadInt16()); if (namedCount > this.buffer.Length && this.Name.Equals("System.Runtime.CompilerServices.RequiredAttributeAttribute")) { // Some CLR libraries have been compiled against a version of // mscorlib that had a fixed parameter to RequiredAttribute. // Simply ignore whatever the parameter was! namedCount = 0; } this.namedArgs = new NamedArg[namedCount]; for (int i = 0; i < namedCount; i++) { SerializationTypes propOrField = (SerializationTypes) reader.ReadByte(); ElementTypes fieldType = (ElementTypes) reader.ReadByte(); ElementTypes arrayType = ElementTypes.END; switch (fieldType) { case ElementTypes.SZARRAY: { arrayType = (ElementTypes) reader.ReadByte(); if (arrayType == (ElementTypes) SerializationTypes.ENUM) { throw new Exception("Not implemented: Array of ENUM "+ "for named field/property"); } break; } case (ElementTypes) SerializationTypes.ENUM: { String enumName = ExtractString(reader); if (enumName.Length == 0) { throw new Exception("Empty enum name"); } // Hope it is a 4-byte enum fieldType = (ElementTypes) SerializationTypes.U4; break; } case (ElementTypes) SerializationTypes.TAGGED_OBJECT: { throw new Exception("Not implemented: "+fieldType+ " for named field/property"); } default: { break; } } String name = ExtractString(reader); Object value; if (fieldType == ElementTypes.SZARRAY) { value = ExtractArrayValue(arrayType, reader); } else { value = ExtractValue(fieldType, reader); } if (propOrField == SerializationTypes.FIELD || propOrField == SerializationTypes.PROPERTY) { this.namedArgs[i] = new NamedArg(propOrField == SerializationTypes.FIELD, -1, name, value); } else { throw new MetaDataLoader.IllegalMetaDataFormatException("Unknown prop-or-field type: "+propOrField); } } } private static MetaDataObject ResolveTypeRef(MetaDataResolver resolver, MetaDataResolver[] resolvers, MetaDataTypeReference typeRef) { MetaDataObject result = resolver.ResolveTypeRef(typeRef); int i = 0; while (result == null && i < resolvers.Length) { MetaDataResolver testResolver = resolvers[i]; i++; if (testResolver != resolver) { result = testResolver.ResolveTypeRef(typeRef); } } return result; } private static MetaDataTypeDefinition ResolveName(MetaDataResolver resolver, MetaDataResolver[] resolvers, String typeName) { MetaDataTypeDefinition typeDef = resolver.ResolveName(typeName); int i = 0; while (typeDef == null && i < resolvers.Length) { MetaDataResolver testResolver = resolvers[i]; i++; if (testResolver != resolver) { typeDef = testResolver.ResolveName(typeName); } } return typeDef; } private static Object ExtractParameter(Signature.Type type, BinaryReader reader, MetaDataResolver resolver, MetaDataResolver[] resolvers) { switch (type.ElementType) { case ElementTypes.VALUETYPE: { MetaDataObject classObject = type.ClassObject; if (classObject is MetaDataTypeReference) { MetaDataTypeReference classReference = (MetaDataTypeReference) classObject; MetaDataObject resolvedObject = ResolveTypeRef(resolver, resolvers, classReference); if (resolvedObject is MetaDataTypeDefinition) { classObject = resolvedObject; } } if (classObject is MetaDataTypeReference) { // We will simply assume it is an I4 enum Console.Out.WriteLine("====>>> WARNING: Making I4 enum assumption for "+classObject); return reader.ReadInt32(); } else if (classObject is MetaDataTypeDefinition) { MetaDataTypeDefinition classDef = (MetaDataTypeDefinition) classObject; MetaDataObject superClass = classDef.Extends; String superName; if (superClass is MetaDataTypeDefinition) { superName = ((MetaDataTypeDefinition) superClass).FullName; } else if (superClass is MetaDataTypeReference) { superName = ((MetaDataTypeReference) superClass).FullName; } else { throw new MetaDataLoader.IllegalMetaDataFormatException("Unexpected superclass of valuetype: "+superClass); } if (!superName.Equals("System.Enum")) { throw new MetaDataLoader.IllegalMetaDataFormatException("Found valuetype that wasn't an Enum"); } return ExtractEnumValue(classDef, reader, resolver, resolvers); } else { throw new MetaDataLoader.IllegalMetaDataFormatException("Unexpected valuetype class: "+classObject); } } case ElementTypes.CLASS: { MetaDataObject classObject = type.ClassObject; if (classObject is MetaDataTypeReference) { MetaDataTypeReference classReference = (MetaDataTypeReference) classObject; MetaDataObject resolvedObject = ResolveTypeRef(resolver, resolvers, classReference); if (resolvedObject is MetaDataTypeDefinition) { classObject = resolvedObject; } } String className; if (classObject is MetaDataTypeReference) { className = ((MetaDataTypeReference) classObject).FullName; } else if (classObject is MetaDataTypeDefinition) { className = ((MetaDataTypeDefinition) classObject).FullName; } else { throw new MetaDataLoader.IllegalMetaDataFormatException("Unexpected class: "+classObject); } if (className.Equals("System.String")) { return ExtractString(reader); } else if (className.Equals("System.Object")) { goto case ElementTypes.OBJECT; } else if (className.Equals("System.Type")) { return ExtractString(reader); } if (!(classObject is MetaDataTypeDefinition)) { throw new MetaDataLoader.IllegalMetaDataFormatException("Cannot determine whether the following is an array: "+classObject); } MetaDataObject superClass = ((MetaDataTypeDefinition) classObject).Extends; Console.WriteLine("class is "+classObject); throw new Exception("Not implemented: object encoding an array"); } case ElementTypes.OBJECT: case (ElementTypes) SerializationTypes.TAGGED_OBJECT: { SerializationTypes objectType = (SerializationTypes) reader.ReadByte(); switch (objectType) { case SerializationTypes.ENUM: { String typeName = ExtractString(reader); // Some Stringified type names include a bunch of // information past the type, none of which we // are concerned about for the moment. int nameEnd = typeName.IndexOf(','); if (nameEnd >= 0) { typeName = typeName.Substring(0, nameEnd); } // A type defined within another type has a "+" // as the separator. Convert it to ".", which is // the convention the rest of the reader uses. typeName = typeName.Replace('+','.'); MetaDataTypeDefinition typeDef = ResolveName(resolver, resolvers, typeName); if (typeDef == null) { throw new Exception("No typedef for "+typeName); } return ExtractEnumValue(typeDef, reader, resolver, resolvers); } case SerializationTypes.TYPE: { return ExtractValue((ElementTypes) objectType, reader); } default: { throw new Exception("Found OBJECT type with type "+objectType); } } } case ElementTypes.SZARRAY: { return ExtractArrayValue(type.TypeObject, reader, resolver, resolvers); } default: { return ExtractValue(type.ElementType, reader); } } } private static Object ExtractValue(ElementTypes type, BinaryReader reader) { switch (type) { case ElementTypes.BOOLEAN: return reader.ReadBoolean(); case ElementTypes.CHAR: return (char) reader.ReadUInt16(); case ElementTypes.I1: return reader.ReadSByte(); case ElementTypes.U1: return reader.ReadByte(); case ElementTypes.I2: return reader.ReadInt16(); case ElementTypes.U2: return reader.ReadUInt16(); case ElementTypes.I4: return reader.ReadInt32(); case ElementTypes.U4: return reader.ReadUInt32(); case ElementTypes.I8: return reader.ReadInt64(); case ElementTypes.U8: return reader.ReadUInt64(); case ElementTypes.R4: return reader.ReadSingle(); case ElementTypes.R8: return reader.ReadDouble(); case ElementTypes.STRING: case (ElementTypes) SerializationTypes.TYPE: return ExtractString(reader); default: throw new MetaDataLoader.IllegalMetaDataFormatException("Found unexpected type "+type+" in custom attribute parameter"); } } private static String ExtractString(BinaryReader reader) { byte firstByte = reader.ReadByte(); if (firstByte == 0xFF) { return null; } int size; if ((firstByte & 0x80) == 0) { // encoded in a single byte size = firstByte; } else if ((firstByte & 0x40) == 0) { // encoded in two bytes size = unchecked(((firstByte & 0x3F) << 8) | reader.ReadByte()); } else { // encoded in four bytes size = unchecked((int) (((firstByte & 0x3F) << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | (reader.ReadByte()))); } byte[] bytes = reader.ReadBytes(size); if (bytes.Length != size) { throw new Exception("Didn't get the bytes requested"); } return (new System.Text.UTF8Encoding()).GetString(bytes); } private static Object ExtractEnumValue(MetaDataTypeDefinition typeDef, BinaryReader reader, MetaDataResolver resolver, MetaDataResolver[] resolvers) { MetaDataField[] classFields = typeDef.Fields; for (int i = 0; i < classFields.Length; i++) { if ((classFields[i].Flags & (int) MetaDataField.FieldAttributes.Static) == 0) { if (!classFields[i].Name.Equals("value__")) { throw new MetaDataLoader.IllegalMetaDataFormatException("Found enum with non-static field "+classFields[i].Name); } return ExtractParameter(classFields[i].Signature.FieldType, reader, resolver, resolvers); } } throw new MetaDataLoader.IllegalMetaDataFormatException("Found enum without non-static field"); } private static Object ExtractArrayValue(Signature.Type type, BinaryReader reader, MetaDataResolver resolver, MetaDataResolver[] resolvers) { int arraySize = reader.ReadInt32(); if (arraySize >= 0) { ElementTypes elementType = type.ElementType; if (elementType == ElementTypes.CLASS) { Object[] array = new Object[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = ExtractParameter(type, reader, resolver, resolvers); } return array; } else { return ExtractArrayValue(elementType, arraySize, reader); } } else { throw new Exception("Not implemented: custom attribute class "+ "array with negative length"); } } private static Object ExtractArrayValue(ElementTypes elementType, BinaryReader reader) { int arraySize = reader.ReadInt32(); if (arraySize >= 0) { return ExtractArrayValue(elementType, arraySize, reader); } else { throw new Exception("Not implemented: custom atribute array "+ "with negative length"); } } private static Object ExtractArrayValue(ElementTypes elementType, int arraySize, BinaryReader reader) { switch (elementType) { case ElementTypes.BOOLEAN: { bool[] array = new bool[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadBoolean(); } return array; } case ElementTypes.CHAR: { char[] array = new char[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = (char) reader.ReadUInt16(); } return array; } case ElementTypes.I1: { sbyte[] array = new sbyte[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadSByte(); } return array; } case ElementTypes.U1: { byte[] array = new byte[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadByte(); } return array; } case ElementTypes.I2: { short[] array = new short[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadInt16(); } return array; } case ElementTypes.U2: { ushort[] array = new ushort[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadUInt16(); } return array; } case ElementTypes.I4: { int[] array = new int[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadInt32(); } return array; } case ElementTypes.U4: { uint[] array = new uint[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadUInt32(); } return array; } case ElementTypes.I8: { long[] array = new long[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadInt64(); } return array; } case ElementTypes.U8: { ulong[] array = new ulong[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadUInt64(); } return array; } case ElementTypes.R4: { float[] array = new float[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadSingle(); } return array; } case ElementTypes.R8: { double[] array = new double[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = reader.ReadDouble(); } return array; } case ElementTypes.STRING: { String[] array = new String[arraySize]; for (int i = 0; i < arraySize; i++) { array[i] = ExtractString(reader); } return array; } default: throw new Exception("Not implemented: custom attribute "+ "array of type "+elementType); } } // Access Methods // Who owns this custom attribute public MetaDataObject Parent { get { return this.parent; } } // The fully qualified name of the custom attribute public String Name { get { return this.name; } } // The type definition or reference of the custom attribute public MetaDataObject TypeDefOrRef { get { return this.typeDefOrRef; } } // The constructor method or memberref of the custom attribute public MetaDataObject Type { get { return this.type; } } // The array of arguments to the custom attribute constructor public Object[] FixedArgs { get { return this.fixedArgs; } } // The array of custom attribute fields and properties explicitly set public NamedArg[] NamedArgs { get { return this.namedArgs; } } // Debug Methods public override String ToString() { return ("MetaDataCustomAttribute("+ this.type+","+this.fixedArgs+","+this.namedArgs+","+ this.parentIndex+ (this.parent==null?"":("<"+this.parent.ToString()+">"))+ ")["+new MetaDataBlob(this.buffer)+"]"); } public override String ToStringLong() { System.Text.StringBuilder sb = new System.Text.StringBuilder("MetaDataCustomAttribute("); sb.Append(this.type); sb.Append(","); if (this.fixedArgs != null) { sb.Append("FixedArgs("); for (int i = 0; i < this.fixedArgs.Length; i++) { sb.Append(this.fixedArgs[i]); sb.Append(","); } sb.Append("),"); } else { sb.Append("FixedArgs(),"); } if (this.namedArgs != null) { sb.Append("NamedArgs("); for (int i = 0; i < this.namedArgs.Length; i++) { sb.Append(this.namedArgs[i]); sb.Append(","); } sb.Append("),"); } else { sb.Append("NamedArgs(),"); } sb.Append(this.parent); sb.Append("("); sb.Append(this.parentIndex); sb.Append("))["); sb.Append(new MetaDataBlob(this.buffer)); sb.Append("]"); return sb.ToString(); } // State private readonly byte[] buffer; private readonly int parentIndex; private String name; private MetaDataObject parent; private MetaDataObject type; private MetaDataObject typeDefOrRef; private Object[] fixedArgs; private NamedArg[] namedArgs; public class FixedArg { // A negative value for arrayDim signifies a nonArray internal FixedArg(SerializationTypes type, int arrayDim, Object value) { this.arrayDim = arrayDim; this.type = type; this.value = value; } public bool IsArray { get { return this.arrayDim >= 0; } } public int ArrayDim { get { if (this.arrayDim < 0) { throw new Exception("FixedArg is not an array"); } return this.arrayDim; } } public SerializationTypes Type { get { return this.type; } } public Object Value { get { return this.value; } } public override String ToString() { return ("FixedArg("+this.type+((arrayDim<0)?"":("["+arrayDim+"]"))+"->"+this.value+")"); } private int arrayDim; private SerializationTypes type; private Object value; } public class NamedArg { internal NamedArg(bool isFieldArg, int arrayDim, String name, Object value) { this.isFieldArg = isFieldArg; this.arrayDim = arrayDim; this.type = type; this.name = name; this.value = value; } public bool IsFieldArg { get { return this.isFieldArg; } } public bool IsArray { get { return this.arrayDim >= 0; } } public int ArrayDim { get { if (this.arrayDim < 0) { throw new Exception("NamedArg is not an array"); } return this.arrayDim; } } public SerializationTypes Type { get { return this.type; } } public String Name { get { return this.name; } } public Object Value { get { return this.value; } } public override String ToString() { return ("NamedArg("+this.name+":"+this.type+((arrayDim<0)?"":("["+arrayDim+"]"))+"->"+this.value+")"); } private bool isFieldArg; private int arrayDim; private String name; private SerializationTypes type; private Object value; } } }