mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
Add scaffolding for the C# 15 runtime-async lowering.
Detect MethodImplOptions.Async (0x2000) in ILReader and unpack Task/Task<T> return types so the IL Leave value and function.AsyncReturnType match the source signature. Add CSharp15_0 (Preview also bumped to 1500) and a RuntimeAsync setting (default on, gated to >=CSharp15_0), expose it in the Languages dropdown, mask the synthetic MethodImplAsync bit out of the decompiled [MethodImpl], and add a .runtimeasync test suffix.
This commit is contained in:
@@ -848,6 +848,8 @@ namespace System.Runtime.CompilerServices
|
||||
suffix += ".mcs2";
|
||||
if ((cscOptions & CompilerOptions.UseMcs5_23) != 0)
|
||||
suffix += ".mcs5";
|
||||
if ((cscOptions & CompilerOptions.EnableRuntimeAsync) != 0)
|
||||
suffix += ".runtimeasync";
|
||||
return suffix;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace ICSharpCode.Decompiler.CSharp
|
||||
CSharp12_0 = 1200,
|
||||
CSharp13_0 = 1300,
|
||||
CSharp14_0 = 1400,
|
||||
Preview = 1400,
|
||||
CSharp15_0 = 1500,
|
||||
Preview = 1500,
|
||||
Latest = 0x7FFFFFFF
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,10 +177,16 @@ namespace ICSharpCode.Decompiler
|
||||
extensionMembers = false;
|
||||
firstClassSpanTypes = false;
|
||||
}
|
||||
if (languageVersion < CSharp.LanguageVersion.CSharp15_0)
|
||||
{
|
||||
runtimeAsync = false;
|
||||
}
|
||||
}
|
||||
|
||||
public CSharp.LanguageVersion GetMinimumRequiredVersion()
|
||||
{
|
||||
if (runtimeAsync)
|
||||
return CSharp.LanguageVersion.CSharp15_0;
|
||||
if (extensionMembers || firstClassSpanTypes)
|
||||
return CSharp.LanguageVersion.CSharp14_0;
|
||||
if (paramsCollections)
|
||||
@@ -2167,7 +2173,7 @@ namespace ICSharpCode.Decompiler
|
||||
/// <summary>
|
||||
/// Gets/Sets whether C# 14.0 extension members should be transformed.
|
||||
/// </summary>
|
||||
[Category("C# 14.0 / VS 202x.yy")]
|
||||
[Category("C# 14.0 / VS 2026")]
|
||||
[Description("DecompilerSettings.ExtensionMembers")]
|
||||
public bool ExtensionMembers {
|
||||
get { return extensionMembers; }
|
||||
@@ -2185,7 +2191,7 @@ namespace ICSharpCode.Decompiler
|
||||
/// <summary>
|
||||
/// Gets/Sets whether (ReadOnly)Span<T> should be treated like built-in types.
|
||||
/// </summary>
|
||||
[Category("C# 14.0 / VS 202x.yy")]
|
||||
[Category("C# 14.0 / VS 2026")]
|
||||
[Description("DecompilerSettings.FirstClassSpanTypes")]
|
||||
public bool FirstClassSpanTypes {
|
||||
get { return firstClassSpanTypes; }
|
||||
@@ -2198,6 +2204,24 @@ namespace ICSharpCode.Decompiler
|
||||
}
|
||||
}
|
||||
|
||||
bool runtimeAsync = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets whether runtime async should be used.
|
||||
/// </summary>
|
||||
[Category("C# 15.0 / VS 202x.yy")]
|
||||
[Description("DecompilerSettings.RuntimeAsync")]
|
||||
public bool RuntimeAsync {
|
||||
get { return runtimeAsync; }
|
||||
set {
|
||||
if (runtimeAsync != value)
|
||||
{
|
||||
runtimeAsync = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool separateLocalVariableDeclarations = false;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -159,6 +159,7 @@ namespace ICSharpCode.Decompiler.IL
|
||||
BitSet isBranchTarget = null!;
|
||||
BlockContainer mainContainer = null!;
|
||||
int currentInstructionStart;
|
||||
bool isRuntimeAsync;
|
||||
|
||||
Dictionary<int, ImportedBlock> blocksByOffset = new Dictionary<int, ImportedBlock>();
|
||||
Queue<ImportedBlock> importQueue = new Queue<ImportedBlock>();
|
||||
@@ -172,6 +173,8 @@ namespace ICSharpCode.Decompiler.IL
|
||||
if (methodDefinitionHandle.IsNil)
|
||||
throw new ArgumentException("methodDefinitionHandle.IsNil");
|
||||
this.method = module.GetDefinition(methodDefinitionHandle);
|
||||
this.isRuntimeAsync = (module.TypeSystemOptions & TypeSystemOptions.RuntimeAsync) != 0
|
||||
&& (module.metadata.GetMethodDefinition(methodDefinitionHandle).ImplAttributes & SRMExtensions.MethodImplAsync) != 0;
|
||||
if (genericContext.ClassTypeParameters == null && genericContext.MethodTypeParameters == null)
|
||||
{
|
||||
// no generic context specified: use the method's own type parameters
|
||||
@@ -187,7 +190,14 @@ namespace ICSharpCode.Decompiler.IL
|
||||
this.reader = body.GetILReader();
|
||||
this.currentStack = ImmutableStack<ILVariable>.Empty;
|
||||
this.expressionStack.Clear();
|
||||
this.methodReturnStackType = method.ReturnType.GetStackType();
|
||||
if (isRuntimeAsync)
|
||||
{
|
||||
this.methodReturnStackType = TaskType.UnpackTask(compilation, method.ReturnType).GetStackType();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.methodReturnStackType = method.ReturnType.GetStackType();
|
||||
}
|
||||
InitParameterVariables();
|
||||
localVariables = InitLocalVariables();
|
||||
foreach (var v in localVariables)
|
||||
@@ -715,6 +725,13 @@ namespace ICSharpCode.Decompiler.IL
|
||||
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler, compilation);
|
||||
blockBuilder.CreateBlocks(mainContainer, blocksByOffset.Values.Select(ib => ib.Block), cancellationToken);
|
||||
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
|
||||
if (isRuntimeAsync)
|
||||
{
|
||||
if (!this.method.ReturnType.IsKnownType(KnownTypeCode.TaskOfT))
|
||||
function.AsyncReturnType = compilation.FindType(KnownTypeCode.Void);
|
||||
else
|
||||
function.AsyncReturnType = TaskType.UnpackTask(compilation, this.method.ReturnType);
|
||||
}
|
||||
function.Variables.AddRange(parameterVariables);
|
||||
function.Variables.AddRange(localVariables);
|
||||
function.LocalVariableSignatureLength = localVariables.Length;
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace ICSharpCode.Decompiler
|
||||
public static partial class SRMExtensions
|
||||
{
|
||||
internal const GenericParameterAttributes AllowByRefLike = (GenericParameterAttributes)0x0020;
|
||||
internal const MethodImplAttributes MethodImplAsync = (MethodImplAttributes)0x2000;
|
||||
|
||||
public static ImmutableArray<MethodImplementationHandle> GetMethodImplementations(
|
||||
this MethodDefinitionHandle handle, MetadataReader reader)
|
||||
|
||||
@@ -154,12 +154,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
|
||||
/// </summary>
|
||||
ExtensionMembers = 0x80000,
|
||||
/// <summary>
|
||||
/// If this option is active, methods with the MethodImplAttribute(MethodImplOptions.Async) are treated as async methods.
|
||||
/// </summary>
|
||||
RuntimeAsync = 0x100000,
|
||||
/// <summary>
|
||||
/// Default settings: typical options for the decompiler, with all C# language features enabled.
|
||||
/// </summary>
|
||||
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
|
||||
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
|
||||
| NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute
|
||||
| RefReadOnlyParameters | ParamsCollections | FirstClassSpanTypes | ExtensionMembers
|
||||
| RuntimeAsync
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -207,6 +212,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
|
||||
typeSystemOptions |= TypeSystemOptions.FirstClassSpanTypes;
|
||||
if (settings.ExtensionMembers)
|
||||
typeSystemOptions |= TypeSystemOptions.ExtensionMembers;
|
||||
if (settings.RuntimeAsync)
|
||||
typeSystemOptions |= TypeSystemOptions.RuntimeAsync;
|
||||
return typeSystemOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -350,6 +350,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
|
||||
var metadata = module.metadata;
|
||||
var def = metadata.GetMethodDefinition(handle);
|
||||
MethodImplAttributes implAttributes = def.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
|
||||
if ((module.TypeSystemOptions & TypeSystemOptions.RuntimeAsync) != 0)
|
||||
{
|
||||
implAttributes &= ~SRMExtensions.MethodImplAsync;
|
||||
}
|
||||
int methodCodeType = (int)(def.ImplAttributes & MethodImplAttributes.CodeTypeMask);
|
||||
|
||||
#region DllImportAttribute
|
||||
|
||||
@@ -24,8 +24,6 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
using ICSharpCode.Decompiler;
|
||||
@@ -119,6 +117,7 @@ namespace ICSharpCode.ILSpy
|
||||
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp12_0.ToString(), "C# 12.0 / VS 2022.8"),
|
||||
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp13_0.ToString(), "C# 13.0 / VS 2022.12"),
|
||||
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp14_0.ToString(), "C# 14.0 / VS 2026"),
|
||||
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp15_0.ToString(), "C# 15.0 / VS 202x.yy"),
|
||||
};
|
||||
}
|
||||
return versions;
|
||||
|
||||
Generated
+10
-1
@@ -1360,7 +1360,16 @@ namespace ICSharpCode.ILSpy.Properties {
|
||||
return ResourceManager.GetString("DecompilerSettings.RequiredMembers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Decompile runtime-async (System.Runtime.CompilerServices.AsyncHelpers) methods.
|
||||
/// </summary>
|
||||
public static string DecompilerSettings_RuntimeAsync {
|
||||
get {
|
||||
return ResourceManager.GetString("DecompilerSettings.RuntimeAsync", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 'scoped' lifetime annotation.
|
||||
/// </summary>
|
||||
|
||||
@@ -474,6 +474,9 @@ Are you sure you want to continue?</value>
|
||||
<data name="DecompilerSettings.RequiredMembers" xml:space="preserve">
|
||||
<value>Required members</value>
|
||||
</data>
|
||||
<data name="DecompilerSettings.RuntimeAsync" xml:space="preserve">
|
||||
<value>Decompile runtime-async (System.Runtime.CompilerServices.AsyncHelpers) methods</value>
|
||||
</data>
|
||||
<data name="DecompilerSettings.ScopedRef" xml:space="preserve">
|
||||
<value>'scoped' lifetime annotation</value>
|
||||
</data>
|
||||
|
||||
Reference in New Issue
Block a user