Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy

pull/1299/head
Siegfried Pammer 7 years ago
parent
commit
186ae4507a
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  3. 237
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
  4. 439
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
  5. 5
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs
  6. 79
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 14
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  8. 12
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  9. 167
      ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs
  10. 2
      ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs
  11. 64
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  12. 26
      ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs
  13. 7
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs
  14. 2
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  15. 10
      ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
  16. 2
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  17. 3
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
  18. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs
  19. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
  20. 31
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
  21. 5
      ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -88,6 +88,7 @@
<Compile Include="TestCases\ILPretty\Issue1323.cs" /> <Compile Include="TestCases\ILPretty\Issue1323.cs" />
<Compile Include="TestCases\Pretty\CustomAttributes2.cs" /> <Compile Include="TestCases\Pretty\CustomAttributes2.cs" />
<Compile Include="TestCases\Pretty\EnumTests.cs" /> <Compile Include="TestCases\Pretty\EnumTests.cs" />
<None Include="TestCases\ILPretty\Unsafe.il" />
<None Include="TestCases\Pretty\NullableRefTypes.cs" /> <None Include="TestCases\Pretty\NullableRefTypes.cs" />
<Compile Include="TestCases\Pretty\TypeMemberTests.cs" /> <Compile Include="TestCases\Pretty\TypeMemberTests.cs" />
<Compile Include="TestCases\Pretty\ValueTypes.cs" /> <Compile Include="TestCases\Pretty\ValueTypes.cs" />

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -160,6 +160,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(); Run();
} }
[Test]
public void Unsafe()
{
Run();
}
[Test] [Test]
public void FSharpLoops_Debug() public void FSharpLoops_Debug()
{ {

237
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs

@ -0,0 +1,237 @@
using System;
using System.Reflection;
[assembly: AssemblyFileVersion("4.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.0.0")]
[assembly: AssemblyTitle("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyDescription("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: AssemblyProduct("Microsoft® .NET Framework")]
[assembly: CLSCompliant(false)]
namespace System.Runtime.CompilerServices
{
public static class Unsafe
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static T Read<T>(void* source)
{
return *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static T ReadUnaligned<T>(void* source)
{
return *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static T ReadUnaligned<T>(ref byte source)
{
return *(T*)(&source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void Write<T>(void* destination, T value)
{
*(T*)destination = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void WriteUnaligned<T>(void* destination, T value)
{
*(T*)destination = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void WriteUnaligned<T>(ref byte destination, T value)
{
*(T*)(&destination) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void Copy<T>(void* destination, ref T source)
{
*(T*)destination = source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void Copy<T>(ref T destination, void* source)
{
destination = *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* AsPointer<T>(ref T value)
{
return &value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static int SizeOf<T>()
{
return sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void CopyBlock(void* destination, void* source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlock(destination, source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyBlock(ref byte destination, ref byte source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlock(ref destination, ref source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void CopyBlockUnaligned(void* destination, void* source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(destination, source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(ref destination, ref source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void InitBlock(void* startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlock(startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitBlock(ref byte startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlock(ref startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlockUnaligned(startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlockUnaligned(ref startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T As<T>(object o) where T : class
{
return (T)o;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static ref T AsRef<T>(void* source)
{
return ref *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(in T source)
{
return ref source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static ref TTo As<TFrom, TTo>(ref TFrom source)
{
return ref *(TTo*)(&source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Unbox<T>(object box) where T : struct
{
return ref (T)box;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Add<T>(ref T source, int elementOffset)
{
return ref Unsafe.Add(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* Add<T>(void* source, int elementOffset)
{
return (byte*)source + (long)elementOffset * (long)sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Add<T>(ref T source, IntPtr elementOffset)
{
return ref Unsafe.Add(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AddByteOffset<T>(ref T source, IntPtr byteOffset)
{
return ref Unsafe.AddByteOffset(ref source, byteOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Subtract<T>(ref T source, int elementOffset)
{
return ref Unsafe.Subtract(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* Subtract<T>(void* source, int elementOffset)
{
return (byte*)source - (long)elementOffset * (long)sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Subtract<T>(ref T source, IntPtr elementOffset)
{
return ref Unsafe.Subtract(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T SubtractByteOffset<T>(ref T source, IntPtr byteOffset)
{
return ref Unsafe.SubtractByteOffset(ref source, byteOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ByteOffset<T>(ref T origin, ref T target)
{
return Unsafe.ByteOffset(ref target, ref origin);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool AreSame<T>(ref T left, ref T right)
{
return (ref left) == (ref right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsAddressGreaterThan<T>(ref T left, ref T right)
{
return (ref left) > (ref right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsAddressLessThan<T>(ref T left, ref T right)
{
return (ref left) < (ref right);
}
}
}

439
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il

@ -0,0 +1,439 @@
#define CORE_ASSEMBLY "System.Runtime"
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 4:0:0:0
}
.assembly System.Runtime.CompilerServices.Unsafe
{
.custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 ) // ...4.0.0.0..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 ) // ...4.0.0.0..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 26 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D // ..&System.Runtim
65 2E 43 6F 6D 70 69 6C 65 72 53 65 72 76 69 63 // e.CompilerServic
65 73 2E 55 6E 73 61 66 65 00 00 ) // es.Unsafe..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 26 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D // ..&System.Runtim
65 2E 43 6F 6D 70 69 6C 65 72 53 65 72 76 69 63 // e.CompilerServic
65 73 2E 55 6E 73 61 66 65 00 00 ) // es.Unsafe..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = (
01 00 15 2e 4e 45 54 46 72 61 6d 65 77 6f 72 6b
41 73 73 65 6d 62 6c 79 00 00 00
) // ".NETFrameworkAssembly", ""
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = (
01 00 0b 53 65 72 76 69 63 65 61 62 6c 65 04 54
72 75 65 00 00
) // "Serviceable", "True"
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2F C2 A9 20 4D 69 63 72 6F 73 6F 66 74 20 // ../.. Microsoft
43 6F 72 70 6F 72 61 74 69 6F 6E 2E 20 20 41 6C // Corporation. Al
6C 20 72 69 67 68 74 73 20 72 65 73 65 72 76 65 // l rights reserve
64 2E 00 00 ) // d...
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 15 4D 69 63 72 6F 73 6F 66 74 20 43 6F 72 // ...Microsoft Cor
70 6F 72 61 74 69 6F 6E 00 00 ) // poration..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 1A 4D 69 63 72 6F 73 6F 66 74 C2 AE 20 2E // ...Microsoft.. .
4E 45 54 20 46 72 61 6D 65 77 6F 72 6B 00 00 ) // NET Framework..
.custom instance void [CORE_ASSEMBLY]System.CLSCompliantAttribute::.ctor(bool) = (
01 00 00 00 00
) // false
.hash algorithm 0x00008004
.ver 4:0:5:0
}
.module System.Runtime.CompilerServices.Unsafe.dll
// MVID: {1E97D84A-565B-49C5-B60A-F31A1A4ACE13}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02ED0000
// =============== CLASS MEMBERS DECLARATION ===================
.class public abstract auto ansi sealed beforefieldinit System.Runtime.CompilerServices.Unsafe
extends [CORE_ASSEMBLY]System.Object
{
.method public hidebysig static !!T Read<T>(void* source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
ldobj !!T
ret
} // end of method Unsafe::Read
.method public hidebysig static !!T ReadUnaligned<T>(void* source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
unaligned. 0x1
ldobj !!T
ret
} // end of method Unsafe::ReadUnaligned
.method public hidebysig static !!T ReadUnaligned<T>(uint8& source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
unaligned. 0x1
ldobj !!T
ret
} // end of method Unsafe::ReadUnaligned
.method public hidebysig static void Write<T>(void* destination,
!!T 'value') cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
stobj !!T
ret
} // end of method Unsafe::Write
.method public hidebysig static void WriteUnaligned<T>(void* destination,
!!T 'value') cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
unaligned. 0x01
stobj !!T
ret
} // end of method Unsafe::WriteUnaligned
.method public hidebysig static void WriteUnaligned<T>(uint8& destination,
!!T 'value') cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
unaligned. 0x01
stobj !!T
ret
} // end of method Unsafe::WriteUnaligned
.method public hidebysig static void Copy<T>(void* destination,
!!T& source) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
ldobj !!T
stobj !!T
ret
} // end of method Unsafe::Copy
.method public hidebysig static void Copy<T>(!!T& destination,
void* source) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
ldobj !!T
stobj !!T
ret
} // end of method Unsafe::Copy
.method public hidebysig static void* AsPointer<T>(!!T& 'value') cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
conv.u
ret
} // end of method Unsafe::AsPointer
.method public hidebysig static int32 SizeOf<T>() cil managed aggressiveinlining
{
.maxstack 1
sizeof !!T
ret
} // end of method Unsafe::SizeOf
.method public hidebysig static void CopyBlock(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
cpblk
ret
} // end of method Unsafe::CopyBlock
.method public hidebysig static void CopyBlock(uint8& destination, uint8& source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
cpblk
ret
} // end of method Unsafe::CopyBlock
.method public hidebysig static void CopyBlockUnaligned(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
cpblk
ret
} // end of method Unsafe::CopyBlockUnaligned
.method public hidebysig static void CopyBlockUnaligned(uint8& destination, uint8& source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
cpblk
ret
} // end of method Unsafe::CopyBlockUnaligned
.method public hidebysig static void InitBlock(void* startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
initblk
ret
} // end of method Unsafe::InitBlock
.method public hidebysig static void InitBlock(uint8& startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
initblk
ret
} // end of method Unsafe::InitBlock
.method public hidebysig static void InitBlockUnaligned(void* startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
initblk
ret
} // end of method Unsafe::InitBlockUnaligned
.method public hidebysig static void InitBlockUnaligned(uint8& startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
initblk
ret
} // end of method Unsafe::InitBlockUnaligned
.method public hidebysig static !!T As<class T>(object o) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
ret
} // end of method Unsafe::As
.method public hidebysig static !!T& AsRef<T>(void* source) cil managed aggressiveinlining
{
// For .NET Core the roundtrip via a local is no longer needed see:
// https://github.com/dotnet/coreclr/issues/13341
// and
// https://github.com/dotnet/coreclr/pull/11218
#ifdef netcoreapp
.maxstack 1
ldarg.0
ret
#else
.locals (int32&)
.maxstack 1
ldarg.0
// Roundtrip via a local to avoid type mismatch on return that the JIT inliner chokes on.
stloc.0
ldloc.0
ret
#endif
} // end of method Unsafe::AsRef
.method public hidebysig static !!T& AsRef<T>(!!T& source) cil managed aggressiveinlining
{
.param [1]
#ifdef netcoreapp
.custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 )
#else
.custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 )
#endif
.maxstack 1
ldarg.0
ret
} // end of method Unsafe::AsRef
.method public hidebysig static !!TTo& As<TFrom, TTo>(!!TFrom& source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
ret
} // end of method Unsafe::As
.method public hidebysig static !!T& Unbox<valuetype .ctor ([CORE_ASSEMBLY]System.ValueType) T> (object 'box') cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
unbox !!T
ret
} // end of method Unsafe::Unbox
.method public hidebysig static !!T& Add<T>(!!T& source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
add
ret
} // end of method Unsafe::Add
.method public hidebysig static void* Add<T>(void* source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
add
ret
} // end of method Unsafe::Add
.method public hidebysig static !!T& Add<T>(!!T& source, native int elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
mul
add
ret
} // end of method Unsafe::Add
.method public hidebysig static !!T& AddByteOffset<T>(!!T& source, native int byteOffset) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
add
ret
} // end of method Unsafe::AddByteOffset
.method public hidebysig static !!T& Subtract<T>(!!T& source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
sub
ret
} // end of method Unsafe::Subtract
.method public hidebysig static void* Subtract<T>(void* source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
sub
ret
} // end of method Unsafe::Subtract
.method public hidebysig static !!T& Subtract<T>(!!T& source, native int elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
mul
sub
ret
} // end of method Unsafe::Subtract
.method public hidebysig static !!T& SubtractByteOffset<T>(!!T& source, native int byteOffset) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
sub
ret
} // end of method Unsafe::SubtractByteOffset
.method public hidebysig static native int ByteOffset<T>(!!T& origin, !!T& target) cil managed aggressiveinlining
{
.maxstack 2
ldarg.1
ldarg.0
sub
ret
} // end of method Unsafe::ByteOffset
.method public hidebysig static bool AreSame<T>(!!T& left, !!T& right) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
ceq
ret
} // end of method Unsafe::AreSame
.method public hidebysig static bool IsAddressGreaterThan<T>(!!T& left, !!T& right) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
cgt.un
ret
} // end of method Unsafe::IsAddressGreaterThan
.method public hidebysig static bool IsAddressLessThan<T>(!!T& left, !!T& right) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
clt.un
ret
} // end of method Unsafe::IsAddressLessThan
} // end of class System.Runtime.CompilerServices.Unsafe
#ifdef netcoreapp
#else
.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute
extends [CORE_ASSEMBLY]System.Attribute
{
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 1
ldarg.0
call instance void [CORE_ASSEMBLY]System.Attribute::.ctor()
ret
} // end of method IsReadOnlyAttribute::.ctor
} // end of class System.Runtime.CompilerServices.IsReadOnlyAttribute
#endif

5
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs

@ -97,6 +97,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
RequiresCast((IFormattable)$"\ta{$"a{args.Length}" == args[0]}"); RequiresCast((IFormattable)$"\ta{$"a{args.Length}" == args[0]}");
} }
public void Issue1497(string[] args)
{
Console.WriteLine($"args[0]: {args[0].Trim(':').Trim('&').Trim(':').Trim('&')} asdf {args.Length:x} test");
}
public void RequiresCast(string value) public void RequiresCast(string value)
{ {
} }

79
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -369,7 +369,7 @@ namespace ICSharpCode.Decompiler.CSharp
pointerType = typeHint as PointerType; pointerType = typeHint as PointerType;
if (pointerType != null && GetPointerArithmeticOffset( if (pointerType != null && GetPointerArithmeticOffset(
inst.Argument, Translate(inst.Argument), inst.Argument, Translate(inst.Argument),
pointerType, checkForOverflow: true, pointerType.ElementType, checkForOverflow: true,
unwrapZeroExtension: true unwrapZeroExtension: true
) is TranslatedExpression offset) ) is TranslatedExpression offset)
{ {
@ -958,7 +958,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else { } else {
return null; return null;
} }
TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType, inst.CheckForOverflow) TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow)
?? FallBackToBytePointer(); ?? FallBackToBytePointer();
if (left.Type.Kind == TypeKind.Pointer) { if (left.Type.Kind == TypeKind.Pointer) {
@ -985,6 +985,68 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
/// <summary>
/// Translates pointer arithmetic with managed pointers:
/// ref + int
/// int + ref
/// ref - int
/// ref - ref
/// </summary>
TranslatedExpression? HandleManagedPointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right)
{
if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub))
return null;
if (inst.CheckForOverflow || inst.IsLifted)
return null;
if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) {
// ref - ref => i
return CallUnsafeIntrinsic("ByteOffset", new[] { left.Expression, right.Expression }, compilation.FindType(KnownTypeCode.IntPtr), inst);
}
if (inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.I
&& left.Type is ByReferenceType brt) {
// ref [+-] int
string name = (inst.Operator == BinaryNumericOperator.Sub ? "Subtract" : "Add");
ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Right, brt.ElementType, inst.CheckForOverflow);
if (offsetInst != null) {
return CallUnsafeIntrinsic(name, new[] { left.Expression, Translate(offsetInst).Expression }, brt, inst);
} else {
return CallUnsafeIntrinsic(name + "ByteOffset", new[] { left.Expression, right.Expression }, brt, inst);
}
}
brt = right.Type as ByReferenceType;
if (inst.LeftInputType == StackType.I && inst.RightInputType == StackType.Ref && brt != null
&& inst.Operator == BinaryNumericOperator.Add) {
// int + ref
ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Left, brt.ElementType, inst.CheckForOverflow);
if (offsetInst != null) {
return CallUnsafeIntrinsic("Add", new[] {
new NamedArgumentExpression("elementOffset", Translate(offsetInst)),
new NamedArgumentExpression("source", right)
}, brt, inst);
} else {
return CallUnsafeIntrinsic("AddByteOffset", new[] {
new NamedArgumentExpression("byteOffset", left.Expression),
new NamedArgumentExpression("source", right)
}, brt, inst);
}
}
return null;
}
internal TranslatedExpression CallUnsafeIntrinsic(string name, Expression[] arguments, IType returnType, ILInstruction inst)
{
var target = new MemberReferenceExpression {
Target = new TypeReferenceExpression(astBuilder.ConvertType(compilation.FindType(KnownTypeCode.Unsafe))),
MemberName = name
};
var invocation = new InvocationExpression(target, arguments).WithILInstruction(inst);
if (returnType is ByReferenceType brt) {
return WrapInRef(invocation.WithRR(new ResolveResult(brt.ElementType)), brt);
} else {
return invocation.WithRR(new ResolveResult(returnType));
}
}
TranslatedExpression EnsureIntegerType(TranslatedExpression expr) TranslatedExpression EnsureIntegerType(TranslatedExpression expr)
{ {
if (!expr.Type.IsCSharpPrimitiveIntegerType()) { if (!expr.Type.IsCSharpPrimitiveIntegerType()) {
@ -998,9 +1060,9 @@ namespace ICSharpCode.Decompiler.CSharp
} }
TranslatedExpression? GetPointerArithmeticOffset(ILInstruction byteOffsetInst, TranslatedExpression byteOffsetExpr, TranslatedExpression? GetPointerArithmeticOffset(ILInstruction byteOffsetInst, TranslatedExpression byteOffsetExpr,
PointerType pointerType, bool checkForOverflow, bool unwrapZeroExtension = false) IType pointerElementType, bool checkForOverflow, bool unwrapZeroExtension = false)
{ {
var countOffsetInst = PointerArithmeticOffset.Detect(byteOffsetInst, pointerType, var countOffsetInst = PointerArithmeticOffset.Detect(byteOffsetInst, pointerElementType,
checkForOverflow: checkForOverflow, checkForOverflow: checkForOverflow,
unwrapZeroExtension: unwrapZeroExtension); unwrapZeroExtension: unwrapZeroExtension);
if (countOffsetInst == null) { if (countOffsetInst == null) {
@ -1086,6 +1148,11 @@ namespace ICSharpCode.Decompiler.CSharp
var left = Translate(inst.Left); var left = Translate(inst.Left);
var right = Translate(inst.Right); var right = Translate(inst.Right);
if (left.Type.Kind == TypeKind.ByReference || right.Type.Kind == TypeKind.ByReference) {
var ptrResult = HandleManagedPointerArithmetic(inst, left, right);
if (ptrResult != null)
return ptrResult.Value;
}
if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer) { if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer) {
var ptrResult = HandlePointerArithmetic(inst, left, right); var ptrResult = HandlePointerArithmetic(inst, left, right);
if (ptrResult != null) if (ptrResult != null)
@ -1350,7 +1417,7 @@ namespace ICSharpCode.Decompiler.CSharp
case AssignmentOperatorType.Add: case AssignmentOperatorType.Add:
case AssignmentOperatorType.Subtract: case AssignmentOperatorType.Subtract:
if (target.Type.Kind == TypeKind.Pointer) { if (target.Type.Kind == TypeKind.Pointer) {
var pao = GetPointerArithmeticOffset(inst.Value, value, (PointerType)target.Type, inst.CheckForOverflow); var pao = GetPointerArithmeticOffset(inst.Value, value, ((PointerType)target.Type).ElementType, inst.CheckForOverflow);
if (pao != null) { if (pao != null) {
value = pao.Value; value = pao.Value;
} else { } else {
@ -2411,7 +2478,7 @@ namespace ICSharpCode.Decompiler.CSharp
throw new ArgumentException("given Block is invalid!"); throw new ArgumentException("given Block is invalid!");
var binary = (BinaryNumericInstruction)target; var binary = (BinaryNumericInstruction)target;
left = left.UnwrapConv(ConversionKind.StopGCTracking); left = left.UnwrapConv(ConversionKind.StopGCTracking);
var offsetInst = PointerArithmeticOffset.Detect(right, pointerType, binary.CheckForOverflow); var offsetInst = PointerArithmeticOffset.Detect(right, pointerType.ElementType, binary.CheckForOverflow);
if (!left.MatchLdLoc(final.Variable) || offsetInst == null) if (!left.MatchLdLoc(final.Variable) || offsetInst == null)
throw new ArgumentException("given Block is invalid!"); throw new ArgumentException("given Block is invalid!");
if (!offsetInst.MatchLdcI(out offset)) if (!offsetInst.MatchLdcI(out offset))

14
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -292,16 +292,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
return callChainLength; return callChainLength;
} }
protected virtual void InsertNewLineWhenInMethodCallChain(MemberReferenceExpression expr) protected virtual bool InsertNewLineWhenInMethodCallChain(MemberReferenceExpression expr)
{ {
int callChainLength = GetCallChainLengthLimited(expr); int callChainLength = GetCallChainLengthLimited(expr);
if (callChainLength < 3) return; if (callChainLength < 3) return false;
if (expr.GetParent(n => n is Statement || n is LambdaExpression || n is InterpolatedStringContent) is InterpolatedStringContent)
return false;
if (callChainLength == 3) if (callChainLength == 3)
writer.Indent(); writer.Indent();
writer.NewLine(); writer.NewLine();
isAtStartOfLine = true; isAtStartOfLine = true;
isAfterSpace = false; isAfterSpace = false;
return true;
} }
protected virtual void OpenBrace(BraceStyle style) protected virtual void OpenBrace(BraceStyle style)
@ -899,13 +902,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
StartNode(memberReferenceExpression); StartNode(memberReferenceExpression);
memberReferenceExpression.Target.AcceptVisitor(this); memberReferenceExpression.Target.AcceptVisitor(this);
InsertNewLineWhenInMethodCallChain(memberReferenceExpression); bool insertedNewLine = InsertNewLineWhenInMethodCallChain(memberReferenceExpression);
WriteToken(Roles.Dot); WriteToken(Roles.Dot);
WriteIdentifier(memberReferenceExpression.MemberNameToken); WriteIdentifier(memberReferenceExpression.MemberNameToken);
WriteTypeArguments(memberReferenceExpression.TypeArguments); WriteTypeArguments(memberReferenceExpression.TypeArguments);
if (!(memberReferenceExpression.Parent is InvocationExpression)) { if (insertedNewLine && !(memberReferenceExpression.Parent is InvocationExpression)) {
if (GetCallChainLengthLimited(memberReferenceExpression) >= 3) writer.Unindent();
writer.Unindent();
} }
EndNode(memberReferenceExpression); EndNode(memberReferenceExpression);
} }

12
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -125,7 +125,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
if (expr is IsExpression || expr is AsExpression) if (expr is IsExpression || expr is AsExpression)
return RelationalAndTypeTesting; return RelationalAndTypeTesting;
if (expr is ConditionalExpression) if (expr is ConditionalExpression || expr is DirectionExpression)
return Conditional; return Conditional;
if (expr is AssignmentExpression || expr is LambdaExpression) if (expr is AssignmentExpression || expr is LambdaExpression)
return Assignment; return Assignment;
@ -339,7 +339,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// (a ? b : c ? d : e) == (a ? b : (c ? d : e)) // (a ? b : c ? d : e) == (a ? b : (c ? d : e))
// (a ? b ? c : d : e) == (a ? (b ? c : d) : e) // (a ? b ? c : d : e) == (a ? (b ? c : d) : e)
// Only ((a ? b : c) ? d : e) strictly needs the additional parentheses // Only ((a ? b : c) ? d : e) strictly needs the additional parentheses
if (InsertParenthesesForReadability) { if (InsertParenthesesForReadability && !IsConditionalRefExpression(conditionalExpression)) {
// Precedence of ?: can be confusing; so always put parentheses in nice-looking mode. // Precedence of ?: can be confusing; so always put parentheses in nice-looking mode.
ParenthesizeIfRequired(conditionalExpression.Condition, NullableRewrap); ParenthesizeIfRequired(conditionalExpression.Condition, NullableRewrap);
ParenthesizeIfRequired(conditionalExpression.TrueExpression, NullableRewrap); ParenthesizeIfRequired(conditionalExpression.TrueExpression, NullableRewrap);
@ -351,7 +351,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
base.VisitConditionalExpression(conditionalExpression); base.VisitConditionalExpression(conditionalExpression);
} }
private bool IsConditionalRefExpression(ConditionalExpression conditionalExpression)
{
return conditionalExpression.TrueExpression is DirectionExpression
|| conditionalExpression.FalseExpression is DirectionExpression;
}
public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression)
{ {
// assignment is right-associative // assignment is right-associative

167
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -3,16 +3,10 @@ using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using static ICSharpCode.Decompiler.Metadata.ILOpCodeExtensions; using static ICSharpCode.Decompiler.Metadata.ILOpCodeExtensions;
@ -20,24 +14,44 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
class RequiredNamespaceCollector class RequiredNamespaceCollector
{ {
static readonly Decompiler.TypeSystem.GenericContext genericContext = default;
readonly HashSet<string> namespaces;
public RequiredNamespaceCollector(HashSet<string> namespaces)
{
this.namespaces = namespaces;
for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) {
var ktr = KnownTypeReference.Get((KnownTypeCode)i);
if (ktr == null) continue;
namespaces.Add(ktr.Namespace);
}
}
public static void CollectNamespaces(MetadataModule module, HashSet<string> namespaces) public static void CollectNamespaces(MetadataModule module, HashSet<string> namespaces)
{ {
var collector = new RequiredNamespaceCollector(namespaces);
foreach (var type in module.TypeDefinitions) { foreach (var type in module.TypeDefinitions) {
CollectNamespaces(type, module, namespaces); collector.CollectNamespaces(type, module, (CodeMappingInfo)null);
} }
CollectAttributeNamespaces(module, namespaces); collector.HandleAttributes(module.GetAssemblyAttributes());
collector.HandleAttributes(module.GetModuleAttributes());
} }
public static void CollectAttributeNamespaces(MetadataModule module, HashSet<string> namespaces) public static void CollectAttributeNamespaces(MetadataModule module, HashSet<string> namespaces)
{ {
HandleAttributes(module.GetAssemblyAttributes(), namespaces); var collector = new RequiredNamespaceCollector(namespaces);
HandleAttributes(module.GetModuleAttributes(), namespaces); collector.HandleAttributes(module.GetAssemblyAttributes());
collector.HandleAttributes(module.GetModuleAttributes());
} }
static readonly Decompiler.TypeSystem.GenericContext genericContext = default; public static void CollectNamespaces(IEntity entity, MetadataModule module, HashSet<string> namespaces)
{
var collector = new RequiredNamespaceCollector(namespaces);
collector.CollectNamespaces(entity, module);
}
public static void CollectNamespaces(IEntity entity, MetadataModule module, void CollectNamespaces(IEntity entity, MetadataModule module, CodeMappingInfo mappingInfo = null)
HashSet<string> namespaces, CodeMappingInfo mappingInfo = null)
{ {
if (entity == null || entity.MetadataToken.IsNil) if (entity == null || entity.MetadataToken.IsNil)
return; return;
@ -46,53 +60,53 @@ namespace ICSharpCode.Decompiler.CSharp
if (mappingInfo == null) if (mappingInfo == null)
mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken); mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken);
namespaces.Add(td.Namespace); namespaces.Add(td.Namespace);
HandleAttributes(td.GetAttributes(), namespaces); HandleAttributes(td.GetAttributes());
HandleTypeParameters(td.TypeParameters, namespaces); HandleTypeParameters(td.TypeParameters);
foreach (var baseType in td.DirectBaseTypes) { foreach (var baseType in td.DirectBaseTypes) {
CollectNamespacesForTypeReference(baseType, namespaces); CollectNamespacesForTypeReference(baseType);
} }
foreach (var nestedType in td.NestedTypes) { foreach (var nestedType in td.NestedTypes) {
CollectNamespaces(nestedType, module, namespaces, mappingInfo); CollectNamespaces(nestedType, module, mappingInfo);
} }
foreach (var field in td.Fields) { foreach (var field in td.Fields) {
CollectNamespaces(field, module, namespaces, mappingInfo); CollectNamespaces(field, module, mappingInfo);
} }
foreach (var property in td.Properties) { foreach (var property in td.Properties) {
CollectNamespaces(property, module, namespaces, mappingInfo); CollectNamespaces(property, module, mappingInfo);
} }
foreach (var @event in td.Events) { foreach (var @event in td.Events) {
CollectNamespaces(@event, module, namespaces, mappingInfo); CollectNamespaces(@event, module, mappingInfo);
} }
foreach (var method in td.Methods) { foreach (var method in td.Methods) {
CollectNamespaces(method, module, namespaces, mappingInfo); CollectNamespaces(method, module, mappingInfo);
} }
break; break;
case IField field: case IField field:
HandleAttributes(field.GetAttributes(), namespaces); HandleAttributes(field.GetAttributes());
CollectNamespacesForTypeReference(field.ReturnType, namespaces); CollectNamespacesForTypeReference(field.ReturnType);
break; break;
case IMethod method: case IMethod method:
HandleAttributes(method.GetAttributes(), namespaces); HandleAttributes(method.GetAttributes());
HandleAttributes(method.GetReturnTypeAttributes(), namespaces); HandleAttributes(method.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(method.ReturnType, namespaces); CollectNamespacesForTypeReference(method.ReturnType);
foreach (var param in method.Parameters) { foreach (var param in method.Parameters) {
HandleAttributes(param.GetAttributes(), namespaces); HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type, namespaces); CollectNamespacesForTypeReference(param.Type);
} }
HandleTypeParameters(method.TypeParameters, namespaces); HandleTypeParameters(method.TypeParameters);
if (!method.MetadataToken.IsNil) { if (!method.MetadataToken.IsNil) {
if (mappingInfo == null) if (mappingInfo == null)
mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken); mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken);
var reader = module.PEFile.Reader; var reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList(); var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) { foreach (var part in parts) {
HandleOverrides(part.GetMethodImplementations(module.metadata), module, namespaces); HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part); var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) { if (method.HasBody) {
MethodBodyBlock body; MethodBodyBlock body;
@ -101,55 +115,58 @@ namespace ICSharpCode.Decompiler.CSharp
} catch (BadImageFormatException) { } catch (BadImageFormatException) {
continue; continue;
} }
CollectNamespacesFromMethodBody(body, module, namespaces); CollectNamespacesFromMethodBody(body, module);
} }
} }
} }
break; break;
case IProperty property: case IProperty property:
HandleAttributes(property.GetAttributes(), namespaces); HandleAttributes(property.GetAttributes());
CollectNamespaces(property.Getter, module, namespaces); CollectNamespaces(property.Getter, module);
CollectNamespaces(property.Setter, module, namespaces); CollectNamespaces(property.Setter, module);
break; break;
case IEvent @event: case IEvent @event:
HandleAttributes(@event.GetAttributes(), namespaces); HandleAttributes(@event.GetAttributes());
CollectNamespaces(@event.AddAccessor, module, namespaces); CollectNamespaces(@event.AddAccessor, module);
CollectNamespaces(@event.RemoveAccessor, module, namespaces); CollectNamespaces(@event.RemoveAccessor, module);
break; break;
} }
} }
static void HandleOverrides(ImmutableArray<MethodImplementationHandle> immutableArray, MetadataModule module, HashSet<string> namespaces) void HandleOverrides(ImmutableArray<MethodImplementationHandle> immutableArray, MetadataModule module)
{ {
foreach (var h in immutableArray) { foreach (var h in immutableArray) {
var methodImpl = module.metadata.GetMethodImplementation(h); var methodImpl = module.metadata.GetMethodImplementation(h);
CollectNamespacesForTypeReference(module.ResolveType(methodImpl.Type, genericContext), namespaces); CollectNamespacesForTypeReference(module.ResolveType(methodImpl.Type, genericContext));
CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodBody, genericContext), module, namespaces); CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodBody, genericContext));
CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodDeclaration, genericContext), module, namespaces); CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodDeclaration, genericContext));
} }
} }
static void CollectNamespacesForTypeReference(IType type, HashSet<string> namespaces) void CollectNamespacesForTypeReference(IType type)
{ {
switch (type) { switch (type) {
case ParameterizedType parameterizedType: case ParameterizedType parameterizedType:
namespaces.Add(parameterizedType.Namespace); namespaces.Add(parameterizedType.Namespace);
CollectNamespacesForTypeReference(parameterizedType.GenericType, namespaces); CollectNamespacesForTypeReference(parameterizedType.GenericType);
foreach (var arg in parameterizedType.TypeArguments) foreach (var arg in parameterizedType.TypeArguments)
CollectNamespacesForTypeReference(arg, namespaces); CollectNamespacesForTypeReference(arg);
break; break;
case TypeWithElementType typeWithElementType: case TypeWithElementType typeWithElementType:
CollectNamespacesForTypeReference(typeWithElementType.ElementType, namespaces); CollectNamespacesForTypeReference(typeWithElementType.ElementType);
break; break;
case TupleType tupleType: case TupleType tupleType:
foreach (var elementType in tupleType.ElementTypes) { foreach (var elementType in tupleType.ElementTypes) {
CollectNamespacesForTypeReference(elementType, namespaces); CollectNamespacesForTypeReference(elementType);
} }
break; break;
default: default:
namespaces.Add(type.Namespace); namespaces.Add(type.Namespace);
break; break;
} }
foreach (var baseType in type.GetAllBaseTypes()) {
namespaces.Add(baseType.Namespace);
}
} }
public static void CollectNamespaces(EntityHandle entity, MetadataModule module, HashSet<string> namespaces) public static void CollectNamespaces(EntityHandle entity, MetadataModule module, HashSet<string> namespaces)
@ -158,43 +175,43 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespaces(module.ResolveEntity(entity, genericContext), module, namespaces); CollectNamespaces(module.ResolveEntity(entity, genericContext), module, namespaces);
} }
public static void HandleAttributes(IEnumerable<IAttribute> attributes, HashSet<string> namespaces) void HandleAttributes(IEnumerable<IAttribute> attributes)
{ {
foreach (var attr in attributes) { foreach (var attr in attributes) {
namespaces.Add(attr.AttributeType.Namespace); namespaces.Add(attr.AttributeType.Namespace);
foreach (var arg in attr.FixedArguments) { foreach (var arg in attr.FixedArguments) {
HandleAttributeValue(arg.Type, arg.Value, namespaces); HandleAttributeValue(arg.Type, arg.Value);
} }
foreach (var arg in attr.NamedArguments) { foreach (var arg in attr.NamedArguments) {
HandleAttributeValue(arg.Type, arg.Value, namespaces); HandleAttributeValue(arg.Type, arg.Value);
} }
} }
} }
static void HandleAttributeValue(IType type, object value, HashSet<string> namespaces) void HandleAttributeValue(IType type, object value)
{ {
CollectNamespacesForTypeReference(type, namespaces); CollectNamespacesForTypeReference(type);
if (value is IType typeofType) if (value is IType typeofType)
CollectNamespacesForTypeReference(typeofType, namespaces); CollectNamespacesForTypeReference(typeofType);
if (value is ImmutableArray<CustomAttributeTypedArgument<IType>> arr) { if (value is ImmutableArray<CustomAttributeTypedArgument<IType>> arr) {
foreach (var element in arr) { foreach (var element in arr) {
HandleAttributeValue(element.Type, element.Value, namespaces); HandleAttributeValue(element.Type, element.Value);
} }
} }
} }
static void HandleTypeParameters(IEnumerable<ITypeParameter> typeParameters, HashSet<string> namespaces) void HandleTypeParameters(IEnumerable<ITypeParameter> typeParameters)
{ {
foreach (var typeParam in typeParameters) { foreach (var typeParam in typeParameters) {
HandleAttributes(typeParam.GetAttributes(), namespaces); HandleAttributes(typeParam.GetAttributes());
foreach (var constraint in typeParam.DirectBaseTypes) { foreach (var constraint in typeParam.DirectBaseTypes) {
CollectNamespacesForTypeReference(constraint, namespaces); CollectNamespacesForTypeReference(constraint);
} }
} }
} }
static void CollectNamespacesFromMethodBody(MethodBodyBlock method, MetadataModule module, HashSet<string> namespaces) void CollectNamespacesFromMethodBody(MethodBodyBlock method, MetadataModule module)
{ {
var metadata = module.metadata; var metadata = module.metadata;
var instructions = method.GetILReader(); var instructions = method.GetILReader();
@ -208,7 +225,7 @@ namespace ICSharpCode.Decompiler.CSharp
localSignature = ImmutableArray<IType>.Empty; localSignature = ImmutableArray<IType>.Empty;
} }
foreach (var type in localSignature) foreach (var type in localSignature)
CollectNamespacesForTypeReference(type, namespaces); CollectNamespacesForTypeReference(type);
} }
foreach (var region in method.ExceptionRegions) { foreach (var region in method.ExceptionRegions) {
@ -220,7 +237,7 @@ namespace ICSharpCode.Decompiler.CSharp
} catch (BadImageFormatException) { } catch (BadImageFormatException) {
continue; continue;
} }
CollectNamespacesForTypeReference(ty, namespaces); CollectNamespacesForTypeReference(ty);
} }
while (instructions.RemainingBytes > 0) { while (instructions.RemainingBytes > 0) {
@ -231,11 +248,11 @@ namespace ICSharpCode.Decompiler.CSharp
return; return;
} }
switch (opCode.GetOperandType()) { switch (opCode.GetOperandType()) {
case Metadata.OperandType.Field: case OperandType.Field:
case Metadata.OperandType.Method: case OperandType.Method:
case Metadata.OperandType.Sig: case OperandType.Sig:
case Metadata.OperandType.Tok: case OperandType.Tok:
case Metadata.OperandType.Type: case OperandType.Type:
var handle = MetadataTokenHelpers.EntityHandleOrNil(instructions.ReadInt32()); var handle = MetadataTokenHelpers.EntityHandleOrNil(instructions.ReadInt32());
if (handle.IsNil) if (handle.IsNil)
break; break;
@ -249,7 +266,7 @@ namespace ICSharpCode.Decompiler.CSharp
} catch (BadImageFormatException) { } catch (BadImageFormatException) {
break; break;
} }
CollectNamespacesForTypeReference(type, namespaces); CollectNamespacesForTypeReference(type);
break; break;
case HandleKind.FieldDefinition: case HandleKind.FieldDefinition:
case HandleKind.MethodDefinition: case HandleKind.MethodDefinition:
@ -261,7 +278,7 @@ namespace ICSharpCode.Decompiler.CSharp
} catch (BadImageFormatException) { } catch (BadImageFormatException) {
break; break;
} }
CollectNamespacesForMemberReference(member, module, namespaces); CollectNamespacesForMemberReference(member);
break; break;
case HandleKind.StandaloneSignature: case HandleKind.StandaloneSignature:
StandaloneSignature sig; StandaloneSignature sig;
@ -277,9 +294,9 @@ namespace ICSharpCode.Decompiler.CSharp
} catch (BadImageFormatException) { } catch (BadImageFormatException) {
break; break;
} }
CollectNamespacesForTypeReference(methodSig.ReturnType, namespaces); CollectNamespacesForTypeReference(methodSig.ReturnType);
foreach (var paramType in methodSig.ParameterTypes) { foreach (var paramType in methodSig.ParameterTypes) {
CollectNamespacesForTypeReference(paramType, namespaces); CollectNamespacesForTypeReference(paramType);
} }
} }
break; break;
@ -296,20 +313,20 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
static void CollectNamespacesForMemberReference(IMember member, MetadataModule module, HashSet<string> namespaces) void CollectNamespacesForMemberReference(IMember member)
{ {
switch (member) { switch (member) {
case IField field: case IField field:
CollectNamespacesForTypeReference(field.DeclaringType, namespaces); CollectNamespacesForTypeReference(field.DeclaringType);
CollectNamespacesForTypeReference(field.ReturnType, namespaces); CollectNamespacesForTypeReference(field.ReturnType);
break; break;
case IMethod method: case IMethod method:
CollectNamespacesForTypeReference(method.DeclaringType, namespaces); CollectNamespacesForTypeReference(method.DeclaringType);
CollectNamespacesForTypeReference(method.ReturnType, namespaces); CollectNamespacesForTypeReference(method.ReturnType);
foreach (var param in method.Parameters) foreach (var param in method.Parameters)
CollectNamespacesForTypeReference(param.Type, namespaces); CollectNamespacesForTypeReference(param.Type);
foreach (var arg in method.TypeArguments) foreach (var arg in method.TypeArguments)
CollectNamespacesForTypeReference(arg, namespaces); CollectNamespacesForTypeReference(arg);
break; break;
} }
} }

