project structure changed

This commit is contained in:
2022-12-06 13:04:43 +06:00
parent f64d655f49
commit a356957377
17 changed files with 47 additions and 38 deletions

145
Internal/ILReader.cs Normal file
View File

@@ -0,0 +1,145 @@
using System.Reflection.Emit;
namespace DTLib.Ben.Demystifier.Internal;
internal class ILReader
{
private static readonly OpCode[] singleByteOpCode;
private static readonly OpCode[] doubleByteOpCode;
private readonly byte[] _cil;
private int ptr;
static ILReader()
{
singleByteOpCode = new OpCode[225];
doubleByteOpCode = new OpCode[31];
var fields = GetOpCodeFields();
for (var i = 0; i < fields.Length; i++)
{
var code = (OpCode)fields[i].GetValue(null)!;
if (code.OpCodeType == OpCodeType.Nternal)
continue;
if (code.Size == 1)
singleByteOpCode[code.Value] = code;
else
doubleByteOpCode[code.Value & 0xff] = code;
}
}
public ILReader(byte[] cil)
{
_cil = cil;
}
public OpCode OpCode { get; private set; }
public int MetadataToken { get; private set; }
public MemberInfo? Operand { get; private set; }
public bool Read(MethodBase methodInfo)
{
if (ptr < _cil.Length)
{
OpCode = ReadOpCode();
Operand = ReadOperand(OpCode, methodInfo);
return true;
}
return false;
}
private OpCode ReadOpCode()
{
var instruction = ReadByte();
if (instruction < 254)
return singleByteOpCode[instruction];
return doubleByteOpCode[ReadByte()];
}
private MemberInfo? ReadOperand(OpCode code, MethodBase methodInfo)
{
MetadataToken = 0;
int inlineLength;
switch (code.OperandType)
{
case OperandType.InlineMethod:
MetadataToken = ReadInt();
Type[]? methodArgs = null;
if (methodInfo.GetType() != typeof(ConstructorInfo) &&
!methodInfo.GetType().IsSubclassOf(typeof(ConstructorInfo)))
methodArgs = methodInfo.GetGenericArguments();
Type[]? typeArgs = null;
if (methodInfo.DeclaringType is not null) typeArgs = methodInfo.DeclaringType.GetGenericArguments();
try
{
return methodInfo.Module.ResolveMember(MetadataToken, typeArgs, methodArgs);
}
catch
{
// Can return System.ArgumentException : Token xxx is not a valid MemberInfo token in the scope of module xxx.dll
return null;
}
case OperandType.InlineNone:
inlineLength = 0;
break;
case OperandType.ShortInlineBrTarget:
case OperandType.ShortInlineVar:
case OperandType.ShortInlineI:
inlineLength = 1;
break;
case OperandType.InlineVar:
inlineLength = 2;
break;
case OperandType.InlineBrTarget:
case OperandType.InlineField:
case OperandType.InlineI:
case OperandType.InlineString:
case OperandType.InlineSig:
case OperandType.InlineSwitch:
case OperandType.InlineTok:
case OperandType.InlineType:
case OperandType.ShortInlineR:
inlineLength = 4;
break;
case OperandType.InlineI8:
case OperandType.InlineR:
inlineLength = 8;
break;
default:
return null;
}
for (var i = 0; i < inlineLength; i++) ReadByte();
return null;
}
private byte ReadByte()
{
return _cil[ptr++];
}
private int ReadInt()
{
var b1 = ReadByte();
var b2 = ReadByte();
var b3 = ReadByte();
var b4 = ReadByte();
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
}
private static FieldInfo[] GetOpCodeFields()
{
return typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static);
}
}

View File

