Browse Source

Implement detection of custom task implementations + tests.

pull/1652/head
Siegfried Pammer 6 years ago
parent
commit
4db22c87e1
  1. 1
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 125
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs
  5. 18
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  6. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  7. 62
      ICSharpCode.Decompiler/TypeSystem/TaskType.cs

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

@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Xml.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "Microsoft.CSharp.dll")),
MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location),
MetadataReference.CreateFromFile(typeof(ValueTask).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Span<>).Assembly.Location),
};
});

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

@ -81,6 +81,7 @@ @@ -81,6 +81,7 @@
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<Compile Include="TestCases\Pretty\OutVariables.cs" />
<Compile Include="TestCases\Pretty\CustomTaskType.cs" />
<None Include="TestCases\VBPretty\VBCompoundAssign.cs" />
<Compile Include="TestCases\Pretty\ThrowExpressions.cs" />
<None Include="TestCases\ILPretty\Issue1145.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -299,6 +299,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -299,6 +299,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void CustomTaskType([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void NullableRefTypes([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{

125
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class CustomTaskType
{
private int memberField;
public async ValueTask SimpleVoidTaskMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
}
public async ValueTask TaskMethodWithoutAwait()
{
Console.WriteLine("No Await");
}
public async ValueTask CapturingThis()
{
await Task.Delay(memberField);
}
public async ValueTask CapturingThisWithoutAwait()
{
Console.WriteLine(memberField);
}
public async ValueTask<bool> SimpleBoolTaskMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
return true;
}
public async void TwoAwaitsWithDifferentAwaiterTypes()
{
Console.WriteLine("Before");
if (await SimpleBoolTaskMethod()) {
await Task.Delay(TimeSpan.FromSeconds(1.0));
}
Console.WriteLine("After");
}
public async void AwaitInLoopCondition()
{
while (await SimpleBoolTaskMethod()) {
Console.WriteLine("Body");
}
}
public async ValueTask AwaitInCatch(bool b, ValueTask<int> task1, ValueTask<int> task2)
{
try {
Console.WriteLine("Start try");
await task1;
Console.WriteLine("End try");
} catch (Exception) {
if (!b) {
await task2;
} else {
Console.WriteLine("No await");
}
}
}
public async ValueTask AwaitInFinally(bool b, ValueTask<int> task1, ValueTask<int> task2)
{
try {
Console.WriteLine("Start try");
await task1;
Console.WriteLine("End try");
} finally {
if (!b) {
await task2;
} else {
Console.WriteLine("No await");
}
}
}
public static async ValueTask<int> GetIntegerSumAsync(IEnumerable<int> items)
{
await Task.Delay(100);
int num = 0;
foreach (int item in items) {
num += item;
}
return num;
}
public static Func<ValueTask<int>> AsyncLambda()
{
return async () => await GetIntegerSumAsync(new int[3] {
1,
2,
3
});
}
public static Func<ValueTask<int>> AsyncDelegate()
{
return async delegate {
await Task.Delay(10);
return 2;
};
}
public static async ValueTask<int> AsyncLocalFunctions()
{
return await Nested(1) + await Nested(2);
async ValueTask<int> Nested(int i)
{
await Task.Delay(i);
return i;
}
}
}
}

