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.

143 lines
6.0 KiB

  1. // Copyright (c) 2021 Siegfried Pammer
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using System.Reflection.Metadata;
  22. using ICSharpCode.Decompiler.Metadata;
  23. using ICSharpCode.Decompiler.TypeSystem;
  24. using ICSharpCode.Decompiler.TypeSystem.Implementation;
  25. using ICSharpCode.Decompiler.Util;
  26. namespace ILSpy.BamlDecompiler
  27. {
  28. class BamlDecompilerTypeSystem : SimpleCompilation, IDecompilerTypeSystem
  29. {
  30. string[] defaultBamlReferences = new[] {
  31. "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
  32. "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
  33. "WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
  34. "PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
  35. "PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
  36. "PresentationUI, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
  37. "System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
  38. };
  39. public BamlDecompilerTypeSystem(PEFile mainModule, IAssemblyResolver assemblyResolver)
  40. {
  41. if (mainModule == null)
  42. throw new ArgumentNullException(nameof(mainModule));
  43. if (assemblyResolver == null)
  44. throw new ArgumentNullException(nameof(assemblyResolver));
  45. // Load referenced assemblies and type-forwarder references.
  46. // This is necessary to make .NET Core/PCL binaries work better.
  47. var referencedAssemblies = new List<PEFile>();
  48. var assemblyReferenceQueue = new Queue<(bool IsAssembly, PEFile MainModule, object Reference)>();
  49. var mainMetadata = mainModule.Metadata;
  50. foreach (var h in mainMetadata.GetModuleReferences())
  51. {
  52. var moduleRef = mainMetadata.GetModuleReference(h);
  53. var moduleName = mainMetadata.GetString(moduleRef.Name);
  54. foreach (var fileHandle in mainMetadata.AssemblyFiles)
  55. {
  56. var file = mainMetadata.GetAssemblyFile(fileHandle);
  57. if (mainMetadata.StringComparer.Equals(file.Name, moduleName) && file.ContainsMetadata)
  58. {
  59. assemblyReferenceQueue.Enqueue((false, mainModule, moduleName));
  60. break;
  61. }
  62. }
  63. }
  64. foreach (var refs in mainModule.AssemblyReferences)
  65. {
  66. assemblyReferenceQueue.Enqueue((true, mainModule, refs));
  67. }
  68. foreach (var bamlReference in defaultBamlReferences)
  69. {
  70. assemblyReferenceQueue.Enqueue((true, mainModule, AssemblyNameReference.Parse(bamlReference)));
  71. }
  72. var comparer = KeyComparer.Create(((bool IsAssembly, PEFile MainModule, object Reference) reference) =>
  73. reference.IsAssembly ? "A:" + ((IAssemblyReference)reference.Reference).FullName :
  74. "M:" + reference.Reference);
  75. var processedAssemblyReferences = new HashSet<(bool IsAssembly, PEFile Parent, object Reference)>(comparer);
  76. while (assemblyReferenceQueue.Count > 0)
  77. {
  78. var asmRef = assemblyReferenceQueue.Dequeue();
  79. if (!processedAssemblyReferences.Add(asmRef))
  80. continue;
  81. PEFile asm;
  82. if (asmRef.IsAssembly)
  83. {
  84. asm = assemblyResolver.Resolve((IAssemblyReference)asmRef.Reference);
  85. }
  86. else
  87. {
  88. asm = assemblyResolver.ResolveModule(asmRef.MainModule, (string)asmRef.Reference);
  89. }
  90. if (asm != null)
  91. {
  92. referencedAssemblies.Add(asm);
  93. var metadata = asm.Metadata;
  94. foreach (var h in metadata.ExportedTypes)
  95. {
  96. var exportedType = metadata.GetExportedType(h);
  97. switch (exportedType.Implementation.Kind)
  98. {
  99. case HandleKind.AssemblyReference:
  100. assemblyReferenceQueue.Enqueue((true, asm, new ICSharpCode.Decompiler.Metadata.AssemblyReference(asm, (AssemblyReferenceHandle)exportedType.Implementation)));
  101. break;
  102. case HandleKind.AssemblyFile:
  103. var file = metadata.GetAssemblyFile((AssemblyFileHandle)exportedType.Implementation);
  104. assemblyReferenceQueue.Enqueue((false, asm, metadata.GetString(file.Name)));
  105. break;
  106. }
  107. }
  108. }
  109. }
  110. var mainModuleWithOptions = mainModule.WithOptions(TypeSystemOptions.Default);
  111. var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(TypeSystemOptions.Default));
  112. // Primitive types are necessary to avoid assertions in ILReader.
  113. // Fallback to MinimalCorlib to provide the primitive types.
  114. if (!HasType(KnownTypeCode.Void) || !HasType(KnownTypeCode.Int32))
  115. {
  116. Init(mainModule.WithOptions(TypeSystemOptions.Default), referencedAssembliesWithOptions.Concat(new[] { MinimalCorlib.Instance }));
  117. }
  118. else
  119. {
  120. Init(mainModuleWithOptions, referencedAssembliesWithOptions);
  121. }
  122. this.MainModule = (MetadataModule)base.MainModule;
  123. bool HasType(KnownTypeCode code)
  124. {
  125. TopLevelTypeName name = KnownTypeReference.Get(code).TypeName;
  126. if (!mainModule.GetTypeDefinition(name).IsNil)
  127. return true;
  128. foreach (var file in referencedAssemblies)
  129. {
  130. if (!file.GetTypeDefinition(name).IsNil)
  131. return true;
  132. }
  133. return false;
  134. }
  135. }
  136. public new MetadataModule MainModule { get; }
  137. }
  138. }