2
ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs

@ -532,7 +532,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
var lookupGroup = lookupGroups[i]; var lookupGroup = lookupGroups[i];
if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) { if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) {
if (method != null) { if (method != null && !lookupGroup.MethodsAreHidden) {
// Find the matching method, and replace it with the override // Find the matching method, and replace it with the override
for (int j = 0; j < lookupGroup.Methods.Count; j++) { for (int j = 0; j < lookupGroup.Methods.Count; j++) {
if (SignatureComparer.Ordinal.Equals(method, lookupGroup.Methods[j])) { if (SignatureComparer.Ordinal.Equals(method, lookupGroup.Methods[j])) {

64
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.settings = settings; this.settings = settings;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
} }
public Statement Convert(ILInstruction inst) public Statement Convert(ILInstruction inst)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -102,7 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
return stmt; return stmt;
} }
protected internal override Statement VisitIfInstruction(IfInstruction inst) protected internal override Statement VisitIfInstruction(IfInstruction inst)
{ {
var condition = exprBuilder.TranslateCondition(inst.Condition); var condition = exprBuilder.TranslateCondition(inst.Condition);
@ -268,14 +268,14 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
} }
/// <summary>Target block that a 'continue;' statement would jump to</summary> /// <summary>Target block that a 'continue;' statement would jump to</summary>
Block continueTarget; Block continueTarget;
/// <summary>Number of ContinueStatements that were created for the current continueTarget</summary> /// <summary>Number of ContinueStatements that were created for the current continueTarget</summary>
int continueCount; int continueCount;
/// <summary>Maps blocks to cases.</summary> /// <summary>Maps blocks to cases.</summary>
Dictionary<Block, ConstantResolveResult> caseLabelMapping; Dictionary<Block, ConstantResolveResult> caseLabelMapping;
protected internal override Statement VisitBranch(Branch inst) protected internal override Statement VisitBranch(Branch inst)
{ {
if (inst.TargetBlock == continueTarget) { if (inst.TargetBlock == continueTarget) {
@ -289,12 +289,12 @@ namespace ICSharpCode.Decompiler.CSharp
} }
return new GotoStatement(inst.TargetLabel); return new GotoStatement(inst.TargetLabel);
} }
/// <summary>Target container that a 'break;' statement would break out of</summary> /// <summary>Target container that a 'break;' statement would break out of</summary>
BlockContainer breakTarget; BlockContainer breakTarget;
/// <summary>Dictionary from BlockContainer to label name for 'goto of_container';</summary> /// <summary>Dictionary from BlockContainer to label name for 'goto of_container';</summary>
readonly Dictionary<BlockContainer, string> endContainerLabels = new Dictionary<BlockContainer, string>(); readonly Dictionary<BlockContainer, string> endContainerLabels = new Dictionary<BlockContainer, string>();
protected internal override Statement VisitLeave(Leave inst) protected internal override Statement VisitLeave(Leave inst)
{ {
if (inst.TargetContainer == breakTarget) if (inst.TargetContainer == breakTarget)
@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
return new ThrowStatement(exprBuilder.Translate(inst.Argument)); return new ThrowStatement(exprBuilder.Translate(inst.Argument));
} }
protected internal override Statement VisitRethrow(Rethrow inst) protected internal override Statement VisitRethrow(Rethrow inst)
{ {
return new ThrowStatement(); return new ThrowStatement();
@ -348,7 +348,7 @@ namespace ICSharpCode.Decompiler.CSharp
tryCatch.TryBlock = tryBlockConverted as BlockStatement ?? new BlockStatement { tryBlockConverted }; tryCatch.TryBlock = tryBlockConverted as BlockStatement ?? new BlockStatement { tryBlockConverted };
return tryCatch; return tryCatch;
} }
protected internal override Statement VisitTryCatch(TryCatch inst) protected internal override Statement VisitTryCatch(TryCatch inst)
{ {
var tryCatch = new TryCatchStatement(); var tryCatch = new TryCatchStatement();
@ -372,14 +372,14 @@ namespace ICSharpCode.Decompiler.CSharp
} }
return tryCatch; return tryCatch;
} }
protected internal override Statement VisitTryFinally(TryFinally inst) protected internal override Statement VisitTryFinally(TryFinally inst)
{ {
var tryCatch = MakeTryCatch(inst.TryBlock); var tryCatch = MakeTryCatch(inst.TryBlock);
tryCatch.FinallyBlock = ConvertAsBlock(inst.FinallyBlock); tryCatch.FinallyBlock = ConvertAsBlock(inst.FinallyBlock);
return tryCatch; return tryCatch;
} }
protected internal override Statement VisitTryFault(TryFault inst) protected internal override Statement VisitTryFault(TryFault inst)
{ {
var tryCatch = new TryCatchStatement(); var tryCatch = new TryCatchStatement();
@ -551,7 +551,7 @@ namespace ICSharpCode.Decompiler.CSharp
// Convert the modified body to C# AST: // Convert the modified body to C# AST:
var whileLoop = (WhileStatement)ConvertAsBlock(container).First(); var whileLoop = (WhileStatement)ConvertAsBlock(container).First();
BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach(); BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();
// Remove the first statement, as it is the foreachVariable = enumerator.Current; statement. // Remove the first statement, as it is the foreachVariable = enumerator.Current; statement.
Statement firstStatement = foreachBody.Statements.First(); Statement firstStatement = foreachBody.Statements.First();
if (firstStatement is LabelStatement) { if (firstStatement is LabelStatement) {
@ -828,7 +828,7 @@ namespace ICSharpCode.Decompiler.CSharp
return blockStmt; return blockStmt;
} }
} }
Statement ConvertLoop(BlockContainer container) Statement ConvertLoop(BlockContainer container)
{ {
ILInstruction condition; ILInstruction condition;
@ -982,28 +982,36 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override Statement VisitInitblk(Initblk inst) protected internal override Statement VisitInitblk(Initblk inst)
{ {
var stmt = new ExpressionStatement(new InvocationExpression { var stmt = new ExpressionStatement(
Target = new IdentifierExpression("memset"), exprBuilder.CallUnsafeIntrinsic(
Arguments = { inst.UnalignedPrefix != 0 ? "InitBlockUnaligned" : "InitBlock",
exprBuilder.Translate(inst.Address), new Expression[] {
exprBuilder.Translate(inst.Value), exprBuilder.Translate(inst.Address),
exprBuilder.Translate(inst.Size) exprBuilder.Translate(inst.Value),
} exprBuilder.Translate(inst.Size)
}); },
exprBuilder.compilation.FindType(KnownTypeCode.Void),
inst
)
);
stmt.InsertChildAfter(null, new Comment(" IL initblk instruction"), Roles.Comment); stmt.InsertChildAfter(null, new Comment(" IL initblk instruction"), Roles.Comment);
return stmt; return stmt;
} }
protected internal override Statement VisitCpblk(Cpblk inst) protected internal override Statement VisitCpblk(Cpblk inst)
{ {
var stmt = new ExpressionStatement(new InvocationExpression { var stmt = new ExpressionStatement(
Target = new IdentifierExpression("memcpy"), exprBuilder.CallUnsafeIntrinsic(
Arguments = { inst.UnalignedPrefix != 0 ? "CopyBlockUnaligned" : "CopyBlock",
exprBuilder.Translate(inst.DestAddress), new Expression[] {
exprBuilder.Translate(inst.SourceAddress), exprBuilder.Translate(inst.DestAddress),
exprBuilder.Translate(inst.Size) exprBuilder.Translate(inst.SourceAddress),
} exprBuilder.Translate(inst.Size)
}); },
exprBuilder.compilation.FindType(KnownTypeCode.Void),
inst
)
);
stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment); stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment);
return stmt; return stmt;
} }

26
ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs

@ -45,7 +45,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
void CombineQueries(AstNode node, Dictionary<string, object> letIdentifiers) void CombineQueries(AstNode node, Dictionary<string, object> letIdentifiers)
{ {
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { AstNode next;
for (AstNode child = node.FirstChild; child != null; child = next) {
// store referece to next child before transformation
next = child.NextSibling;
CombineQueries(child, letIdentifiers); CombineQueries(child, letIdentifiers);
} }
QueryExpression query = node as QueryExpression; QueryExpression query = node as QueryExpression;
@ -76,7 +79,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Initializers = { Initializers = {
new Repeat( new Repeat(
new Choice { new Choice {
new IdentifierExpression(Pattern.AnyString).WithName("expr"), // capture variable with same name new IdentifierExpression(Pattern.AnyString).WithName("expr"), // name is equivalent to name = name
new MemberReferenceExpression(new AnyNode(), Pattern.AnyString).WithName("expr"), // expr.name is equivalent to name = expr.name
new NamedExpression { new NamedExpression {
Name = Pattern.AnyString, Name = Pattern.AnyString,
Expression = new AnyNode() Expression = new AnyNode()
@ -117,20 +121,28 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
case IdentifierExpression identifier: case IdentifierExpression identifier:
// nothing to add // nothing to add
continue; continue;
case MemberReferenceExpression member:
AddQueryLetClause(member.MemberName, member);
break;
case NamedExpression namedExpression: case NamedExpression namedExpression:
if (namedExpression.Expression is IdentifierExpression identifierExpression && namedExpression.Name == identifierExpression.Identifier) { if (namedExpression.Expression is IdentifierExpression identifierExpression && namedExpression.Name == identifierExpression.Identifier) {
letClauses[namedExpression.Name] = identifierExpression.Annotation<ILVariableResolveResult>(); letClauses[namedExpression.Name] = identifierExpression.Annotation<ILVariableResolveResult>();
continue; continue;
} }
QueryLetClause letClause = new QueryLetClause { Identifier = namedExpression.Name, Expression = namedExpression.Expression.Detach() }; AddQueryLetClause(namedExpression.Name, namedExpression.Expression);
var annotation = new LetIdentifierAnnotation();
letClause.AddAnnotation(annotation);
letClauses[namedExpression.Name] = annotation;
query.Clauses.InsertAfter(insertionPos, letClause);
break; break;
} }
} }
return true; return true;
void AddQueryLetClause(string name, Expression expression)
{
QueryLetClause letClause = new QueryLetClause { Identifier = name, Expression = expression.Detach() };
var annotation = new LetIdentifierAnnotation();
letClause.AddAnnotation(annotation);
letClauses[name] = annotation;
query.Clauses.InsertAfter(insertionPos, letClause);
}
} }
/// <summary> /// <summary>

7
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

@ -325,12 +325,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary>Matches simple lambdas of the form "a => b"</summary> /// <summary>Matches simple lambdas of the form "a => b"</summary>
bool MatchSimpleLambda(Expression expr, out ParameterDeclaration parameter, out Expression body) bool MatchSimpleLambda(Expression expr, out ParameterDeclaration parameter, out Expression body)
{ {
// HACK : remove workaround after all unnecessary casts are eliminated. var lambda = expr as LambdaExpression;
LambdaExpression lambda;
if (expr is CastExpression cast)
lambda = cast.Expression as LambdaExpression;
else
lambda = expr as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) { if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) {
ParameterDeclaration p = lambda.Parameters.Single(); ParameterDeclaration p = lambda.Parameters.Single();
if (p.ParameterModifier == ParameterModifier.None) { if (p.ParameterModifier == ParameterModifier.None) {

2
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -129,7 +129,7 @@ namespace ICSharpCode.Decompiler.IL
// ensure that the byte offset is a multiple of the pointer size // ensure that the byte offset is a multiple of the pointer size
return PointerArithmeticOffset.Detect( return PointerArithmeticOffset.Detect(
binary.Right, binary.Right,
(PointerType)type, ((PointerType)type).ElementType,
checkForOverflow: binary.CheckForOverflow checkForOverflow: binary.CheckForOverflow
) != null; ) != null;
default: default:

10
ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs

@ -17,17 +17,17 @@ namespace ICSharpCode.Decompiler.IL
/// Returns null if no such instruction can be found. /// Returns null if no such instruction can be found.
/// </summary> /// </summary>
/// <param name="byteOffsetInst">Input instruction.</param> /// <param name="byteOffsetInst">Input instruction.</param>
/// <param name="pointerType">The pointer type.</param> /// <param name="pointerElementType">The target type of the pointer type.</param>
/// <param name="checkForOverflow">Whether the pointer arithmetic operation checks for overflow.</param> /// <param name="checkForOverflow">Whether the pointer arithmetic operation checks for overflow.</param>
/// <param name="unwrapZeroExtension">Whether to allow zero extensions in the mul argument.</param> /// <param name="unwrapZeroExtension">Whether to allow zero extensions in the mul argument.</param>
public static ILInstruction Detect(ILInstruction byteOffsetInst, PointerType pointerType, public static ILInstruction Detect(ILInstruction byteOffsetInst, IType pointerElementType,
bool checkForOverflow, bool checkForOverflow,
bool unwrapZeroExtension = false) bool unwrapZeroExtension = false)
{ {
if (byteOffsetInst is Conv conv && conv.InputType == StackType.I8 && conv.ResultType == StackType.I) { if (byteOffsetInst is Conv conv && conv.InputType == StackType.I8 && conv.ResultType == StackType.I) {
byteOffsetInst = conv.Argument; byteOffsetInst = conv.Argument;
} }
int? elementSize = ComputeSizeOf(pointerType.ElementType); int? elementSize = ComputeSizeOf(pointerElementType);
if (elementSize == 1) { if (elementSize == 1) {
return byteOffsetInst; return byteOffsetInst;
} else if (byteOffsetInst is BinaryNumericInstruction mul && mul.Operator == BinaryNumericOperator.Mul) { } else if (byteOffsetInst is BinaryNumericInstruction mul && mul.Operator == BinaryNumericOperator.Mul) {
@ -36,14 +36,14 @@ namespace ICSharpCode.Decompiler.IL
if (mul.CheckForOverflow != checkForOverflow) if (mul.CheckForOverflow != checkForOverflow)
return null; return null;
if (elementSize > 0 && mul.Right.MatchLdcI(elementSize.Value) if (elementSize > 0 && mul.Right.MatchLdcI(elementSize.Value)
|| mul.Right.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerType.ElementType)) { || mul.Right.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerElementType)) {
var countOffsetInst = mul.Left; var countOffsetInst = mul.Left;
if (unwrapZeroExtension) { if (unwrapZeroExtension) {
countOffsetInst = countOffsetInst.UnwrapConv(ConversionKind.ZeroExtend); countOffsetInst = countOffsetInst.UnwrapConv(ConversionKind.ZeroExtend);
} }
return countOffsetInst; return countOffsetInst;
} }
} else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerType.ElementType)) { } else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerElementType)) {
return new LdcI4(1).WithILRange(byteOffsetInst); return new LdcI4(1).WithILRange(byteOffsetInst);
} else if (byteOffsetInst.MatchLdcI(out long val)) { } else if (byteOffsetInst.MatchLdcI(out long val)) {
// If the offset is a constant, it's possible that the compiler // If the offset is a constant, it's possible that the compiler

2
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -350,7 +350,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool MatchesElementCount(ILInstruction sizeInBytesInstr, IType elementType, ILInstruction elementCountInstr2) bool MatchesElementCount(ILInstruction sizeInBytesInstr, IType elementType, ILInstruction elementCountInstr2)
{ {
var pointerType = new PointerType(elementType); var pointerType = new PointerType(elementType);
var elementCountInstr = PointerArithmeticOffset.Detect(sizeInBytesInstr, pointerType, checkForOverflow: true, unwrapZeroExtension: true); var elementCountInstr = PointerArithmeticOffset.Detect(sizeInBytesInstr, pointerType.ElementType, checkForOverflow: true, unwrapZeroExtension: true);
if (!elementCountInstr.Match(elementCountInstr2).Success) if (!elementCountInstr.Match(elementCountInstr2).Success)
return false; return false;
return true; return true;

3
ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

@ -179,7 +179,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Newer versions of Roslyn use extra variables: // Newer versions of Roslyn use extra variables:
if (i >= 2 && switchValue.MatchLdLoc(out var otherSwitchValueVar) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 1 if (i >= 2 && switchValue.MatchLdLoc(out var otherSwitchValueVar) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 1
&& instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue)) { && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out var newSwitchValue)) {
switchValue = newSwitchValue;
extraLoad = true; extraLoad = true;
} }
} else if (instructions[i - 1] is StLoc stloc) { } else if (instructions[i - 1] is StLoc stloc) {

4
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -275,7 +275,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!left.MatchLdLoc(store)) if (!left.MatchLdLoc(store))
break; break;
var offsetInst = PointerArithmeticOffset.Detect(right, new PointerType(elementType), ((BinaryNumericInstruction)target).CheckForOverflow); var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow);
if (offsetInst == null) if (offsetInst == null)
return false; return false;
if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset) if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset)
@ -283,7 +283,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
minExpectedOffset = offset; minExpectedOffset = offset;
} }
if (values == null) { if (values == null) {
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, new PointerType(elementType), checkForOverflow: true); var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true);
if (countInstruction == null || !countInstruction.MatchLdcI(out long valuesLength) || valuesLength < 1) if (countInstruction == null || !countInstruction.MatchLdcI(out long valuesLength) || valuesLength < 1)
return false; return false;
values = new StObj[(int)valuesLength]; values = new StObj[(int)valuesLength];

4
ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

@ -66,6 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst)) if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst))
return false; return false;
// Cannot build a collection/object initializer attached to an AnonymousTypeCreateExpression:s
// anon = new { A = 5 } { 3,4,5 } is invalid syntax.
if (newObjInst.Method.DeclaringType.ContainsAnonymousType())
return false;
instType = newObjInst.Method.DeclaringType; instType = newObjInst.Method.DeclaringType;
break; break;
case DefaultValue defaultVal: case DefaultValue defaultVal:

