Browse Source

Add feature: C#11 nint without NativeIntegerAttribute

Because it is no longer possible to distinguish IntPtr from nint, this required a lot of testcase adjustment.
pull/2873/head
Daniel Grunwald 2 years ago
parent
commit
efeaf1356f
  1. 1
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 3
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs
  4. 7
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs
  5. 14
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs
  6. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  7. 36
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs
  8. 11
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs
  9. 17
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
  10. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  11. 2
      ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs
  12. 14
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  13. 9
      ILSpy/Properties/Resources.Designer.cs
  14. 3
      ILSpy/Properties/Resources.resx
  15. 4
      README.md

1
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -343,6 +343,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
{ {
preprocessorSymbols.Add("NETCORE"); preprocessorSymbols.Add("NETCORE");
preprocessorSymbols.Add("NET60"); preprocessorSymbols.Add("NET60");
preprocessorSymbols.Add("NET70");
} }
preprocessorSymbols.Add("ROSLYN"); preprocessorSymbols.Add("ROSLYN");
preprocessorSymbols.Add("CS60"); preprocessorSymbols.Add("CS60");

3
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -326,6 +326,7 @@ namespace ICSharpCode.Decompiler.Tests
RemoveDeadStores = (cscOptions == CompilerOptions.None), RemoveDeadStores = (cscOptions == CompilerOptions.None),
UseExpressionBodyForCalculatedGetterOnlyProperties = false, UseExpressionBodyForCalculatedGetterOnlyProperties = false,
FileScopedNamespaces = false, FileScopedNamespaces = false,
NumericIntPtr = false,
}); });
} }
@ -486,7 +487,7 @@ namespace ICSharpCode.Decompiler.Tests
} }
[Test] [Test]
public async Task NativeInts([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) public async Task NativeInts([ValueSource(nameof(roslyn3OrNewerWithNet40Options))] CompilerOptions cscOptions)
{ {
await RunForLibrary(cscOptions: cscOptions); await RunForLibrary(cscOptions: cscOptions);
} }

18
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs

@ -1,10 +1,25 @@
using System; #if !(CS110 && NET70)
using System;
#endif
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
internal class ConstantsTests internal class ConstantsTests
{ {
#if CS90
public nint? NullableNInt()
{
return null;
}
public nuint? NullableNUInt()
{
return null;
}
#endif
#if !(CS110 && NET70)
public IntPtr? NullableIntPtr() public IntPtr? NullableIntPtr()
{ {
return null; return null;
@ -14,6 +29,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
return null; return null;
} }
#endif
public ulong Issue1308(ulong u = 8uL) public ulong Issue1308(ulong u = 8uL)
{ {

7
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs

@ -436,10 +436,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return true.Equals(a); return true.Equals(a);
} }
#if CS110 && NET70
private static nint NewIntPtr(dynamic a)
{
return new nint(a);
}
#else
private static IntPtr NewIntPtr(dynamic a) private static IntPtr NewIntPtr(dynamic a)
{ {
return new IntPtr(a); return new IntPtr(a);
} }
#endif
private static dynamic GetDynamic(int i) private static dynamic GetDynamic(int i)
{ {

14
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs

@ -1,4 +1,6 @@
using System; #if !(CS110 && NET70)
using System;
#endif
using System.Text; using System.Text;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
@ -17,10 +19,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return &Overloaded; return &Overloaded;
} }
#if !(CS110 && NET70)
public unsafe IntPtr GetAddressAsIntPtr() public unsafe IntPtr GetAddressAsIntPtr()
{ {
return (IntPtr)(delegate*<void>)(&Overloaded); return (IntPtr)(delegate*<void>)(&Overloaded);
} }
#endif
public unsafe nint GetAddressAsNInt()
{
return (nint)(delegate*<void>)(&Overloaded);
}
public unsafe void* GetAddressAsVoidPtr() public unsafe void* GetAddressAsVoidPtr()
{ {
@ -93,6 +102,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
internal class FunctionPointersWithNativeIntegerTypes internal class FunctionPointersWithNativeIntegerTypes
{ {
public unsafe delegate*<nint, nint, nint> F1; public unsafe delegate*<nint, nint, nint> F1;
#if !(CS110 && NET70)
public unsafe delegate*<IntPtr, IntPtr, nint> F2; public unsafe delegate*<IntPtr, IntPtr, nint> F2;
public unsafe delegate*<nint, IntPtr, IntPtr> F3; public unsafe delegate*<nint, IntPtr, IntPtr> F3;
public unsafe delegate*<IntPtr, nint, IntPtr> F4; public unsafe delegate*<IntPtr, nint, IntPtr> F4;
@ -100,6 +110,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public unsafe delegate*<nint, delegate*<IntPtr, IntPtr, IntPtr>> F6; public unsafe delegate*<nint, delegate*<IntPtr, IntPtr, IntPtr>> F6;
public unsafe delegate*<delegate*<IntPtr, IntPtr, nint>, IntPtr> F7; public unsafe delegate*<delegate*<IntPtr, IntPtr, nint>, IntPtr> F7;
public unsafe delegate*<IntPtr, delegate*<IntPtr, nint, IntPtr>> F8; public unsafe delegate*<IntPtr, delegate*<IntPtr, nint, IntPtr>> F8;
public unsafe delegate*<IntPtr, delegate*<IntPtr, IntPtr, IntPtr>> F9;
#endif
} }
internal class FunctionPointersWithRefParams internal class FunctionPointersWithRefParams

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs

@ -844,10 +844,10 @@ namespace LocalFunctions
#if CS90 #if CS90
public void Issue2196() public void Issue2196()
{ {
EnumWindows(IntPtr.Zero, IntPtr.Zero); EnumWindows(0L, 0L);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "EnumWindows")] [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "EnumWindows")]
static extern int EnumWindows(IntPtr hWnd, IntPtr lParam); static extern int EnumWindows(long hWnd, long lParam);
} }
#endif #endif
} }

