You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
10 KiB

7 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Immutable;
  4. using System.Linq;
  5. using System.Reflection.Metadata;
  6. using System.Reflection.Metadata.Ecma335;
  7. using System.Reflection.PortableExecutable;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using ICSharpCode.Decompiler.Disassembler;
  11. using ICSharpCode.Decompiler.Metadata;
  12. using ICSharpCode.Decompiler.Semantics;
  13. using ICSharpCode.Decompiler.TypeSystem;
  14. using ICSharpCode.Decompiler.TypeSystem.Implementation;
  15. using ICSharpCode.Decompiler.Util;
  16. using static ICSharpCode.Decompiler.Metadata.ILOpCodeExtensions;
  17. namespace ICSharpCode.Decompiler.CSharp
  18. {
  19. class RequiredNamespaceCollector
  20. {
  21. public static void CollectNamespaces(MetadataModule module, HashSet<string> namespaces)
  22. {
  23. foreach (var type in module.TypeDefinitions) {
  24. CollectNamespaces(type, module, namespaces);
  25. }
  26. CollectAttributeNamespaces(module, namespaces);
  27. }
  28. public static void CollectAttributeNamespaces(MetadataModule module, HashSet<string> namespaces)
  29. {
  30. HandleAttributes(module.GetAssemblyAttributes(), namespaces);
  31. HandleAttributes(module.GetModuleAttributes(), namespaces);
  32. }
  33. static readonly Decompiler.TypeSystem.GenericContext genericContext = default;
  34. public static void CollectNamespaces(IEntity entity, MetadataModule module,
  35. HashSet<string> namespaces, CodeMappingInfo mappingInfo = null)
  36. {
  37. if (entity == null || entity.MetadataToken.IsNil)
  38. return;
  39. switch (entity) {
  40. case ITypeDefinition td:
  41. if (mappingInfo == null)
  42. mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken);
  43. namespaces.Add(td.Namespace);
  44. HandleAttributes(td.GetAttributes(), namespaces);
  45. HandleTypeParameters(td.TypeParameters, namespaces);
  46. foreach (var baseType in td.DirectBaseTypes) {
  47. CollectNamespacesForTypeReference(baseType, namespaces);
  48. }
  49. foreach (var nestedType in td.NestedTypes) {
  50. CollectNamespaces(nestedType, module, namespaces, mappingInfo);
  51. }
  52. foreach (var field in td.Fields) {
  53. CollectNamespaces(field, module, namespaces, mappingInfo);
  54. }
  55. foreach (var property in td.Properties) {
  56. CollectNamespaces(property, module, namespaces, mappingInfo);
  57. }
  58. foreach (var @event in td.Events) {
  59. CollectNamespaces(@event, module, namespaces, mappingInfo);
  60. }
  61. foreach (var method in td.Methods) {
  62. CollectNamespaces(method, module, namespaces, mappingInfo);
  63. }
  64. break;
  65. case IField field:
  66. HandleAttributes(field.GetAttributes(), namespaces);
  67. CollectNamespacesForTypeReference(field.ReturnType, namespaces);
  68. break;
  69. case IMethod method:
  70. HandleAttributes(method.GetAttributes(), namespaces);
  71. HandleAttributes(method.GetReturnTypeAttributes(), namespaces);
  72. CollectNamespacesForTypeReference(method.ReturnType, namespaces);
  73. foreach (var param in method.Parameters) {
  74. HandleAttributes(param.GetAttributes(), namespaces);
  75. CollectNamespacesForTypeReference(param.Type, namespaces);
  76. }
  77. HandleTypeParameters(method.TypeParameters, namespaces);
  78. if (!method.MetadataToken.IsNil && method.HasBody) {
  79. if (mappingInfo == null)
  80. mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken);
  81. var reader = module.PEFile.Reader;
  82. var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
  83. foreach (var part in parts) {
  84. var methodDef = module.metadata.GetMethodDefinition(part);
  85. MethodBodyBlock body;
  86. try {
  87. body = reader.GetMethodBody(methodDef.RelativeVirtualAddress);
  88. } catch (BadImageFormatException) {
  89. continue;
  90. }
  91. CollectNamespacesFromMethodBody(body, module, namespaces);
  92. }
  93. }
  94. break;
  95. case IProperty property:
  96. HandleAttributes(property.GetAttributes(), namespaces);
  97. CollectNamespaces(property.Getter, module, namespaces);
  98. CollectNamespaces(property.Setter, module, namespaces);
  99. break;
  100. case IEvent @event:
  101. HandleAttributes(@event.GetAttributes(), namespaces);
  102. CollectNamespaces(@event.AddAccessor, module, namespaces);
  103. CollectNamespaces(@event.RemoveAccessor, module, namespaces);
  104. break;
  105. }
  106. }
  107. static void CollectNamespacesForTypeReference(IType type, HashSet<string> namespaces)
  108. {
  109. switch (type) {
  110. case ParameterizedType parameterizedType:
  111. namespaces.Add(parameterizedType.Namespace);
  112. CollectNamespacesForTypeReference(parameterizedType.GenericType, namespaces);
  113. foreach (var arg in parameterizedType.TypeArguments)
  114. CollectNamespacesForTypeReference(arg, namespaces);
  115. break;
  116. case TypeWithElementType typeWithElementType:
  117. CollectNamespacesForTypeReference(typeWithElementType.ElementType, namespaces);
  118. break;
  119. case TupleType tupleType:
  120. foreach (var elementType in tupleType.ElementTypes) {
  121. CollectNamespacesForTypeReference(elementType, namespaces);
  122. }
  123. break;
  124. default:
  125. namespaces.Add(type.Namespace);
  126. break;
  127. }
  128. }
  129. public static void CollectNamespaces(EntityHandle entity, MetadataModule module, HashSet<string> namespaces)
  130. {
  131. if (entity.IsNil) return;
  132. CollectNamespaces(module.ResolveEntity(entity, genericContext), module, namespaces);
  133. }
  134. public static void HandleAttributes(IEnumerable<IAttribute> attributes, HashSet<string> namespaces)
  135. {
  136. foreach (var attr in attributes) {
  137. namespaces.Add(attr.AttributeType.Namespace);
  138. foreach (var arg in attr.FixedArguments) {
  139. HandleAttributeValue(arg.Type, arg.Value, namespaces);
  140. }
  141. foreach (var arg in attr.NamedArguments) {
  142. HandleAttributeValue(arg.Type, arg.Value, namespaces);
  143. }
  144. }
  145. }
  146. static void HandleAttributeValue(IType type, object value, HashSet<string> namespaces)
  147. {
  148. CollectNamespacesForTypeReference(type, namespaces);
  149. if (value is IType typeofType)
  150. CollectNamespacesForTypeReference(typeofType, namespaces);
  151. if (value is ImmutableArray<CustomAttributeTypedArgument<IType>> arr) {
  152. foreach (var element in arr) {
  153. HandleAttributeValue(element.Type, element.Value, namespaces);
  154. }
  155. }
  156. }
  157. static void HandleTypeParameters(IEnumerable<ITypeParameter> typeParameters, HashSet<string> namespaces)
  158. {
  159. foreach (var typeParam in typeParameters) {
  160. HandleAttributes(typeParam.GetAttributes(), namespaces);
  161. foreach (var constraint in typeParam.DirectBaseTypes) {
  162. CollectNamespacesForTypeReference(constraint, namespaces);
  163. }
  164. }
  165. }
  166. static void CollectNamespacesFromMethodBody(MethodBodyBlock method, MetadataModule module, HashSet<string> namespaces)
  167. {
  168. var metadata = module.metadata;
  169. var instructions = method.GetILReader();
  170. if (!method.LocalSignature.IsNil) {
  171. ImmutableArray<IType> localSignature;
  172. try {
  173. localSignature = module.DecodeLocalSignature(method.LocalSignature, genericContext);
  174. } catch (BadImageFormatException) {
  175. // Issue #1211: ignore invalid local signatures
  176. localSignature = ImmutableArray<IType>.Empty;
  177. }
  178. foreach (var type in localSignature)
  179. CollectNamespacesForTypeReference(type, namespaces);
  180. }
  181. foreach (var region in method.ExceptionRegions) {
  182. if (region.CatchType.IsNil)
  183. continue;
  184. IType ty;
  185. try {
  186. ty = module.ResolveType(region.CatchType, genericContext);
  187. } catch (BadImageFormatException) {
  188. continue;
  189. }
  190. CollectNamespacesForTypeReference(ty, namespaces);
  191. }
  192. while (instructions.RemainingBytes > 0) {
  193. ILOpCode opCode;
  194. try {
  195. opCode = instructions.DecodeOpCode();
  196. } catch (BadImageFormatException) {
  197. return;
  198. }
  199. switch (opCode.GetOperandType()) {
  200. case Metadata.OperandType.Field:
  201. case Metadata.OperandType.Method:
  202. case Metadata.OperandType.Sig:
  203. case Metadata.OperandType.Tok:
  204. case Metadata.OperandType.Type:
  205. var handle = MetadataTokenHelpers.EntityHandleOrNil(instructions.ReadInt32());
  206. if (handle.IsNil)
  207. break;
  208. switch (handle.Kind) {
  209. case HandleKind.TypeDefinition:
  210. case HandleKind.TypeReference:
  211. case HandleKind.TypeSpecification:
  212. IType type;
  213. try {
  214. type = module.ResolveType(handle, genericContext);
  215. } catch (BadImageFormatException) {
  216. break;
  217. }
  218. CollectNamespacesForTypeReference(type, namespaces);
  219. break;
  220. case HandleKind.FieldDefinition:
  221. case HandleKind.MethodDefinition:
  222. case HandleKind.MethodSpecification:
  223. case HandleKind.MemberReference:
  224. IMember member;
  225. try {
  226. member = module.ResolveEntity(handle, genericContext) as IMember;
  227. } catch (BadImageFormatException) {
  228. break;
  229. }
  230. CollectNamespacesForMemberReference(member, module, namespaces);
  231. break;
  232. case HandleKind.StandaloneSignature:
  233. StandaloneSignature sig;
  234. try {
  235. sig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)handle);
  236. } catch (BadImageFormatException) {
  237. break;
  238. }
  239. if (sig.GetKind() == StandaloneSignatureKind.Method) {
  240. MethodSignature<IType> methodSig;
  241. try {
  242. methodSig = module.DecodeMethodSignature((StandaloneSignatureHandle)handle, genericContext);
  243. } catch (BadImageFormatException) {
  244. break;
  245. }
  246. CollectNamespacesForTypeReference(methodSig.ReturnType, namespaces);
  247. foreach (var paramType in methodSig.ParameterTypes) {
  248. CollectNamespacesForTypeReference(paramType, namespaces);
  249. }
  250. }
  251. break;
  252. }
  253. break;
  254. default:
  255. try {
  256. instructions.SkipOperand(opCode);
  257. } catch (BadImageFormatException) {
  258. return;
  259. }
  260. break;
  261. }
  262. }
  263. }
  264. static void CollectNamespacesForMemberReference(IMember member, MetadataModule module, HashSet<string> namespaces)
  265. {
  266. switch (member) {
  267. case IField field:
  268. CollectNamespacesForTypeReference(field.DeclaringType, namespaces);
  269. CollectNamespacesForTypeReference(field.ReturnType, namespaces);
  270. break;
  271. case IMethod method:
  272. CollectNamespacesForTypeReference(method.DeclaringType, namespaces);
  273. CollectNamespacesForTypeReference(method.ReturnType, namespaces);
  274. foreach (var param in method.Parameters)
  275. CollectNamespacesForTypeReference(param.Type, namespaces);
  276. foreach (var arg in method.TypeArguments)
  277. CollectNamespacesForTypeReference(arg, namespaces);
  278. break;
  279. }
  280. }
  281. }
  282. }