Browse Source

Add WebCIL support

pull/3184/head
Siegfried Pammer 1 year ago
parent
commit
921bf36261
  1. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  2. 39
      ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs
  3. 1
      ICSharpCode.Decompiler/Metadata/MetadataFile.cs
  4. 284
      ICSharpCode.Decompiler/Metadata/WebCilFile.cs
  5. 8
      ICSharpCode.Decompiler/SRMExtensions.cs
  6. 8
      ICSharpCode.Decompiler/SingleFileBundle.cs
  7. 10
      ICSharpCode.ILSpyX/LoadedAssembly.cs
  8. 1
      ILSpy/Images/Images.cs
  9. 8
      ILSpy/Images/WebAssembly.svg
  10. 7
      ILSpy/Images/WebAssembly.xaml
  11. 2
      ILSpy/MainWindow.xaml.cs
  12. 70
      ILSpy/TreeNodes/AssemblyTreeNode.cs

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -147,6 +147,7 @@
<Compile Include="TypeSystem\ITypeDefinitionOrUnknown.cs" /> <Compile Include="TypeSystem\ITypeDefinitionOrUnknown.cs" />
<Compile Include="Util\BitOperations.cs" /> <Compile Include="Util\BitOperations.cs" />
<Compile Include="Util\Index.cs" /> <Compile Include="Util\Index.cs" />
<Compile Include="Metadata\WebCilFile.cs" />
<None Include="Properties\DecompilerVersionInfo.template.cs" /> <None Include="Properties\DecompilerVersionInfo.template.cs" />
<Compile Include="Semantics\OutVarResolveResult.cs" /> <Compile Include="Semantics\OutVarResolveResult.cs" />
<Compile Include="SingleFileBundle.cs" /> <Compile Include="SingleFileBundle.cs" />

39
ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

@ -1,6 +1,25 @@
using System; // Copyright (c) 2018 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata; using System.Reflection.Metadata;
@ -426,5 +445,23 @@ namespace ICSharpCode.Decompiler.Metadata
return new(metadataReader.MetadataPointer, metadataReader.MetadataLength); return new(metadataReader.MetadataPointer, metadataReader.MetadataLength);
} }
} }
public static uint ReadULEB128(this BinaryReader reader)
{
uint val = 0;
int shift = 0;
while (true)
{
byte b = reader.ReadByte();
val |= (b & 0b0111_1111u) << shift;
if ((b & 0b1000_0000) == 0)
break;
shift += 7;
if (shift >= 35)
throw new OverflowException();
}
return val;
}
} }
} }

1
ICSharpCode.Decompiler/Metadata/MetadataFile.cs

@ -51,6 +51,7 @@ namespace ICSharpCode.Decompiler.Metadata
{ {
PortableExecutable, PortableExecutable,
ProgramDebugDatabase, ProgramDebugDatabase,
WebCIL,
Metadata Metadata
} }

284
ICSharpCode.Decompiler/Metadata/WebCilFile.cs