36
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs

@ -26,23 +26,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private const nint nint_const = 42; private const nint nint_const = 42;
private const nuint nuint_const = 99u; private const nuint nuint_const = 99u;
#if CS110 && NET70
// C#11 on .NET7 no longer uses NativeIntegerAttribute,
// instead nint+IntPtr are considered to be the same type.
private nint intptr;
private nuint uintptr;
#else
private IntPtr intptr; private IntPtr intptr;
private UIntPtr uintptr; private UIntPtr uintptr;
#endif
private nint i; private nint i;
private nuint u; private nuint u;
private int i32; private int i32;
private uint u32; private uint u32;
private long i64; private long i64;
private ulong u64; private ulong u64;
#if !(CS110 && NET70)
private (IntPtr, nint, UIntPtr, nuint) tuple_field; private (IntPtr, nint, UIntPtr, nuint) tuple_field;
private (object, int, IntPtr, nint, UIntPtr, nuint) tuple_field2; private (object, int, IntPtr, nint, UIntPtr, nuint) tuple_field2;
private Dictionary<nint, IntPtr> dict1; private Dictionary<nint, IntPtr> dict1;
private Dictionary<IntPtr, nint> dict2; private Dictionary<IntPtr, nint> dict2;
private Dictionary<IntPtr?, nint?> dict3; private Dictionary<IntPtr?, nint?> dict3;
private Dictionary<IntPtr, nint[]> dict4; private Dictionary<IntPtr, nint[]> dict4;
#endif
private Dictionary<nuint, nint[]> dict5;
public void Convert() public void Convert()
{ {
i = (nint)u;
u = (nuint)i;
#if !(CS110 && NET70)
intptr = i; intptr = i;
intptr = (nint)u; intptr = (nint)u;
intptr = (nint)(nuint)uintptr; intptr = (nint)(nuint)uintptr;
@ -58,15 +71,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
u = (nuint)i; u = (nuint)i;
u = uintptr; u = uintptr;
u = (nuint)(nint)intptr; u = (nuint)(nint)intptr;
#endif
} }
public void Convert2() public void Convert2()
{ {
i32 = (int)i; i32 = (int)i;
i = i32; i = i32;
#if !(CS110 && NET70)
intptr = (IntPtr)i32; intptr = (IntPtr)i32;
i64 = (long)intptr; i64 = (long)intptr;
#endif
i64 = i; i64 = i;
i = (nint)i64; i = (nint)i64;
@ -79,7 +95,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void Arithmetic() public void Arithmetic()
{ {
#if !(CS110 && NET70)
Console.WriteLine((nint)intptr * 2); Console.WriteLine((nint)intptr * 2);
#endif
Console.WriteLine(i * 2); Console.WriteLine(i * 2);
Console.WriteLine(i + (nint)u); Console.WriteLine(i + (nint)u);
@ -155,6 +173,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
GetInstance(3).u *= 2u; GetInstance(3).u *= 2u;
} }
#if !(CS110 && NET70)
GetInstance(4).intptr += (nint)i32; GetInstance(4).intptr += (nint)i32;
checked checked
{ {
@ -164,10 +183,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
// multiplication results in compiler-error without the cast // multiplication results in compiler-error without the cast
GetInstance(6).intptr *= (nint)2; GetInstance(6).intptr *= (nint)2;
#endif
GetInstance(7).i <<= i32; GetInstance(7).i += i32;
GetInstance(8).i <<= i32;
} }
#if !(CS110 && NET70)
public void LocalTypeFromStore() public void LocalTypeFromStore()
{ {
nint num = 42; nint num = 42;
@ -188,10 +210,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
intptr = num3; intptr = num3;
intptr = intPtr; intptr = intPtr;
} }
#endif
public void LocalTypeFromUse() public void LocalTypeFromUse()
{ {
#if CS110 && NET70
nint num = intptr;
nint num2 = intptr;
Console.WriteLine();
intptr = num;
i = num2 + 1;
#else
IntPtr intPtr = intptr; IntPtr intPtr = intptr;
nint num = intptr; nint num = intptr;
@ -199,6 +230,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
intptr = intPtr; intptr = intPtr;
i = num + 1; i = num + 1;
#endif
} }
public nint NegateUnsigned(nuint x) public nint NegateUnsigned(nuint x)

11
ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs

@ -89,6 +89,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
} }
#if CS110 && NET70
[DllImport("ws2_32.dll", SetLastError = true)]
internal static extern nint ioctlsocket([In] nint socketHandle, [In] int cmd, [In][Out] ref int argp);
public void CallMethodWithInOutParameter()
{
int argp = 0;
ioctlsocket(nint.Zero, 0, ref argp);
}
#else
[DllImport("ws2_32.dll", SetLastError = true)] [DllImport("ws2_32.dll", SetLastError = true)]
internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In][Out] ref int argp); internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In][Out] ref int argp);
@ -97,5 +107,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
int argp = 0; int argp = 0;
ioctlsocket(IntPtr.Zero, 0, ref argp); ioctlsocket(IntPtr.Zero, 0, ref argp);
} }
#endif
} }
} }