31
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

@ -81,30 +81,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out; const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out;
public bool IsRef { public bool IsRef => DetectRefKind() == CSharp.Syntax.ParameterModifier.Ref;
get {
if (!(Type.Kind == TypeKind.ByReference && (attributes & inOut) != ParameterAttributes.Out))
return false;
if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) == 0)
return true;
var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle);
return !parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly);
}
}
public bool IsOut => Type.Kind == TypeKind.ByReference && (attributes & inOut) == ParameterAttributes.Out; public bool IsOut => Type.Kind == TypeKind.ByReference && (attributes & inOut) == ParameterAttributes.Out;
public bool IsIn => DetectRefKind() == CSharp.Syntax.ParameterModifier.In;
public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0; public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0;
public bool IsIn { CSharp.Syntax.ParameterModifier DetectRefKind()
get { {
if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) == 0 || if (Type.Kind != TypeKind.ByReference)
Type.Kind != TypeKind.ByReference || (attributes & inOut) != ParameterAttributes.In) return CSharp.Syntax.ParameterModifier.None;
return false; if ((attributes & inOut) == ParameterAttributes.Out)
return CSharp.Syntax.ParameterModifier.Out;
if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0) {
var metadata = module.metadata; var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle); var parameterDef = metadata.GetParameter(handle);
return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly); if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly))
return CSharp.Syntax.ParameterModifier.In;
} }
return CSharp.Syntax.ParameterModifier.Ref;
} }
public bool IsParams { public bool IsParams {

5
ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
ReadOnlySpanOfT, ReadOnlySpanOfT,
/// <summary><c>System.Memory{T}</c></summary> /// <summary><c>System.Memory{T}</c></summary>
MemoryOfT, MemoryOfT,
/// <summary><c>System.Runtime.CompilerServices.Unsafe</c></summary>
Unsafe,
} }
/// <summary> /// <summary>
@ -143,7 +145,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
[Serializable] [Serializable]
public sealed class KnownTypeReference : ITypeReference public sealed class KnownTypeReference : ITypeReference
{ {
internal const int KnownTypeCodeCount = (int)KnownTypeCode.MemoryOfT + 1; internal const int KnownTypeCodeCount = (int)KnownTypeCode.Unsafe + 1;
static readonly KnownTypeReference[] knownTypeReferences = new KnownTypeReference[KnownTypeCodeCount] { static readonly KnownTypeReference[] knownTypeReferences = new KnownTypeReference[KnownTypeCodeCount] {
null, // None null, // None
@ -200,6 +202,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new KnownTypeReference(KnownTypeCode.SpanOfT, TypeKind.Struct, "System", "Span", 1), new KnownTypeReference(KnownTypeCode.SpanOfT, TypeKind.Struct, "System", "Span", 1),
new KnownTypeReference(KnownTypeCode.ReadOnlySpanOfT, TypeKind.Struct, "System", "ReadOnlySpan", 1), new KnownTypeReference(KnownTypeCode.ReadOnlySpanOfT, TypeKind.Struct, "System", "ReadOnlySpan", 1),
new KnownTypeReference(KnownTypeCode.MemoryOfT, TypeKind.Struct, "System", "Memory", 1), new KnownTypeReference(KnownTypeCode.MemoryOfT, TypeKind.Struct, "System", "Memory", 1),
new KnownTypeReference(KnownTypeCode.Unsafe, TypeKind.Class, "System.Runtime.CompilerServices", "Unsafe", 0),
}; };
/// <summary> /// <summary>

Loading…
Cancel
Save