From 58a9736d4f5551079102925024a37adcc1bda215 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 1 Sep 2024 20:29:57 +0200 Subject: [PATCH] #3273: Add support for undocumented EventPtr, FieldPtr, MethodPtr, ParamPtr and PropertyPtr tables. --- ILSpy/Metadata/CorTables/PtrTableTreeNode.cs | 135 +++++++++++++++++++ ILSpy/Metadata/MetadataTablesTreeNode.cs | 6 + 2 files changed, 141 insertions(+) create mode 100644 ILSpy/Metadata/CorTables/PtrTableTreeNode.cs diff --git a/ILSpy/Metadata/CorTables/PtrTableTreeNode.cs b/ILSpy/Metadata/CorTables/PtrTableTreeNode.cs new file mode 100644 index 0000000000..245507368d --- /dev/null +++ b/ILSpy/Metadata/CorTables/PtrTableTreeNode.cs @@ -0,0 +1,135 @@ +// Copyright (c) 2024 AlphaSierraPapa for the SharpDevelop Team +// +// 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.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using ICSharpCode.Decompiler.Metadata; + +namespace ICSharpCode.ILSpy.Metadata +{ + class PtrTableTreeNode : MetadataTableTreeNode + { + readonly TableIndex referencedTableKind; + + public PtrTableTreeNode(TableIndex kind, MetadataFile metadataFile) + : base(kind, metadataFile) + { + if (kind is not (TableIndex.EventPtr or TableIndex.FieldPtr or TableIndex.MethodPtr or TableIndex.ParamPtr or TableIndex.PropertyPtr)) + { + throw new ArgumentOutOfRangeException("PtrTable does not support " + kind); + } + + this.referencedTableKind = kind switch { + TableIndex.EventPtr => TableIndex.Event, + TableIndex.FieldPtr => TableIndex.Field, + TableIndex.MethodPtr => TableIndex.MethodDef, + TableIndex.ParamPtr => TableIndex.Param, + TableIndex.PropertyPtr => TableIndex.Property, + _ => throw new NotImplementedException(), // unreachable + }; + } + + public override bool View(ViewModels.TabPageModel tabPage) + { + tabPage.Title = Text.ToString(); + tabPage.SupportsLanguageSwitching = false; + + var view = Helpers.PrepareDataGrid(tabPage, this); + var metadata = metadataFile.Metadata; + + var list = new List(); + PtrEntry scrollTargetEntry = default; + + var length = metadata.GetTableRowCount(Kind); + ReadOnlySpan ptr = metadata.AsReadOnlySpan(); + + int handleDefSize = metadataFile.Metadata.GetTableRowCount(referencedTableKind) < ushort.MaxValue ? 2 : 4; + + for (int rid = 1; rid <= length; rid++) + { + var entry = new PtrEntry(metadataFile, Kind, referencedTableKind, handleDefSize, ptr, rid); + if (entry.RID == this.scrollTarget) + { + scrollTargetEntry = entry; + } + list.Add(entry); + } + + view.ItemsSource = list; + + tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) + { + ScrollItemIntoView(view, scrollTargetEntry); + } + + return true; + } + + readonly struct HandlePtr + { + public readonly EntityHandle Handle; + + public HandlePtr(ReadOnlySpan ptr, TableIndex kind, int handleSize) + { + Handle = MetadataTokens.EntityHandle(((int)kind << 24) | Helpers.GetValueLittleEndian(ptr, handleSize)); + } + } + + struct PtrEntry + { + readonly MetadataFile metadataFile; + readonly HandlePtr handlePtr; + readonly TableIndex kind; + + public int RID { get; } + + public int Token => ((int)kind << 24) | RID; + + public int Offset { get; } + + [ColumnInfo("X8", Kind = ColumnKind.Token)] + public int Handle => MetadataTokens.GetToken(handlePtr.Handle); + + public void OnHandleClick() + { + MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, handlePtr.Handle, protocol: "metadata")); + } + + string handleTooltip; + public string HandleTooltip => GenerateTooltip(ref handleTooltip, metadataFile, handlePtr.Handle); + + public PtrEntry(MetadataFile metadataFile, TableIndex kind, TableIndex handleKind, int handleDefSize, ReadOnlySpan ptr, int row) + { + this.metadataFile = metadataFile; + this.RID = row; + this.kind = kind; + var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(kind) + + metadataFile.Metadata.GetTableRowSize(kind) * (row - 1); + this.Offset = metadataFile.MetadataOffset + rowOffset; + + this.handlePtr = new HandlePtr(ptr.Slice(rowOffset), handleKind, handleDefSize); + this.handleTooltip = null; + } + } + } +} diff --git a/ILSpy/Metadata/MetadataTablesTreeNode.cs b/ILSpy/Metadata/MetadataTablesTreeNode.cs index 830734e7cc..3724ba590a 100644 --- a/ILSpy/Metadata/MetadataTablesTreeNode.cs +++ b/ILSpy/Metadata/MetadataTablesTreeNode.cs @@ -141,6 +141,12 @@ internal static MetadataTableTreeNode CreateTableTreeNode(TableIndex table, Meta return new StateMachineMethodTableTreeNode(metadataFile); case TableIndex.CustomDebugInformation: return new CustomDebugInformationTableTreeNode(metadataFile); + case TableIndex.FieldPtr: + case TableIndex.EventPtr: + case TableIndex.MethodPtr: + case TableIndex.ParamPtr: + case TableIndex.PropertyPtr: + return new PtrTableTreeNode(table, metadataFile); default: return new UnsupportedMetadataTableTreeNode(table, metadataFile); }