17
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs

@ -30,7 +30,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
public static object StaticObj; public static object StaticObj;
#if CS110 && NET70
public nint A;
#else
public IntPtr A; public IntPtr A;
#endif
} }
private struct UnmanagedStruct private struct UnmanagedStruct
@ -605,6 +609,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return value.Integers[(int)s]; return value.Integers[(int)s];
} }
#if CS90
private unsafe static void* CastNIntToVoidPtr(nint intptr)
{
return (void*)intptr;
}
private unsafe static void* CastNIntToVoidPtr(nuint intptr)
{
return (void*)intptr;
}
#endif
#if !(CS110 && NET70)
private unsafe static void* CastToVoidPtr(IntPtr intptr) private unsafe static void* CastToVoidPtr(IntPtr intptr)
{ {
return (void*)intptr; return (void*)intptr;
@ -614,6 +630,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
return (void*)intptr; return (void*)intptr;
} }
#endif
private unsafe static void* CastToVoidPtr(int* intptr) private unsafe static void* CastToVoidPtr(int* intptr)
{ {

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -152,12 +152,13 @@ namespace ICSharpCode.Decompiler
parameterNullCheck = false; parameterNullCheck = false;
lifetimeAnnotations = false; lifetimeAnnotations = false;
requiredMembers = false; requiredMembers = false;
numericIntPtr = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (parameterNullCheck || lifetimeAnnotations || requiredMembers) if (parameterNullCheck || lifetimeAnnotations || requiredMembers || numericIntPtr)
return CSharp.LanguageVersion.CSharp11_0; return CSharp.LanguageVersion.CSharp11_0;
if (fileScopedNamespaces || recordStructs) if (fileScopedNamespaces || recordStructs)
return CSharp.LanguageVersion.CSharp10_0; return CSharp.LanguageVersion.CSharp10_0;
@ -211,6 +212,24 @@ namespace ICSharpCode.Decompiler
} }
} }
bool numericIntPtr = true;
/// <summary>
/// Treat <c>IntPtr</c>/<c>UIntPtr</c> as <c>nint</c>/<c>nuint</c>.
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.NumericIntPtr")]
public bool NumericIntPtr {
get { return numericIntPtr; }
set {
if (numericIntPtr != value)
{
numericIntPtr = value;
OnPropertyChanged();
}
}
}
bool covariantReturns = true; bool covariantReturns = true;
/// <summary> /// <summary>

2
ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs

@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
bool hasDynamicAttribute = false; bool hasDynamicAttribute = false;
bool[] dynamicAttributeData = null; bool[] dynamicAttributeData = null;
bool hasNativeIntegersAttribute = false; bool hasNativeIntegersAttribute = (options & TypeSystemOptions.NativeIntegersWithoutAttribute) != 0;
bool[] nativeIntegersAttributeData = null; bool[] nativeIntegersAttributeData = null;
string[] tupleElementNames = null; string[] tupleElementNames = null;
Nullability nullability; Nullability nullability;

