// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections; using System.Collections.Generic; using System.Text; using ICSharpCode.SharpDevelop.Dom.ReflectionLayer; using Mono.Cecil; using Mono.Collections.Generic; namespace ICSharpCode.SharpDevelop.Dom { public static class CecilReader { sealed class DummyAssemblyResolver : IAssemblyResolver { public AssemblyDefinition Resolve(AssemblyNameReference name) { return null; } public AssemblyDefinition Resolve(string fullName) { return null; } public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) { throw new NotImplementedException(); } public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) { throw new NotImplementedException(); } } public static ReflectionProjectContent LoadAssembly(string fileName, ProjectContentRegistry registry) { if (fileName == null) throw new ArgumentNullException("fileName"); if (registry == null) throw new ArgumentNullException("registry"); LoggingService.Info("Cecil: Load from " + fileName); AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() }); List referencedAssemblies = new List(); foreach (ModuleDefinition module in asm.Modules) { foreach (AssemblyNameReference anr in module.AssemblyReferences) { referencedAssemblies.Add(new DomAssemblyName(anr.FullName)); } } return new CecilProjectContent(asm.Name.FullName, fileName, referencedAssemblies.ToArray(), asm, registry); } static void AddAttributes(IProjectContent pc, IEntity member, IList list, ICustomAttributeProvider attributeProvider) { if (!attributeProvider.HasCustomAttributes) return; foreach (CustomAttribute att in attributeProvider.CustomAttributes) { DefaultAttribute a = new DefaultAttribute(CreateType(pc, member, att.Constructor.DeclaringType)); // Currently Cecil returns string instead of TypeReference for typeof() arguments to attributes try { foreach (var argument in att.ConstructorArguments) { a.PositionalArguments.Add(GetValue(pc, member, argument)); } foreach (CustomAttributeNamedArgument entry in att.Properties) { // some obfuscated assemblies may contain duplicate named arguments; we'll have to ignore those if (!a.NamedArguments.ContainsKey(entry.Name)) a.NamedArguments.Add(entry.Name, GetValue(pc, member, entry.Argument)); } } catch (InvalidOperationException) { // Workaround for Cecil bug. (some types cannot be resolved) } list.Add(a); } } static object GetValue(IProjectContent pc, IEntity member, CustomAttributeArgument argument) { if (argument.Value is TypeReference) return CreateType(pc, member, (TypeReference)argument.Value); else return argument.Value; } static void AddConstraintsFromType(ITypeParameter tp, GenericParameter g) { foreach (TypeReference constraint in g.Constraints) { if (tp.Method != null) { tp.Constraints.Add(CreateType(tp.Class.ProjectContent, tp.Method, constraint)); } else { tp.Constraints.Add(CreateType(tp.Class.ProjectContent, tp.Class, constraint)); } } } /// /// Create a SharpDevelop return type from a Cecil type reference. /// internal static IReturnType CreateType(IProjectContent pc, IEntity member, TypeReference type, ICustomAttributeProvider attributeProvider = null) { int typeIndex = 0; return CreateType(pc, member, type, attributeProvider, ref typeIndex); } static IReturnType CreateType(IProjectContent pc, IEntity member, TypeReference type, ICustomAttributeProvider attributeProvider, ref int typeIndex) { while (type is OptionalModifierType || type is RequiredModifierType) { type = ((TypeSpecification)type).ElementType; } if (type == null) { LoggingService.Warn("CecilReader: Null type for: " + member); return new VoidReturnType(pc); } if (type is ByReferenceType) { // TODO: Use ByRefRefReturnType return CreateType(pc, member, (type as ByReferenceType).ElementType, attributeProvider, ref typeIndex); } else if (type is PointerType) { typeIndex++; return new PointerReturnType(CreateType(pc, member, (type as PointerType).ElementType, attributeProvider, ref typeIndex)); } else if (type is ArrayType) { typeIndex++; return new ArrayReturnType(pc, CreateType(pc, member, (type as ArrayType).ElementType, attributeProvider, ref typeIndex), (type as ArrayType).Rank); } else if (type is GenericInstanceType) { GenericInstanceType gType = (GenericInstanceType)type; IReturnType baseType = CreateType(pc, member, gType.ElementType, attributeProvider, ref typeIndex); IReturnType[] para = new IReturnType[gType.GenericArguments.Count]; for (int i = 0; i < para.Length; ++i) { typeIndex++; para[i] = CreateType(pc, member, gType.GenericArguments[i], attributeProvider, ref typeIndex); } return new ConstructedReturnType(baseType, para); } else if (type is GenericParameter) { GenericParameter typeGP = type as GenericParameter; if (typeGP.Owner is MethodDefinition) { IMethod method = member as IMethod; if (method != null) { if (typeGP.Position < method.TypeParameters.Count) { return new GenericReturnType(method.TypeParameters[typeGP.Position]); } } return new GenericReturnType(new DefaultTypeParameter(method, typeGP.Name, typeGP.Position)); } else { IClass c = (member is IClass) ? (IClass)member : (member is IMember) ? ((IMember)member).DeclaringType : null; if (c != null && typeGP.Position < c.TypeParameters.Count) { if (c.TypeParameters[typeGP.Position].Name == type.Name) { return new GenericReturnType(c.TypeParameters[typeGP.Position]); } } return new GenericReturnType(new DefaultTypeParameter(c, typeGP.Name, typeGP.Position)); } } else { string name = type.FullName; if (name == null) throw new ApplicationException("type.FullName returned null. Type: " + type.ToString()); int typeParameterCount; if (name.IndexOf('/') > 0) { typeParameterCount = 0; StringBuilder newName = new StringBuilder(); foreach (string namepart in name.Split('/')) { if (newName.Length > 0) newName.Append('.'); int partTypeParameterCount; newName.Append(ReflectionClass.SplitTypeParameterCountFromReflectionName(namepart, out partTypeParameterCount)); typeParameterCount += partTypeParameterCount; } name = newName.ToString(); } else { name = ReflectionClass.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount); } if (typeParameterCount == 0 && name == "System.Object" && HasDynamicAttribute(attributeProvider, typeIndex)) return new DynamicReturnType(pc); IClass c = pc.GetClass(name, typeParameterCount); if (c != null) { return c.DefaultReturnType; } else { // example where name is not found: pointers like System.Char* // or when the class is in a assembly that is not referenced return new GetClassReturnType(pc, name, typeParameterCount); } } } static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) { if (attributeProvider == null || attributeProvider.HasCustomAttributes == false) return false; foreach (CustomAttribute a in attributeProvider.CustomAttributes) { if (a.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.DynamicAttribute") { if (a.ConstructorArguments.Count == 1) { CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) return (bool)values[typeIndex].Value; } return true; } } return false; } private sealed class CecilProjectContent : ReflectionProjectContent { public CecilProjectContent(string fullName, string fileName, DomAssemblyName[] referencedAssemblies, AssemblyDefinition assembly, ProjectContentRegistry registry) : base(fullName, fileName, referencedAssemblies, registry) { foreach (ModuleDefinition module in assembly.Modules) { AddTypes(module.Types); } AddAttributes(this, null, this.AssemblyCompilationUnit.Attributes, assembly); InitializeSpecialClasses(); this.AssemblyCompilationUnit.Freeze(); } void AddTypes(Collection types) { foreach (TypeDefinition td in types) { if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) { string name = td.FullName; if (name.Length == 0 || name[0] == '<') continue; name = ReflectionClass.SplitTypeParameterCountFromReflectionName(name); AddClassToNamespaceListInternal(new CecilClass(this.AssemblyCompilationUnit, null, td, name)); } } } } private sealed class CecilClass : DefaultClass { public static bool IsDelegate(TypeDefinition type) { if (type.BaseType == null) return false; else return type.BaseType.FullName == "System.Delegate" || type.BaseType.FullName == "System.MulticastDelegate"; } protected override bool KeepInheritanceTree { get { return true; } } public CecilClass(ICompilationUnit compilationUnit, IClass declaringType, TypeDefinition td, string fullName) : base(compilationUnit, declaringType) { this.FullyQualifiedName = fullName; AddAttributes(compilationUnit.ProjectContent, this, this.Attributes, td); // set classtype if (td.IsInterface) { this.ClassType = ClassType.Interface; } else if (td.IsEnum) { this.ClassType = ClassType.Enum; } else if (td.IsValueType) { this.ClassType = ClassType.Struct; } else if (IsDelegate(td)) { this.ClassType = ClassType.Delegate; } else { this.ClassType = ClassType.Class; } if (td.GenericParameters.Count > 0) { foreach (GenericParameter g in td.GenericParameters) { this.TypeParameters.Add(new DefaultTypeParameter(this, g.Name, g.Position)); } int i = 0; foreach (GenericParameter g in td.GenericParameters) { AddConstraintsFromType(this.TypeParameters[i++], g); } } ModifierEnum modifiers = ModifierEnum.None; if (td.IsSealed) { modifiers |= ModifierEnum.Sealed; } if (td.IsAbstract) { modifiers |= ModifierEnum.Abstract; } if (td.IsSealed && td.IsAbstract) { modifiers |= ModifierEnum.Static; } if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic) { modifiers |= ModifierEnum.Public; } else if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily) { modifiers |= ModifierEnum.Protected; } else if ((td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) { modifiers |= ModifierEnum.Protected; } else { modifiers |= ModifierEnum.Public; } this.Modifiers = modifiers; // set base classes if (td.BaseType != null) { BaseTypes.Add(CreateType(this.ProjectContent, this, td.BaseType)); } foreach (TypeReference iface in td.Interfaces) { BaseTypes.Add(CreateType(this.ProjectContent, this, iface)); } ReflectionClass.ApplySpecialsFromAttributes(this); InitMembers(td); } void InitMembers(TypeDefinition type) { string defaultMemberName = null; foreach (CustomAttribute att in type.CustomAttributes) { if (att.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute" && att.ConstructorArguments.Count == 1) { defaultMemberName = att.ConstructorArguments[0].Value as string; } } foreach (TypeDefinition nestedType in type.NestedTypes) { TypeAttributes visibility = nestedType.Attributes & TypeAttributes.VisibilityMask; if (visibility == TypeAttributes.NestedPublic || visibility == TypeAttributes.NestedFamily || visibility == TypeAttributes.NestedFamORAssem) { string name = nestedType.Name; int pos = name.LastIndexOf('/'); if (pos > 0) name = name.Substring(pos + 1); if (name.Length == 0 || name[0] == '<') continue; name = ReflectionClass.SplitTypeParameterCountFromReflectionName(name); name = this.FullyQualifiedName + "." + name; InnerClasses.Add(new CecilClass(this.CompilationUnit, this, nestedType, name)); } } foreach (FieldDefinition field in type.Fields) { if (IsVisible(field.Attributes) && !field.IsSpecialName) { DefaultField f = new DefaultField(this, field.Name); f.Modifiers = TranslateModifiers(field); f.ReturnType = CreateType(this.ProjectContent, this, field.FieldType, field); AddAttributes(CompilationUnit.ProjectContent, f, f.Attributes, field); Fields.Add(f); } } foreach (PropertyDefinition property in type.Properties) { AddProperty(defaultMemberName, property); } foreach (EventDefinition eventDef in type.Events) { if (eventDef.AddMethod != null && IsVisible(eventDef.AddMethod.Attributes)) { DefaultEvent e = new DefaultEvent(this, eventDef.Name); if (this.ClassType == ClassType.Interface) { e.Modifiers = ModifierEnum.Public | ModifierEnum.Abstract; } else { e.Modifiers = TranslateModifiers(eventDef); } e.ReturnType = CreateType(this.ProjectContent, this, eventDef.EventType, eventDef); AddAttributes(CompilationUnit.ProjectContent, e, e.Attributes, eventDef); Events.Add(e); } } this.AddDefaultConstructorIfRequired = (this.ClassType == ClassType.Struct || this.ClassType == ClassType.Enum); foreach (MethodDefinition method in type.Methods) { if (method.IsConstructor || !method.IsSpecialName) { AddMethod(method); } } } void AddProperty(string defaultMemberName, PropertyDefinition property) { if ((property.GetMethod != null && IsVisible(property.GetMethod.Attributes)) || (property.SetMethod != null && IsVisible(property.SetMethod.Attributes))) { DefaultProperty p = new DefaultProperty(this, property.Name); if (this.ClassType == ClassType.Interface) { p.Modifiers = ModifierEnum.Public | ModifierEnum.Abstract; } else { p.Modifiers = TranslateModifiers(property); } p.ReturnType = CreateType(this.ProjectContent, this, property.PropertyType, property); p.CanGet = property.GetMethod != null && IsVisible(property.GetMethod.Attributes); p.CanSet = property.SetMethod != null && IsVisible(property.SetMethod.Attributes); if (p.CanGet) p.GetterModifiers = GetAccessorVisibility(p, property.GetMethod); if (p.CanSet) p.SetterModifiers = GetAccessorVisibility(p, property.SetMethod); if (p.Name == defaultMemberName) { p.IsIndexer = true; } AddParameters(p, property.Parameters); AddAttributes(CompilationUnit.ProjectContent, p, p.Attributes, property); Properties.Add(p); } } static ModifierEnum GetAccessorVisibility(IProperty p, MethodDefinition accessor) { ModifierEnum visibility = ModifierEnum.VisibilityMask & TranslateModifiers(accessor); if (visibility == (p.Modifiers & ModifierEnum.VisibilityMask)) return ModifierEnum.None; else return visibility; } void AddMethod(MethodDefinition method) { if (IsVisible(method.Attributes)) { DefaultMethod m = new DefaultMethod(this, method.IsConstructor ? "#ctor" : method.Name); if (method.GenericParameters.Count > 0) { foreach (GenericParameter g in method.GenericParameters) { m.TypeParameters.Add(new DefaultTypeParameter(m, g.Name, g.Position)); } int i = 0; foreach (GenericParameter g in method.GenericParameters) { AddConstraintsFromType(m.TypeParameters[i++], g); } } if (method.IsConstructor) m.ReturnType = this.DefaultReturnType; else m.ReturnType = CreateType(this.ProjectContent, m, method.ReturnType, method.MethodReturnType); AddAttributes(CompilationUnit.ProjectContent, m, m.Attributes, method); if (this.ClassType == ClassType.Interface) { m.Modifiers = ModifierEnum.Public | ModifierEnum.Abstract; } else { m.Modifiers = TranslateModifiers(method); } AddParameters(m, method.Parameters); AddExplicitInterfaceImplementations(method.Overrides, m); ReflectionLayer.ReflectionMethod.ApplySpecialsFromAttributes(m); Methods.Add(m); } } void AddExplicitInterfaceImplementations(Collection overrides, IMember targetMember) { foreach (MethodReference overrideRef in overrides) { if (overrideRef.Name == targetMember.Name && targetMember.IsPublic) { continue; // is like implicit interface implementation / normal override } targetMember.InterfaceImplementations.Add(new ExplicitInterfaceImplementation( CreateType(this.ProjectContent, targetMember, overrideRef.DeclaringType), overrideRef.Name )); } } void AddParameters(IMethodOrProperty target, Collection plist) { foreach (ParameterDefinition par in plist) { IReturnType pReturnType = CreateType(this.ProjectContent, target, par.ParameterType, par); DefaultParameter p = new DefaultParameter(par.Name, pReturnType, DomRegion.Empty); if (par.ParameterType is ByReferenceType) { if ((par.Attributes & ParameterAttributes.Out) == ParameterAttributes.Out) { p.Modifiers = ParameterModifiers.Out; } else { p.Modifiers = ParameterModifiers.Ref; } } else { p.Modifiers = ParameterModifiers.In; } if ((par.Attributes & ParameterAttributes.Optional) == ParameterAttributes.Optional) { p.Modifiers |= ParameterModifiers.Optional; } if (p.ReturnType.IsArrayReturnType) { foreach (CustomAttribute att in par.CustomAttributes) { if (att.Constructor.DeclaringType.FullName == typeof(ParamArrayAttribute).FullName) { p.Modifiers |= ParameterModifiers.Params; } } } target.Parameters.Add(p); } } static bool IsVisible(MethodAttributes att) { return ((att & MethodAttributes.Public) == MethodAttributes.Public) || ((att & MethodAttributes.Family) == MethodAttributes.Family) || ((att & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem); } static bool IsVisible(FieldAttributes att) { return ((att & FieldAttributes.Public) == FieldAttributes.Public) || ((att & FieldAttributes.Family) == FieldAttributes.Family) || ((att & FieldAttributes.FamORAssem) == FieldAttributes.FamORAssem); } static ModifierEnum TranslateModifiers(MethodDefinition method) { ModifierEnum m = ModifierEnum.None; if (method.IsStatic) { m |= ModifierEnum.Static; } else { if (method.IsAbstract) { m |= ModifierEnum.Abstract; } else if (method.IsFinal) { m |= ModifierEnum.Sealed; } else if (method.Overrides.Count > 0) { m |= ModifierEnum.Override; } else if (method.IsVirtual) { if (method.IsNewSlot) m |= ModifierEnum.Virtual; else m |= ModifierEnum.Override; } } if ((method.Attributes & MethodAttributes.Public) == MethodAttributes.Public) m |= ModifierEnum.Public; else m |= ModifierEnum.Protected; return m; } static ModifierEnum TranslateModifiers(PropertyDefinition property) { return TranslateModifiers(property.GetMethod ?? property.SetMethod); } static ModifierEnum TranslateModifiers(EventDefinition @event) { return TranslateModifiers(@event.AddMethod); } static ModifierEnum TranslateModifiers(FieldDefinition field) { ModifierEnum m = ModifierEnum.None; if (field.IsStatic) m |= ModifierEnum.Static; if (field.IsLiteral) m |= ModifierEnum.Const; else if (field.IsInitOnly) m |= ModifierEnum.Readonly; if ((field.Attributes & FieldAttributes.Public) == FieldAttributes.Public) m |= ModifierEnum.Public; else m |= ModifierEnum.Protected; return m; } } } }