18
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -199,24 +199,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -199,24 +199,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
taskType = function.Method.ReturnType;
builderType = startCall.Method.DeclaringTypeDefinition;
const string ns = "System.Runtime.CompilerServices";
if (taskType.IsKnownType(KnownTypeCode.Void)) {
methodType = AsyncMethodType.Void;
underlyingReturnType = taskType;
if (builderType?.FullTypeName != new TopLevelTypeName(ns, "AsyncVoidMethodBuilder"))
if (builderType?.FullTypeName != new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncVoidMethodBuilder"))
return false;
} else if (taskType.IsKnownType(KnownTypeCode.Task)) {
} else if (TaskType.IsNonGenericTaskType(taskType, out var builderTypeName)) {
methodType = AsyncMethodType.Task;
underlyingReturnType = context.TypeSystem.FindType(KnownTypeCode.Void);
if (builderType?.FullTypeName != new TopLevelTypeName(ns, "AsyncTaskMethodBuilder", 0))
if (builderType?.FullTypeName != builderTypeName)
return false;
} else if (taskType.IsKnownType(KnownTypeCode.TaskOfT)) {
} else if (TaskType.IsGenericTaskType(taskType, out builderTypeName)) {
methodType = AsyncMethodType.TaskOfT;
underlyingReturnType = TaskType.UnpackTask(context.TypeSystem, taskType);
if (builderType?.FullTypeName != new TopLevelTypeName(ns, "AsyncTaskMethodBuilder", 1))
if (taskType.IsKnownType(KnownTypeCode.TaskOfT))
underlyingReturnType = TaskType.UnpackTask(context.TypeSystem, taskType);
else
underlyingReturnType = startCall.Method.DeclaringType.TypeArguments[0];
if (builderType?.FullTypeName != builderTypeName)
return false;
} else {
return false; // TODO: generalized async return type
return false;
}
if (startCall.Arguments.Count != 2)
return false;

2
ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs

@ -63,6 +63,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -63,6 +63,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IsByRefLike,
IteratorStateMachine,
AsyncStateMachine,
AsyncMethodBuilder,
// Field attributes:
FieldOffset,
@ -129,6 +130,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -129,6 +130,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Runtime.CompilerServices", "IsByRefLikeAttribute"),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(IteratorStateMachineAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(AsyncStateMachineAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncMethodBuilderAttribute"),
// Field attributes:
new TopLevelTypeName("System.Runtime.InteropServices", nameof(FieldOffsetAttribute)),
new TopLevelTypeName("System", nameof(NonSerializedAttribute)),

62
ICSharpCode.Decompiler/TypeSystem/TaskType.cs

@ -54,7 +54,67 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -54,7 +54,67 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return false;
}
/// <summary>
/// Gets whether the specified type is a Task-like type.
/// </summary>
public static bool IsCustomTask(IType type, out IType builderType)
{
builderType = null;
ITypeDefinition def = type.GetDefinition();
if (def != null) {
if (def.TypeParameterCount > 1)
return false;
var attribute = def.GetAttribute(KnownAttribute.AsyncMethodBuilder);
if (attribute == null || attribute.FixedArguments.Length != 1)
return false;
var arg = attribute.FixedArguments[0];
if (!arg.Type.IsKnownType(KnownTypeCode.Type))
return false;
builderType = (IType)arg.Value;
return true;
}
return false;
}
const string ns = "System.Runtime.CompilerServices";
/// <summary>
/// Gets whether the specified type is a non-generic Task-like type.
/// </summary>
/// <param name="builderTypeName">Returns the full type-name of the builder type, if successful.</param>
public static bool IsNonGenericTaskType(IType task, out FullTypeName builderTypeName)
{
if (task.IsKnownType(KnownTypeCode.Task)) {
builderTypeName = new TopLevelTypeName(ns, "AsyncTaskMethodBuilder");
return true;
}
if (IsCustomTask(task, out var builderType)) {
builderTypeName = new FullTypeName(builderType.ReflectionName);
return builderTypeName.TypeParameterCount == 0;
}
builderTypeName = default;
return false;
}
/// <summary>
/// Gets whether the specified type is a generic Task-like type.
/// </summary>
/// <param name="builderTypeName">Returns the full type-name of the builder type, if successful.</param>
public static bool IsGenericTaskType(IType task, out FullTypeName builderTypeName)
{
if (task.IsKnownType(KnownTypeCode.TaskOfT)) {
builderTypeName = new TopLevelTypeName(ns, "AsyncTaskMethodBuilder", 1);
return true;
}
if (IsCustomTask(task, out var builderType)) {
builderTypeName = new FullTypeName(builderType.ReflectionName);
return builderTypeName.TypeParameterCount == 1;
}
builderTypeName = default;
return false;
}
/// <summary>
/// Creates a task type.
/// </summary>

Loading…
Cancel
Save