diff --git a/source/Container/Container.csproj b/source/Container/Container.csproj index 083a378e..1864372d 100644 --- a/source/Container/Container.csproj +++ b/source/Container/Container.csproj @@ -9,7 +9,7 @@ - + diff --git a/source/Container/Runtime/MemoryBytesInspector.cs b/source/Container/Runtime/MemoryBytesInspector.cs index f5859bcb..2e8c5879 100644 --- a/source/Container/Runtime/MemoryBytesInspector.cs +++ b/source/Container/Runtime/MemoryBytesInspector.cs @@ -4,133 +4,133 @@ using Microsoft.Diagnostics.Runtime; using SharpLab.Runtime.Internal; -namespace SharpLab.Container.Runtime { - internal class MemoryBytesInspector : IMemoryBytesInspector { - private readonly Pool _runtimePool; +namespace SharpLab.Container.Runtime; - public MemoryBytesInspector(Pool runtimePool) { - _runtimePool = runtimePool; - } +internal class MemoryBytesInspector : IMemoryBytesInspector { + private readonly Pool _runtimePool; - public MemoryInspection InspectHeap(object @object) { - if (@object == null) - throw new ArgumentNullException(nameof(@object), $"Inspect.Heap can't inspect null, as it does not point to a valid location on the heap."); + public MemoryBytesInspector(Pool runtimePool) { + _runtimePool = runtimePool; + } - using var runtimeLease = _runtimePool.GetOrCreate(); - var runtime = runtimeLease.Object; - runtime.FlushCachedData(); + public MemoryInspection InspectHeap(object @object) { + if (@object == null) + throw new ArgumentNullException(nameof(@object), $"Inspect.Heap can't inspect null, as it does not point to a valid location on the heap."); - var address = (ulong)GetHeapPointer(@object); - var objectType = runtime.Heap.GetObjectType(address); - if (objectType == null) - throw new ClrInformationNotFoundException($"Failed to find object type for address 0x{address:X}."); + using var runtimeLease = _runtimePool.GetOrCreate(); + var runtime = runtimeLease.Object; + runtime.FlushCachedData(); - var objectSize = runtime.Heap.GetObjectSize(address, objectType); + var address = (ulong)GetHeapPointer(@object); + var objectType = runtime.Heap.GetObjectType(address); + if (objectType == null) + throw new ClrInformationNotFoundException($"Failed to find object type for address 0x{address:X}."); - // Move by one pointer size back -- Object Header, - // see https://blogs.msdn.microsoft.com/seteplia/2017/05/26/managed-object-internals-part-1-layout/ - // - // Not sure if there is a better way to get this through ClrMD yet. - // https://github.com/Microsoft/clrmd/issues/99 - var objectStart = address - (uint)IntPtr.Size; - var data = ReadMemory(runtime, objectStart, objectSize); + var objectSize = runtime.Heap.GetObject(address, objectType).Size; - var labels = CreateLabelsFromType(objectType, address, objectStart, first: (index: 2, offset: 2 * IntPtr.Size)); - labels[0] = new MemoryInspectionLabel("header", 0, IntPtr.Size); - labels[1] = new MemoryInspectionLabel("type handle", IntPtr.Size, IntPtr.Size); + // Move by one pointer size back -- Object Header, + // see https://blogs.msdn.microsoft.com/seteplia/2017/05/26/managed-object-internals-part-1-layout/ + // + // Not sure if there is a better way to get this through ClrMD yet. + // https://github.com/Microsoft/clrmd/issues/99 + var objectStart = address - (uint)IntPtr.Size; + var data = ReadMemory(runtime, objectStart, objectSize); - return new MemoryInspection($"{objectType.Name} at 0x{address:X}", labels, data); - } + var labels = CreateLabelsFromType(objectType, address, objectStart, first: (index: 2, offset: 2 * IntPtr.Size)); + labels[0] = new MemoryInspectionLabel("header", 0, IntPtr.Size); + labels[1] = new MemoryInspectionLabel("type handle", IntPtr.Size, IntPtr.Size); - public unsafe MemoryInspection InspectStack(in T value) { - using var runtimeLease = _runtimePool.GetOrCreate(); - var runtime = runtimeLease.Object; + return new MemoryInspection($"{objectType.Name} at 0x{address:X}", labels, data); + } - var type = typeof(T); + public unsafe MemoryInspection InspectStack(in T value) { + using var runtimeLease = _runtimePool.GetOrCreate(); + var runtime = runtimeLease.Object; - var address = (ulong)Unsafe.AsPointer(ref Unsafe.AsRef(in value)); - var size = type.IsValueType ? (ulong)Unsafe.SizeOf() : (uint)IntPtr.Size; - var data = ReadMemory(runtime, address, size); + var type = typeof(T); - MemoryInspectionLabel[] labels; - if (type.IsValueType && !type.IsPrimitive) { - runtime.FlushCachedData(); - var methodTableAddress = (ulong)type.TypeHandle.Value; - var runtimeType = runtime.GetTypeByMethodTable(methodTableAddress) - ?? throw new ClrInformationNotFoundException($"Could not find type by method table at 0x{methodTableAddress:X}"); - labels = CreateLabelsFromType(runtimeType, address, address + (uint)IntPtr.Size); - } - else { - labels = Array.Empty(); - } - - var title = type.IsValueType - ? $"{type.FullName}" - : $"Pointer to {type.FullName}"; + var address = (ulong)Unsafe.AsPointer(ref Unsafe.AsRef(in value)); + var size = type.IsValueType ? (ulong)Unsafe.SizeOf() : (uint)IntPtr.Size; + var data = ReadMemory(runtime, address, size); - return new MemoryInspection(title, labels, data); + MemoryInspectionLabel[] labels; + if (type.IsValueType && !type.IsPrimitive) { + runtime.FlushCachedData(); + var methodTableAddress = (ulong)type.TypeHandle.Value; + var runtimeType = runtime.GetTypeByMethodTable(methodTableAddress) + ?? throw new ClrInformationNotFoundException($"Could not find type by method table at 0x{methodTableAddress:X}"); + labels = CreateLabelsFromType(runtimeType, address, address + (uint)IntPtr.Size); } - - private static byte[] ReadMemory(ClrRuntime runtime, ulong address, ulong size) { - var data = new byte[size]; - runtime.DataTarget!.DataReader.Read(address, data); - return data; + else { + labels = Array.Empty(); } - private MemoryInspectionLabel[] CreateLabelsFromType( - ClrType objectType, - ulong objectAddress, - ulong offsetBase, - (int index, int offset) first = default - ) { - MemoryInspectionLabel[] labels; - if (objectType.IsArray) { - var length = objectType.Heap.GetObject(objectAddress).AsArray().Length; - labels = new MemoryInspectionLabel[first.index + 1 + length]; - labels[first.index] = new MemoryInspectionLabel("length", first.offset, IntPtr.Size); - for (var i = 0; i < length; i++) { - var elementAddress = objectType.GetArrayElementAddress(objectAddress, i); - var offset = (int)(elementAddress - offsetBase); - labels[first.index + 1 + i] = new MemoryInspectionLabel( - i.ToString(), - offset, - objectType.ComponentSize, - GetNestedLabels(objectType.ComponentType!, elementAddress, offsetBase) - ); - } - return labels; - } + var title = type.IsValueType + ? $"{type.FullName}" + : $"Pointer to {type.FullName}"; - var fields = objectType.Fields; - var fieldCount = fields.Length; - labels = new MemoryInspectionLabel[first.index + fieldCount]; - for (var i = 0; i < fieldCount; i++) { - var field = fields[i]; - if (field.Type == null) - throw new ClrInformationNotFoundException($"Could not get type for field {field.Name}."); - - var fieldAddress = field.GetAddress(objectAddress); - var offset = (int)(fieldAddress - offsetBase); - labels[first.index + i] = new MemoryInspectionLabel( - field.Name ?? "", + return new MemoryInspection(title, labels, data); + } + + private static byte[] ReadMemory(ClrRuntime runtime, ulong address, ulong size) { + var data = new byte[size]; + runtime.DataTarget!.DataReader.Read(address, data); + return data; + } + + private MemoryInspectionLabel[] CreateLabelsFromType( + ClrType objectType, + ulong objectAddress, + ulong offsetBase, + (int index, int offset) first = default + ) { + MemoryInspectionLabel[] labels; + if (objectType.IsArray) { + var length = objectType.Heap.GetObject(objectAddress).AsArray().Length; + labels = new MemoryInspectionLabel[first.index + 1 + length]; + labels[first.index] = new MemoryInspectionLabel("length", first.offset, IntPtr.Size); + for (var i = 0; i < length; i++) { + var elementAddress = objectType.GetArrayElementAddress(objectAddress, i); + var offset = (int)(elementAddress - offsetBase); + labels[first.index + 1 + i] = new MemoryInspectionLabel( + i.ToString(), offset, - field.Size, - GetNestedLabels(field.Type, fieldAddress, offsetBase) + objectType.ComponentSize, + GetNestedLabels(objectType.ComponentType!, elementAddress, offsetBase) ); } return labels; } - private IReadOnlyList GetNestedLabels(ClrType type, ulong valueAddress, ulong offsetBase) { - if (type.IsPrimitive || !type.IsValueType) - return Array.Empty(); - - return CreateLabelsFromType(type, valueAddress, offsetBase + (uint)IntPtr.Size); + var fields = objectType.Fields; + var fieldCount = fields.Length; + labels = new MemoryInspectionLabel[first.index + fieldCount]; + for (var i = 0; i < fieldCount; i++) { + var field = fields[i]; + if (field.Type == null) + throw new ClrInformationNotFoundException($"Could not get type for field {field.Name}."); + + var fieldAddress = field.GetAddress(objectAddress); + var offset = (int)(fieldAddress - offsetBase); + labels[first.index + i] = new MemoryInspectionLabel( + field.Name ?? "", + offset, + field.Size, + GetNestedLabels(field.Type, fieldAddress, offsetBase) + ); } + return labels; + } - private static unsafe IntPtr GetHeapPointer(object @object) { - var indirect = Unsafe.AsPointer(ref @object); - return **(IntPtr**)(&indirect); - } + private IReadOnlyList GetNestedLabels(ClrType type, ulong valueAddress, ulong offsetBase) { + if (type.IsPrimitive || !type.IsValueType) + return Array.Empty(); + + return CreateLabelsFromType(type, valueAddress, offsetBase + (uint)IntPtr.Size); + } + + private static unsafe IntPtr GetHeapPointer(object @object) { + var indirect = Unsafe.AsPointer(ref @object); + return **(IntPtr**)(&indirect); } } diff --git a/source/NetFramework/Runtime/Inspect.cs b/source/NetFramework/Runtime/Inspect.cs index 0011d552..5dc8b8c0 100644 --- a/source/NetFramework/Runtime/Inspect.cs +++ b/source/NetFramework/Runtime/Inspect.cs @@ -18,7 +18,7 @@ public static void Heap(object @object) { if (objectType == null) throw new Exception($"Failed to find object type for address 0x{address:X}."); - var objectSize = runtime.Heap.GetObjectSize(address, objectType); + var objectSize = runtime.Heap.GetObject(address, objectType).Size; // Move by one pointer size back -- Object Header, // see https://blogs.msdn.microsoft.com/seteplia/2017/05/26/managed-object-internals-part-1-layout/ diff --git a/source/NetFramework/Runtime/Runtime.csproj b/source/NetFramework/Runtime/Runtime.csproj index a2c9cf04..bb610009 100644 --- a/source/NetFramework/Runtime/Runtime.csproj +++ b/source/NetFramework/Runtime/Runtime.csproj @@ -10,8 +10,8 @@ - + - + \ No newline at end of file diff --git a/source/NetFramework/Server/Decompilation/JitAsmDecompilerBase.cs b/source/NetFramework/Server/Decompilation/JitAsmDecompilerBase.cs index d732f1fb..105a3681 100644 --- a/source/NetFramework/Server/Decompilation/JitAsmDecompilerBase.cs +++ b/source/NetFramework/Server/Decompilation/JitAsmDecompilerBase.cs @@ -3,239 +3,192 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using JetBrains.Annotations; using Microsoft.Diagnostics.Runtime; -using Microsoft.Diagnostics.Runtime.DacInterface; +using Pidgin; using SharpDisasm; using SharpDisasm.Translators; using SharpLab.Server.Common; using SharpLab.Server.Decompilation.Internal; -namespace SharpLab.Server.Decompilation { - [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] - public abstract class JitAsmDecompilerBase : IDecompiler { - public string LanguageName => TargetNames.JitAsm; +namespace SharpLab.Server.Decompilation; - public void Decompile(CompilationStreamPair streams, TextWriter codeWriter) { - Argument.NotNull(nameof(streams), streams); - Argument.NotNull(nameof(codeWriter), codeWriter); +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public abstract class JitAsmDecompilerBase : IDecompiler { + public string LanguageName => TargetNames.JitAsm; - using var resultScope = JitCompileAndGetMethods(streams.AssemblyStream); - using var dataTarget = DataTarget.AttachToProcess(Current.ProcessId, suspend: false); + public void Decompile(CompilationStreamPair streams, TextWriter codeWriter) { + Argument.NotNull(nameof(streams), streams); + Argument.NotNull(nameof(codeWriter), codeWriter); - var currentMethodAddressRef = new Reference(); - var runtime = dataTarget.ClrVersions.Single(v => v.Flavor == ClrFlavor).CreateRuntime(); - var translator = new IntelTranslator { - SymbolResolver = (Instruction instruction, long addr, ref long offset) => - ResolveSymbol(runtime, instruction, addr, currentMethodAddressRef.Value) - }; + using var resultScope = JitCompileAndGetMethods(streams.AssemblyStream); + using var dataTarget = DataTarget.AttachToProcess(Current.ProcessId, suspend: false); - WriteJitInfo(runtime.ClrInfo, codeWriter); - WriteProfilerState(codeWriter); - codeWriter.WriteLine(); + var currentMethodAddressRef = new Reference(); + var runtime = dataTarget.ClrVersions.Single(v => v.Flavor == ClrFlavor).CreateRuntime(); + var translator = new IntelTranslator { + SymbolResolver = (Instruction instruction, long addr, ref long offset) => + ResolveSymbol(runtime, instruction, addr, currentMethodAddressRef.Value) + }; - var architecture = MapArchitecture(runtime.ClrInfo.DacInfo.TargetArchitecture); - foreach (var result in resultScope.Results) { - DisassembleAndWrite(result, runtime, architecture, translator, currentMethodAddressRef, codeWriter); - codeWriter.WriteLine(); - } + WriteJitInfo(runtime.ClrInfo, codeWriter); + WriteProfilerState(codeWriter); + codeWriter.WriteLine(); + + var architecture = MapArchitecture(runtime.DataTarget.DataReader.Architecture); + foreach (var result in resultScope.Results) { + DisassembleAndWrite(result, runtime, architecture, translator, currentMethodAddressRef, codeWriter); + codeWriter.WriteLine(); } + } - protected abstract ClrFlavor ClrFlavor { get; } - protected abstract JitAsmResultScope JitCompileAndGetMethods(MemoryStream assemblyStream); + protected abstract ClrFlavor ClrFlavor { get; } + protected abstract JitAsmResultScope JitCompileAndGetMethods(MemoryStream assemblyStream); - private void WriteJitInfo(ClrInfo clr, TextWriter writer) { - writer.WriteLine( - "; {0:G} CLR {1} on {2}", - clr.Flavor, clr.Version, clr.DacInfo.TargetArchitecture.ToString("G").ToLowerInvariant() - ); - } + private void WriteJitInfo(ClrInfo clr, TextWriter writer) { + writer.WriteLine( + "; {0:G} CLR {1} on {2}", + clr.Flavor, clr.Version, clr.DataTarget.DataReader.Architecture.ToString("G").ToLowerInvariant() + ); + } - private void WriteProfilerState(TextWriter writer) { - if (!ProfilerState.Active) - return; + private void WriteProfilerState(TextWriter writer) { + if (!ProfilerState.Active) + return; - writer.WriteLine("; Note: Running under profiler, which affects JIT assembly in heap allocations."); + writer.WriteLine("; Note: Running under profiler, which affects JIT assembly in heap allocations."); + } + + private static string? ResolveSymbol(ClrRuntime runtime, Instruction instruction, long addr, ulong currentMethodAddress) { + var operand = instruction.Operands.Length > 0 ? instruction.Operands[0] : null; + if (operand?.PtrOffset == 0) { + var lvalue = GetOperandLValue(operand!); + if (lvalue == null) + return $"{operand!.RawValue} ; failed to resolve lval ({operand.Size}), please report at https://github.com/ashmind/SharpLab/issues"; + var baseOffset = instruction.PC - currentMethodAddress; + return $"L{baseOffset + lvalue:x4}"; } - private static string? ResolveSymbol(ClrRuntime runtime, Instruction instruction, long addr, ulong currentMethodAddress) { - var operand = instruction.Operands.Length > 0 ? instruction.Operands[0] : null; - if (operand?.PtrOffset == 0) { - var lvalue = GetOperandLValue(operand!); - if (lvalue == null) - return $"{operand!.RawValue} ; failed to resolve lval ({operand.Size}), please report at https://github.com/ashmind/SharpLab/issues"; - var baseOffset = instruction.PC - currentMethodAddress; - return $"L{baseOffset + lvalue:x4}"; - } + return runtime.GetMethodByInstructionPointer(unchecked((ulong)addr))?.Signature; + } - return runtime.GetMethodByInstructionPointer(unchecked((ulong)addr))?.Signature; + private static ulong? GetOperandLValue(Operand operand) { + switch (operand.Size) { + case 8: return (ulong)operand.LvalSByte; + case 16: return (ulong)operand.LvalSWord; + case 32: return (ulong)operand.LvalSDWord; + default: return null; } + } - private static ulong? GetOperandLValue(Operand operand) { - switch (operand.Size) { - case 8: return (ulong)operand.LvalSByte; - case 16: return (ulong)operand.LvalSWord; - case 32: return (ulong)operand.LvalSDWord; - default: return null; - } + private void DisassembleAndWrite(MethodJitResult result, ClrRuntime runtime, ArchitectureMode architecture, Translator translator, Reference methodAddressRef, TextWriter writer) { + void WriteSignatureFromClrMethod() { + var signature = runtime.GetMethodByHandle(unchecked((ulong)result.Handle.ToInt64()))?.Signature; + WriteSignature(signature); } - private void DisassembleAndWrite(MethodJitResult result, ClrRuntime runtime, ArchitectureMode architecture, Translator translator, Reference methodAddressRef, TextWriter writer) { - void WriteSignatureFromClrMethod() { - var signature = runtime.GetMethodByHandle(unchecked((ulong)result.Handle.ToInt64()))?.Signature; - WriteSignature(signature); + void WriteSignature(string? signature) { + if (signature != null) { + writer.WriteLine(signature); } - - void WriteSignature(string? signature) { - if (signature != null) { - writer.WriteLine(signature); - } - else { - writer.WriteLine("Unknown (0x{0:X})", (ulong)result.Handle.ToInt64()); - writer.WriteLine(" ; Method signature was not found -- please report this issue."); - } - } - - switch (result.Status) { - case MethodJitStatus.IgnoredPInvoke: - WriteSignatureFromClrMethod(); - writer.WriteLine(" ; Cannot produce JIT assembly for a P/Invoke method."); - return; - case MethodJitStatus.IgnoredRuntime: - WriteSignatureFromClrMethod(); - writer.WriteLine(" ; Cannot produce JIT assembly for runtime-implemented method."); - return; - case MethodJitStatus.IgnoredOpenGenericWithNoAttribute: - WriteSignatureFromClrMethod(); - writer.WriteLine(" ; Open generics cannot be JIT-compiled."); - writer.WriteLine(" ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types."); - writer.WriteLine(" ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }."); - return; + else { + writer.WriteLine("Unknown (0x{0:X})", (ulong)result.Handle.ToInt64()); + writer.WriteLine(" ; Method signature was not found -- please report this issue."); } + } - if (FindJitCompiledMethod(runtime, result) is not {} method) { + switch (result.Status) { + case MethodJitStatus.IgnoredPInvoke: WriteSignatureFromClrMethod(); - if (result.Status == MethodJitStatus.SuccessGeneric) { - writer.WriteLine(" ; Failed to find JIT output for generic method (reference types?)."); - writer.WriteLine(" ; If you know a solution, please comment at https://github.com/ashmind/SharpLab/issues/99."); - return; - } - - writer.WriteLine(" ; Failed to find JIT output — please report at https://github.com/ashmind/SharpLab/issues."); + writer.WriteLine(" ; Cannot produce JIT assembly for a P/Invoke method."); + return; + case MethodJitStatus.IgnoredRuntime: + WriteSignatureFromClrMethod(); + writer.WriteLine(" ; Cannot produce JIT assembly for runtime-implemented method."); + return; + case MethodJitStatus.IgnoredOpenGenericWithNoAttribute: + WriteSignatureFromClrMethod(); + writer.WriteLine(" ; Open generics cannot be JIT-compiled."); + writer.WriteLine(" ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types."); + writer.WriteLine(" ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }."); return; - } - - WriteSignature(method.Signature); - var methodAddress = method.MethodAddress; - methodAddressRef.Value = methodAddress; - using (var disasm = new Disassembler(new IntPtr(unchecked((long)methodAddress)), (int)method.MethodSize, architecture, methodAddress)) { - foreach (var instruction in disasm.Disassemble()) { - writer.Write(" L"); - writer.Write((instruction.Offset - methodAddress).ToString("x4")); - writer.Write(": "); - writer.WriteLine(translator.Translate(instruction)); - } - } - } - - private ClrMethodData? FindJitCompiledMethod(ClrRuntime runtime, MethodJitResult result) { - var sos = runtime.DacLibrary.SOSDacInterface; - - var methodDescAddress = unchecked((ulong)result.Handle.ToInt64()); - if (!sos.GetMethodDescData(methodDescAddress, 0, out var methodDesc)) - return null; - - return GetJitCompiledMethodByMethodDescIfValid(sos, methodDesc) - ?? FindJitCompiledMethodInMethodTable(sos, methodDesc); } - private ClrMethodData? GetJitCompiledMethodByMethodDescIfValid(SOSDac sos, MethodDescData methodDesc) { - // https://github.com/microsoft/clrmd/issues/935 - var codeHeaderAddress = methodDesc.HasNativeCode != 0 - ? (ulong)methodDesc.NativeCodeAddr - : sos.GetMethodTableSlot(methodDesc.MethodTable, methodDesc.SlotNumber); - - if (codeHeaderAddress == unchecked((ulong)-1)) - return null; - - if (!sos.GetCodeHeaderData(codeHeaderAddress, out var codeHeader)) - return null; - - return GetJitCompiledMethodByCodeHeaderIfValid(sos, codeHeader); + if (FindJitCompiledMethod(runtime, result) is not {} method) { + WriteSignatureFromClrMethod(); + writer.WriteLine(" ; Failed to find JIT output. This might appear more frequently than before due to a library update."); + writer.WriteLine(" ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress."); + return; } - private ClrMethodData? GetJitCompiledMethodByCodeHeaderIfValid(SOSDac sos, CodeHeaderData codeHeader) { - if (codeHeader.MethodStart.Value == -1 || codeHeader.HotRegionSize == 0) - return null; - - return new( - sos.GetMethodDescName(codeHeader.MethodDesc), - unchecked((ulong)codeHeader.MethodStart.Value), - codeHeader.HotRegionSize - ); + WriteSignature(method.Signature); + var methodAddress = method.MethodAddress; + methodAddressRef.Value = methodAddress; + using (var disasm = new Disassembler(new IntPtr(unchecked((long)methodAddress)), (int)method.MethodSize, architecture, methodAddress)) { + foreach (var instruction in disasm.Disassemble()) { + writer.Write(" L"); + writer.Write((instruction.Offset - methodAddress).ToString("x4")); + writer.Write(": "); + writer.WriteLine(translator.Translate(instruction)); + } } + } - private ClrMethodData? FindJitCompiledMethodInMethodTable(SOSDac sos, MethodDescData originalMethodDesc) { - // I can't really explain this, but it seems that some methods - // are present multiple times in the same type -- one compiled - // and one not compiled. - - if (!sos.GetMethodTableData(originalMethodDesc.MethodTable, out var methodTable)) - return null; + private ClrMethodData? FindJitCompiledMethod(ClrRuntime runtime, MethodJitResult result) { + lock (runtime) + runtime.FlushCachedData(); - ClrMethodData? methodData = null; - for (var i = 0u; i < methodTable.NumMethods; i++) { - if (i == originalMethodDesc.SlotNumber) - continue; + var methodDescAddress = unchecked((ulong)result.Handle.ToInt64()); + if (runtime.GetMethodByHandle(methodDescAddress) is not { } method) + return null; - var slot = sos.GetMethodTableSlot(originalMethodDesc.MethodTable, i); - if (!sos.GetCodeHeaderData(slot, out var candidateCodeHeader)) - continue; + if (method.CompilationType == MethodCompilationType.None) + return null; - if (!sos.GetMethodDescData(candidateCodeHeader.MethodDesc, 0, out var candidateMethodDesc)) - continue; + if (method.NativeCode == 0) + return null; - if (candidateMethodDesc.MDToken != originalMethodDesc.MDToken) - continue; + if (method.HotColdInfo.HotSize == 0) + return null; - methodData = GetJitCompiledMethodByCodeHeaderIfValid(sos, candidateCodeHeader); - if (methodData != null) - break; - } - return methodData; - } + return new( + method.Signature, + method.NativeCode, + method.HotColdInfo.HotSize + ); + } - private ArchitectureMode MapArchitecture(Architecture architecture) => architecture switch { - Architecture.Amd64 => ArchitectureMode.x86_64, - Architecture.X86 => ArchitectureMode.x86_32, - // ReSharper disable once HeapView.BoxingAllocation - // ReSharper disable once HeapView.ObjectAllocation.Evident - _ => throw new Exception($"Unsupported architecture mode {architecture}."), - }; + private ArchitectureMode MapArchitecture(Architecture architecture) => architecture switch { + Architecture.X64 => ArchitectureMode.x86_64, + Architecture.X86 => ArchitectureMode.x86_32, + // ReSharper disable once HeapView.BoxingAllocation + // ReSharper disable once HeapView.ObjectAllocation.Evident + _ => throw new Exception($"Unsupported architecture mode {architecture}."), + }; + + private class Reference { + #pragma warning disable CS8618 // Non-nullable field is uninitialized. + public T Value { get; set; } + #pragma warning restore CS8618 // Non-nullable field is uninitialized. + } - private class Reference { - #pragma warning disable CS8618 // Non-nullable field is uninitialized. - public T Value { get; set; } - #pragma warning restore CS8618 // Non-nullable field is uninitialized. + private static class Remote { + public static IReadOnlyList GetCompiledMethods(byte[] assemblyBytes) { + var assembly = Assembly.Load(assemblyBytes); + return IsolatedJitAsmDecompilerCore.JitCompileAndGetMethods(assembly); } + } - private static class Remote { - public static IReadOnlyList GetCompiledMethods(byte[] assemblyBytes) { - var assembly = Assembly.Load(assemblyBytes); - return IsolatedJitAsmDecompilerCore.JitCompileAndGetMethods(assembly); - } + private readonly struct ClrMethodData { + public ClrMethodData(string? signature, ulong methodAddress, uint methodSize) { + Signature = signature; + MethodAddress = methodAddress; + MethodSize = methodSize; } - private readonly struct ClrMethodData { - public ClrMethodData(string? signature, ulong methodAddress, uint methodSize) { - Signature = signature; - MethodAddress = methodAddress; - MethodSize = methodSize; - } - - public string? Signature { get; } - public ulong MethodAddress { get; } - public uint MethodSize { get; } - } + public string? Signature { get; } + public ulong MethodAddress { get; } + public uint MethodSize { get; } } } \ No newline at end of file diff --git a/source/NetFramework/Server/Server.csproj b/source/NetFramework/Server/Server.csproj index 97de2fe0..afa9002f 100644 --- a/source/NetFramework/Server/Server.csproj +++ b/source/NetFramework/Server/Server.csproj @@ -30,7 +30,7 @@ - + diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/ArrayElement.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/ArrayElement.cs index 3ee34e29..e2d42a8c 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/ArrayElement.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/ArrayElement.cs @@ -6,7 +6,7 @@ static int M(int[] x) { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Delegate.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Delegate.cs index e95aea6f..2ce18872 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Delegate.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Delegate.cs @@ -2,7 +2,7 @@ /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -11,16 +11,20 @@ L0000: mov [rcx+0x8], edx L0003: ret -D..ctor(System.Object, IntPtr) +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Cannot produce JIT assembly for runtime-implemented method. -D.Invoke() +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Cannot produce JIT assembly for runtime-implemented method. -D.BeginInvoke(System.AsyncCallback, System.Object) +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Cannot produce JIT assembly for runtime-implemented method. -D.EndInvoke(System.IAsyncResult) +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Cannot produce JIT assembly for runtime-implemented method. */ \ No newline at end of file diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/DllImport.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/DllImport.cs index dbacef47..5551171a 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/DllImport.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/DllImport.cs @@ -8,7 +8,7 @@ public static class NativeMethods /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -17,7 +17,8 @@ public static class NativeMethods L0000: mov [rcx+0x8], edx L0003: ret -NativeMethods.GetLastError() +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Cannot produce JIT assembly for a P/Invoke method. */ \ No newline at end of file diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs index 0a2c6931..8298097d 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs @@ -11,7 +11,7 @@ static T M() { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -31,8 +31,9 @@ static T M() { L000d: mov rax, rcx L0010: ret -C`1[[System.__Canon, mscorlib]].M() - L0000: xor eax, eax - L0002: ret +Unknown (0x) + ; Method signature was not found -- please report this issue. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs index 2ae1ad73..27d41735 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs @@ -10,7 +10,7 @@ static T M() { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -30,8 +30,9 @@ static T M() { L000d: mov rax, rcx L0010: ret -C.M[[System.String, mscorlib]]() - ; Failed to find JIT output for generic method (reference types?). - ; If you know a solution, please comment at https://github.com/ashmind/SharpLab/issues/99. +Unknown (0x) + ; Method signature was not found -- please report this issue. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs index f0d2eafe..6e371476 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs @@ -12,7 +12,7 @@ static class N { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -25,16 +25,19 @@ static class N { L0000: xor eax, eax L0002: ret -C`1+N`1[[System.Int32, mscorlib],[System.__Canon, mscorlib]].M(System.__Canon) - L0000: xor eax, eax - L0002: ret +Unknown (0x) + ; Method signature was not found -- please report this issue. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. -C`1+N`1[[System.__Canon, mscorlib],[System.Int32, mscorlib]].M(Int32) - L0000: xor eax, eax - L0002: ret +Unknown (0x) + ; Method signature was not found -- please report this issue. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. -C`1+N`1[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].M(System.__Canon) - L0000: xor eax, eax - L0002: ret +Unknown (0x) + ; Method signature was not found -- please report this issue. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs index f290088c..b838b163 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs @@ -10,7 +10,7 @@ static class N { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -23,8 +23,9 @@ static class N { L0000: xor eax, eax L0002: ret -C+N`1[[System.__Canon, mscorlib]].get_M() - L0000: xor eax, eax - L0002: ret +Unknown (0x) + ; Method signature was not found -- please report this issue. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs index d6ddc051..17ee2159 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs @@ -10,7 +10,7 @@ static class N { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -23,8 +23,9 @@ static class N { L0000: xor eax, eax L0002: ret -C`1+N[[System.__Canon, mscorlib]].M() - L0000: xor eax, eax - L0002: ret +Unknown (0x) + ; Method signature was not found -- please report this issue. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs index ec3f3f2b..33f4e432 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs @@ -18,7 +18,7 @@ static void M() {} /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret @@ -27,22 +27,26 @@ static void M() {} L0000: mov [rcx+0x8], edx L0003: ret -C`1.M() +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Open generics cannot be JIT-compiled. ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types. ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }. -C`1+N.M() +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Open generics cannot be JIT-compiled. ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types. ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }. -C.M() +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Open generics cannot be JIT-compiled. ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types. ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }. -C+N`1.M() +Unknown (0x) + ; Method signature was not found -- please report this issue. ; Open generics cannot be JIT-compiled. ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types. ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }. diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/JumpBack.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/JumpBack.cs index 327e2e54..7d56a4ab 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/JumpBack.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/JumpBack.cs @@ -12,7 +12,7 @@ public int M(int a) { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/MultipleReturns.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/MultipleReturns.cs index 5cdbca93..76dc07b4 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/MultipleReturns.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/MultipleReturns.cs @@ -6,7 +6,7 @@ static int M(bool x) { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Nested.Simple.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Nested.Simple.cs index 1678f113..2a4a8916 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Nested.Simple.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Nested.Simple.cs @@ -6,7 +6,7 @@ static class N { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret diff --git a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Simple.cs b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Simple.cs index 1c234d71..773069f5 100644 --- a/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Simple.cs +++ b/source/NetFramework/Tests/Decompilation/TestCode/JitAsm/Simple.cs @@ -4,7 +4,7 @@ static class C { /* asm -; Desktop CLR on amd64 +; Desktop CLR on x64 Microsoft.CodeAnalysis.EmbeddedAttribute..ctor() L0000: ret diff --git a/source/Runtime/Runtime.csproj b/source/Runtime/Runtime.csproj index 74e8a259..0be6a26b 100644 --- a/source/Runtime/Runtime.csproj +++ b/source/Runtime/Runtime.csproj @@ -8,7 +8,7 @@ - + diff --git a/source/Server/Common/Diagnostics/DiagnosticLog.cs b/source/Server/Common/Diagnostics/DiagnosticLog.cs index 6ddcf73a..9cd27303 100644 --- a/source/Server/Common/Diagnostics/DiagnosticLog.cs +++ b/source/Server/Common/Diagnostics/DiagnosticLog.cs @@ -4,57 +4,57 @@ using System.Threading; using Mono.Cecil; -namespace SharpLab.Server.Common.Diagnostics { - public static class DiagnosticLog { - private static readonly AsyncLocal> _getPathByStepName = new(); - private static readonly AsyncLocal> _logMessage = new(); - - public static void Enable(Action logMessage, Func getPathByStepName) { - _logMessage.Value = logMessage; - _getPathByStepName.Value = getPathByStepName; - } - - public static bool IsEnabled() { - return _getPathByStepName.Value != null; - } - - public static void LogMessage(string message) { - _logMessage.Value?.Invoke(message); - } - - public static void LogAssembly(string stepName, ModuleDefinition module) { - var path = GetLogPathWithoutExtension(stepName); - if (path == null) - return; - module.Write(path + ".dll"); - } - - public static void LogAssembly(string stepName, MemoryStream assemblyStream, MemoryStream? symbolStream) { - var path = GetLogPathWithoutExtension(stepName); - if (path == null) - return; - File.WriteAllBytes(path + ".dll", assemblyStream.ToArray()); - if (symbolStream != null) - File.WriteAllBytes(path + ".pdb", symbolStream.ToArray()); - } - - public static void LogText(string stepName, string text) { - var path = GetLogPathWithoutExtension(stepName); - if (path == null) - return; - File.WriteAllText(path + ".txt", text); - } - - private static string? GetLogPathWithoutExtension(string stepName) { - if (_getPathByStepName.Value is not {} getPathByStepName) - return null; - - var path = getPathByStepName(stepName); - var directoryPath = Path.GetDirectoryName(path); - if (directoryPath != null && !Directory.Exists(directoryPath)) - Directory.CreateDirectory(directoryPath); - return path; - } +namespace SharpLab.Server.Common.Diagnostics; + +public static class DiagnosticLog { + private static readonly AsyncLocal> _getPathByStepName = new(); + private static readonly AsyncLocal> _logMessage = new(); + + public static void Enable(Action logMessage, Func getPathByStepName) { + _logMessage.Value = logMessage; + _getPathByStepName.Value = getPathByStepName; + } + + public static bool IsEnabled() { + return _getPathByStepName.Value != null; + } + + public static void LogMessage(string message) { + _logMessage.Value?.Invoke(message); + } + + public static void LogAssembly(string stepName, ModuleDefinition module) { + var path = GetLogPathWithoutExtension(stepName); + if (path == null) + return; + module.Write(path + ".dll"); + } + + public static void LogAssembly(string stepName, MemoryStream assemblyStream, MemoryStream? symbolStream) { + var path = GetLogPathWithoutExtension(stepName); + if (path == null) + return; + File.WriteAllBytes(path + ".dll", assemblyStream.ToArray()); + if (symbolStream != null) + File.WriteAllBytes(path + ".pdb", symbolStream.ToArray()); + } + + public static void LogText(string stepName, string text) { + var path = GetLogPathWithoutExtension(stepName); + if (path == null) + return; + File.WriteAllText(path + ".txt", text); + } + + private static string? GetLogPathWithoutExtension(string stepName) { + if (_getPathByStepName.Value is not {} getPathByStepName) + return null; + + var path = getPathByStepName(stepName); + var directoryPath = Path.GetDirectoryName(path); + if (directoryPath != null && !Directory.Exists(directoryPath)) + Directory.CreateDirectory(directoryPath); + return path; } } #endif \ No newline at end of file diff --git a/source/Server/Decompilation/JitAsmDecompiler.cs b/source/Server/Decompilation/JitAsmDecompiler.cs index d4f4bbb7..04826f32 100644 --- a/source/Server/Decompilation/JitAsmDecompiler.cs +++ b/source/Server/Decompilation/JitAsmDecompiler.cs @@ -9,7 +9,6 @@ using Iced.Intel; using JetBrains.Annotations; using Microsoft.Diagnostics.Runtime; -using Microsoft.Diagnostics.Runtime.DacInterface; using MirrorSharp.Advanced; using SharpLab.Runtime; using SharpLab.Runtime.Internal; @@ -17,386 +16,369 @@ using SharpLab.Server.Common.Diagnostics; using SharpLab.Server.Decompilation.Internal; -namespace SharpLab.Server.Decompilation { - [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] - public class JitAsmDecompiler : IDecompiler { - private static readonly FormatterOptions FormatterOptions = new() { - HexPrefix = "0x", - HexSuffix = null, - UppercaseHex = false, - SpaceAfterOperandSeparator = true - }; - private readonly Pool _runtimePool; - private readonly JitAsmSettings _settings; +namespace SharpLab.Server.Decompilation; - public string LanguageName => TargetNames.JitAsm; +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public class JitAsmDecompiler : IDecompiler { + private static readonly FormatterOptions FormatterOptions = new() { + HexPrefix = "0x", + HexSuffix = null, + UppercaseHex = false, + SpaceAfterOperandSeparator = true + }; + private readonly Pool _runtimePool; + private readonly JitAsmSettings _settings; - public JitAsmDecompiler(Pool runtimePool, JitAsmSettings settings) { - _runtimePool = runtimePool; - _settings = settings; - } + public string LanguageName => TargetNames.JitAsm; - public void Decompile(CompilationStreamPair streams, TextWriter codeWriter, IWorkSession session) { - Argument.NotNull(nameof(streams), streams); - Argument.NotNull(nameof(codeWriter), codeWriter); - Argument.NotNull(nameof(session), session); + public JitAsmDecompiler(Pool runtimePool, JitAsmSettings settings) { + _runtimePool = runtimePool; + _settings = settings; + } - using var loadContext = new CustomAssemblyLoadContext(shouldShareAssembly: _ => true); - var assembly = loadContext.LoadFromStream(streams.AssemblyStream); - EnsureNoJitSideEffects(assembly); + public void Decompile(CompilationStreamPair streams, TextWriter codeWriter, IWorkSession session) { + Argument.NotNull(nameof(streams), streams); + Argument.NotNull(nameof(codeWriter), codeWriter); + Argument.NotNull(nameof(session), session); - using var runtimeLease = _runtimePool.GetOrCreate(); - var runtime = runtimeLease.Object; + using var loadContext = new CustomAssemblyLoadContext(shouldShareAssembly: _ => true); + var assembly = loadContext.LoadFromStream(streams.AssemblyStream); + EnsureNoJitSideEffects(assembly); - runtime.FlushCachedData(); - var context = new JitWriteContext(codeWriter, runtime); + using var runtimeLease = _runtimePool.GetOrCreate(); + var runtime = runtimeLease.Object; - WriteJitInfo(runtime.ClrInfo, codeWriter); - WriteProfilerState(codeWriter); + runtime.FlushCachedData(); + var context = new JitWriteContext(codeWriter, runtime); - DisassembleAndWriteTypesInOrder(context, assembly); - } + WriteJitInfo(runtime.ClrInfo, codeWriter); + WriteProfilerState(codeWriter); - private void EnsureNoJitSideEffects(Assembly assembly) { - try { - foreach (var type in assembly.DefinedTypes) { - foreach (var constructor in type.DeclaredConstructors) { - if (constructor.IsStatic) - throw new NotSupportedException($"Type {type} has a static constructor, which is not supported by SharpLab JIT decompiler."); - } + DisassembleAndWriteTypesInOrder(context, assembly); + } + + private void EnsureNoJitSideEffects(Assembly assembly) { + try { + foreach (var type in assembly.DefinedTypes) { + foreach (var constructor in type.DeclaredConstructors) { + if (constructor.IsStatic) + throw new NotSupportedException($"Type {type} has a static constructor, which is not supported by SharpLab JIT decompiler."); + } - foreach (var method in type.DeclaredMethods) { - foreach (var attribute in method.CustomAttributes) { - if (attribute.AttributeType is { Name: "ModuleInitializerAttribute", Namespace: "System.Runtime.CompilerServices" }) - throw new NotSupportedException($"Method {method} is a module initializer, which is not supported by SharpLab JIT decompiler."); - } + foreach (var method in type.DeclaredMethods) { + foreach (var attribute in method.CustomAttributes) { + if (attribute.AttributeType is { Name: "ModuleInitializerAttribute", Namespace: "System.Runtime.CompilerServices" }) + throw new NotSupportedException($"Method {method} is a module initializer, which is not supported by SharpLab JIT decompiler."); } } } - catch (ReflectionTypeLoadException ex) { - throw new NotSupportedException("Unable to validate whether code has static constructors or module initializers (not supported by SharpLab JIT decompiler).", ex); - } - } - - private void WriteJitInfo(ClrInfo clr, TextWriter writer) { - writer.WriteLine( - "; {0:G} CLR {1} on {2}", - clr.Flavor, clr.Version, clr.DataTarget.DataReader.Architecture.ToString("G").ToLowerInvariant() - ); } - - private void WriteProfilerState(TextWriter writer) { - if (!ProfilerState.Active) - return; - - writer.WriteLine("; Note: Running under profiler, which affects JIT assembly in heap allocations."); + catch (ReflectionTypeLoadException ex) { + throw new NotSupportedException("Unable to validate whether code has static constructors or module initializers (not supported by SharpLab JIT decompiler).", ex); } + } - private void DisassembleAndWriteTypesInOrder(JitWriteContext context, Assembly assembly) { - var lastNonUserTypeIndex = -1; - var types = assembly.GetTypes(); - for (var i = 0; i < types.Length; i++) { - var type = types[i]; - - if (type.IsNested) - continue; // it's easier to handle nested generic types recursively, so we suppress all nested for consistency - - if (IsNonUserCode(type)) { - lastNonUserTypeIndex = i; - continue; - } + private void WriteJitInfo(ClrInfo clr, TextWriter writer) { + writer.WriteLine( + "; {0:G} CLR {1} on {2}", + clr.Flavor, clr.Version, clr.DataTarget.DataReader.Architecture.ToString("G").ToLowerInvariant() + ); + } - DisassembleAndWriteMembers(context, type.GetTypeInfo()); - } + private void WriteProfilerState(TextWriter writer) { + if (!ProfilerState.Active) + return; - if (lastNonUserTypeIndex >= 0) { - for (var i = 0; i <= lastNonUserTypeIndex; i++) { - DisassembleAndWriteMembers(context, types[i].GetTypeInfo()); - } - } - } - - private bool IsNonUserCode(Type type) { - // Note: the logic cannot be reused, but should match C# and IL - return type.Namespace != null - && type.IsDefined(); - } + writer.WriteLine("; Note: Running under profiler, which affects JIT assembly in heap allocations."); + } - private void DisassembleAndWriteMembers(JitWriteContext context, TypeInfo type, ImmutableArray? genericArgumentTypes = null) { - if (type.IsGenericTypeDefinition) { - if (TryDisassembleAndWriteMembersOfGeneric(context, type, genericArgumentTypes)) - return; - } + private void DisassembleAndWriteTypesInOrder(JitWriteContext context, Assembly assembly) { + var lastNonUserTypeIndex = -1; + var types = assembly.GetTypes(); + for (var i = 0; i < types.Length; i++) { + var type = types[i]; - foreach (var constructor in type.DeclaredConstructors) { - DisassembleAndWriteMethod(context, constructor); - } + if (type.IsNested) + continue; // it's easier to handle nested generic types recursively, so we suppress all nested for consistency - foreach (var method in type.DeclaredMethods) { - if (method.IsAbstract) - continue; - DisassembleAndWriteMethod(context, method); + if (IsNonUserCode(type)) { + lastNonUserTypeIndex = i; + continue; } - foreach (var nested in type.DeclaredNestedTypes) { - DisassembleAndWriteMembers(context, nested, genericArgumentTypes); - } + DisassembleAndWriteMembers(context, type.GetTypeInfo()); } - private bool TryDisassembleAndWriteMembersOfGeneric(JitWriteContext context, TypeInfo type, ImmutableArray? parentArgumentTypes = null) { - var hadAttribute = false; - foreach (var attribute in type.GetCustomAttributes(false)) { - hadAttribute = true; - - var fullArgumentTypes = (parentArgumentTypes ?? ImmutableArray.Empty) - .AddRange(attribute.ArgumentTypes); - var genericInstance = ApplyJitGenericAttribute(type, fullArgumentTypes.ToArray(), static (t, a) => t.MakeGenericType(a)); - DisassembleAndWriteMembers(context, genericInstance.GetTypeInfo(), fullArgumentTypes); + if (lastNonUserTypeIndex >= 0) { + for (var i = 0; i <= lastNonUserTypeIndex; i++) { + DisassembleAndWriteMembers(context, types[i].GetTypeInfo()); } - if (hadAttribute) - return true; - - if (parentArgumentTypes != null) { - var genericInstance = ApplyJitGenericAttribute(type, parentArgumentTypes.Value.ToArray(), static (t, a) => t.MakeGenericType(a)); - DisassembleAndWriteMembers(context, genericInstance.GetTypeInfo(), parentArgumentTypes); - return true; - } - - return false; } + } - private void DisassembleAndWriteMethod(JitWriteContext context, MethodBase method) { - #if DEBUG - DiagnosticLog.LogMessage($"[JitAsm] Processing method {method.Name}"); - #endif + private bool IsNonUserCode(Type type) { + // Note: the logic cannot be reused, but should match C# and IL + return type.Namespace != null + && type.IsDefined(); + } - if ((method.MethodImplementationFlags & MethodImplAttributes.Runtime) == MethodImplAttributes.Runtime) { - WriteSignatureFromReflection(context, method); - context.Writer.WriteLine(" ; Cannot produce JIT assembly for runtime-implemented method."); + private void DisassembleAndWriteMembers(JitWriteContext context, TypeInfo type, ImmutableArray? genericArgumentTypes = null) { + if (type.IsGenericTypeDefinition) { + if (TryDisassembleAndWriteMembersOfGeneric(context, type, genericArgumentTypes)) return; - } + } - if ((method.MethodImplementationFlags & MethodImplAttributes.InternalCall) == MethodImplAttributes.InternalCall) { - WriteSignatureFromReflection(context, method); - context.Writer.WriteLine(" ; Cannot produce JIT assembly for an internal call method."); - return; - } + foreach (var constructor in type.DeclaredConstructors) { + DisassembleAndWriteMethod(context, constructor); + } - if ((method.Attributes & MethodAttributes.PinvokeImpl) == MethodAttributes.PinvokeImpl) { - WriteSignatureFromReflection(context, method); - context.Writer.WriteLine(" ; Cannot produce JIT assembly for a P/Invoke method."); - return; - } + foreach (var method in type.DeclaredMethods) { + if (method.IsAbstract) + continue; + DisassembleAndWriteMethod(context, method); + } - if (method.DeclaringType?.IsGenericTypeDefinition ?? false) { - WriteIgnoredOpenGeneric(context, method); - return; - } + foreach (var nested in type.DeclaredNestedTypes) { + DisassembleAndWriteMembers(context, nested, genericArgumentTypes); + } + } - if (method.IsGenericMethodDefinition) { - DisassembleAndWriteGenericMethod(context, (MethodInfo)method); - return; - } + private bool TryDisassembleAndWriteMembersOfGeneric(JitWriteContext context, TypeInfo type, ImmutableArray? parentArgumentTypes = null) { + var hadAttribute = false; + foreach (var attribute in type.GetCustomAttributes(false)) { + hadAttribute = true; - DisassembleAndWriteSimpleMethod(context, method); + var fullArgumentTypes = (parentArgumentTypes ?? []) + .AddRange(attribute.ArgumentTypes); + var genericInstance = ApplyJitGenericAttribute(type, fullArgumentTypes.ToArray(), static (t, a) => t.MakeGenericType(a)); + DisassembleAndWriteMembers(context, genericInstance.GetTypeInfo(), fullArgumentTypes); } + if (hadAttribute) + return true; - private void DisassembleAndWriteGenericMethod(JitWriteContext context, MethodInfo method) { - var hasAttribute = false; - foreach (var attribute in method.GetCustomAttributes()) { - hasAttribute = true; - var genericInstance = ApplyJitGenericAttribute(method, attribute.ArgumentTypes, static (m, a) => m.MakeGenericMethod(a)); - DisassembleAndWriteSimpleMethod(context, genericInstance); - } - if (!hasAttribute) - WriteIgnoredOpenGeneric(context, method); + if (parentArgumentTypes != null) { + var genericInstance = ApplyJitGenericAttribute(type, parentArgumentTypes.Value.ToArray(), static (t, a) => t.MakeGenericType(a)); + DisassembleAndWriteMembers(context, genericInstance.GetTypeInfo(), parentArgumentTypes); + return true; } - private void DisassembleAndWriteSimpleMethod(JitWriteContext context, MethodBase method) { - var handle = method.MethodHandle; - RuntimeHelpers.PrepareMethod(handle); - - var clrMethodData = FindJitCompiledMethod(context, handle); + return false; + } - var writer = context.Writer; - if (clrMethodData?.Signature is {} signature) { - writer.WriteLine(); - writer.WriteLine(signature); - } - else { - WriteSignatureFromReflection(context, method); - } + private void DisassembleAndWriteMethod(JitWriteContext context, MethodBase method) { + #if DEBUG + DiagnosticLog.LogMessage($"[JitAsm] Processing method {method.Name}"); + #endif - if (clrMethodData == null) { - if (method.IsGenericMethod) { - writer.WriteLine(" ; Failed to find JIT output for generic method (reference types?)."); - writer.WriteLine(" ; If you know a solution, please comment at https://github.com/ashmind/SharpLab/issues/99."); - return; - } + if ((method.MethodImplementationFlags & MethodImplAttributes.Runtime) == MethodImplAttributes.Runtime) { + WriteSignatureFromReflection(context, method); + context.Writer.WriteLine(" ; Cannot produce JIT assembly for runtime-implemented method."); + return; + } - writer.WriteLine(" ; Failed to find JIT output — please report at https://github.com/ashmind/SharpLab/issues."); - return; - } + if ((method.MethodImplementationFlags & MethodImplAttributes.InternalCall) == MethodImplAttributes.InternalCall) { + WriteSignatureFromReflection(context, method); + context.Writer.WriteLine(" ; Cannot produce JIT assembly for an internal call method."); + return; + } - var methodAddress = clrMethodData.Value.MethodAddress; - var methodLength = clrMethodData.Value.MethodSize; + if ((method.Attributes & MethodAttributes.PinvokeImpl) == MethodAttributes.PinvokeImpl) { + WriteSignatureFromReflection(context, method); + context.Writer.WriteLine(" ; Cannot produce JIT assembly for a P/Invoke method."); + return; + } - var reader = new MemoryCodeReader(new IntPtr(unchecked((long)methodAddress)), methodLength); - var decoder = Decoder.Create(MapArchitectureToBitness(context.Runtime.DataTarget.DataReader.Architecture), reader); + if (method.DeclaringType?.IsGenericTypeDefinition ?? false) { + WriteIgnoredOpenGeneric(context, method); + return; + } - var instructions = new InstructionList(); - decoder.IP = methodAddress; - while (decoder.IP < (methodAddress + methodLength)) { - decoder.Decode(out instructions.AllocUninitializedElement()); - } + if (method.IsGenericMethodDefinition) { + DisassembleAndWriteGenericMethod(context, (MethodInfo)method); + return; + } - var resolver = new JitAsmSymbolResolver(context.Runtime, methodAddress, methodLength, _settings); - var formatter = new IntelFormatter(FormatterOptions, resolver); - var output = new StringOutput(); - foreach (ref var instruction in instructions) { - formatter.Format(instruction, output); + DisassembleAndWriteSimpleMethod(context, method); + } - writer.Write(" L"); - writer.Write((instruction.IP - methodAddress).ToString("x4")); - writer.Write(": "); - writer.WriteLine(output.ToStringAndReset()); - } + private void DisassembleAndWriteGenericMethod(JitWriteContext context, MethodInfo method) { + var hasAttribute = false; + foreach (var attribute in method.GetCustomAttributes()) { + hasAttribute = true; + var genericInstance = ApplyJitGenericAttribute(method, attribute.ArgumentTypes, static (m, a) => m.MakeGenericMethod(a)); + DisassembleAndWriteSimpleMethod(context, genericInstance); } + if (!hasAttribute) + WriteIgnoredOpenGeneric(context, method); + } - private ClrMethodData? FindJitCompiledMethod(JitWriteContext context, RuntimeMethodHandle handle) { - lock (context.Runtime.DacLibrary.DacPrivateInterface) - context.Runtime.DacLibrary.DacPrivateInterface.Flush(); - var sos = context.Runtime.DacLibrary.SOSDacInterface; + private void DisassembleAndWriteSimpleMethod(JitWriteContext context, MethodBase method) { + var handle = method.MethodHandle; + RuntimeHelpers.PrepareMethod(handle); - var methodDescAddress = unchecked((ulong)handle.Value.ToInt64()); - if (sos.GetMethodDescData(methodDescAddress, 0, out var methodDesc) is var hresult && !hresult) { - #if DEBUG - DiagnosticLog.LogMessage($"[JitAsm] Failed to get GetMethodDescData(0x{methodDescAddress:X}): {hresult}"); - #endif - return null; - } + var clrMethodData = FindJitCompiledMethod(context.Runtime, method.MethodHandle); - return GetJitCompiledMethodByMethodDescIfValid(sos, methodDesc) - ?? FindJitCompiledMethodInMethodTable(sos, methodDesc); + var writer = context.Writer; + if (clrMethodData?.Signature is {} signature) { + writer.WriteLine(); + writer.WriteLine(signature); + } + else { + WriteSignatureFromReflection(context, method); } - private ClrMethodData? GetJitCompiledMethodByMethodDescIfValid(SOSDac sos, MethodDescData methodDesc) { - // https://github.com/microsoft/clrmd/issues/935 - var codeHeaderAddress = methodDesc.HasNativeCode != 0 - ? (ulong)methodDesc.NativeCodeAddr - : sos.GetMethodTableSlot(methodDesc.MethodTable, methodDesc.SlotNumber); + if (clrMethodData == null) { + writer.WriteLine(" ; Failed to find JIT output. This might appear more frequently than before due to a library update."); + writer.WriteLine(" ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress."); + return; + } - if (codeHeaderAddress == unchecked((ulong)-1)) - return null; + var methodAddress = clrMethodData.Value.MethodAddress; + var methodLength = clrMethodData.Value.MethodSize; - if (!sos.GetCodeHeaderData(codeHeaderAddress, out var codeHeader)) - return null; + var reader = new MemoryCodeReader(new IntPtr(unchecked((long)methodAddress)), methodLength); + var decoder = Decoder.Create(MapArchitectureToBitness(context.Runtime.DataTarget.DataReader.Architecture), reader); - return GetJitCompiledMethodByCodeHeaderIfValid(sos, codeHeader); + var instructions = new InstructionList(); + decoder.IP = methodAddress; + while (decoder.IP < (methodAddress + methodLength)) { + decoder.Decode(out instructions.AllocUninitializedElement()); } - private ClrMethodData? GetJitCompiledMethodByCodeHeaderIfValid(SOSDac sos, CodeHeaderData codeHeader) { - if (codeHeader.MethodStart.Value == -1 || codeHeader.HotRegionSize == 0) - return null; + var resolver = new JitAsmSymbolResolver(context.Runtime, methodAddress, methodLength, _settings); + var formatter = new IntelFormatter(FormatterOptions, resolver); + var output = new StringOutput(); + foreach (ref var instruction in instructions) { + formatter.Format(instruction, output); - return new( - sos.GetMethodDescName(codeHeader.MethodDesc), - unchecked((ulong)codeHeader.MethodStart.Value), - codeHeader.HotRegionSize - ); + writer.Write(" L"); + writer.Write((instruction.IP - methodAddress).ToString("x4")); + writer.Write(": "); + writer.WriteLine(output.ToStringAndReset()); } + } - private ClrMethodData? FindJitCompiledMethodInMethodTable(SOSDac sos, MethodDescData originalMethodDesc) { - // I can't really explain this, but it seems that some methods - // are present multiple times in the same type -- one compiled - // and one not compiled. - - if (!sos.GetMethodTableData(originalMethodDesc.MethodTable, out var methodTable)) - return null; - - ClrMethodData? methodData = null; - for (var i = 0u; i < methodTable.NumMethods; i++) { - if (i == originalMethodDesc.SlotNumber) - continue; - - var slot = sos.GetMethodTableSlot(originalMethodDesc.MethodTable, i); - if (!sos.GetCodeHeaderData(slot, out var candidateCodeHeader)) - continue; + private ClrMethodData? FindJitCompiledMethod(ClrRuntime runtime, RuntimeMethodHandle handle) { + lock (runtime) + runtime.FlushCachedData(); - if (!sos.GetMethodDescData(candidateCodeHeader.MethodDesc, 0, out var candidateMethodDesc)) - continue; + var methodDescAddress = unchecked((ulong)handle.Value.ToInt64()); + if (runtime.GetMethodByHandle(methodDescAddress) is not { } method) { + #if DEBUG + DiagnosticLog.LogMessage($"[JitAsm] Failed to GetMethodByHandle(0x{methodDescAddress:X})."); + #endif + return null; + } - if (candidateMethodDesc.MDToken != originalMethodDesc.MDToken) - continue; + if (method.CompilationType == MethodCompilationType.None) { + #if DEBUG + DiagnosticLog.LogMessage($"[JitAsm] Method {method.Signature} compilation type is None."); + #endif + return null; + } - methodData = GetJitCompiledMethodByCodeHeaderIfValid(sos, candidateCodeHeader); - if (methodData != null) - break; - } - return methodData; + if (method.NativeCode == 0) { + #if DEBUG + DiagnosticLog.LogMessage($"[JitAsm] Method {method.Signature} native code is 0."); + #endif + return null; } - private void WriteIgnoredOpenGeneric(JitWriteContext context, MethodBase method) { - WriteSignatureFromReflection(context, method); - var writer = context.Writer; - writer.WriteLine(" ; Open generics cannot be JIT-compiled."); - writer.WriteLine(" ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types."); - writer.WriteLine(" ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }."); + if (method.HotColdInfo.HotSize == 0) { + #if DEBUG + DiagnosticLog.LogMessage($"[JitAsm] Method {method.Signature} hot size is 0."); + #endif + return null; } - private void WriteSignatureFromReflection(JitWriteContext context, MethodBase method) { - context.Writer.WriteLine(); + return new( + method.Signature, + method.NativeCode, + method.HotColdInfo.HotSize + ); + } + + private void WriteIgnoredOpenGeneric(JitWriteContext context, MethodBase method) { + WriteSignatureFromReflection(context, method); + var writer = context.Writer; + writer.WriteLine(" ; Open generics cannot be JIT-compiled."); + writer.WriteLine(" ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types."); + writer.WriteLine(" ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }."); + } - var md = (ulong)method.MethodHandle.Value.ToInt64(); - var signature = context.Runtime.DacLibrary.SOSDacInterface.GetMethodDescName(md); + private void WriteSignatureFromReflection(JitWriteContext context, MethodBase method) { + var writer = context.Writer; - context.Writer.WriteLine(signature ?? "Unknown Method"); + writer.WriteLine(); + if (method.DeclaringType is { } declaringType) { + writer.Write(declaringType.FullName); + writer.Write("."); } - private TMember ApplyJitGenericAttribute(TMember definition, Type[] arguments, Func makeGeneric) - where TMember : MemberInfo - { - try { - return makeGeneric(definition, arguments); - } - catch (ArgumentException ex) { - throw new JitGenericAttributeException($"Failed to apply JitGenericAttribute to {definition.Name}: {ex.Message}", ex); - } - catch (Exception ex) when ( - ex is BadImageFormatException or TypeLoadException - && arguments.FirstOrDefault(static a => a.IsByRefLike) is {} refStructArgument - ) { - throw new JitGenericAttributeException($"JitGenericAttribute argument {refStructArgument.Name} is a ref struct, which is not supported in generics.", ex); + writer.Write(method.Name); + if (method.IsGenericMethod) { + writer.Write("[["); + var first = true; + foreach (var type in method.GetGenericArguments()) { + if (first) { + first = false; + } + else { + writer.Write(", "); + } + writer.Write(type.FullName); + writer.Write(", "); + writer.Write(type.Assembly.GetName().Name); } + writer.Write("]]"); } - private int MapArchitectureToBitness(Architecture architecture) => architecture switch - { - Architecture.X64 => 64, - Architecture.X86 => 32, - _ => throw new Exception($"Unsupported architecture {architecture}.") - }; + writer.WriteLine(method.GetParameters().Length > 0 ? "(...)" : "()"); + } - private class JitWriteContext { - public JitWriteContext(TextWriter writer, ClrRuntime runtime) { - Writer = writer; - Runtime = runtime; - } - - public TextWriter Writer { get; } - public ClrRuntime Runtime { get; } + private TMember ApplyJitGenericAttribute(TMember definition, Type[] arguments, Func makeGeneric) + where TMember : MemberInfo + { + try { + return makeGeneric(definition, arguments); + } + catch (ArgumentException ex) { + throw new JitGenericAttributeException($"Failed to apply JitGenericAttribute to {definition.Name}: {ex.Message}", ex); } + catch (Exception ex) when ( + ex is BadImageFormatException or TypeLoadException + && arguments.FirstOrDefault(static a => a.IsByRefLike) is {} refStructArgument + ) { + throw new JitGenericAttributeException($"JitGenericAttribute argument {refStructArgument.Name} is a ref struct, which is not supported in generics.", ex); + } + } - private readonly struct ClrMethodData { - public ClrMethodData(string? signature, ulong methodAddress, uint methodSize) { - Signature = signature; - MethodAddress = methodAddress; - MethodSize = methodSize; - } + private int MapArchitectureToBitness(Architecture architecture) => architecture switch + { + Architecture.X64 => 64, + Architecture.X86 => 32, + _ => throw new Exception($"Unsupported architecture {architecture}.") + }; + + private class JitWriteContext { + public JitWriteContext(TextWriter writer, ClrRuntime runtime) { + Writer = writer; + Runtime = runtime; + } + + public TextWriter Writer { get; } + public ClrRuntime Runtime { get; } + } - public string? Signature { get; } - public ulong MethodAddress { get; } - public uint MethodSize { get; } + private readonly struct ClrMethodData { + public ClrMethodData(string? signature, ulong methodAddress, uint methodSize) { + Signature = signature; + MethodAddress = methodAddress; + MethodSize = methodSize; } + + public string? Signature { get; } + public ulong MethodAddress { get; } + public uint MethodSize { get; } } } \ No newline at end of file diff --git a/source/Server/Server.csproj b/source/Server/Server.csproj index cb3ccd12..4815ad7d 100644 --- a/source/Server/Server.csproj +++ b/source/Server/Server.csproj @@ -42,7 +42,7 @@ - + diff --git a/source/Tests/Decompilation/TestCode/FSharp/SimpleUnion.fs b/source/Tests/Decompilation/TestCode/FSharp/SimpleUnion.fs index 3458ba9e..1c376544 100644 --- a/source/Tests/Decompilation/TestCode/FSharp/SimpleUnion.fs +++ b/source/Tests/Decompilation/TestCode/FSharp/SimpleUnion.fs @@ -77,64 +77,12 @@ _+T.__DebugDisplay() L00c1: jmp qword ptr [rax+0x20] _+T.ToString() - L0000: push r14 - L0002: push rdi - L0003: push rsi - L0004: push rbp - L0005: push rbx - L0006: sub rsp, 0x20 - L000a: mov rbx, rcx - L000d: mov rcx, 0x - L0017: call 0x - L001c: mov rsi, rax - L001f: mov rcx, 0x - L0029: mov rdx, [rcx] - L002c: lea rcx, [rsi+8] - L0030: call 0x - L0035: xor edx, edx - L0037: mov [rsi+0x10], rdx - L003b: mov [rsi+0x18], rdx - L003f: mov rdx, rsi - L0042: mov rcx, 0x - L004c: call qword ptr [0x] - L0052: mov rdi, rax - L0055: mov rbp, [rsi+0x10] - L0059: test rbp, rbp - L005c: jne short L006b - L005e: mov rcx, rdi - L0061: cmp [rcx], ecx - L0063: call qword ptr [0x] - L0069: jmp short L00aa - L006b: mov rcx, rdi - L006e: cmp [rcx], ecx - L0070: call qword ptr [0x] - L0076: mov r14, rax - L0079: mov ecx, [rdi+0x28] - L007c: call qword ptr [0x] - L0082: mov rcx, rax - L0085: mov r8, [rsi+0x18] - L0089: mov rdx, rbp - L008c: mov r9, r14 - L008f: cmp [rcx], ecx - L0091: call qword ptr [0x] - L0097: mov rdx, rax - L009a: mov rcx, 0x - L00a4: call qword ptr [0x] - L00aa: movzx edx, byte ptr [rbx] - L00ad: mov rcx, rax - L00b0: mov rax, [rax] - L00b3: mov rax, [rax+0x40] - L00b7: add rsp, 0x20 - L00bb: pop rbx - L00bc: pop rbp - L00bd: pop rsi - L00be: pop rdi - L00bf: pop r14 - L00c1: jmp qword ptr [rax+0x20] + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. -_+T.Equals(System.Object) - L0000: xor eax, eax - L0002: ret +_+T.Equals(...) + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. .$_.main@() L0000: ret diff --git a/source/Tests/Decompilation/TestCode/JitAsm/AsyncRegression.cs b/source/Tests/Decompilation/TestCode/JitAsm/AsyncRegression.cs index 67c1623b..448e9f04 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/AsyncRegression.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/AsyncRegression.cs @@ -20,7 +20,7 @@ static async Task Foo(int x) { C.M(Int32) L0000: sub rsp, 0x28 L0004: add ecx, 0x12345 - L000a: call C.Foo(Int32) + L000a: call 0x L000f: mov rcx, rax L0012: mov eax, [rcx+0x34] L0015: and eax, 0x @@ -41,7 +41,7 @@ static async Task Foo(int x) { L0010: mov [rsp+0x2c], ecx L0014: mov dword ptr [rsp+0x28], 0x L001c: lea rcx, [rsp+0x28] - L0021: call System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[C+d__1, _]](d__1 ByRef) + L0021: call 0x L0026: mov rax, [rsp+0x30] L002b: test rax, rax L002e: je short L0035 @@ -52,81 +52,11 @@ static async Task Foo(int x) { L0040: jmp short L0030 C+d__1.MoveNext() - L0000: push rbp - L0001: push rsi - L0002: push rbx - L0003: sub rsp, 0x30 - L0007: lea rbp, [rsp+0x40] - L000c: mov [rbp-0x20], rsp - L0010: mov [rbp+0x10], rcx - L0014: mov ebx, [rcx+4] - L0017: mov dword ptr [rcx], 0x - L001d: lea rsi, [rcx+8] - L0021: cmp qword ptr [rsi], 0 - L0025: jne short L007d - L0027: mov ecx, ebx - L0029: lea eax, [rbx+1] - L002c: cmp eax, 0xa - L002f: jb short L0057 - L0031: mov rcx, 0x - L003b: call 0x - L0040: mov rdx, rax - L0043: mov dword ptr [rdx+0x34], 0x - L004a: mov [rdx+0x38], ebx - L004d: mov rcx, rsi - L0050: call 0x - L0055: jmp short L0075 - L0057: mov rax, 0x - L0061: mov rax, [rax] - L0064: lea edx, [rcx+1] - L0067: cmp edx, 0xa - L006a: jae short L008a - L006c: inc ecx - L006e: mov rdx, [rax+rcx*8+0x10] - L0073: jmp short L004d - L0075: add rsp, 0x30 - L0079: pop rbx - L007a: pop rsi - L007b: pop rbp - L007c: ret - L007d: mov rcx, [rsi] - L0080: mov edx, ebx - L0082: call qword ptr [0x] - L0088: jmp short L0075 - L008a: call 0x - L008f: int3 - L0090: push rbp - L0091: push rsi - L0092: push rbx - L0093: sub rsp, 0x30 - L0097: mov rbp, [rcx+0x20] - L009b: mov [rsp+0x20], rbp - L00a0: lea rbp, [rbp+0x40] - L00a4: mov rcx, [rbp+0x10] - L00a8: mov dword ptr [rcx], 0x - L00ae: add rcx, 8 - L00b2: call qword ptr [0x] - L00b8: lea rax, [L0075] - L00bf: add rsp, 0x30 - L00c3: pop rbx - L00c4: pop rsi - L00c5: pop rbp - L00c6: ret + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. -C+d__1.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine) - L0000: sub rsp, 0x28 - L0004: mov rcx, [rcx+8] - L0008: test rdx, rdx - L000b: je short L0017 - L000d: test rcx, rcx - L0010: jne short L0023 - L0012: add rsp, 0x28 - L0016: ret - L0017: mov ecx, 0x3d - L001c: call qword ptr [0x] - L0022: int3 - L0023: mov ecx, 0x28 - L0028: call qword ptr [0x] - L002e: int3 +C+d__1.SetStateMachine(...) + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/Tests/Decompilation/TestCode/JitAsm/Delegate.cs b/source/Tests/Decompilation/TestCode/JitAsm/Delegate.cs index 6359004c..48a9b7d1 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/Delegate.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/Delegate.cs @@ -4,16 +4,16 @@ ; Core CLR on x64 -D..ctor(System.Object, IntPtr) +D..ctor(...) ; Cannot produce JIT assembly for runtime-implemented method. D.Invoke() ; Cannot produce JIT assembly for runtime-implemented method. -D.BeginInvoke(System.AsyncCallback, System.Object) +D.BeginInvoke(...) ; Cannot produce JIT assembly for runtime-implemented method. -D.EndInvoke(System.IAsyncResult) +D.EndInvoke(...) ; Cannot produce JIT assembly for runtime-implemented method. */ \ No newline at end of file diff --git a/source/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs b/source/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs index 1e20e142..c321411c 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/Generic.ClassWithAttribute.cs @@ -24,8 +24,8 @@ static T M() { L000b: mov rax, rcx L000e: ret -C`1[[System.__Canon, System.Private.CoreLib]].M() - L0000: xor eax, eax - L0002: ret +C`1[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].M() + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs b/source/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs index 0db354a5..9e0371fc 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/Generic.MethodWithAttribute.cs @@ -24,7 +24,7 @@ static T M() { L000e: ret C.M[[System.String, System.Private.CoreLib]]() - ; Failed to find JIT output for generic method (reference types?). - ; If you know a solution, please comment at https://github.com/ashmind/SharpLab/issues/99. + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs index 0594e6e6..356b200f 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnBoth.cs @@ -18,16 +18,16 @@ static class N { L0000: xor eax, eax L0002: ret -C`1+N`1[[System.Int32, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]].M(System.__Canon) - L0000: xor eax, eax - L0002: ret +C`1+N`1[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].M(...) + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. -C`1+N`1[[System.__Canon, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]].M(Int32) - L0000: xor eax, eax - L0002: ret +C`1+N`1[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].M(...) + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. -C`1+N`1[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]].M(System.__Canon) - L0000: xor eax, eax - L0002: ret +C`1+N`1[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].M(...) + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs index 77a2b45c..51a6c974 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnNested.cs @@ -16,8 +16,8 @@ static class N { L0000: xor eax, eax L0002: ret -C+N`1[[System.__Canon, System.Private.CoreLib]].get_M() - L0000: xor eax, eax - L0002: ret +C+N`1[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_M() + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs index fc4617a8..6f8baa82 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Nested.AttributeOnTop.cs @@ -16,8 +16,8 @@ static class N { L0000: xor eax, eax L0002: ret -C`1+N[[System.__Canon, System.Private.CoreLib]].M() - L0000: xor eax, eax - L0002: ret +C`1+N[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].M() + ; Failed to find JIT output. This might appear more frequently than before due to a library update. + ; Please monitor https://github.com/ashmind/SharpLab/issues/1334 for progress. */ \ No newline at end of file diff --git a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs index 3632248b..6c00565f 100644 --- a/source/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs +++ b/source/Tests/Decompilation/TestCode/JitAsm/Generic.Open.Multiple.cs @@ -30,7 +30,7 @@ static void M() {} ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types. ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }. -C.M() +C.M[[, _]]() ; Open generics cannot be JIT-compiled. ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types. ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M() { ... }. diff --git a/source/Tests/Execution/FSharpTests.cs b/source/Tests/Execution/FSharpTests.cs index 12547e03..d7169e8f 100644 --- a/source/Tests/Execution/FSharpTests.cs +++ b/source/Tests/Execution/FSharpTests.cs @@ -10,7 +10,7 @@ namespace SharpLab.Tests.Execution; [Collection(TestCollectionNames.Execution)] public class FSharpTests { public FSharpTests(ITestOutputHelper output) { - TestDiagnosticLog.Enable(output); + // TestDiagnosticLog.Enable(output); } [Fact]