提交 718f7873 编写于 作者: T TomasMatousek 提交者: RoslynTeam

Switch the state machine to a class when emitting unoptimized debug code.

Currently the async state machine is a struct. Struct is better for performance (no allocation) but prevents us to modify local variables in the async method in EnC. The CLR doesn't support adding fields to a struct. (changeset 1252494)
上级 e038a4e7
......@@ -11,7 +11,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
sealed internal class AsyncMethodToStateMachineRewriter : MethodToStateMachineRewriter
internal sealed class AsyncMethodToStateMachineRewriter : MethodToStateMachineRewriter
{
/// <summary>
/// The method being rewritten.
......@@ -63,8 +63,8 @@ sealed internal class AsyncMethodToStateMachineRewriter : MethodToStateMachineRe
DiagnosticBag diagnostics,
bool generateDebugInfo)
: base(F, method, state, variablesCaptured, initialProxies, diagnostics,
useFinalizerBookkeeping: false,
generateDebugInfo: generateDebugInfo)
useFinalizerBookkeeping: false,
generateDebugInfo: generateDebugInfo)
{
this.method = method;
this.asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection;
......@@ -87,6 +87,7 @@ private FieldSymbol GetAwaiterField(TypeSymbol awaiterType)
result = F.StateMachineField(awaiterType, GeneratedNames.AsyncAwaiterFieldName(CompilationState.GenerateTempNumber()), isPublic: true);
awaiterFields.Add(awaiterType, result);
}
return result;
}
......@@ -116,12 +117,12 @@ internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod)
F.Try(
F.Block(
ImmutableArray<LocalSymbol>.Empty,
// switch (state) ...
F.HiddenSequencePoint(),
// switch (state) ...
F.HiddenSequencePoint(),
Dispatch(),
F.Label(initialLabel),
// [body]
rewrittenBody
// [body]
rewrittenBody
),
F.CatchBlocks(
F.Catch(
......@@ -129,10 +130,10 @@ internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod)
F.Block(
F.NoOp(method.ReturnsVoid ? NoOpStatementFlavor.AsyncMethodCatchHandler : NoOpStatementFlavor.Default),
F.HiddenSequencePoint(),
// this.state = finishedState
F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
// builder.SetException(ex)
F.ExpressionStatement(
// this.state = finishedState
F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
// builder.SetException(ex)
F.ExpressionStatement(
F.Call(
F.Field(F.This(), asyncMethodBuilderField),
asyncMethodBuilderMemberCollection.SetException,
......@@ -262,13 +263,13 @@ private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpressi
}
var awaitIfIncomplete = F.Block(
// temp $awaiterTemp = <expr>.GetAwaiter();
F.Assignment(
// temp $awaiterTemp = <expr>.GetAwaiter();
F.Assignment(
F.Local(awaiterTemp),
MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)),
// if(!($awaiterTemp.IsCompleted)) { ... }
F.If(
// if(!($awaiterTemp.IsCompleted)) { ... }
F.If(
condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)),
thenClause: GenerateAwaitForIncompleteTask(awaiterTemp)));
......@@ -286,7 +287,7 @@ private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpressi
// $resultTemp
LocalSymbol resultTemp = F.SynthesizedLocal(type);
return F.Block(
ImmutableArray.Create(awaiterTemp, resultTemp),
ImmutableArray.Create(awaiterTemp, resultTemp),
awaitIfIncomplete,
F.Assignment(F.Local(resultTemp), getResultCall),
F.ExpressionStatement(nullAwaiter),
......@@ -297,7 +298,7 @@ private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpressi
// $awaiterTemp.GetResult();
// $awaiterTemp = null;
return F.Block(
ImmutableArray.Create(awaiterTemp),
ImmutableArray.Create(awaiterTemp),
awaitIfIncomplete,
F.ExpressionStatement(getResultCall),
F.ExpressionStatement(nullAwaiter));
......@@ -307,7 +308,7 @@ private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpressi
private BoundExpression MakeCallMaybeDynamic(
BoundExpression receiver,
MethodSymbol methodSymbol = null,
string methodName = null,
string methodName = null,
bool resultsDiscarded = false)
{
if ((object)methodSymbol != null)
......@@ -327,7 +328,7 @@ private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpressi
receiver,
typeArguments: ImmutableArray<TypeSymbol>.Empty,
loweredArguments: ImmutableArray<BoundExpression>.Empty,
argumentNames: ImmutableArray<string>.Empty,
argumentNames: ImmutableArray<string>.Empty,
refKinds: ImmutableArray<RefKind>.Empty,
hasImplicitReceiver: false,
resultDiscarded: resultsDiscarded).ToExpression();
......@@ -366,16 +367,16 @@ private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp)
var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance();
blockBuilder.Add(
// this.state = cachedState = stateForLabel
F.Assignment(F.Field(F.This(), stateField), F.AssignmentExpression(F.Local(cachedState), F.Literal(stateNumber))));
// this.state = cachedState = stateForLabel
F.Assignment(F.Field(F.This(), stateField), F.AssignmentExpression(F.Local(cachedState), F.Literal(stateNumber))));
blockBuilder.Add(
// Emit await yield point to be injected into PDB
F.NoOp(NoOpStatementFlavor.AwaitYieldPoint));
// Emit await yield point to be injected into PDB
F.NoOp(NoOpStatementFlavor.AwaitYieldPoint));
blockBuilder.Add(
// this.<>t__awaiter = $awaiterTemp
F.Assignment(
// this.<>t__awaiter = $awaiterTemp
F.Assignment(
F.Field(F.This(), awaiterField),
(awaiterField.Type == awaiterTemp.Type)
? F.Local(awaiterTemp)
......@@ -392,13 +393,13 @@ private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp)
F.Label(resumeLabel));
blockBuilder.Add(
// Emit await resume point to be injected into PDB
F.NoOp(NoOpStatementFlavor.AwaitResumePoint));
// Emit await resume point to be injected into PDB
F.NoOp(NoOpStatementFlavor.AwaitResumePoint));
blockBuilder.Add(
// $awaiterTemp = this.<>t__awaiter or $awaiterTemp = (AwaiterType)this.<>t__awaiter
// $this.<>t__awaiter = null;
F.Assignment(
// $awaiterTemp = this.<>t__awaiter or $awaiterTemp = (AwaiterType)this.<>t__awaiter
// $this.<>t__awaiter = null;
F.Assignment(
F.Local(awaiterTemp),
awaiterTemp.Type == awaiterField.Type
? F.Field(F.This(), awaiterField)
......@@ -408,8 +409,8 @@ private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp)
F.Assignment(F.Field(F.This(), awaiterField), F.NullOrDefault(awaiterField.Type)));
blockBuilder.Add(
// this.state = cachedState = NotStartedStateMachine
F.Assignment(F.Field(F.This(), stateField), F.AssignmentExpression(F.Local(cachedState), F.Literal(StateMachineStates.NotStartedStateMachine))));
// this.state = cachedState = NotStartedStateMachine
F.Assignment(F.Field(F.This(), stateField), F.AssignmentExpression(F.Local(cachedState), F.Literal(StateMachineStates.NotStartedStateMachine))));
return F.Block(blockBuilder.ToImmutableAndFree());
}
......@@ -439,14 +440,22 @@ private BoundStatement GenerateAwaitOnCompletedDynamic(LocalSymbol awaiterTemp)
F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_INotifyCompletion),
null);
return F.Block(
ImmutableArray.Create(criticalNotifyCompletedTemp),
LocalSymbol thisTemp = (F.CurrentClass.TypeKind == TypeKind.Class) ? F.SynthesizedLocal(F.CurrentClass) : null;
var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance();
blockBuilder.Add(
F.Assignment(
F.Local(criticalNotifyCompletedTemp),
// Use reference conversion rather than dynamic conversion:
F.As(F.Local(awaiterTemp), criticalNotifyCompletedTemp.Type)),
// Use reference conversion rather than dynamic conversion:
F.As(F.Local(awaiterTemp), criticalNotifyCompletedTemp.Type)));
if (thisTemp != null)
{
blockBuilder.Add(F.Assignment(F.Local(thisTemp), F.This()));
}
blockBuilder.Add(
F.If(
condition: F.ObjectEqual(F.Local(criticalNotifyCompletedTemp), F.Null(criticalNotifyCompletedTemp.Type)),
......@@ -454,15 +463,15 @@ private BoundStatement GenerateAwaitOnCompletedDynamic(LocalSymbol awaiterTemp)
ImmutableArray.Create(notifyCompletionTemp),
F.Assignment(
F.Local(notifyCompletionTemp),
// Use reference conversion rather than dynamic conversion:
F.Convert(notifyCompletionTemp.Type, F.Local(awaiterTemp), ConversionKind.ExplicitReference)),
// Use reference conversion rather than dynamic conversion:
F.Convert(notifyCompletionTemp.Type, F.Local(awaiterTemp), ConversionKind.ExplicitReference)),
F.ExpressionStatement(
F.Call(
F.Field(F.This(), asyncMethodBuilderField),
asyncMethodBuilderMemberCollection.AwaitOnCompleted.Construct(
notifyCompletionTemp.Type,
F.This().Type),
F.Local(notifyCompletionTemp), F.This())),
F.Local(notifyCompletionTemp), F.This(thisTemp))),
F.Assignment(
F.Local(notifyCompletionTemp),
F.NullOrDefault(notifyCompletionTemp.Type))),
......@@ -474,32 +483,56 @@ private BoundStatement GenerateAwaitOnCompletedDynamic(LocalSymbol awaiterTemp)
asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted.Construct(
criticalNotifyCompletedTemp.Type,
F.This().Type),
F.Local(criticalNotifyCompletedTemp), F.This())))),
F.Local(criticalNotifyCompletedTemp), F.This(thisTemp))))));
blockBuilder.Add(
F.Assignment(
F.Local(criticalNotifyCompletedTemp),
F.NullOrDefault(criticalNotifyCompletedTemp.Type)));
return F.Block(
SingletonOrPair(criticalNotifyCompletedTemp, thisTemp),
blockBuilder.ToImmutableAndFree());
}
private BoundExpressionStatement GenerateAwaitOnCompleted(TypeSymbol loweredAwaiterType, LocalSymbol awaiterTemp)
private BoundStatement GenerateAwaitOnCompleted(TypeSymbol loweredAwaiterType, LocalSymbol awaiterTemp)
{
// this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterTemp, ref this)
// or
// this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterArrayTemp[0], ref this)
LocalSymbol thisTemp = (F.CurrentClass.TypeKind == TypeKind.Class) ? F.SynthesizedLocal(F.CurrentClass) : null;
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
var useUnsafeOnCompleted = F.Compilation.Conversions.ClassifyImplicitConversion(
loweredAwaiterType,
F.Compilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_ICriticalNotifyCompletion),
ref useSiteDiagnostics).IsImplicit;
var onCompleted = (useUnsafeOnCompleted ?
asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted :
asyncMethodBuilderMemberCollection.AwaitOnCompleted).Construct(loweredAwaiterType, F.This().Type);
return F.ExpressionStatement(
BoundExpression result =
F.Call(
F.Field(F.This(), asyncMethodBuilderField),
onCompleted,
F.Local(awaiterTemp), F.This()));
F.Local(awaiterTemp), F.This(thisTemp));
if (thisTemp != null)
{
result = F.Sequence(
ImmutableArray.Create(thisTemp),
ImmutableArray.Create<BoundExpression>(F.AssignmentExpression(F.Local(thisTemp), F.This())),
result);
}
return F.ExpressionStatement(result);
}
private static ImmutableArray<LocalSymbol> SingletonOrPair(LocalSymbol first, LocalSymbol secondOpt)
{
return (secondOpt == null) ? ImmutableArray.Create(first) : ImmutableArray.Create(first, secondOpt);
}
public override BoundNode VisitReturnStatement(BoundReturnStatement node)
......@@ -517,4 +550,4 @@ public override BoundNode VisitReturnStatement(BoundReturnStatement node)
#endregion Visitors
}
}
\ No newline at end of file
}
......@@ -31,9 +31,12 @@ internal partial class AsyncRewriter : StateMachineRewriter
return body;
}
// The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class.
var typeKind = compilationState.Compilation.Options.EnableEditAndContinue ? TypeKind.Class : TypeKind.Struct;
var bodyWithAwaitLifted = AwaitLiftingRewriter.Rewrite(body, method, compilationState, diagnostics);
stateMachineType = new AsyncStateMachine(method);
stateMachineType = new AsyncStateMachine(method, typeKind);
compilationState.ModuleBuilderOpt.CompilationState.SetStateMachineType(method, stateMachineType);
var rewriter = new AsyncRewriter(bodyWithAwaitLifted, method, stateMachineType, compilationState, diagnostics, generateDebugInfo);
if (!rewriter.constructedSuccessfully)
......@@ -85,17 +88,26 @@ protected override void GenerateMethodImplementations()
var IAsyncStateMachine_SetStateMachine = F.WellKnownMethod(WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_SetStateMachine);
// Add IAsyncStateMachine.MoveNext()
{
var moveNextMethod = OpenMethodImplementation(IAsyncStateMachine_MoveNext, "MoveNext", asyncKickoffMethod: this.method, hasMethodBodyDependency: true);
GenerateMoveNext(moveNextMethod);
}
var moveNextMethod = OpenMethodImplementation(IAsyncStateMachine_MoveNext, "MoveNext", asyncKickoffMethod: this.method, hasMethodBodyDependency: true);
GenerateMoveNext(moveNextMethod);
// Add IAsyncStateMachine.SetStateMachine()
OpenMethodImplementation(IAsyncStateMachine_SetStateMachine, "SetStateMachine", debuggerHidden: true, hasMethodBodyDependency: false);
// SetStateMachine is used to initialize the underlying AsyncMethodBuilder's reference to the boxed copy of the state machine.
// If the state machine is a class there is no copy made and thus the initialization is not necessary.
// In fact it is an error to reinitialize the builder since it already is initialized.
if (F.CurrentClass.TypeKind == TypeKind.Class)
{
F.CloseMethod(F.Return());
}
else
{
OpenMethodImplementation(IAsyncStateMachine_SetStateMachine, "SetStateMachine", debuggerHidden: true, hasMethodBodyDependency: false);
F.CloseMethod(
// this.builderField.SetStateMachine(sm)
F.Block(
// this.builderField.SetStateMachine(sm)
F.ExpressionStatement(
F.Call(
F.Field(F.This(), builderField),
......@@ -103,6 +115,13 @@ protected override void GenerateMethodImplementations()
new BoundExpression[] { F.Parameter(F.CurrentMethod.Parameters[0]) })),
F.Return()));
}
// Constructor
if (stateMachineClass.TypeKind == TypeKind.Class)
{
F.CurrentMethod = stateMachineClass.Constructor;
F.CloseMethod(F.Block(ImmutableArray.Create(F.BaseInitialization(), F.Return())));
}
}
protected override bool IsStateFieldPublic
......@@ -133,6 +152,15 @@ protected override BoundStatement GenerateReplacementBody(LocalSymbol stateMachi
var builderVariable = F.SynthesizedLocal(methodScopeAsyncMethodBuilderMemberCollection.BuilderType, null);
if (stateMachineVariable.Type.TypeKind == TypeKind.Class)
{
// local = new {state machine type}();
bodyBuilder.Add(
F.Assignment(
F.Local(stateMachineVariable),
F.New(((AsyncStateMachine)stateMachineVariable.Type).InstanceConstructors[0])));
}
// local.$builder = System.Runtime.CompilerServices.AsyncTaskMethodBuilder<typeArgs>.Create();
bodyBuilder.Add(
F.Assignment(
......
......@@ -11,14 +11,16 @@ namespace Microsoft.CodeAnalysis.CSharp
/// </summary>
internal sealed class AsyncStateMachine : SynthesizedContainer, ISynthesizedMethodBodyImplementationSymbol
{
private readonly TypeKind typeKind;
private readonly MethodSymbol constructor;
private readonly MethodSymbol asyncMethod;
private readonly ImmutableArray<NamedTypeSymbol> interfaces;
public AsyncStateMachine(MethodSymbol asyncMethod)
public AsyncStateMachine(MethodSymbol asyncMethod, TypeKind typeKind)
: base(GeneratedNames.MakeIteratorOrAsyncDisplayClassName(asyncMethod.Name, SequenceNumber(asyncMethod)), asyncMethod)
{
// TODO: report use-site errors on these types
this.typeKind = typeKind;
this.asyncMethod = asyncMethod;
this.interfaces = ImmutableArray.Create(asyncMethod.DeclaringCompilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_IAsyncStateMachine));
this.constructor = new AsyncConstructor(this);
......@@ -42,7 +44,7 @@ private static int SequenceNumber(MethodSymbol method)
public override TypeKind TypeKind
{
get { return TypeKind.Struct; }
get { return typeKind; }
}
internal override MethodSymbol Constructor
......
......@@ -174,6 +174,11 @@ public BoundThisReference This()
return new BoundThisReference(Syntax, CurrentMethod.ThisParameter.Type) { WasCompilerGenerated = true };
}
public BoundExpression This(LocalSymbol thisTempOpt)
{
return (thisTempOpt != null) ? Local(thisTempOpt) : (BoundExpression)This();
}
public BoundBaseReference Base()
{
Debug.Assert((object)CurrentMethod != null && !CurrentMethod.IsStatic);
......
......@@ -392,11 +392,11 @@ internal SynthesizedAttributeData SynthesizeDebuggableAttribute()
if (optimizationsDisabled)
{
constantVal |= disableOptsDebuggingMode.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false).Int32Value;
}
if (emittingFullDebugInfo)
{
constantVal |= enableENCDebuggingMode.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false).Int32Value;
}
if (options.EnableEditAndContinue)
{
constantVal |= enableENCDebuggingMode.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false).Int32Value;
}
var typedConstantDebugMode = new TypedConstant(debuggingModesType, TypedConstantKind.Enum, constantVal);
......
......@@ -34,6 +34,75 @@ private CompilationVerifier CompileAndVerify(string source, string expectedOutpu
return base.CompileAndVerify(compilation, expectedOutput: expectedOutput, emitOptions: emitOptions);
}
[Fact]
public void StructVsClass()
{
var source = @"
using System.Threading;
using System.Threading.Tasks;
class Test
{
public static async Task F()
{
await Task.Factory.StartNew(() => { System.Console.WriteLine(123); });
}
public static void Main()
{
F().Wait();
}
}";
var c = CreateCompilationWithMscorlib45(source);
CompilationOptions options;
options = TestOptions.OptimizedExe;
Assert.False(options.EnableEditAndContinue);
CompileAndVerify(c.WithOptions(options), symbolValidator: module =>
{
var stateMachine = module.GlobalNamespace.GetMember<NamedTypeSymbol>("Test").GetMember<NamedTypeSymbol>("<F>d__1");
Assert.Equal(TypeKind.Struct, stateMachine.TypeKind);
}, expectedOutput: "123");
options = TestOptions.UnoptimizedExe;
Assert.False(options.EnableEditAndContinue);
CompileAndVerify(c.WithOptions(options), symbolValidator: module =>
{
var stateMachine = module.GlobalNamespace.GetMember<NamedTypeSymbol>("Test").GetMember<NamedTypeSymbol>("<F>d__1");
Assert.Equal(TypeKind.Struct, stateMachine.TypeKind);
}, expectedOutput: "123");
options = TestOptions.OptimizedExe.WithDebugInformationKind(DebugInformationKind.Full);
Assert.False(options.EnableEditAndContinue);
CompileAndVerify(c.WithOptions(options), symbolValidator: module =>
{
var stateMachine = module.GlobalNamespace.GetMember<NamedTypeSymbol>("Test").GetMember<NamedTypeSymbol>("<F>d__1");
Assert.Equal(TypeKind.Struct, stateMachine.TypeKind);
}, expectedOutput: "123");
options = TestOptions.UnoptimizedExe.WithDebugInformationKind(DebugInformationKind.PDBOnly);
Assert.False(options.EnableEditAndContinue);
CompileAndVerify(c.WithOptions(options), symbolValidator: module =>
{
var stateMachine = module.GlobalNamespace.GetMember<NamedTypeSymbol>("Test").GetMember<NamedTypeSymbol>("<F>d__1");
Assert.Equal(TypeKind.Struct, stateMachine.TypeKind);
}, expectedOutput: "123");
options = TestOptions.UnoptimizedExe.WithDebugInformationKind(DebugInformationKind.Full);
Assert.True(options.EnableEditAndContinue);
CompileAndVerify(c.WithOptions(options), symbolValidator: module =>
{
var stateMachine = module.GlobalNamespace.GetMember<NamedTypeSymbol>("Test").GetMember<NamedTypeSymbol>("<F>d__1");
Assert.Equal(TypeKind.Class, stateMachine.TypeKind);
}, expectedOutput: "123");
}
[Fact]
public void VoidReturningAsync()
{
......@@ -5860,7 +5929,7 @@ public static void Main()
}
[Fact]
public void IL_Task_T()
public void AsyncStateMachineIL_Struct_TaskT()
{
var source = @"
using System;
......@@ -5883,8 +5952,9 @@ public static void Main()
var expected = @"
42
";
CompileAndVerify(source, expectedOutput: expected)
.VerifyIL("Test.F", @"
var c = CompileAndVerify(source, expectedOutput: expected);
c.VerifyIL("Test.F", @"
{
// Code size 49 (0x31)
.maxstack 2
......@@ -5907,8 +5977,9 @@ .maxstack 2
IL_002b: call ""System.Threading.Tasks.Task<int> System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Task.get""
IL_0030: ret
}
")
.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
");
c.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
{
// Code size 188 (0xbc)
.maxstack 3
......@@ -5995,6 +6066,190 @@ .maxstack 3
IL_00b6: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00bb: ret
}
");
c.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.SetStateMachine", @"
{
// Code size 13 (0xd)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0006: ldarg.1
IL_0007: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)""
IL_000c: ret
}
");
}
[Fact]
public void AsyncStateMachineIL_Class_TaskT()
{
var source = @"
using System;
using System.Threading.Tasks;
class Test
{
public static async Task<int> F()
{
return await Task.Factory.StartNew(() => 42);
}
public static void Main()
{
var t = F();
t.Wait();
Console.WriteLine(t.Result);
}
}";
var expected = @"
42
";
var c = CompileAndVerify(source, expectedOutput: expected, compOptions: TestOptions.UnoptimizedExe.WithDebugInformationKind(DebugInformationKind.Full));
c.VerifyIL("Test.F", @"
{
// Code size 54 (0x36)
.maxstack 2
.locals init (Test.<F>d__1 V_0,
System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> V_1,
System.Threading.Tasks.Task<int> V_2)
IL_0000: newobj ""Test.<F>d__1..ctor()""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Create()""
IL_000c: stfld ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0011: ldloc.0
IL_0012: ldc.i4.m1
IL_0013: stfld ""int Test.<F>d__1.<>1__state""
IL_0018: ldloc.0
IL_0019: ldfld ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_001e: stloc.1
IL_001f: ldloca.s V_1
IL_0021: ldloca.s V_0
IL_0023: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Start<Test.<F>d__1>(ref Test.<F>d__1)""
IL_0028: ldloc.0
IL_0029: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_002e: call ""System.Threading.Tasks.Task<int> System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Task.get""
IL_0033: stloc.2
IL_0034: ldloc.2
IL_0035: ret
}
");
c.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
{
// Code size 215 (0xd7)
.maxstack 3
.locals init (int V_0, //cachedState
int V_1, //<>t__exprRetValue
int V_2,
System.Runtime.CompilerServices.TaskAwaiter<int> V_3,
int V_4,
bool V_5,
Test.<F>d__1 V_6,
System.Exception V_7) //<>t__ex
IL_0000: ldarg.0
IL_0001: ldfld ""int Test.<F>d__1.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0012
IL_000a: br.s IL_000c
IL_000c: ldloc.0
IL_000d: ldc.i4.1
IL_000e: beq.s IL_0014
IL_0010: br.s IL_0016
IL_0012: br.s IL_0016
IL_0014: br.s IL_0072
IL_0016: call ""System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get""
IL_001b: ldsfld ""System.Func<int> Test.CS$<>9__CachedAnonymousMethodDelegate1""
IL_0020: dup
IL_0021: brtrue.s IL_0036
IL_0023: pop
IL_0024: ldnull
IL_0025: ldftn ""int Test.<F>b__0()""
IL_002b: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_0030: dup
IL_0031: stsfld ""System.Func<int> Test.CS$<>9__CachedAnonymousMethodDelegate1""
IL_0036: callvirt ""System.Threading.Tasks.Task<int> System.Threading.Tasks.TaskFactory.StartNew<int>(System.Func<int>)""
IL_003b: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0040: stloc.3
IL_0041: ldloca.s V_3
IL_0043: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_0048: stloc.s V_5
IL_004a: ldloc.s V_5
IL_004c: brtrue.s IL_0090
IL_004e: ldarg.0
IL_004f: ldc.i4.1
IL_0050: dup
IL_0051: stloc.0
IL_0052: stfld ""int Test.<F>d__1.<>1__state""
IL_0057: ldarg.0
IL_0058: ldloc.3
IL_0059: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter2""
IL_005e: ldarg.0
IL_005f: stloc.s V_6
IL_0061: ldarg.0
IL_0062: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0067: ldloca.s V_3
IL_0069: ldloca.s V_6
IL_006b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<F>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<F>d__1)""
IL_0070: leave.s IL_00d6
IL_0072: ldarg.0
IL_0073: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter2""
IL_0078: stloc.3
IL_0079: ldarg.0
IL_007a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter2""
IL_007f: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0085: ldarg.0
IL_0086: ldc.i4.m1
IL_0087: dup
IL_0088: stloc.0
IL_0089: stfld ""int Test.<F>d__1.<>1__state""
IL_008e: br.s IL_0090
IL_0090: ldloca.s V_3
IL_0092: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_0097: stloc.s V_4
IL_0099: ldloca.s V_3
IL_009b: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00a1: ldloc.s V_4
IL_00a3: stloc.2
IL_00a4: ldloc.2
IL_00a5: stloc.1
IL_00a6: leave.s IL_00c2
}
catch System.Exception
{
IL_00a8: stloc.s V_7
IL_00aa: nop
IL_00ab: ldarg.0
IL_00ac: ldc.i4.s -2
IL_00ae: stfld ""int Test.<F>d__1.<>1__state""
IL_00b3: ldarg.0
IL_00b4: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00b9: ldloc.s V_7
IL_00bb: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00c0: leave.s IL_00d6
}
IL_00c2: ldarg.0
IL_00c3: ldc.i4.s -2
IL_00c5: stfld ""int Test.<F>d__1.<>1__state""
IL_00ca: ldarg.0
IL_00cb: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00d0: ldloc.1
IL_00d1: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00d6: ret
}
");
c.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.SetStateMachine", @"
{
// Code size 1 (0x1)
.maxstack 0
IL_0000: ret
}
");
}
......@@ -6707,11 +6962,11 @@ class C
// CONSIDER: It would be nice if we didn't squiggle the whole method body, but this is a corner case.
comp.VerifyEmitDiagnostics(
// (4,16): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
// (4,16): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M"),
// (5,5): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder' is not defined or imported
// (5,5): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder' is not defined or imported
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"{}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder"),
// (5,5): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException'
// (5,5): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException'
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"{}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder", "SetException"));
}
......
......@@ -378,28 +378,28 @@ static async void Await(dynamic d)
<entry il_offset=""0x7"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x19"" start_row=""5"" start_column=""5"" end_row=""5"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x1a"" start_row=""6"" start_column=""9"" end_row=""6"" end_column=""27"" file_ref=""0"" />
<entry il_offset=""0x230"" start_row=""7"" start_column=""5"" end_row=""7"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x232"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x234"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x24c"" start_row=""7"" start_column=""5"" end_row=""7"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x254"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x235"" start_row=""7"" start_column=""5"" end_row=""7"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x237"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x239"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x251"" start_row=""7"" start_column=""5"" end_row=""7"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x259"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
</sequencepoints>
<locals>
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x261"" attributes=""0"" />
<local name=""rez"" il_index=""1"" il_start=""0x19"" il_end=""0x232"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""8"" il_start=""0x232"" il_end=""0x24c"" attributes=""0"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x266"" attributes=""0"" />
<local name=""rez"" il_index=""1"" il_start=""0x19"" il_end=""0x237"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""9"" il_start=""0x237"" il_end=""0x251"" attributes=""0"" />
</locals>
<scope startOffset=""0x0"" endOffset=""0x261"">
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x261"" attributes=""0"" />
<scope startOffset=""0x19"" endOffset=""0x232"">
<local name=""rez"" il_index=""1"" il_start=""0x19"" il_end=""0x232"" attributes=""0"" />
<scope startOffset=""0x0"" endOffset=""0x266"">
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x266"" attributes=""0"" />
<scope startOffset=""0x19"" endOffset=""0x237"">
<local name=""rez"" il_index=""1"" il_start=""0x19"" il_end=""0x237"" attributes=""0"" />
</scope>
<scope startOffset=""0x232"" endOffset=""0x24c"">
<local name=""&lt;&gt;t__ex"" il_index=""8"" il_start=""0x232"" il_end=""0x24c"" attributes=""0"" />
<scope startOffset=""0x237"" endOffset=""0x251"">
<local name=""&lt;&gt;t__ex"" il_index=""9"" il_start=""0x237"" il_end=""0x251"" attributes=""0"" />
</scope>
</scope>
<async-info kickoff-method=""100663297"" catch-IL-offset=""564"">
<await yield=""340"" resume=""417"" method=""100663299"" />
<async-info kickoff-method=""100663297"" catch-IL-offset=""569"">
<await yield=""340"" resume=""422"" method=""100663300"" />
</async-info>
</method>
</methods>
......@@ -451,7 +451,7 @@ static async Task M(byte b)
<customDebugInfo version=""4"" count=""2"">
<forward version=""4"" kind=""ForwardInfo"" size=""12"" declaringType=""C+&lt;&gt;c__DisplayClass0"" methodName=""&lt;M&gt;b__2"" parameterNames="""" />
<iteratorLocals version=""4"" kind=""IteratorLocals"" size=""20"" bucketCount=""1"">
<bucket startOffset=""0x30"" endOffset=""0x1CF"" />
<bucket startOffset=""0x30"" endOffset=""0x1D8"" />
</iteratorLocals>
</customDebugInfo>
<sequencepoints total=""16"">
......@@ -464,28 +464,28 @@ static async Task M(byte b)
<entry il_offset=""0x54"" start_row=""11"" start_column=""9"" end_row=""11"" end_column=""21"" file_ref=""0"" />
<entry il_offset=""0x60"" start_row=""13"" start_column=""9"" end_row=""13"" end_column=""47"" file_ref=""0"" />
<entry il_offset=""0x77"" start_row=""15"" start_column=""9"" end_row=""15"" end_column=""21"" file_ref=""0"" />
<entry il_offset=""0xea"" start_row=""16"" start_column=""9"" end_row=""16"" end_column=""21"" file_ref=""0"" />
<entry il_offset=""0x15d"" start_row=""17"" start_column=""9"" end_row=""17"" end_column=""21"" file_ref=""0"" />
<entry il_offset=""0x1cd"" start_row=""18"" start_column=""5"" end_row=""18"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x1cf"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x1d1"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x1e8"" start_row=""18"" start_column=""5"" end_row=""18"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x1f0"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0xed"" start_row=""16"" start_column=""9"" end_row=""16"" end_column=""21"" file_ref=""0"" />
<entry il_offset=""0x163"" start_row=""17"" start_column=""9"" end_row=""17"" end_column=""21"" file_ref=""0"" />
<entry il_offset=""0x1d6"" start_row=""18"" start_column=""5"" end_row=""18"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x1d8"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x1db"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x1f3"" start_row=""18"" start_column=""5"" end_row=""18"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x1fb"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
</sequencepoints>
<locals>
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x1fd"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""3"" il_start=""0x1cf"" il_end=""0x1e8"" attributes=""0"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x208"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""4"" il_start=""0x1d8"" il_end=""0x1f3"" attributes=""0"" />
</locals>
<scope startOffset=""0x0"" endOffset=""0x1fd"">
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x1fd"" attributes=""0"" />
<scope startOffset=""0x1cf"" endOffset=""0x1e8"">
<local name=""&lt;&gt;t__ex"" il_index=""3"" il_start=""0x1cf"" il_end=""0x1e8"" attributes=""0"" />
<scope startOffset=""0x0"" endOffset=""0x208"">
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x208"" attributes=""0"" />
<scope startOffset=""0x1d8"" endOffset=""0x1f3"">
<local name=""&lt;&gt;t__ex"" il_index=""4"" il_start=""0x1d8"" il_end=""0x1f3"" attributes=""0"" />
</scope>
</scope>
<async-info kickoff-method=""100663297"">
<await yield=""161"" resume=""188"" method=""100663301"" />
<await yield=""276"" resume=""303"" method=""100663301"" />
<await yield=""391"" resume=""415"" method=""100663301"" />
<await yield=""161"" resume=""191"" method=""100663302"" />
<await yield=""279"" resume=""309"" method=""100663302"" />
<await yield=""397"" resume=""424"" method=""100663302"" />
</async-info>
</method>
</methods>
......@@ -549,28 +549,28 @@ static async Task M(byte b)
<entry il_offset=""0x2b"" start_row=""11"" start_column=""9"" end_row=""11"" end_column=""21"" file_ref=""0"" />
<entry il_offset=""0x32"" start_row=""13"" start_column=""9"" end_row=""13"" end_column=""47"" file_ref=""0"" />
<entry il_offset=""0x44"" start_row=""15"" start_column=""9"" end_row=""15"" end_column=""47"" file_ref=""0"" />
<entry il_offset=""0xb0"" start_row=""16"" start_column=""5"" end_row=""16"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0xb2"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0xb5"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0xcd"" start_row=""16"" start_column=""5"" end_row=""16"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0xd5"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0xb4"" start_row=""16"" start_column=""5"" end_row=""16"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0xb6"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0xb9"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0xd1"" start_row=""16"" start_column=""5"" end_row=""16"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0xd9"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
</sequencepoints>
<locals>
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0xe2"" attributes=""0"" />
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0x16"" il_end=""0xb2"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""4"" il_start=""0xb2"" il_end=""0xcd"" attributes=""0"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0xe6"" attributes=""0"" />
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0x16"" il_end=""0xb6"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""5"" il_start=""0xb6"" il_end=""0xd1"" attributes=""0"" />
</locals>
<scope startOffset=""0x0"" endOffset=""0xe2"">
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0xe2"" attributes=""0"" />
<scope startOffset=""0x16"" endOffset=""0xb2"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0x16"" il_end=""0xb2"" attributes=""0"" />
<scope startOffset=""0x0"" endOffset=""0xe6"">
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0xe6"" attributes=""0"" />
<scope startOffset=""0x16"" endOffset=""0xb6"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0x16"" il_end=""0xb6"" attributes=""0"" />
</scope>
<scope startOffset=""0xb2"" endOffset=""0xcd"">
<local name=""&lt;&gt;t__ex"" il_index=""4"" il_start=""0xb2"" il_end=""0xcd"" attributes=""0"" />
<scope startOffset=""0xb6"" endOffset=""0xd1"">
<local name=""&lt;&gt;t__ex"" il_index=""5"" il_start=""0xb6"" il_end=""0xd1"" attributes=""0"" />
</scope>
</scope>
<async-info kickoff-method=""100663297"">
<await yield=""106"" resume=""130"" method=""100663301"" />
<await yield=""106"" resume=""134"" method=""100663302"" />
</async-info>
</method>
</methods>
......@@ -622,7 +622,7 @@ static async Task M(object o)
<namespace usingCount=""2"" />
</using>
<iteratorLocals version=""4"" kind=""IteratorLocals"" size=""20"" bucketCount=""1"">
<bucket startOffset=""0x19"" endOffset=""0x288"" />
<bucket startOffset=""0x19"" endOffset=""0x28D"" />
</iteratorLocals>
</customDebugInfo>
<sequencepoints total=""11"">
......@@ -631,27 +631,27 @@ static async Task M(object o)
<entry il_offset=""0x19"" start_row=""8"" start_column=""5"" end_row=""8"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x1a"" start_row=""9"" start_column=""9"" end_row=""9"" end_column=""23"" file_ref=""0"" />
<entry il_offset=""0x26"" start_row=""10"" start_column=""9"" end_row=""10"" end_column=""20"" file_ref=""0"" />
<entry il_offset=""0x22e"" start_row=""11"" start_column=""9"" end_row=""11"" end_column=""22"" file_ref=""0"" />
<entry il_offset=""0x286"" start_row=""12"" start_column=""5"" end_row=""12"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x288"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x28b"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x2a3"" start_row=""12"" start_column=""5"" end_row=""12"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x2ab"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x233"" start_row=""11"" start_column=""9"" end_row=""11"" end_column=""22"" file_ref=""0"" />
<entry il_offset=""0x28b"" start_row=""12"" start_column=""5"" end_row=""12"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x28d"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x290"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x2a8"" start_row=""12"" start_column=""5"" end_row=""12"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x2b0"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
</sequencepoints>
<locals>
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x2b8"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""5"" il_start=""0x288"" il_end=""0x2a3"" attributes=""0"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x2bd"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""6"" il_start=""0x28d"" il_end=""0x2a8"" attributes=""0"" />
</locals>
<scope startOffset=""0x0"" endOffset=""0x2b8"">
<scope startOffset=""0x0"" endOffset=""0x2bd"">
<namespace name=""System"" />
<namespace name=""System.Threading.Tasks"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x2b8"" attributes=""0"" />
<scope startOffset=""0x288"" endOffset=""0x2a3"">
<local name=""&lt;&gt;t__ex"" il_index=""5"" il_start=""0x288"" il_end=""0x2a3"" attributes=""0"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x2bd"" attributes=""0"" />
<scope startOffset=""0x28d"" endOffset=""0x2a8"">
<local name=""&lt;&gt;t__ex"" il_index=""6"" il_start=""0x28d"" il_end=""0x2a8"" attributes=""0"" />
</scope>
</scope>
<async-info kickoff-method=""100663297"">
<await yield=""376"" resume=""448"" method=""100663299"" />
<await yield=""376"" resume=""453"" method=""100663300"" />
</async-info>
</method>
</methods>
......@@ -699,30 +699,30 @@ static async Task M(object o)
<entry il_offset=""0x19"" start_row=""8"" start_column=""5"" end_row=""8"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x1a"" start_row=""9"" start_column=""9"" end_row=""9"" end_column=""23"" file_ref=""0"" />
<entry il_offset=""0x21"" start_row=""10"" start_column=""9"" end_row=""10"" end_column=""20"" file_ref=""0"" />
<entry il_offset=""0x227"" start_row=""11"" start_column=""5"" end_row=""11"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x229"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x22c"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x244"" start_row=""11"" start_column=""5"" end_row=""11"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x24c"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x22c"" start_row=""11"" start_column=""5"" end_row=""11"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x22e"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x231"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
<entry il_offset=""0x249"" start_row=""11"" start_column=""5"" end_row=""11"" end_column=""6"" file_ref=""0"" />
<entry il_offset=""0x251"" hidden=""true"" start_row=""16707566"" start_column=""0"" end_row=""16707566"" end_column=""0"" file_ref=""0"" />
</sequencepoints>
<locals>
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x259"" attributes=""0"" />
<local name=""d"" il_index=""1"" il_start=""0x19"" il_end=""0x229"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""6"" il_start=""0x229"" il_end=""0x244"" attributes=""0"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x25e"" attributes=""0"" />
<local name=""d"" il_index=""1"" il_start=""0x19"" il_end=""0x22e"" attributes=""0"" />
<local name=""&lt;&gt;t__ex"" il_index=""7"" il_start=""0x22e"" il_end=""0x249"" attributes=""0"" />
</locals>
<scope startOffset=""0x0"" endOffset=""0x259"">
<scope startOffset=""0x0"" endOffset=""0x25e"">
<namespace name=""System"" />
<namespace name=""System.Threading.Tasks"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x259"" attributes=""0"" />
<scope startOffset=""0x19"" endOffset=""0x229"">
<local name=""d"" il_index=""1"" il_start=""0x19"" il_end=""0x229"" attributes=""0"" />
<local name=""cachedState"" il_index=""0"" il_start=""0x0"" il_end=""0x25e"" attributes=""0"" />
<scope startOffset=""0x19"" endOffset=""0x22e"">
<local name=""d"" il_index=""1"" il_start=""0x19"" il_end=""0x22e"" attributes=""0"" />
</scope>
<scope startOffset=""0x229"" endOffset=""0x244"">
<local name=""&lt;&gt;t__ex"" il_index=""6"" il_start=""0x229"" il_end=""0x244"" attributes=""0"" />
<scope startOffset=""0x22e"" endOffset=""0x249"">
<local name=""&lt;&gt;t__ex"" il_index=""7"" il_start=""0x22e"" il_end=""0x249"" attributes=""0"" />
</scope>
</scope>
<async-info kickoff-method=""100663297"">
<await yield=""366"" resume=""441"" method=""100663299"" />
<await yield=""366"" resume=""446"" method=""100663300"" />
</async-info>
</method>
</methods>
......
......@@ -26,6 +26,7 @@ public void TestFieldsForEqualsAndGetHashCode()
"CryptoKeyFile",
"DebugInformationKind",
"DelaySign",
"EnableEditAndContinue",
"Errors",
"Features",
"FileAlignment",
......
......@@ -376,6 +376,14 @@ internal bool CanReuseCompilationReferenceManager(CompilationOptions other)
&& object.Equals(this.AssemblyIdentityComparer, other.AssemblyIdentityComparer);
}
internal bool EnableEditAndContinue
{
get
{
return DebugInformationKind == DebugInformationKind.Full && !Optimize;
}
}
internal static bool IsValidFileAlignment(int value)
{
switch (value)
......
......@@ -1412,10 +1412,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
If Not options.Optimize Then
debuggingMode = debuggingMode Or DebuggableAttribute.DebuggingModes.DisableOptimizations
End If
If options.DebugInformationKind = DebugInformationKind.Full Then
debuggingMode = debuggingMode Or DebuggableAttribute.DebuggingModes.EnableEditAndContinue
End If
If options.EnableEditAndContinue Then
debuggingMode = debuggingMode Or DebuggableAttribute.DebuggingModes.EnableEditAndContinue
End If
Dim typedConstantDebugMode = New TypedConstant(int32Type, TypedConstantKind.Enum, CInt(debuggingMode))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册