14
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -125,11 +125,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
LifetimeAnnotations = 0x4000, LifetimeAnnotations = 0x4000,
/// <summary> /// <summary>
/// Replace 'IntPtr' types with the 'nint' type even in absence of [NativeIntegerAttribute].
/// Note: DecompilerTypeSystem constructor removes this setting from the options if
/// not targeting .NET 7 or later.
/// </summary>
NativeIntegersWithoutAttribute = 0x8000,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled. /// Default settings: typical options for the decompiler, with all C# languages features enabled.
/// </summary> /// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | LifetimeAnnotations | NativeIntegers | FunctionPointers | LifetimeAnnotations | NativeIntegersWithoutAttribute
} }
/// <summary> /// <summary>
@ -167,6 +173,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.FunctionPointers; typeSystemOptions |= TypeSystemOptions.FunctionPointers;
if (settings.LifetimeAnnotations) if (settings.LifetimeAnnotations)
typeSystemOptions |= TypeSystemOptions.LifetimeAnnotations; typeSystemOptions |= TypeSystemOptions.LifetimeAnnotations;
if (settings.NumericIntPtr)
typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute;
return typeSystemOptions; return typeSystemOptions;
} }
@ -304,6 +312,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
} }
} }
if (!(identifier == TargetFrameworkIdentifier.NET && version >= new Version(7, 0)))
{
typeSystemOptions &= ~TypeSystemOptions.NativeIntegersWithoutAttribute;
}
var mainModuleWithOptions = mainModule.WithOptions(typeSystemOptions); var mainModuleWithOptions = mainModule.WithOptions(typeSystemOptions);
var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(typeSystemOptions)); var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(typeSystemOptions));
// Primitive types are necessary to avoid assertions in ILReader. // Primitive types are necessary to avoid assertions in ILReader.

9
ILSpy/Properties/Resources.Designer.cs generated

@ -1108,6 +1108,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Treat (U)IntPtr as n(u)int.
/// </summary>
public static string DecompilerSettings_NumericIntPtr {
get {
return ResourceManager.GetString("DecompilerSettings.NumericIntPtr", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Object/collection initializer expressions. /// Looks up a localized string similar to Object/collection initializer expressions.
/// </summary> /// </summary>

3
ILSpy/Properties/Resources.resx

@ -393,6 +393,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.NullableReferenceTypes" xml:space="preserve"> <data name="DecompilerSettings.NullableReferenceTypes" xml:space="preserve">
<value>Nullable reference types</value> <value>Nullable reference types</value>
</data> </data>
<data name="DecompilerSettings.NumericIntPtr" xml:space="preserve">
<value>Treat (U)IntPtr as n(u)int</value>
</data>
<data name="DecompilerSettings.ObjectCollectionInitializerExpressions" xml:space="preserve"> <data name="DecompilerSettings.ObjectCollectionInitializerExpressions" xml:space="preserve">
<value>Object/collection initializer expressions</value> <value>Object/collection initializer expressions</value>
</data> </data>

4
README.md

@ -24,7 +24,7 @@ Features
------- -------
* Decompilation to C# (check out the [language support status](https://github.com/icsharpcode/ILSpy/issues/829)) * Decompilation to C# (check out the [language support status](https://github.com/icsharpcode/ILSpy/issues/829))
* Whole-project decompilation (csproj, not sln!) * Whole-project decompilation
* Search for types/methods/properties (learn about the [options](https://github.com/icsharpcode/ILSpy/wiki/Search-Options)) * Search for types/methods/properties (learn about the [options](https://github.com/icsharpcode/ILSpy/wiki/Search-Options))
* Hyperlink-based type/method/property navigation * Hyperlink-based type/method/property navigation
* Base/Derived types navigation, history * Base/Derived types navigation, history
@ -65,7 +65,7 @@ How to build
- ILSpy.XPlat.slnf: for the cross-platform CLI or PowerShell cmdlets - ILSpy.XPlat.slnf: for the cross-platform CLI or PowerShell cmdlets
- ILSpy.AddIn.slnf: for the Visual Studio plugin - ILSpy.AddIn.slnf: for the Visual Studio plugin
**Note:** Visual Studio 16.3 and later include a version of the .NET (Core) SDK that is managed by the Visual Studio installer - once you update, it may get upgraded too. **Note:** Visual Studio includes a version of the .NET SDK that is managed by the Visual Studio installer - once you update, it may get upgraded too.
Please note that ILSpy is only compatible with the .NET 6.0 SDK and Visual Studio will refuse to load some projects in the solution (and unit tests will fail). Please note that ILSpy is only compatible with the .NET 6.0 SDK and Visual Studio will refuse to load some projects in the solution (and unit tests will fail).
If this problem occurs, please manually install the .NET 6.0 SDK from [here](https://dotnet.microsoft.com/download/dotnet/6.0). If this problem occurs, please manually install the .NET 6.0 SDK from [here](https://dotnet.microsoft.com/download/dotnet/6.0).

Loading…
Cancel
Save