@ -0,0 +1,284 @@
// Copyright (c) 2024 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Reflection.Metadata;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem;
#nullable enable
namespace ICSharpCode.Decompiler.Metadata
{
public class WebCilFile : MetadataFile, IDisposable, IModuleReference
{
readonly MemoryMappedViewAccessor view;
readonly long webcilOffset;
private WebCilFile(string fileName, long webcilOffset, long metadataOffset, MemoryMappedViewAccessor view, ImmutableArray<SectionHeader> sectionHeaders, ImmutableArray<WasmSection> wasmSections, MetadataReaderProvider provider, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
: base(MetadataFileKind.WebCIL, fileName, provider, metadataOptions, 0)
{
this.webcilOffset = webcilOffset;
this.MetadataOffset = (int)metadataOffset;
this.view = view;
this.SectionHeaders = sectionHeaders;
this.WasmSections = wasmSections;
}
public static WebCilFile? FromStream(string fileName, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
{
using var memoryMappedFile = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
var view = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
try
{
// read magic "\0asm"
if (view.ReadUInt32(0) != WASM_MAGIC)
return null;
// read version
if (view.ReadUInt32(4) != 1)
return null;
using var stream = view.AsStream();
using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
stream.Position += 8;
long metadataOffset = -1;
List<WasmSection> sections = new List<WasmSection>();
while (stream.Position < stream.Length)
{
WasmSectionId id = (WasmSectionId)reader.ReadByte();
uint size = reader.ReadULEB128();
sections.Add(new WasmSection(id, stream.Position, size, view));
if (id == WasmSectionId.Custom && size == 0)
{
break;
}
stream.Seek(size, SeekOrigin.Current);
}
foreach (var section in sections)
{
if (section.Id != WasmSectionId.Data || metadataOffset > -1)
continue;
stream.Seek(section.Offset, SeekOrigin.Begin);
uint numSegments = reader.ReadULEB128();
if (numSegments != 2)
continue;
// skip the first segment
if (reader.ReadByte() != 1)
continue;
long segmentLength = reader.ReadULEB128();
long segmentStart = reader.BaseStream.Position;
reader.BaseStream.Seek(segmentLength, SeekOrigin.Current);
if (reader.ReadByte() != 1)
continue;
segmentLength = reader.ReadULEB128();
if (TryReadWebCilSegment(reader, out var header, out metadataOffset, out var webcilOffset, out var sectionHeaders))
{
stream.Seek(metadataOffset, SeekOrigin.Begin);
var metadata = MetadataReaderProvider.FromMetadataStream(stream, MetadataStreamOptions.LeaveOpen | MetadataStreamOptions.PrefetchMetadata);
var result = new WebCilFile(fileName, webcilOffset, metadataOffset, view, ImmutableArray.Create(sectionHeaders), sections.ToImmutableArray(), metadata, metadataOptions);
view = null; // don't dispose the view, we're still using it in the sections
return result;
}
}
return null;
}
finally
{
view?.Dispose();
}
}
static unsafe bool TryReadWebCilSegment(BinaryReader reader, out WebcilHeader webcilHeader, out long metadataOffset, out long webcilOffset, [NotNullWhen(true)] out SectionHeader[]? sectionHeaders)
{
webcilHeader = default;
metadataOffset = -1;
sectionHeaders = null;
webcilOffset = reader.BaseStream.Position;
if (reader.ReadUInt32() != WEBCIL_MAGIC)
return false;
webcilHeader.VersionMajor = reader.ReadUInt16();
webcilHeader.VersionMinor = reader.ReadUInt16();
webcilHeader.CoffSections = reader.ReadUInt16();
_ = reader.ReadUInt16(); // reserved0
webcilHeader.PECliHeaderRVA = reader.ReadUInt32();
webcilHeader.PECliHeaderSize = reader.ReadUInt32();
webcilHeader.PEDebugRVA = reader.ReadUInt32();
webcilHeader.PEDebugSize = reader.ReadUInt32();
sectionHeaders = new SectionHeader[webcilHeader.CoffSections];
for (int i = 0; i < webcilHeader.CoffSections; i++)
{
sectionHeaders[i].VirtualSize = reader.ReadUInt32();
sectionHeaders[i].VirtualAddress = reader.ReadUInt32();
sectionHeaders[i].RawDataSize = reader.ReadUInt32();
sectionHeaders[i].RawDataPtr = reader.ReadUInt32();
}
long corHeaderStart = TranslateRVA(sectionHeaders, webcilOffset, webcilHeader.PECliHeaderRVA);
if (reader.BaseStream.Seek(corHeaderStart, SeekOrigin.Begin) != corHeaderStart)
return false;
int byteCount = reader.ReadInt32();
int majorVersion = reader.ReadUInt16();
int minorVersion = reader.ReadUInt16();
metadataOffset = TranslateRVA(sectionHeaders, webcilOffset, (uint)reader.ReadInt32());
return reader.BaseStream.Seek(metadataOffset, SeekOrigin.Begin) == metadataOffset;
}
public override int MetadataOffset { get; }
private static int GetContainingSectionIndex(IEnumerable<SectionHeader> sections, int rva)
{
int i = 0;
foreach (var section in sections)
{
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
{
return i;
}
i++;
}
return -1;
}
private static long TranslateRVA(IEnumerable<SectionHeader> sections, long webcilOffset, uint rva)
{
foreach (var section in sections)
{
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
{
return section.RawDataPtr + (rva - section.VirtualAddress) + webcilOffset;
}
}
throw new BadImageFormatException("RVA not found in any section");
}
public override MethodBodyBlock GetMethodBody(int rva)
{
var reader = GetSectionData(rva).GetReader();
return MethodBodyBlock.Create(reader);
}
public override int GetContainingSectionIndex(int rva)
{
return GetContainingSectionIndex(SectionHeaders, rva);
}
public override unsafe SectionData GetSectionData(int rva)
{
foreach (var section in SectionHeaders)
{
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
{
byte* ptr = (byte*)0;
view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
return new SectionData(ptr + section.RawDataPtr + webcilOffset + (rva - section.VirtualAddress), (int)section.RawDataSize);
}
}
throw new BadImageFormatException("RVA not found in any section");
}
public override ImmutableArray<SectionHeader> SectionHeaders { get; }
public ImmutableArray<WasmSection> WasmSections { get; }
IModule? IModuleReference.Resolve(ITypeResolveContext context)
{
return new MetadataModule(context.Compilation, this, TypeSystemOptions.Default);
}
public void Dispose()
{
view.Dispose();
}
public struct WebcilHeader
{
public ushort VersionMajor;
public ushort VersionMinor;
public ushort CoffSections;
public uint PECliHeaderRVA;
public uint PECliHeaderSize;
public uint PEDebugRVA;
public uint PEDebugSize;
}
const uint WASM_MAGIC = 0x6d736100u; // "\0asm"
const uint WEBCIL_MAGIC = 0x4c496257u; // "WbIL"
[DebuggerDisplay("WasmSection {Id}: {Offset} {Size}")]
public class WasmSection
{
public WasmSectionId Id;
public long Offset;
public uint Size;
private MemoryMappedViewAccessor view;
public WasmSection(WasmSectionId id, long offset, uint size, MemoryMappedViewAccessor view)
{
this.Id = id;
this.Size = size;
this.Offset = offset;
this.view = view;
}
}
public enum WasmSectionId : byte
{
// order matters: enum values must match the WebAssembly spec
Custom = 0,
Type = 1,
Import = 2,
Function = 3,
Table = 4,
Memory = 5,
Global = 6,
Export = 7,
Start = 8,
Element = 9,
Code = 10,
Data = 11,
DataCount = 12,
}
}
}

8
ICSharpCode.Decompiler/SRMExtensions.cs

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
@ -748,5 +750,11 @@ namespace ICSharpCode.Decompiler
_ => callConv.ToString().ToLowerInvariant() _ => callConv.ToString().ToLowerInvariant()
}; };
} }
public static UnmanagedMemoryStream AsStream(this MemoryMappedViewAccessor view)
{
long size = checked((long)view.SafeMemoryMappedViewHandle.ByteLength);
return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, 0, size);
}
} }
} }

