提交 16eee3d6 编写于 作者: T TomasMatousek

Currently the async state machine is a struct. Struct is better for...

 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.

     This change switches the state machine to a class when emitting unoptimized debug code (/debug:full /optimize-). It keeps it struct otherwise. (changeset 1251785)
上级 fea1be1a
......@@ -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,70 @@ 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 var1, LocalSymbol var2 = null)
{
return (var2 == null) ? ImmutableArray.Create(var1) : ImmutableArray.Create(var1, var2);
}
private void MakeThisExpression(out LocalSymbol thisTemp, out BoundExpression @this)
{
if (F.CurrentClass.TypeKind == TypeKind.Class)
{
thisTemp = F.SynthesizedLocal(F.CurrentClass);
@this = F.Local(thisTemp);
}
else
{
thisTemp = null;
@this = F.This();
}
}
public override BoundNode VisitReturnStatement(BoundReturnStatement node)
......@@ -517,4 +564,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,23 +88,28 @@ 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);
F.CloseMethod(
F.Block(
// this.builderField.SetStateMachine(sm)
F.ExpressionStatement(
F.Call(
F.Field(F.This(), builderField),
asyncMethodBuilderMemberCollection.SetStateMachine,
new BoundExpression[] { F.Parameter(F.CurrentMethod.Parameters[0]) })),
F.Return()));
// Constructor
if (stateMachineClass.TypeKind == TypeKind.Class)
{
OpenMethodImplementation(IAsyncStateMachine_SetStateMachine, "SetStateMachine", debuggerHidden: true, hasMethodBodyDependency: false);
F.CloseMethod(
F.Block(
// this.builderField.SetStateMachine(sm)
F.ExpressionStatement(
F.Call(
F.Field(F.This(), builderField),
asyncMethodBuilderMemberCollection.SetStateMachine,
new BoundExpression[] { F.Parameter(F.CurrentMethod.Parameters[0]) })),
F.Return()));
F.CurrentMethod = stateMachineClass.Constructor;
F.CloseMethod(F.Block(ImmutableArray.Create(F.BaseInitialization(), F.Return())));
}
}
......
......@@ -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,70 @@ 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 void F()
{
await Task.Factory.StartNew(() => {});
}
}";
var c = CreateCompilationWithMscorlib45(source);
CompilationOptions options;
options = TestOptions.OptimizedDll;
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);
});
options = TestOptions.UnoptimizedDll;
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);
});
options = TestOptions.OptimizedDll.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);
});
options = TestOptions.UnoptimizedDll.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);
});
options = TestOptions.UnoptimizedDll.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);
});
}
[Fact]
public void VoidReturningAsync()
{
......@@ -6709,21 +6773,19 @@ async void M()
// 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.
// async void M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16),
// (5,5): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder' is not defined or imported
// {
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"{
}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder").WithLocation(5, 5),
// (5,5): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException'
// {
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"{
}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder", "SetException").WithLocation(5, 5)
);
// (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.
// async void M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16),
// (5,5): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder' is not defined or imported
// {
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"{
}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder").WithLocation(5, 5),
// (5,5): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException'
// {
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"{
}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder", "SetException").WithLocation(5, 5));
}
[Fact]
[WorkItem(868822, "DevDiv")]
public void AsyncDelegates()
......
......@@ -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.
先完成此消息的编辑!
想要评论请 注册