@@ -0,0 +1,113 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
namespace DTLib.Ben.Demystifier.Internal;
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.StackTrace.Sources/StackFrame/PortablePdbReader.cs
internal class PortablePdbReader : IDisposable
{
private readonly Dictionary<string, MetadataReaderProvider> _cache = new(StringComparer.Ordinal);
public void Dispose()
{
foreach (var entry in _cache) entry.Value.Dispose();
_cache.Clear();
}
public void PopulateStackFrame(StackFrame frameInfo, MethodBase method, int IlOffset, out string fileName,
out int row, out int column)
{
fileName = "";
row = 0;
column = 0;
if (method.Module.Assembly.IsDynamic) return;
var metadataReader = GetMetadataReader(method.Module.Assembly.Location);
if (metadataReader is null) return;
var methodToken = MetadataTokens.Handle(method.MetadataToken);
Debug.Assert(methodToken.Kind == HandleKind.MethodDefinition);
var handle = ((MethodDefinitionHandle)methodToken).ToDebugInformationHandle();
if (!handle.IsNil)
{
var methodDebugInfo = metadataReader.GetMethodDebugInformation(handle);
var sequencePoints = methodDebugInfo.GetSequencePoints();
SequencePoint? bestPointSoFar = null;
foreach (var point in sequencePoints)
{
if (point.Offset > IlOffset) break;
if (point.StartLine != SequencePoint.HiddenLine) bestPointSoFar = point;
}
if (bestPointSoFar.HasValue)
{
row = bestPointSoFar.Value.StartLine;
column = bestPointSoFar.Value.StartColumn;
fileName = metadataReader.GetString(metadataReader.GetDocument(bestPointSoFar.Value.Document).Name);
}
}
}
private MetadataReader? GetMetadataReader(string assemblyPath)
{
if (!_cache.TryGetValue(assemblyPath, out var provider) && provider is not null)
{
var pdbPath = GetPdbPath(assemblyPath);
if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath!))
{
var pdbStream = File.OpenRead(pdbPath);
provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
}
_cache[assemblyPath] = provider;
}
return provider?.GetMetadataReader();
}
private static string? GetPdbPath(string assemblyPath)
{
if (string.IsNullOrEmpty(assemblyPath)) return null;
if (File.Exists(assemblyPath))
{
var peStream = File.OpenRead(assemblyPath);
using var peReader = new PEReader(peStream);
foreach (var entry in peReader.ReadDebugDirectory())
if (entry.Type == DebugDirectoryEntryType.CodeView)
{
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
var peDirectory = Path.GetDirectoryName(assemblyPath);
return peDirectory is null
? null
: Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path));
}
}
return null;
}
private static bool IsPortable(string pdbPath)
{
using var pdbStream = File.OpenRead(pdbPath);
return pdbStream.ReadByte() == 'B' &&
pdbStream.ReadByte() == 'S' &&
pdbStream.ReadByte() == 'J' &&
pdbStream.ReadByte() == 'B';
}
}

View File

@@ -0,0 +1,60 @@
// Copyright (c) Ben A Adams. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
namespace DTLib.Ben.Demystifier.Internal;
/// <summary>
/// A helper class that contains utilities methods for dealing with reflection.
/// </summary>
public static class ReflectionHelper
{
private static PropertyInfo? transformerNamesLazyPropertyInfo;
/// <summary>
/// Returns true if the <paramref name="type" /> is a value tuple type.
/// </summary>
public static bool IsValueTuple(this Type type)
{
return type.Namespace == "System" && type.Name.Contains("ValueTuple`");
}
/// <summary>
/// Returns true if the given <paramref name="attribute" /> is of type <code>TupleElementNameAttribute</code>.
/// </summary>
/// <remarks>
/// To avoid compile-time dependency hell with System.ValueTuple, this method uses reflection and not checks statically
/// that
/// the given <paramref name="attribute" /> is <code>TupleElementNameAttribute</code>.
/// </remarks>
public static bool IsTupleElementNameAttribute(this Attribute attribute)
{
var attributeType = attribute.GetType();
return attributeType.Namespace == "System.Runtime.CompilerServices" &&
attributeType.Name == "TupleElementNamesAttribute";
}
/// <summary>
/// Returns 'TransformNames' property value from a given <paramref name="attribute" />.
/// </summary>
/// <remarks>
/// To avoid compile-time dependency hell with System.ValueTuple, this method uses reflection
/// instead of casting the attribute to a specific type.
/// </remarks>
public static IList<string>? GetTransformerNames(this Attribute attribute)
{
Debug.Assert(attribute.IsTupleElementNameAttribute());
var propertyInfo = GetTransformNamesPropertyInfo(attribute.GetType())!;
return propertyInfo.GetValue(attribute) as IList<string>;
}
private static PropertyInfo GetTransformNamesPropertyInfo(Type attributeType)
{
#pragma warning disable 8634
return LazyInitializer.EnsureInitialized(ref transformerNamesLazyPropertyInfo,
#pragma warning restore 8634
() => attributeType.GetProperty("TransformNames", BindingFlags.Instance | BindingFlags.Public)!)!;
}
}