8
ICSharpCode.Decompiler/SingleFileBundle.cs

@ -104,18 +104,12 @@ namespace ICSharpCode.Decompiler
public string RelativePath; // Path of an embedded file, relative to the Bundle source-directory. public string RelativePath; // Path of an embedded file, relative to the Bundle source-directory.
} }
static UnmanagedMemoryStream AsStream(MemoryMappedViewAccessor view)
{
long size = checked((long)view.SafeMemoryMappedViewHandle.ByteLength);
return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, 0, size);
}
/// <summary> /// <summary>
/// Reads the manifest header from the memory mapping. /// Reads the manifest header from the memory mapping.
/// </summary> /// </summary>
public static Header ReadManifest(MemoryMappedViewAccessor view, long bundleHeaderOffset) public static Header ReadManifest(MemoryMappedViewAccessor view, long bundleHeaderOffset)
{ {
using var stream = AsStream(view); using var stream = view.AsStream();
stream.Seek(bundleHeaderOffset, SeekOrigin.Begin); stream.Seek(bundleHeaderOffset, SeekOrigin.Begin);
return ReadManifest(stream); return ReadManifest(stream);
} }

10
ICSharpCode.ILSpyX/LoadedAssembly.cs

@ -383,6 +383,16 @@ namespace ICSharpCode.ILSpyX
bundle.LoadedAssembly = this; bundle.LoadedAssembly = this;
return new LoadResult(loadAssemblyException, bundle); return new LoadResult(loadAssemblyException, bundle);
} }
// If it's not a .NET module, maybe it's a WASM module
var wasm = WebCilFile.FromStream(fileName);
if (wasm != null)
{
lock (loadedAssemblies)
{
loadedAssemblies.Add(wasm, this);
}
return new LoadResult(loadAssemblyException, wasm);
}
// If it's not a .NET module, maybe it's a zip archive (e.g. .nupkg) // If it's not a .NET module, maybe it's a zip archive (e.g. .nupkg)
try try
{ {

1
ILSpy/Images/Images.cs

@ -59,6 +59,7 @@ namespace ICSharpCode.ILSpy
public static readonly ImageSource ReferenceFolder = Load("ReferenceFolder"); public static readonly ImageSource ReferenceFolder = Load("ReferenceFolder");
public static readonly ImageSource NuGet = Load(null, "Images/NuGet.png"); public static readonly ImageSource NuGet = Load(null, "Images/NuGet.png");
public static readonly ImageSource MetadataFile = Load("MetadataFile"); public static readonly ImageSource MetadataFile = Load("MetadataFile");
public static readonly ImageSource WebAssemblyFile = Load("WebAssembly");
public static readonly ImageSource ProgramDebugDatabase = Load("ProgramDebugDatabase"); public static readonly ImageSource ProgramDebugDatabase = Load("ProgramDebugDatabase");
public static readonly ImageSource Metadata = Load("Metadata"); public static readonly ImageSource Metadata = Load("Metadata");

8
ILSpy/Images/WebAssembly.svg

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created by AtomCrusher for the English Wikipedia -->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="612" height="612">
<!-- Block -->
<path d="m376 0c0 1.08 0 2.16 0 3.3 0 38.76-31.42 70.17-70.17 70.17-38.76 0-70.17-31.42-70.17-70.17l0 0c0-1.14 0-2.22 0-3.3L0 0l0 612 612 0 0-612z" fill="#654ff0"/>
<!-- Letters -->
<path d="m142.16 329.81 40.56 0 27.69 147.47 0.5 0 33.28-147.47 37.94 0 30.06 149.28 0.59 0 31.56-149.28 39.78 0-51.69 216.69-40.25 0-29.81-147.47-0.78 0-31.91 147.47-41 0zm287.69 0 63.94 0 63.5 216.69-41.84 0-13.81-48.22-72.84 0-10.66 48.22-40.75 0zm24.34 53.41-17.69 79.5 55.06 0-20.31-79.5z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 709 B

7
ILSpy/Images/WebAssembly.xaml

@ -0,0 +1,7 @@
<DrawingGroup xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ClipGeometry="M0,0 V612 H612 V0 H0 Z">
<DrawingGroup.Transform>
<TranslateTransform X="0" Y="2.6645352591003757E-15" />
</DrawingGroup.Transform>
<GeometryDrawing Brush="#FF654FF0" Geometry="F1 M612,612z M0,0z M376,0C376,1.08 376,2.16 376,3.3 376,42.06 344.58,73.47 305.83,73.47 267.07,73.47 235.66,42.05 235.66,3.3L235.66,3.3C235.66,2.16,235.66,1.08,235.66,-2.66453525910038E-15L0,0 0,612 612,612 612,0z" />
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M612,612z M0,0z M142.16,329.81L182.72,329.81 210.41,477.28 210.91,477.28 244.19,329.81 282.13,329.81 312.19,479.09 312.78,479.09 344.34,329.81 384.12,329.81 332.43,546.5 292.18,546.5 262.37,399.03 261.59,399.03 229.68,546.5 188.68,546.5z M429.85,329.81L493.79,329.81 557.29,546.5 515.45,546.5 501.64,498.28 428.8,498.28 418.14,546.5 377.39,546.5z M454.19,383.22L436.5,462.72 491.56,462.72 471.25,383.22z" />
</DrawingGroup>

2
ILSpy/MainWindow.xaml.cs

@ -1385,7 +1385,7 @@ namespace ICSharpCode.ILSpy
{ {
e.Handled = true; e.Handled = true;
OpenFileDialog dlg = new OpenFileDialog(); OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd|Nuget Packages (*.nupkg)|*.nupkg|Portable Program Database (*.pdb)|*.pdb|All files|*.*"; dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd;*.wasm|Nuget Packages (*.nupkg)|*.nupkg|Portable Program Database (*.pdb)|*.pdb|All files|*.*";
dlg.Multiselect = true; dlg.Multiselect = true;
dlg.RestoreDirectory = true; dlg.RestoreDirectory = true;
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)

70
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -103,6 +103,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return loadResult.MetadataFile.Kind switch { return loadResult.MetadataFile.Kind switch {
MetadataFile.MetadataFileKind.PortableExecutable => Images.Assembly, MetadataFile.MetadataFileKind.PortableExecutable => Images.Assembly,
MetadataFile.MetadataFileKind.ProgramDebugDatabase => Images.ProgramDebugDatabase, MetadataFile.MetadataFileKind.ProgramDebugDatabase => Images.ProgramDebugDatabase,
MetadataFile.MetadataFileKind.WebCIL => Images.WebAssemblyFile,
_ => Images.MetadataFile, _ => Images.MetadataFile,
}; };
} }
@ -203,6 +204,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
case MetadataFile.MetadataFileKind.PortableExecutable: case MetadataFile.MetadataFileKind.PortableExecutable:
LoadChildrenForPEFile(loadResult.PEFile); LoadChildrenForPEFile(loadResult.PEFile);
break; break;
case MetadataFile.MetadataFileKind.WebCIL:
LoadChildrenForWebCilFile((WebCilFile)loadResult.MetadataFile);
break;
default: default:
var metadata = loadResult.MetadataFile; var metadata = loadResult.MetadataFile;
this.Children.Add(new MetadataTablesTreeNode(metadata)); this.Children.Add(new MetadataTablesTreeNode(metadata));
@ -291,6 +295,72 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
} }
void LoadChildrenForWebCilFile(WebCilFile module)
{
typeSystem = LoadedAssembly.GetTypeSystemOrNull();
var assembly = (MetadataModule)typeSystem.MainModule;
this.Children.Add(new MetadataTreeNode(module, Resources.Metadata));
Decompiler.DebugInfo.IDebugInfoProvider debugInfo = LoadedAssembly.GetDebugInfoOrNull();
if (debugInfo is PortableDebugInfoProvider ppdb
&& ppdb.GetMetadataReader() is System.Reflection.Metadata.MetadataReader reader)
{
this.Children.Add(new MetadataTreeNode(ppdb.ToMetadataFile(), $"Debug Metadata ({(ppdb.IsEmbedded ? "Embedded" : "From portable PDB")})"));
}
this.Children.Add(new ReferenceFolderTreeNode(module, this));
if (module.Resources.Any())
this.Children.Add(new ResourceListTreeNode(module));
foreach (NamespaceTreeNode ns in namespaces.Values)
{
ns.Children.Clear();
}
namespaces.Clear();
bool useNestedStructure = MainWindow.Instance.CurrentDisplaySettings.UseNestedNamespaceNodes;
foreach (var type in assembly.TopLevelTypeDefinitions.OrderBy(t => t.ReflectionName, NaturalStringComparer.Instance))
{
var ns = GetOrCreateNamespaceTreeNode(type.Namespace);
TypeTreeNode node = new TypeTreeNode(type, this);
typeDict[(TypeDefinitionHandle)type.MetadataToken] = node;
ns.Children.Add(node);
}
foreach (NamespaceTreeNode ns in namespaces.Values
.Where(ns => ns.Children.Count > 0 && ns.Parent == null)
.OrderBy(n => n.Name, NaturalStringComparer.Instance))
{
this.Children.Add(ns);
SetPublicAPI(ns);
}
NamespaceTreeNode GetOrCreateNamespaceTreeNode(string @namespace)
{
if (!namespaces.TryGetValue(@namespace, out NamespaceTreeNode ns))
{
if (useNestedStructure)
{
int decimalIndex = @namespace.LastIndexOf('.');
if (decimalIndex < 0)
{
var escapedNamespace = Language.EscapeName(@namespace);
ns = new NamespaceTreeNode(escapedNamespace);
}
else
{
var parentNamespaceTreeNode = GetOrCreateNamespaceTreeNode(@namespace.Substring(0, decimalIndex));
var escapedInnerNamespace = Language.EscapeName(@namespace.Substring(decimalIndex + 1));
ns = new NamespaceTreeNode(escapedInnerNamespace);
parentNamespaceTreeNode.Children.Add(ns);
}
}
else
{
var escapedNamespace = Language.EscapeName(@namespace);
ns = new NamespaceTreeNode(escapedNamespace);
}
namespaces.Add(@namespace, ns);
}
return ns;
}
}
private static void SetPublicAPI(NamespaceTreeNode ns) private static void SetPublicAPI(NamespaceTreeNode ns)
{ {
foreach (NamespaceTreeNode innerNamespace in ns.Children.OfType<NamespaceTreeNode>()) foreach (NamespaceTreeNode innerNamespace in ns.Children.OfType<NamespaceTreeNode>())

Loading…
Cancel
Save