提交 2bfa7040 编写于 作者: T TomasMatousek

Hoist all long-lived (user-defined and synthesized) variables to state machine...

Hoist all long-lived (user-defined and synthesized) variables to state machine fields in debug builds.

In order to provide good debugging experience and enable EnC of async and iterator methods we need to lift locals whose lifetime may span state machine suspension points to fields of the state machine.

For example,

IEnumerable<int> Enumerate() { int x = 1; F(x); yield return 1; }

might be changed to

IEnumerable<int> Enumerate() { int x = 1; F(x); yield return 1; G(x); }

during debugging (or G(x) might just be evaluated in EE while the program is broken on a breakpoint following the yield statement) and thus x has to be hoisted upfront even though it’s lifetime doesn’t cross suspension point in the initial compilation.

In addition to lifting the locals we also need to generate an EnC map for them (syntax offset + debug id + type -> slot index), so that we can reuse the state of existing locals during EnC.

The implementation is similar to unhoisted local variable mapping and exploits an existing notion of “slots” used by the EE to encode local scopes of hoisted variables.
All hoisted variables are assigned slot indices in a syntax order (just to make it deterministic, the order doesn’t matter). The variables are hoisted to fields with names <>5__N (user-defined – this name pattern is already recognized by both Dev12 and Roslyn EEs) and <>s__N (synthesized). N is a slot number. For these locals we emit EnC custom debug information in the same format as we do for unhoisted locals. We attach this CDI to the iterator/async method.
 (changeset 1364072)
上级 8df4a2ab
......@@ -542,20 +542,15 @@
<Field Name="Locals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
</Node>
<!--
BoundIteratorScope represents a scope within a translated iterator method, in which
some local variables have been moved to fields of the class that implements the iterator.
The fields have names of the form "<localName>x_i" where x is the name of the local
variable and i is a unique index assigned to these hoisted variables, assigned sequentially
starting at 0. Caveat: when lambda locals (i.e. locals holding onto lambda display class instances)
are hoisted, they retain their original names of the form CS$<>8__localsi.
BoundIteratorScope represents a scope within a translated iterator method.
It is used to emit debugging information that allows the EE to map
fields to locals.
-->
<Node Name="BoundIteratorScope" Base="BoundStatement">
<Field Name="Fields" Type="ImmutableArray&lt;SynthesizedFieldSymbolBase&gt;"/>
<Field Name="Fields" Type="ImmutableArray&lt;StateMachineFieldSymbol&gt;"/>
<Field Name="Statement" Type="BoundStatement" Null="disallow"/>
</Node>
<!--
Bound node that represents a single local declaration:
......
......@@ -38,9 +38,7 @@ internal sealed partial class CodeGenerator
// not 0 when in a protected region with a handler.
private int tryNestingLevel = 0;
// Dispenser of unique ordinals for synthesized variable names that have the same kind and syntax offset.
// The key is (local.SyntaxOffset << 8) | local.SynthesizedKind.
private PooledDictionary<long, int> synthesizedLocalOrdinals;
private readonly SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals = new SynthesizedLocalOrdinalsDispenser();
private int uniqueNameId;
// label used when when return is emitted in a form of store/goto
......@@ -211,11 +209,7 @@ private void GenerateImpl()
builder.Realize();
}
if (this.synthesizedLocalOrdinals != null)
{
this.synthesizedLocalOrdinals.Free();
this.synthesizedLocalOrdinals = null;
}
this.synthesizedLocalOrdinals.Free();
}
private void HandleReturn()
......
......@@ -603,9 +603,7 @@ private void EmitIteratorScope(BoundIteratorScope scope)
builder.OpenIteratorScope();
foreach (var field in scope.Fields)
{
int index = field.UserDefinedHoistedLocalId;
Debug.Assert(index >= 1);
builder.DefineIteratorLocal(index);
builder.DefineUserDefinedStateMachineHoistedLocal(field.HoistedLocalSlotIndex);
}
EmitStatement(scope.Statement);
......@@ -1401,7 +1399,7 @@ private string GetLocalDebugName(ILocalSymbolInternal local, out LocalDebugId lo
var syntax = local.GetDeclaratorSyntax();
int syntaxOffset = this.method.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree);
int ordinal = AssignLocalOrdinal(localKind, syntaxOffset);
int ordinal = synthesizedLocalOrdinals.AssignLocalOrdinal(localKind, syntaxOffset);
// user-defined locals should have 0 ordinal:
Debug.Assert(ordinal == 0 || localKind != SynthesizedLocalKind.UserDefined);
......@@ -1412,36 +1410,6 @@ private string GetLocalDebugName(ILocalSymbolInternal local, out LocalDebugId lo
return local.Name ?? GeneratedNames.MakeSynthesizedLocalName(localKind, ref uniqueNameId);
}
private int AssignLocalOrdinal(SynthesizedLocalKind localKind, int syntaxOffset)
{
#if !DEBUG
// Optimization (avoid growing the dictionary below):
// User-defined locals have to have a distinct syntax offset, thus ordinal is always 0.
if (localKind == SynthesizedLocalKind.UserDefined)
{
return 0;
}
#endif
int ordinal;
long key = (long)syntaxOffset << 8 | (long)localKind;
// Group by syntax offset and kind.
// Variables associated with the same syntax and kind will be assigned different ordinals.
if (synthesizedLocalOrdinals == null)
{
synthesizedLocalOrdinals = PooledDictionary<long, int>.GetInstance();
ordinal = 0;
}
else if (!synthesizedLocalOrdinals.TryGetValue(key, out ordinal))
{
ordinal = 0;
}
synthesizedLocalOrdinals[key] = ordinal + 1;
Debug.Assert(ordinal == 0 || localKind != SynthesizedLocalKind.UserDefined);
return ordinal;
}
private bool IsSlotReusable(LocalSymbol local)
{
return local.SynthesizedKind.IsSlotReusable(this.optimizations);
......
......@@ -1176,7 +1176,7 @@ private BoundStatement ChainImplicitStructConstructor(MethodSymbol methodSymbol,
private static MethodBody GenerateMethodBody(
PEModuleBuilder moduleBuilder,
MethodSymbol method,
BoundStatement block,
BoundStatement block,
StateMachineTypeSymbol stateMachineTypeOpt,
VariableSlotAllocator variableSlotAllocatorOpt,
DiagnosticBag diagnostics,
......@@ -1196,11 +1196,14 @@ private BoundStatement ChainImplicitStructConstructor(MethodSymbol methodSymbol,
try
{
Cci.AsyncMethodBodyDebugInfo asyncDebugInfo = null;
var codeGen = new CodeGen.CodeGenerator(method, block, builder, moduleBuilder, diagnosticsForThisMethod, optimizations, emittingPdbs);
// We need to save additional debugging information for MoveNext of an async state machine.
var stateMachineMethod = method as SynthesizedStateMachineMethod;
if (stateMachineMethod != null && stateMachineMethod.StateMachineType.KickoffMethod.IsAsync && method.Name == WellKnownMemberNames.MoveNextMethodName)
bool isStateMachineMoveNextMethod = stateMachineMethod != null && method.Name == WellKnownMemberNames.MoveNextMethodName;
if (isStateMachineMoveNextMethod && stateMachineMethod.StateMachineType.KickoffMethod.IsAsync)
{
int asyncCatchHandlerOffset;
ImmutableArray<int> asyncYieldPoints;
......@@ -1234,12 +1237,19 @@ private BoundStatement ChainImplicitStructConstructor(MethodSymbol methodSymbol,
}
// Only compiler-generated MoveNext methods have iterator scopes. See if this is one.
bool hasIteratorScopes =
method.Locations.IsEmpty && method.Name == WellKnownMemberNames.MoveNextMethodName &&
(method.ExplicitInterfaceImplementations.Contains(compilation.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext) as MethodSymbol) ||
method.ExplicitInterfaceImplementations.Contains(compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_MoveNext) as MethodSymbol));
var stateMachineHoistedLocalScopes = default(ImmutableArray<Cci.StateMachineHoistedLocalScope>);
if (isStateMachineMoveNextMethod)
{
stateMachineHoistedLocalScopes = builder.GetHoistedLocalScopes();
}
var stateMachineHoistedLocalSlots = default(ImmutableArray<LocalSlotDebugInfo>);
if (optimizations == OptimizationLevel.Debug && stateMachineTypeOpt != null)
{
Debug.Assert(method.IsAsync || method.IsIterator);
var iteratorScopes = hasIteratorScopes ? builder.GetIteratorScopes() : ImmutableArray<Cci.LocalScope>.Empty;
stateMachineHoistedLocalSlots = GetStateMachineSlotDebugInfo(moduleBuilder.GetSynthesizedFields(stateMachineTypeOpt));
}
return new MethodBody(
builder.RealizedIL,
......@@ -1254,7 +1264,8 @@ private BoundStatement ChainImplicitStructConstructor(MethodSymbol methodSymbol,
namespaceScopes,
Cci.NamespaceScopeEncoding.InPlace,
(stateMachineTypeOpt != null) ? stateMachineTypeOpt.Name : null,
iteratorScopes,
stateMachineHoistedLocalScopes,
stateMachineHoistedLocalSlots,
asyncDebugInfo);
}
finally
......@@ -1269,6 +1280,30 @@ private BoundStatement ChainImplicitStructConstructor(MethodSymbol methodSymbol,
}
}
private static ImmutableArray<LocalSlotDebugInfo> GetStateMachineSlotDebugInfo(IEnumerable<Cci.IFieldDefinition> fieldDefs)
{
var slots = ArrayBuilder<LocalSlotDebugInfo>.GetInstance();
foreach (StateMachineFieldSymbol field in fieldDefs)
{
int index = field.HoistedLocalSlotIndex;
if (index < 0)
{
continue;
}
while (index >= slots.Count)
{
// Empty slots may be present if variables were deleted during EnC.
slots.Add(new LocalSlotDebugInfo(SynthesizedLocalKind.EmitterTemp, LocalDebugId.None));
}
Debug.Assert(!field.SlotDebugInfo.Id.IsNone);
slots[index] = field.SlotDebugInfo;
}
return slots.ToImmutableAndFree();
}
// NOTE: can return null if the method has no body.
internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics)
{
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
......@@ -173,6 +174,9 @@ internal override VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBasel
CSharpSymbolMatcher symbolMap;
ImmutableArray<EncLocalInfo> previousLocals;
IReadOnlyDictionary<EncLocalInfo, string> previousHoistedLocalMap;
IReadOnlyDictionary<Cci.ITypeReference, string> awaiterMap;
int hoistedLocalSlotCount;
uint methodIndex = (uint)MetadataTokens.GetRowNumber(handle);
......@@ -180,27 +184,116 @@ internal override VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBasel
if (baseline.LocalsForMethodsAddedOrChanged.TryGetValue(methodIndex, out previousLocals))
{
symbolMap = this.mapToPrevious;
// TODO:
previousHoistedLocalMap = null;
awaiterMap = null;
hoistedLocalSlotCount = 0;
}
else
{
// Method has not changed since initial generation. Generate a map
// using the local names provided with the initial metadata.
ImmutableArray<MetadataDecoder.LocalInfo> slotMetadata;
if (!metadataDecoder.TryGetLocals(handle, out slotMetadata))
var debugInfo = baseline.DebugInformationProvider(handle);
TypeSymbol stateMachineType = TryGetStateMachineType(handle);
if (stateMachineType != null)
{
// TODO: Report error that metadata is not supported.
return null;
var localSlotDebugInfo = debugInfo.LocalSlots.NullToEmpty();
// method is async/iterator kickoff method
GetStateMachineFieldMap(stateMachineType, localSlotDebugInfo, out previousHoistedLocalMap, out awaiterMap);
// Kickoff method has no interesting locals on its own.
// We use the EnC method debug infromation for hoisted locals.
previousLocals = ImmutableArray<EncLocalInfo>.Empty;
hoistedLocalSlotCount = localSlotDebugInfo.Length;
}
else
{
ImmutableArray<MetadataDecoder.LocalInfo> slotMetadata;
if (!metadataDecoder.TryGetLocals(handle, out slotMetadata))
{
// TODO: Report error that metadata is not supported.
return null;
}
var debugInfo = baseline.DebugInformationProvider(handle);
previousLocals = CreateLocalSlotMap(debugInfo, slotMetadata);
Debug.Assert(previousLocals.Length == slotMetadata.Length);
previousHoistedLocalMap = null;
awaiterMap = null;
hoistedLocalSlotCount = 0;
}
previousLocals = CreateLocalSlotMap(debugInfo, slotMetadata);
Debug.Assert(previousLocals.Length == slotMetadata.Length);
symbolMap = this.mapToMetadata;
}
return new EncVariableSlotAllocator(symbolMap, methodEntry.SyntaxMap, methodEntry.PreviousMethod, previousLocals);
return new EncVariableSlotAllocator(symbolMap, methodEntry.SyntaxMap, methodEntry.PreviousMethod, previousLocals, hoistedLocalSlotCount, previousHoistedLocalMap, awaiterMap);
}
private void GetStateMachineFieldMap(
TypeSymbol stateMachineType,
ImmutableArray<LocalSlotDebugInfo> localSlotDebugInfo,
out IReadOnlyDictionary<EncLocalInfo, string> hoistedLocalMap,
out IReadOnlyDictionary<Cci.ITypeReference, string> awaiterMap)
{
var hoistedLocals = new Dictionary<EncLocalInfo, string>();
var awaiters = new Dictionary<Cci.ITypeReference, string>();
foreach (var member in stateMachineType.GetMembers())
{
if (member.Kind == SymbolKind.Field)
{
string name = member.Name;
int slotIndex;
if (GeneratedNames.TryParseHoistedLocalSlotIndex(name, out slotIndex))
{
var field = (FieldSymbol)member;
if (slotIndex >= localSlotDebugInfo.Length)
{
// invalid metadata
continue;
}
var key = new EncLocalInfo(
localSlotDebugInfo[slotIndex].Id,
(Cci.ITypeReference)field.Type,
LocalSlotConstraints.None,
localSlotDebugInfo[slotIndex].SynthesizedKind,
signature: null);
// correct metadata won't contain duplicate ids, but malformed might, ignore the duplicate:
hoistedLocals[key] = name;
}
if (GeneratedNames.IsAsyncAwaiterFieldName(name))
{
var field = (FieldSymbol)member;
// correct metadata won't contain duplicates, but malformed might, ignore the duplicate:
awaiters[(Cci.ITypeReference)field.Type] = field.Name;
}
}
}
hoistedLocalMap = hoistedLocals;
awaiterMap = awaiters;
}
private TypeSymbol TryGetStateMachineType(Handle methodHandle)
{
string typeName;
if (metadataDecoder.Module.HasStringValuedAttribute(methodHandle, AttributeDescription.AsyncStateMachineAttribute, out typeName) ||
metadataDecoder.Module.HasStringValuedAttribute(methodHandle, AttributeDescription.IteratorStateMachineAttribute, out typeName))
{
return metadataDecoder.GetTypeSymbolForSerializedType(typeName);
}
return null;
}
/// <summary>
......@@ -213,7 +306,7 @@ internal override VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBasel
ImmutableArray<MetadataDecoder.LocalInfo> slotMetadata)
{
var result = new EncLocalInfo[slotMetadata.Length];
var localSlots = methodEncInfo.LocalSlots;
if (!localSlots.IsDefault)
{
......@@ -225,8 +318,8 @@ internal override VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBasel
for (int slotIndex = 0; slotIndex < slotCount; slotIndex++)
{
ValueTuple<SynthesizedLocalKind, LocalDebugId> slot = localSlots[slotIndex];
if (slot.Item1.IsLongLived())
var slot = localSlots[slotIndex];
if (slot.SynthesizedKind.IsLongLived())
{
var metadata = slotMetadata[slotIndex];
......@@ -234,7 +327,7 @@ internal override VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBasel
// previous version of the local if it had custom modifiers.
if (metadata.CustomModifiers.IsDefaultOrEmpty)
{
var local = new EncLocalInfo(slot.Item2, (Cci.ITypeReference)metadata.Type, metadata.Constraints, slot.Item1, metadata.SignatureOpt);
var local = new EncLocalInfo(slot.Id, (Cci.ITypeReference)metadata.Type, metadata.Constraints, slot.SynthesizedKind, metadata.SignatureOpt);
map.Add(local, slotIndex);
}
}
......
......@@ -256,13 +256,11 @@ protected bool IsInside
}
}
[DebuggerHidden]
public override BoundNode Visit(BoundNode node)
{
return VisitAlways(node);
}
[DebuggerHidden]
protected BoundNode VisitAlways(BoundNode node)
{
BoundNode result = null;
......
......@@ -5,8 +5,10 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
......@@ -50,7 +52,7 @@ internal sealed class AsyncMethodToStateMachineRewriter : MethodToStateMachineRe
private readonly LoweredDynamicOperationFactory dynamicFactory;
private readonly Dictionary<TypeSymbol, FieldSymbol> awaiterFields = new Dictionary<TypeSymbol, FieldSymbol>();
private readonly Dictionary<TypeSymbol, FieldSymbol> awaiterFields;
internal AsyncMethodToStateMachineRewriter(
MethodSymbol method,
......@@ -58,10 +60,13 @@ internal sealed class AsyncMethodToStateMachineRewriter : MethodToStateMachineRe
SyntheticBoundNodeFactory F,
FieldSymbol state,
FieldSymbol builder,
IReadOnlySet<Symbol> variablesCaptured,
IReadOnlySet<Symbol> hoistedVariables,
IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies,
SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals,
VariableSlotAllocator slotAllocatorOpt,
int nextFreeHoistedLocalSlot,
DiagnosticBag diagnostics)
: base(F, method, state, variablesCaptured, nonReusableLocalProxies, diagnostics, useFinalizerBookkeeping: false)
: base(F, method, state, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics, useFinalizerBookkeeping: false)
{
this.method = method;
this.asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection;
......@@ -74,14 +79,27 @@ internal sealed class AsyncMethodToStateMachineRewriter : MethodToStateMachineRe
: null;
this.dynamicFactory = new LoweredDynamicOperationFactory(F);
this.awaiterFields = new Dictionary<TypeSymbol, FieldSymbol>(TypeSymbol.EqualsIgnoringDynamicComparer);
}
private FieldSymbol GetAwaiterField(TypeSymbol awaiterType)
{
FieldSymbol result;
// Awaiters of the same type always share the same slot, regardless of what await expressions they belong to.
// Even in case of nested await expressions only one awaiter is active.
// So we don't need to tie the awaiter variable to a particular await expression and only use its type
// to find the previous awaiter field.
if (!awaiterFields.TryGetValue(awaiterType, out result))
{
result = F.StateMachineField(awaiterType, GeneratedNames.AsyncAwaiterFieldName(CompilationState.GenerateTempNumber()), isPublic: true);
string fieldName = slotAllocatorOpt?.GetPreviousAwaiter((Cci.ITypeReference)awaiterType);
if (fieldName == null)
{
fieldName = GeneratedNames.AsyncAwaiterFieldName(CompilationState.GenerateTempNumber());
}
result = F.StateMachineField(awaiterType, fieldName);
awaiterFields.Add(awaiterType, result);
}
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
......@@ -74,9 +75,10 @@ protected override bool PreserveInitialParameterValues
protected override void GenerateControlFields()
{
base.GenerateControlFields();
// the fields are initialized from async method, so they need to be public:
builderField = F.StateMachineField(asyncMethodBuilderMemberCollection.BuilderType, GeneratedNames.AsyncBuilderFieldName(), isPublic: true);
this.stateField = F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeStateMachineStateName(), isPublic: true);
this.builderField = F.StateMachineField(asyncMethodBuilderMemberCollection.BuilderType, GeneratedNames.AsyncBuilderFieldName(), isPublic: true);
}
protected override void GenerateMethodImplementations()
......@@ -132,11 +134,6 @@ protected override void GenerateMethodImplementations()
}
}
protected override bool IsStateFieldPublic
{
get { return true; }
}
protected override void InitializeStateMachine(ArrayBuilder<BoundStatement> bodyBuilder, NamedTypeSymbol frameType, LocalSymbol stateMachineLocal)
{
if (frameType.TypeKind == TypeKind.Class)
......@@ -149,7 +146,7 @@ protected override void InitializeStateMachine(ArrayBuilder<BoundStatement> body
}
}
protected override BoundStatement GenerateReplacementBody(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType)
protected override BoundStatement GenerateStateMachineCreation(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType)
{
try
{
......@@ -215,8 +212,11 @@ private void GenerateMoveNext(SynthesizedImplementationMethod moveNextMethod)
F: F,
state: stateField,
builder: builderField,
variablesCaptured: variablesCaptured,
hoistedVariables: hoistedVariables,
nonReusableLocalProxies: nonReusableLocalProxies,
synthesizedLocalOrdinals: synthesizedLocalOrdinals,
slotAllocatorOpt: slotAllocatorOpt,
nextFreeHoistedLocalSlot: nextFreeHoistedLocalSlot,
diagnostics: diagnostics);
rewriter.GenerateMoveNext(body, moveNextMethod);
......
......@@ -7,6 +7,8 @@
using System.Diagnostics;
using System.Linq;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Symbols;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -53,10 +55,13 @@ internal sealed partial class IteratorMethodToStateMachineRewriter : MethodToSta
MethodSymbol originalMethod,
FieldSymbol state,
FieldSymbol current,
IReadOnlySet<Symbol> variablesCaptured,
IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> initialProxies,
IReadOnlySet<Symbol> hoistedVariables,
IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies,
SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals,
VariableSlotAllocator slotAllocatorOpt,
int nextFreeHoistedLocalSlot,
DiagnosticBag diagnostics)
: base(F, originalMethod, state, variablesCaptured, initialProxies, diagnostics, useFinalizerBookkeeping: false)
: base(F, originalMethod, state, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics, useFinalizerBookkeeping: false)
{
this.current = current;
}
......
......@@ -83,10 +83,10 @@ protected override bool PreserveInitialParameterValues
protected override void GenerateControlFields()
{
base.GenerateControlFields();
this.stateField = F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeStateMachineStateName());
// Add a field: T current
currentField = F.StateMachineField(elementType, GeneratedNames.MakeIteratorCurrentBackingFieldName(), isPublic: false);
currentField = F.StateMachineField(elementType, GeneratedNames.MakeIteratorCurrentBackingFieldName());
// if it is an enumerable, and either Environment.CurrentManagedThreadId or System.Thread are available
// add a field: int initialThreadId
......@@ -96,7 +96,7 @@ protected override void GenerateControlFields()
(object)F.WellKnownMember(WellKnownMember.System_Environment__CurrentManagedThreadId, isOptional: true) != null);
initialThreadIdField = addInitialThreadId
? F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeIteratorCurrentThreadIdName(), isPublic: false)
? F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeIteratorCurrentThreadIdName())
: null;
}
......@@ -299,11 +299,6 @@ private void GenerateConstructor(BoundExpression managedThreadId)
bodyBuilder = null;
}
protected override bool IsStateFieldPublic
{
get { return false; }
}
protected override void InitializeStateMachine(ArrayBuilder<BoundStatement> bodyBuilder, NamedTypeSymbol frameType, LocalSymbol stateMachineLocal)
{
// var stateMachineLocal = new IteratorImplementationClass(N)
......@@ -315,7 +310,7 @@ protected override void InitializeStateMachine(ArrayBuilder<BoundStatement> body
F.New(stateMachineType.Constructor.AsMember(frameType), F.Literal(initialState))));
}
protected override BoundStatement GenerateReplacementBody(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType)
protected override BoundStatement GenerateStateMachineCreation(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType)
{
return F.Return(F.Local(stateMachineVariable));
}
......@@ -329,8 +324,11 @@ protected override BoundStatement GenerateReplacementBody(LocalSymbol stateMachi
method,
stateField,
currentField,
variablesCaptured,
hoistedVariables,
nonReusableLocalProxies,
synthesizedLocalOrdinals,
slotAllocatorOpt,
nextFreeHoistedLocalSlot,
diagnostics);
rewriter.GenerateMoveNextAndDispose(body, moveNextMethod, disposeMethod);
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
......@@ -56,12 +57,12 @@ private static string GetCapturedVariableFieldName(Symbol variable, ref int uniq
{
if (local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass)
{
return GeneratedNames.MakeLambdaDisplayClassStorageName(uniqueId++);
return GeneratedNames.MakeLambdaDisplayLocalName(uniqueId++);
}
if (local.SynthesizedKind == SynthesizedLocalKind.ExceptionFilterAwaitHoistedExceptionLocal)
{
return GeneratedNames.MakeHoistedLocalFieldName(string.Empty, uniqueId++);
return GeneratedNames.MakeHoistedLocalFieldName(local.SynthesizedKind, uniqueId++);
}
}
......@@ -85,11 +86,6 @@ private static TypeSymbol GetCapturedVariableFieldType(SynthesizedContainer fram
return frame.TypeMap.SubstituteType((object)local != null ? local.Type : ((ParameterSymbol)variable).Type);
}
internal override int UserDefinedHoistedLocalId
{
get { throw ExceptionUtilities.Unreachable; }
}
internal override TypeSymbol GetFieldType(ConsList<FieldSymbol> fieldsBeingBound)
{
return this.type;
......
......@@ -24,9 +24,27 @@ public CapturedSymbolReplacement(bool isReusable)
internal sealed class CapturedToFrameSymbolReplacement : CapturedSymbolReplacement
{
public readonly SynthesizedFieldSymbolBase HoistedField;
public readonly LambdaCapturedVariable HoistedField;
public CapturedToFrameSymbolReplacement(SynthesizedFieldSymbolBase hoistedField, bool isReusable)
public CapturedToFrameSymbolReplacement(LambdaCapturedVariable hoistedField, bool isReusable)
: base(isReusable)
{
this.HoistedField = hoistedField;
}
public override BoundExpression Replacement(CSharpSyntaxNode node, Func<NamedTypeSymbol, BoundExpression> makeFrame)
{
var frame = makeFrame(this.HoistedField.ContainingType);
var field = this.HoistedField.AsMember((NamedTypeSymbol)frame.Type);
return new BoundFieldAccess(node, frame, field, default(ConstantValue));
}
}
internal sealed class CapturedToStateMachineFieldReplacement : CapturedSymbolReplacement
{
public readonly StateMachineFieldSymbol HoistedField;
public CapturedToStateMachineFieldReplacement(StateMachineFieldSymbol hoistedField, bool isReusable)
: base(isReusable)
{
this.HoistedField = hoistedField;
......@@ -43,10 +61,10 @@ public override BoundExpression Replacement(CSharpSyntaxNode node, Func<NamedTyp
internal sealed class CapturedToExpressionSymbolReplacement : CapturedSymbolReplacement
{
private readonly BoundExpression replacement;
public readonly ImmutableArray<SynthesizedFieldSymbolBase> HoistedFields;
public readonly ImmutableArray<StateMachineFieldSymbol> HoistedFields;
public CapturedToExpressionSymbolReplacement(BoundExpression replacement, ImmutableArray<SynthesizedFieldSymbolBase> hoistedFields)
: base(isReusable: false)
public CapturedToExpressionSymbolReplacement(BoundExpression replacement, ImmutableArray<StateMachineFieldSymbol> hoistedFields, bool isReusable)
: base(isReusable)
{
this.replacement = replacement;
this.HoistedFields = hoistedFields;
......
......@@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
......@@ -22,10 +23,15 @@ namespace Microsoft.CodeAnalysis.CSharp
/// </remarks>
internal sealed class IteratorAndAsyncCaptureWalker : DataFlowPass
{
// The analyzer collects captured variables and their usages. The syntax locations are used to report errors.
private readonly MultiDictionary<Symbol, CSharpSyntaxNode> variablesCaptured = new MultiDictionary<Symbol, CSharpSyntaxNode>();
// In Release builds we hoist only variables (locals and parameters) that are captured.
// This set will contain such variables after the bound tree is visited.
private readonly OrderedSet<Symbol> variablesToHoist;
private bool seenYieldInCurrentTry = false;
// Contains variables that are captured but can't be hoisted since their type can't be allocated on heap.
// The value is a list of all uses of each such variable.
private MultiDictionary<Symbol, CSharpSyntaxNode> lazyDisallowedCaptures;
private bool seenYieldInCurrentTry;
private IteratorAndAsyncCaptureWalker(CSharpCompilation compilation, MethodSymbol method, BoundNode node, NeverEmptyStructTypeCache emptyStructCache, HashSet<Symbol> initiallyAssignedVariables)
: base(compilation,
......@@ -35,9 +41,11 @@ private IteratorAndAsyncCaptureWalker(CSharpCompilation compilation, MethodSymbo
trackUnassignments: true,
initiallyAssignedVariables: initiallyAssignedVariables)
{
this.variablesToHoist = new OrderedSet<Symbol>();
}
public static MultiDictionary<Symbol, CSharpSyntaxNode> Analyze(CSharpCompilation compilation, MethodSymbol method, BoundNode node)
// Returns deterministically ordered list of variables that ought to be hoisted.
public static OrderedSet<Symbol> Analyze(CSharpCompilation compilation, MethodSymbol method, BoundNode node, DiagnosticBag diagnostics)
{
var initiallyAssignedVariables = UnassignedVariablesWalker.Analyze(compilation, method, node);
var walker = new IteratorAndAsyncCaptureWalker(compilation, method, node, new NeverEmptyStructTypeCache(), initiallyAssignedVariables);
......@@ -45,17 +53,89 @@ private IteratorAndAsyncCaptureWalker(CSharpCompilation compilation, MethodSymbo
walker.Analyze(ref badRegion);
Debug.Assert(!badRegion);
var result = walker.variablesCaptured;
if (!method.IsStatic && method.ContainingType.TypeKind == TypeKind.Struct)
{
// It is possible that the enclosing method only *writes* to the enclosing struct, but in that
// case it should be considered captured anyway so that we have a proxy for it to write to.
result.Add(method.ThisParameter, node.Syntax);
walker.CaptureVariable(method.ThisParameter, node.Syntax);
}
var variablesToHoist = walker.variablesToHoist;
var lazyDisallowedCaptures = walker.lazyDisallowedCaptures;
var allVariables = walker.variableBySlot;
walker.Free();
return result;
if (lazyDisallowedCaptures != null)
{
foreach (var variable in lazyDisallowedCaptures.Keys)
{
var type = (variable.Kind == SymbolKind.Local) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type;
foreach (CSharpSyntaxNode syntax in lazyDisallowedCaptures[variable])
{
// CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method
diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, type);
}
}
}
if (compilation.Options.OptimizationLevel != OptimizationLevel.Release)
{
Debug.Assert(variablesToHoist.Count == 0);
// In debug build we hoist all locals and parameters:
variablesToHoist.AddRange(from v in allVariables
where v.Symbol != null && HoistInDebugBuild(v.Symbol)
select v.Symbol);
}
return variablesToHoist;
}
private static int GetVariableSourcePosition(Symbol symbol)
{
if (symbol.Locations.IsEmpty)
{
// All user-defined locals and long-lived synthesized variables have to have a position.
Debug.Assert(symbol is SynthesizedParameterSymbol || !((LocalSymbol)symbol).SynthesizedKind.IsLongLived());
// Short-lived locals don't get hoisted to fields, so their order doesn't matter.
return -1;
}
return symbol.Locations[0].SourceSpan.Start;
}
private static bool HoistInDebugBuild(Symbol symbol)
{
// in Debug build hoist all parameters that can be hoisted:
if (symbol.Kind == SymbolKind.Parameter)
{
var parameter = (ParameterSymbol)symbol;
return !parameter.Type.IsRestrictedType();
}
if (symbol.Kind == SymbolKind.Local)
{
LocalSymbol local = (LocalSymbol)symbol;
if (local.IsConst)
{
return false;
}
// hoist all user-defined locals that can be hoisted:
if (local.SynthesizedKind == SynthesizedLocalKind.UserDefined)
{
return !local.Type.IsRestrictedType();
}
// hoist all synthesized variables that have to survive state machine suspension:
return local.SynthesizedKind.MustSurviveStateMachineSuspension();
}
return false;
}
private void MarkLocalsUnassigned()
......@@ -95,10 +175,40 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no
protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
{
variablesCaptured.Clear();
variablesToHoist.Clear();
if (lazyDisallowedCaptures != null)
{
lazyDisallowedCaptures.Clear();
}
return base.Scan(ref badRegion);
}
private void CaptureVariable(Symbol variable, CSharpSyntaxNode syntax)
{
var type = (variable.Kind == SymbolKind.Local) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type;
if (type.IsRestrictedType())
{
// error has already been reported:
if (variable is SynthesizedLocal)
{
return;
}
if (lazyDisallowedCaptures == null)
{
lazyDisallowedCaptures = new MultiDictionary<Symbol, CSharpSyntaxNode>();
}
lazyDisallowedCaptures.Add(variable, syntax);
}
else if (compilation.Options.OptimizationLevel == OptimizationLevel.Release)
{
variablesToHoist.Add(variable);
}
}
protected override void EnterParameter(ParameterSymbol parameter)
{
// parameters are NOT intitially assigned here - if that is a problem, then
......@@ -110,7 +220,7 @@ protected override void ReportUnassigned(Symbol symbol, CSharpSyntaxNode node)
{
if (symbol is LocalSymbol || symbol is ParameterSymbol)
{
variablesCaptured.Add(symbol, node);
CaptureVariable(symbol, node);
}
}
......@@ -123,7 +233,7 @@ protected override LocalState UnreachableState()
protected override void ReportUnassigned(FieldSymbol fieldSymbol, int unassignedSlot, CSharpSyntaxNode node)
{
variablesCaptured.Add(GetNonFieldSymbol(unassignedSlot), node);
CaptureVariable(GetNonFieldSymbol(unassignedSlot), node);
}
protected override void VisitLvalueParameter(BoundParameter node)
......@@ -142,7 +252,7 @@ private void TryHoistTopLevelParameter(BoundParameter node)
{
if (node.ParameterSymbol.ContainingSymbol == topLevelMethod)
{
variablesCaptured.Add(node.ParameterSymbol, node.Syntax);
CaptureVariable(node.ParameterSymbol, node.Syntax);
}
}
......@@ -151,7 +261,7 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
if (node.ReceiverOpt != null && node.ReceiverOpt.Kind == BoundKind.ThisReference)
{
var thisSymbol = topLevelMethod.ThisParameter;
variablesCaptured.Add(thisSymbol, node.Syntax);
CaptureVariable(thisSymbol, node.Syntax);
}
return base.VisitFieldAccess(node);
......@@ -159,13 +269,13 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
public override BoundNode VisitThisReference(BoundThisReference node)
{
variablesCaptured.Add(topLevelMethod.ThisParameter, node.Syntax);
CaptureVariable(topLevelMethod.ThisParameter, node.Syntax);
return base.VisitThisReference(node);
}
public override BoundNode VisitBaseReference(BoundBaseReference node)
{
variablesCaptured.Add(topLevelMethod.ThisParameter, node.Syntax);
CaptureVariable(topLevelMethod.ThisParameter, node.Syntax);
return base.VisitBaseReference(node);
}
......@@ -184,7 +294,7 @@ protected override void VisitFinallyBlock(BoundStatement finallyBlock, ref Local
{
// Locals cannot be used to communicate between the finally block and the rest of the method.
// So we just capture any outside variables that are used inside.
new OutsideVariablesUsedInside(variablesCaptured, this.topLevelMethod).Visit(finallyBlock);
new OutsideVariablesUsedInside(this, this.topLevelMethod).Visit(finallyBlock);
}
base.VisitFinallyBlock(finallyBlock, ref unsetInFinally);
......@@ -192,14 +302,15 @@ protected override void VisitFinallyBlock(BoundStatement finallyBlock, ref Local
private sealed class OutsideVariablesUsedInside : BoundTreeWalker
{
private HashSet<Symbol> localsInScope = new HashSet<Symbol>();
private readonly MultiDictionary<Symbol, CSharpSyntaxNode> variablesCaptured;
private readonly HashSet<Symbol> localsInScope;
private readonly IteratorAndAsyncCaptureWalker analyzer;
private readonly MethodSymbol topLevelMethod;
public OutsideVariablesUsedInside(MultiDictionary<Symbol, CSharpSyntaxNode> variablesCaptured, MethodSymbol topLevelMethod)
public OutsideVariablesUsedInside(IteratorAndAsyncCaptureWalker analyzer, MethodSymbol topLevelMethod)
{
this.variablesCaptured = variablesCaptured;
this.analyzer = analyzer;
this.topLevelMethod = topLevelMethod;
this.localsInScope = new HashSet<Symbol>();
}
public override BoundNode VisitBlock(BoundBlock node)
......@@ -261,7 +372,7 @@ private void Capture(Symbol s, CSharpSyntaxNode syntax)
{
if ((object)s != null && !localsInScope.Contains(s))
{
this.variablesCaptured.Add(s, syntax);
analyzer.CaptureVariable(s, syntax);
}
}
}
......
......@@ -2,6 +2,7 @@
using System;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
......@@ -14,26 +15,26 @@ internal sealed class StateMachineFieldSymbol : SynthesizedFieldSymbolBase, ISyn
{
private readonly TypeSymbol type;
// 0 if the corresponding captured local is synthesized,
// or the field doesn't correspond to a hoisted local.
// > 0 if it corresponds to the field name
private readonly int userDefinedHoistedLocalId;
// -1 if the field doesn't represent a long-lived local
internal readonly int HoistedLocalSlotIndex;
public StateMachineFieldSymbol(NamedTypeSymbol stateMachineType, TypeSymbol type, string fieldName, bool isPublic)
: base(stateMachineType, fieldName, isPublic: isPublic, isReadOnly: false, isStatic: false)
internal readonly LocalSlotDebugInfo SlotDebugInfo;
// Some fields need to be public since they are initialized directly by the kickoff method.
public StateMachineFieldSymbol(NamedTypeSymbol stateMachineType, TypeSymbol type, string name, bool isPublic)
: this(stateMachineType, type, name, new LocalSlotDebugInfo(SynthesizedLocalKind.LoweringTemp, LocalDebugId.None), slotIndex: -1, isPublic: isPublic)
{
Debug.Assert((object)type != null);
this.type = type;
}
public StateMachineFieldSymbol(NamedTypeSymbol stateMachineType, TypeSymbol type, string localName, int userDefinedHoistedLocalId)
: base(stateMachineType, localName, isPublic: true, isReadOnly: false, isStatic: false)
public StateMachineFieldSymbol(NamedTypeSymbol stateMachineType, TypeSymbol type, string name, LocalSlotDebugInfo slotDebugInfo, int slotIndex, bool isPublic)
: base(stateMachineType, name, isPublic: isPublic, isReadOnly: false, isStatic: false)
{
Debug.Assert(userDefinedHoistedLocalId >= 1);
Debug.Assert((object)type != null);
Debug.Assert(slotDebugInfo.SynthesizedKind.IsLongLived() == (slotIndex >= 0));
this.type = type;
this.userDefinedHoistedLocalId = userDefinedHoistedLocalId;
this.HoistedLocalSlotIndex = slotIndex;
this.SlotDebugInfo = slotDebugInfo;
}
internal override TypeSymbol GetFieldType(ConsList<FieldSymbol> fieldsBeingBound)
......@@ -41,15 +42,9 @@ internal override TypeSymbol GetFieldType(ConsList<FieldSymbol> fieldsBeingBound
return this.type;
}
internal override int UserDefinedHoistedLocalId
{
get { return userDefinedHoistedLocalId; }
}
bool ISynthesizedMethodBodyImplementationSymbol.HasMethodBodyDependency
{
// TODO: hoisted temps?
get { return false; }
get { return true; }
}
IMethodSymbol ISynthesizedMethodBodyImplementationSymbol.Method
......
......@@ -7,7 +7,6 @@
using System.Linq;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
......@@ -20,10 +19,12 @@ internal abstract class StateMachineRewriter
protected readonly SyntheticBoundNodeFactory F;
protected readonly SynthesizedContainer stateMachineType;
protected readonly VariableSlotAllocator slotAllocatorOpt;
protected readonly SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals;
protected FieldSymbol stateField;
protected IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies;
protected IReadOnlySet<Symbol> variablesCaptured;
protected int nextFreeHoistedLocalSlot;
protected IReadOnlySet<Symbol> hoistedVariables;
protected Dictionary<Symbol, CapturedSymbolReplacement> initialParameters;
protected StateMachineRewriter(
......@@ -44,6 +45,7 @@ internal abstract class StateMachineRewriter
this.method = method;
this.stateMachineType = stateMachineType;
this.slotAllocatorOpt = slotAllocatorOpt;
this.synthesizedLocalOrdinals = new SynthesizedLocalOrdinalsDispenser();
this.diagnostics = diagnostics;
this.F = new SyntheticBoundNodeFactory(method, body.Syntax, compilationState, diagnostics);
......@@ -59,12 +61,7 @@ internal abstract class StateMachineRewriter
/// <summary>
/// Add fields to the state machine class that control the state machine.
/// </summary>
protected virtual void GenerateControlFields()
{
// Add a field: int _state
var intType = F.SpecialType(SpecialType.System_Int32);
this.stateField = F.StateMachineField(intType, GeneratedNames.MakeStateMachineStateName(), IsStateFieldPublic);
}
protected abstract void GenerateControlFields();
/// <summary>
/// Initialize the state machine class.
......@@ -72,17 +69,15 @@ protected virtual void GenerateControlFields()
protected abstract void InitializeStateMachine(ArrayBuilder<BoundStatement> bodyBuilder, NamedTypeSymbol frameType, LocalSymbol stateMachineLocal);
/// <summary>
/// Generate implementation-specific state machine initialization for the replacement method body.
/// Generate implementation-specific state machine initialization for the kickoff method body.
/// </summary>
protected abstract BoundStatement GenerateReplacementBody(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType);
protected abstract BoundStatement GenerateStateMachineCreation(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType);
/// <summary>
/// Generate implementation-specific state machine member method implementations.
/// </summary>
protected abstract void GenerateMethodImplementations();
protected abstract bool IsStateFieldPublic { get; }
protected BoundStatement Rewrite()
{
if (this.body.HasErrors)
......@@ -101,122 +96,152 @@ protected BoundStatement Rewrite()
}
// fields for the captured variables of the method
var variablesCaptured = IteratorAndAsyncCaptureWalker.Analyze(F.CompilationState.ModuleBuilderOpt.Compilation, method, body);
this.nonReusableLocalProxies = CreateNonReusableLocalProxies(variablesCaptured);
this.variablesCaptured = variablesCaptured;
var variablesToHoist = IteratorAndAsyncCaptureWalker.Analyze(F.Compilation, method, body, diagnostics);
CreateNonReusableLocalProxies(variablesToHoist, out this.nonReusableLocalProxies, out this.nextFreeHoistedLocalSlot);
this.hoistedVariables = variablesToHoist;
GenerateMethodImplementations();
// Return a replacement body for the original method
return ReplaceOriginalMethod();
// Return a replacement body for the kickoff method
return GenerateKickoffMethodBody();
}
private IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> CreateNonReusableLocalProxies(MultiDictionary<Symbol, CSharpSyntaxNode> variablesCaptured)
private void CreateNonReusableLocalProxies(
IEnumerable<Symbol> variablesToHoist,
out IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> proxies,
out int nextFreeHoistedLocalSlot)
{
var proxies = new Dictionary<Symbol, CapturedSymbolReplacement>();
var proxiesBuilder = new Dictionary<Symbol, CapturedSymbolReplacement>();
var typeMap = stateMachineType.TypeMap;
bool isDebugBuild = F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug;
bool mapToPreviousFields = isDebugBuild && slotAllocatorOpt != null;
var orderedCaptured =
from local in variablesCaptured.Keys
orderby local.Name, (local.Locations.Length == 0) ? 0 : local.Locations[0].SourceSpan.Start
select local;
nextFreeHoistedLocalSlot = mapToPreviousFields ? slotAllocatorOpt.HoistedLocalSlotCount : 0;
foreach (var capturedVariable in orderedCaptured)
foreach (var variable in variablesToHoist)
{
if (capturedVariable.Kind == SymbolKind.Local)
Debug.Assert(variable.Kind == SymbolKind.Local || variable.Kind == SymbolKind.Parameter);
if (variable.Kind == SymbolKind.Local)
{
var local = (LocalSymbol)capturedVariable;
if (local.SynthesizedKind == SynthesizedLocalKind.UserDefined ||
local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass)
var local = (LocalSymbol)variable;
var synthesizedKind = local.SynthesizedKind;
if (!synthesizedKind.MustSurviveStateMachineSuspension())
{
continue;
}
// no need to hoist constants
if (local.IsConst)
{
continue;
}
if (local.RefKind != RefKind.None)
{
// we'll create proxies for these variables later:
Debug.Assert(synthesizedKind == SynthesizedLocalKind.AwaitSpill);
continue;
}
Debug.Assert(local.RefKind == RefKind.None);
StateMachineFieldSymbol field = null;
if (!local.SynthesizedKind.IsSlotReusable(F.Compilation.Options.OptimizationLevel))
{
// variable needs to be hoisted
var fieldType = typeMap.SubstituteType(local.Type);
LocalDebugId id;
string fieldName = null;
int slotIndex = -1;
if (isDebugBuild)
{
// Calculate local debug id.
//
// EnC: When emitting the baseline (gen 0) the id is stored in a custom debug information attached to the kickoff method.
// When emitting a delta the id is only used to map to the existing field in the previous generation.
SyntaxNode declaratorSyntax = local.GetDeclaratorSyntax();
int syntaxOffset = this.method.CalculateLocalSyntaxOffset(declaratorSyntax.SpanStart, declaratorSyntax.SyntaxTree);
int ordinal = synthesizedLocalOrdinals.AssignLocalOrdinal(synthesizedKind, syntaxOffset);
id = new LocalDebugId(syntaxOffset, ordinal);
if (mapToPreviousFields)
{
// map local id to the previous id, if available:
fieldName = slotAllocatorOpt.GetPreviousHoistedLocal(declaratorSyntax, (Cci.ITypeReference)fieldType, synthesizedKind, id);
if (fieldName != null)
{
GeneratedNames.TryParseHoistedLocalSlotIndex(fieldName, out slotIndex);
}
}
}
else
{
id = LocalDebugId.None;
}
if (fieldName == null)
{
slotIndex = nextFreeHoistedLocalSlot++;
fieldName = GeneratedNames.MakeHoistedLocalFieldName(synthesizedKind, slotIndex, local.Name);
}
field = F.StateMachineField(fieldType, fieldName, new LocalSlotDebugInfo(synthesizedKind, id), slotIndex);
}
if (field != null)
{
// create proxies for user-defined variables and for lambda closures:
Debug.Assert(local.RefKind == RefKind.None);
proxies.Add(local, MakeNonReusableLocalProxy(typeMap, variablesCaptured, local));
proxiesBuilder.Add(local, new CapturedToStateMachineFieldReplacement(field, isReusable: false));
}
}
else
{
var parameter = (ParameterSymbol)capturedVariable;
var parameter = (ParameterSymbol)variable;
if (parameter.IsThis)
{
var proxyField = F.StateMachineField(method.ContainingType, GeneratedNames.ThisProxyName(), isPublic: true);
proxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField, isReusable: false));
var containingType = method.ContainingType;
var proxyField = F.StateMachineField(containingType, GeneratedNames.ThisProxyName(), isPublic: true);
proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false));
if (PreserveInitialParameterValues)
{
var initialThis = method.ContainingType.IsStructType() ?
F.StateMachineField(method.ContainingType, GeneratedNames.StateMachineThisParameterProxyName(), isPublic: true) : proxyField;
var initialThis = containingType.IsStructType() ?
F.StateMachineField(containingType, GeneratedNames.StateMachineThisParameterProxyName(), isPublic: true) : proxyField;
initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement(initialThis, isReusable: false));
initialParameters.Add(parameter, new CapturedToStateMachineFieldReplacement(initialThis, isReusable: false));
}
}
else
{
var proxyField = F.StateMachineField(typeMap.SubstituteType(parameter.Type), parameter.Name, isPublic: true);
proxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField, isReusable: false));
// The field needs to be public iff it is initialized directly from the kickoff method
// (i.e. not for IEnumerable which loads the values from parameter proxies).
var proxyField = F.StateMachineField(typeMap.SubstituteType(parameter.Type), parameter.Name, isPublic: !PreserveInitialParameterValues);
proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false));
if (PreserveInitialParameterValues)
{
string proxyName = GeneratedNames.StateMachineParameterProxyName(parameter.Name);
initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement(
F.StateMachineField(typeMap.SubstituteType(parameter.Type), proxyName, isPublic: true),
isReusable: false));
}
if (parameter.Type.IsRestrictedType())
{
// CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method
diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, parameter.Locations[0], parameter.Type);
var field = F.StateMachineField(typeMap.SubstituteType(parameter.Type), GeneratedNames.StateMachineParameterProxyName(parameter.Name), isPublic: true);
initialParameters.Add(parameter, new CapturedToStateMachineFieldReplacement(field, isReusable: false));
}
}
}
}
return proxies;
proxies = proxiesBuilder;
}
private CapturedSymbolReplacement MakeNonReusableLocalProxy(TypeMap TypeMap, MultiDictionary<Symbol, CSharpSyntaxNode> locations, LocalSymbol local)
{
Debug.Assert(local.RefKind == RefKind.None);
CapturedSymbolReplacement result = new CapturedToFrameSymbolReplacement(MakeHoistedLocalField(TypeMap, local, local.Type), isReusable: false);
if (local.Type.IsRestrictedType())
{
foreach (CSharpSyntaxNode syntax in locations[local])
{
// CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method
diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, local.Type);
}
}
return result;
}
private int nextLocalNumber = 1;
private SynthesizedFieldSymbolBase MakeHoistedLocalField(TypeMap TypeMap, LocalSymbol local, TypeSymbol type)
{
Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.UserDefined ||
local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass);
int index = nextLocalNumber++;
// Special Case: There's logic in the EE to recognize locals that have been captured by a lambda
// and would have been hoisted for the state machine. Basically, we just hoist the local containing
// the instance of the lambda display class and retain its original name (rather than using an
// iterator local name). See FUNCBRECEE::ImportIteratorMethodInheritedLocals.
string fieldName = (local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass)
? GeneratedNames.MakeLambdaDisplayClassStorageName(index)
: GeneratedNames.MakeHoistedLocalFieldName(local.Name, index);
return F.StateMachineField(TypeMap.SubstituteType(type), fieldName, index);
}
private BoundStatement ReplaceOriginalMethod()
private BoundStatement GenerateKickoffMethodBody()
{
F.CurrentMethod = method;
var bodyBuilder = ArrayBuilder<BoundStatement>.GetInstance();
var frameType = method.IsGenericMethod ? stateMachineType.Construct(method.TypeArguments) : stateMachineType;
LocalSymbol stateMachineVariable = F.SynthesizedLocal(frameType, null);
InitializeStateMachine(bodyBuilder, frameType, stateMachineVariable);
......@@ -246,7 +271,7 @@ private BoundStatement ReplaceOriginalMethod()
}
}
bodyBuilder.Add(GenerateReplacementBody(stateMachineVariable, frameType));
bodyBuilder.Add(GenerateStateMachineCreation(stateMachineVariable, frameType));
return F.Block(
ImmutableArray.Create(stateMachineVariable),
bodyBuilder.ToImmutableAndFree());
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
namespace Microsoft.CodeAnalysis.CSharp
......
......@@ -153,16 +153,16 @@ public BoundHoistedFieldAccess HoistedField(FieldSymbol field)
return new BoundHoistedFieldAccess(Syntax, field, field.Type);
}
public SynthesizedFieldSymbolBase StateMachineField(TypeSymbol fieldType, string name, bool isPublic)
public StateMachineFieldSymbol StateMachineField(TypeSymbol type, string name, bool isPublic = false)
{
var result = new StateMachineFieldSymbol(CurrentClass, fieldType, name, isPublic);
var result = new StateMachineFieldSymbol(CurrentClass, type, name, isPublic);
AddField(CurrentClass, result);
return result;
}
public SynthesizedFieldSymbolBase StateMachineField(TypeSymbol fieldType, string localName, int userDefinedHoistedLocalId)
public StateMachineFieldSymbol StateMachineField(TypeSymbol type, string name, LocalSlotDebugInfo slotDebugInfo, int slotIndex)
{
var result = new StateMachineFieldSymbol(CurrentClass, fieldType, localName, userDefinedHoistedLocalId);
var result = new StateMachineFieldSymbol(CurrentClass, type, name, slotDebugInfo, slotIndex, isPublic: false);
AddField(CurrentClass, result);
return result;
}
......
......@@ -133,6 +133,71 @@ internal static string MakeLambdaCacheFieldName(int uniqueId)
return "CS$<>9__CachedAnonymousMethodDelegate" + uniqueId;
}
internal static string MakeHoistedLocalFieldName(SynthesizedLocalKind kind, int slotIndex, string localNameOpt = null)
{
Debug.Assert((localNameOpt != null) == (kind == SynthesizedLocalKind.UserDefined));
Debug.Assert(slotIndex >= 0);
Debug.Assert(kind.IsLongLived());
// Lambda display class local follows a different naming pattern.
// EE depends on the name format.
// There's logic in the EE to recognize locals that have been captured by a lambda
// and would have been hoisted for the state machine. Basically, we just hoist the local containing
// the instance of the lambda display class and retain its original name (rather than using an
// iterator local name). See FUNCBRECEE::ImportIteratorMethodInheritedLocals.
var result = PooledStringBuilder.GetInstance();
var builder = result.Builder;
builder.Append('<');
if (localNameOpt != null)
{
Debug.Assert(localNameOpt.IndexOf('.') == -1);
builder.Append(localNameOpt);
}
builder.Append('>');
if (kind == SynthesizedLocalKind.LambdaDisplayClass)
{
builder.Append((char)GeneratedNameKind.DisplayClassLocalOrField);
}
else if (kind == SynthesizedLocalKind.UserDefined)
{
builder.Append((char)GeneratedNameKind.HoistedLocalField);
}
else
{
builder.Append('s');
}
builder.Append("__");
builder.Append(slotIndex + 1);
return result.ToStringAndFree();
}
// Extracts the slot index from a name of a field that stores hoisted variables.
// Such a name ends with "__{slot index}".
// Returned slot index is >= 0.
internal static bool TryParseHoistedLocalSlotIndex(string fieldName, out int slotIndex)
{
int lastUnder = fieldName.LastIndexOf('_');
if (lastUnder - 1 < 0 || lastUnder == fieldName.Length || fieldName[lastUnder - 1] != '_')
{
slotIndex = -1;
return false;
}
if (int.TryParse(fieldName.Substring(lastUnder + 1), out slotIndex) && slotIndex >= 1)
{
slotIndex--;
return true;
}
slotIndex = -1;
return false;
}
internal static string MakeCachedFrameInstanceName()
{
return "CS$<>9__inst";
......@@ -145,13 +210,13 @@ internal static string MakeSynthesizedLocalName(SynthesizedLocalKind kind, ref i
// Lambda display class local has to be named. EE depends on the name format.
if (kind == SynthesizedLocalKind.LambdaDisplayClass)
{
return MakeLambdaDisplayClassStorageName(uniqueId++);
return MakeLambdaDisplayLocalName(uniqueId++);
}
return null;
}
internal static string MakeLambdaDisplayClassStorageName(int uniqueId)
internal static string MakeLambdaDisplayLocalName(int uniqueId)
{
Debug.Assert((char)GeneratedNameKind.DisplayClassLocalOrField == '8');
return SynthesizedLocalNamePrefix + "<>8__locals" + uniqueId;
......@@ -219,13 +284,6 @@ internal static string MakeIteratorCurrentThreadIdName()
return "<>l__initialThreadId";
}
internal static string MakeHoistedLocalFieldName(string localName, int localNumber)
{
Debug.Assert((char)GeneratedNameKind.HoistedLocalField == '5');
Debug.Assert(localName == EnsureNoDotsInName(localName));
return "<" + localName + ">5__" + localNumber;
}
internal static string ThisProxyName()
{
Debug.Assert((char)GeneratedNameKind.ThisProxy == '4');
......@@ -262,7 +320,12 @@ internal static string AsyncBuilderFieldName()
internal static string AsyncAwaiterFieldName(int number)
{
return "<>u__$awaiter" + number;
return "<>u__" + number;
}
internal static bool IsAsyncAwaiterFieldName(string name)
{
return name.StartsWith("<>u__", StringComparison.Ordinal);
}
internal static string ReusableHoistedLocalFieldName(int number)
......
......@@ -81,10 +81,5 @@ internal override void AddSynthesizedAttributes(ModuleCompilationState compilati
// of special name C# compiler uses for backing fields, which is not desirable.
AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDebuggerBrowsableNeverAttribute());
}
internal override int UserDefinedHoistedLocalId
{
get { throw ExceptionUtilities.Unreachable; }
}
}
}
\ No newline at end of file
......@@ -24,10 +24,5 @@ internal override void AddSynthesizedAttributes(ModuleCompilationState compilati
{
// no attributes should be emitted
}
internal override int UserDefinedHoistedLocalId
{
get { throw ExceptionUtilities.Unreachable; }
}
}
}
\ No newline at end of file
......@@ -36,10 +36,5 @@ internal override TypeSymbol GetFieldType(ConsList<FieldSymbol> fieldsBeingBound
{
return this.type;
}
internal override int UserDefinedHoistedLocalId
{
get { throw ExceptionUtilities.Unreachable; }
}
}
}
\ No newline at end of file
......@@ -56,11 +56,6 @@ internal override void AddSynthesizedAttributes(ModuleCompilationState compilati
}
}
/// <summary>
/// Each hoisted iterator/async local has an associated index (1-based).
/// </summary>
internal abstract int UserDefinedHoistedLocalId { get; }
internal abstract override TypeSymbol GetFieldType(ConsList<FieldSymbol> fieldsBeingBound);
public override string Name
......
......@@ -350,7 +350,7 @@ .maxstack 3
IL_003c: stfld ""int Test.<G>d__1.<>1__state""
IL_0041: ldarg.0
IL_0042: ldloc.3
IL_0043: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_0043: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0048: ldarg.0
IL_0049: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_004e: ldloca.s V_3
......@@ -358,10 +358,10 @@ .maxstack 3
IL_0051: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<G>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<G>d__1)""
IL_0056: leave IL_00dc
IL_005b: ldarg.0
IL_005c: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_005c: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0061: stloc.3
IL_0062: ldarg.0
IL_0063: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_0063: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0068: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_006e: ldarg.0
IL_006f: ldc.i4.m1
......@@ -501,21 +501,58 @@ public static void Main()
var expected = @"
2
";
var v = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expected);
var v = CompileAndVerify(source, AsyncRefs, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: expected, symbolValidator: module =>
{
Assert.Equal(new[]
{
"<>1__state",
"<>t__builder",
"<x>5__1",
"<>s__2", // pending exception
"<>s__3", // pending branch
"<>s__4", // return value
"<>s__5", // spill
"<>s__6", // spill
"<>s__7", // spill
"<>u__0", // awaiter
}, module.GetFieldNames("Test.<G>d__1"));
});
v.VerifyPdb("Test.G", @"
<symbols>
<entryPoint declaringType=""Test"" methodName=""Main"" parameterNames="""" />
<methods>
<method containingType=""Test"" name=""G"" parameterNames="""">
<customDebugInfo version=""4"" count=""2"">
<forwardIterator version=""4"" kind=""ForwardIterator"" size=""24"" name=""&lt;G&gt;d__1"" />
<encLocalSlotMap version=""4"" kind=""EditAndContinueLocalSlotMap"" size=""28"">
<slot kind=""0"" offset=""15"" />
<slot kind=""22"" offset=""33"" />
<slot kind=""23"" offset=""33"" />
<slot kind=""20"" offset=""33"" />
<slot kind=""28"" offset=""65"" />
<slot kind=""28"" offset=""156"" />
<slot kind=""28"" offset=""156"" ordinal=""1"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencepoints total=""0"" />
<locals />
</method>
</methods>
</symbols>
");
v.VerifyIL("Test.<G>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
{
// Code size 467 (0x1d3)
// Code size 479 (0x1df)
.maxstack 3
.locals init (int V_0,
int V_1,
int V_2,
System.Runtime.CompilerServices.TaskAwaiter<int> V_3,
int V_4,
Test.<G>d__1 V_5,
object V_6,
int V_7,
System.Exception V_8)
System.Runtime.CompilerServices.TaskAwaiter<int> V_2,
int V_3,
Test.<G>d__1 V_4,
object V_5,
System.Exception V_6)
~IL_0000: ldarg.0
IL_0001: ldfld ""int Test.<G>d__1.<>1__state""
IL_0006: stloc.0
......@@ -529,17 +566,17 @@ .maxstack 3
IL_000e: beq.s IL_0014
IL_0010: br.s IL_0019
IL_0012: br.s IL_002f
IL_0014: br IL_0117
IL_0014: br IL_011f
-IL_0019: nop
-IL_001a: ldarg.0
IL_001b: ldc.i4.0
IL_001c: stfld ""int Test.<G>d__1.<x>5__1""
~IL_0021: ldarg.0
IL_0022: ldnull
IL_0023: stfld ""object Test.<G>d__1.<>7__wrap1""
IL_0023: stfld ""object Test.<G>d__1.<>s__2""
IL_0028: ldarg.0
IL_0029: ldc.i4.0
IL_002a: stfld ""int Test.<G>d__1.<>7__wrap2""
IL_002a: stfld ""int Test.<G>d__1.<>s__3""
~IL_002f: nop
.try
{
......@@ -550,8 +587,8 @@ .maxstack 3
-IL_0037: nop
-IL_0038: call ""System.Threading.Tasks.Task<int> Test.F()""
IL_003d: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0042: stloc.3
IL_0043: ldloca.s V_3
IL_0042: stloc.2
IL_0043: ldloca.s V_2
IL_0045: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_004a: brtrue.s IL_0090
IL_004c: ldarg.0
......@@ -560,164 +597,168 @@ .maxstack 3
IL_004f: stloc.0
IL_0050: stfld ""int Test.<G>d__1.<>1__state""
IL_0055: ldarg.0
IL_0056: ldloc.3
IL_0057: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_0056: ldloc.2
IL_0057: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_005c: ldarg.0
IL_005d: stloc.s V_5
IL_005d: stloc.s V_4
IL_005f: ldarg.0
IL_0060: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_0065: ldloca.s V_3
IL_0067: ldloca.s V_5
IL_0065: ldloca.s V_2
IL_0067: ldloca.s V_4
IL_0069: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<G>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<G>d__1)""
IL_006e: nop
IL_006f: leave IL_01d2
IL_006f: leave IL_01de
IL_0074: ldarg.0
IL_0075: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_007a: stloc.3
IL_0075: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_007a: stloc.2
IL_007b: ldarg.0
IL_007c: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_007c: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0081: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0087: ldarg.0
IL_0088: ldc.i4.m1
IL_0089: dup
IL_008a: stloc.0
IL_008b: stfld ""int Test.<G>d__1.<>1__state""
IL_0090: ldloca.s V_3
IL_0090: ldloca.s V_2
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: ldarg.0
IL_00a5: ldloc.2
IL_00a6: stfld ""int Test.<G>d__1.<x>5__1""
-IL_00ab: ldarg.0
IL_00ac: ldarg.0
IL_00ad: ldfld ""int Test.<G>d__1.<x>5__1""
IL_00b2: stfld ""int Test.<G>d__1.<>7__wrap3""
IL_00b7: br.s IL_00b9
IL_00b9: ldarg.0
IL_00ba: ldc.i4.1
IL_00bb: stfld ""int Test.<G>d__1.<>7__wrap2""
IL_00c0: leave.s IL_00ce
IL_0097: stloc.3
IL_0098: ldloca.s V_2
IL_009a: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00a0: ldarg.0
IL_00a1: ldloc.3
IL_00a2: stfld ""int Test.<G>d__1.<>s__5""
IL_00a7: ldarg.0
IL_00a8: ldarg.0
IL_00a9: ldfld ""int Test.<G>d__1.<>s__5""
IL_00ae: stfld ""int Test.<G>d__1.<x>5__1""
-IL_00b3: ldarg.0
IL_00b4: ldarg.0
IL_00b5: ldfld ""int Test.<G>d__1.<x>5__1""
IL_00ba: stfld ""int Test.<G>d__1.<>s__4""
IL_00bf: br.s IL_00c1
IL_00c1: ldarg.0
IL_00c2: ldc.i4.1
IL_00c3: stfld ""int Test.<G>d__1.<>s__3""
IL_00c8: leave.s IL_00d6
}
catch object
{
~IL_00c2: stloc.s V_6
IL_00c4: ldarg.0
IL_00c5: ldloc.s V_6
IL_00c7: stfld ""object Test.<G>d__1.<>7__wrap1""
IL_00cc: leave.s IL_00ce
~IL_00ca: stloc.s V_5
IL_00cc: ldarg.0
IL_00cd: ldloc.s V_5
IL_00cf: stfld ""object Test.<G>d__1.<>s__2""
IL_00d4: leave.s IL_00d6
}
-IL_00ce: nop
-IL_00cf: ldarg.0
IL_00d0: ldarg.0
IL_00d1: ldfld ""int Test.<G>d__1.<x>5__1""
IL_00d6: stfld ""int Test.<G>d__1.<>7__wrap4""
IL_00db: call ""System.Threading.Tasks.Task<int> Test.F()""
IL_00e0: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_00e5: stloc.3
IL_00e6: ldloca.s V_3
IL_00e8: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_00ed: brtrue.s IL_0133
IL_00ef: ldarg.0
IL_00f0: ldc.i4.1
IL_00f1: dup
IL_00f2: stloc.0
IL_00f3: stfld ""int Test.<G>d__1.<>1__state""
IL_00f8: ldarg.0
IL_00f9: ldloc.3
IL_00fa: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_00ff: ldarg.0
IL_0100: stloc.s V_5
IL_0102: ldarg.0
IL_0103: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_0108: ldloca.s V_3
IL_010a: ldloca.s V_5
IL_010c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<G>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<G>d__1)""
IL_0111: nop
IL_0112: leave IL_01d2
IL_0117: ldarg.0
IL_0118: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_011d: stloc.3
IL_011e: ldarg.0
IL_011f: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_0124: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_012a: ldarg.0
IL_012b: ldc.i4.m1
IL_012c: dup
IL_012d: stloc.0
IL_012e: stfld ""int Test.<G>d__1.<>1__state""
IL_0133: ldloca.s V_3
IL_0135: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_013a: stloc.s V_4
IL_013c: ldloca.s V_3
IL_013e: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0144: ldloc.s V_4
IL_0146: stloc.s V_7
IL_0148: ldarg.0
IL_0149: ldarg.0
IL_014a: ldfld ""int Test.<G>d__1.<>7__wrap4""
IL_014f: ldloc.s V_7
IL_0151: add
IL_0152: stfld ""int Test.<G>d__1.<x>5__1""
-IL_0157: nop
~IL_0158: ldarg.0
IL_0159: ldfld ""object Test.<G>d__1.<>7__wrap1""
IL_015e: stloc.s V_6
IL_0160: ldloc.s V_6
IL_0162: brfalse.s IL_0181
IL_0164: ldloc.s V_6
IL_0166: isinst ""System.Exception""
IL_016b: stloc.s V_8
IL_016d: ldloc.s V_8
IL_016f: brtrue.s IL_0174
IL_0171: ldloc.s V_6
IL_0173: throw
IL_0174: ldloc.s V_8
IL_0176: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_017b: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_0180: nop
IL_0181: ldarg.0
IL_0182: ldfld ""int Test.<G>d__1.<>7__wrap2""
IL_0187: stloc.s V_4
IL_0189: ldloc.s V_4
IL_018b: ldc.i4.1
IL_018c: beq.s IL_0190
IL_018e: br.s IL_0199
IL_0190: ldarg.0
IL_0191: ldfld ""int Test.<G>d__1.<>7__wrap3""
IL_0196: stloc.1
IL_0197: leave.s IL_01bd
IL_0199: ldarg.0
IL_019a: ldnull
IL_019b: stfld ""object Test.<G>d__1.<>7__wrap1""
IL_01a0: leave.s IL_01bd
-IL_00d6: nop
-IL_00d7: ldarg.0
IL_00d8: ldarg.0
IL_00d9: ldfld ""int Test.<G>d__1.<x>5__1""
IL_00de: stfld ""int Test.<G>d__1.<>s__6""
IL_00e3: call ""System.Threading.Tasks.Task<int> Test.F()""
IL_00e8: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_00ed: stloc.2
IL_00ee: ldloca.s V_2
IL_00f0: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_00f5: brtrue.s IL_013b
IL_00f7: ldarg.0
IL_00f8: ldc.i4.1
IL_00f9: dup
IL_00fa: stloc.0
IL_00fb: stfld ""int Test.<G>d__1.<>1__state""
IL_0100: ldarg.0
IL_0101: ldloc.2
IL_0102: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0107: ldarg.0
IL_0108: stloc.s V_4
IL_010a: ldarg.0
IL_010b: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_0110: ldloca.s V_2
IL_0112: ldloca.s V_4
IL_0114: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<G>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<G>d__1)""
IL_0119: nop
IL_011a: leave IL_01de
IL_011f: ldarg.0
IL_0120: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0125: stloc.2
IL_0126: ldarg.0
IL_0127: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_012c: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0132: ldarg.0
IL_0133: ldc.i4.m1
IL_0134: dup
IL_0135: stloc.0
IL_0136: stfld ""int Test.<G>d__1.<>1__state""
IL_013b: ldloca.s V_2
IL_013d: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_0142: stloc.3
IL_0143: ldloca.s V_2
IL_0145: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_014b: ldarg.0
IL_014c: ldloc.3
IL_014d: stfld ""int Test.<G>d__1.<>s__7""
IL_0152: ldarg.0
IL_0153: ldarg.0
IL_0154: ldfld ""int Test.<G>d__1.<>s__6""
IL_0159: ldarg.0
IL_015a: ldfld ""int Test.<G>d__1.<>s__7""
IL_015f: add
IL_0160: stfld ""int Test.<G>d__1.<x>5__1""
-IL_0165: nop
~IL_0166: ldarg.0
IL_0167: ldfld ""object Test.<G>d__1.<>s__2""
IL_016c: stloc.s V_5
IL_016e: ldloc.s V_5
IL_0170: brfalse.s IL_018f
IL_0172: ldloc.s V_5
IL_0174: isinst ""System.Exception""
IL_0179: stloc.s V_6
IL_017b: ldloc.s V_6
IL_017d: brtrue.s IL_0182
IL_017f: ldloc.s V_5
IL_0181: throw
IL_0182: ldloc.s V_6
IL_0184: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_0189: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_018e: nop
IL_018f: ldarg.0
IL_0190: ldfld ""int Test.<G>d__1.<>s__3""
IL_0195: stloc.3
IL_0196: ldloc.3
IL_0197: ldc.i4.1
IL_0198: beq.s IL_019c
IL_019a: br.s IL_01a5
IL_019c: ldarg.0
IL_019d: ldfld ""int Test.<G>d__1.<>s__4""
IL_01a2: stloc.1
IL_01a3: leave.s IL_01c9
IL_01a5: ldarg.0
IL_01a6: ldnull
IL_01a7: stfld ""object Test.<G>d__1.<>s__2""
IL_01ac: leave.s IL_01c9
}
catch System.Exception
{
~IL_01a2: stloc.s V_8
IL_01a4: nop
IL_01a5: ldarg.0
IL_01a6: ldc.i4.s -2
IL_01a8: stfld ""int Test.<G>d__1.<>1__state""
IL_01ad: ldarg.0
IL_01ae: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_01b3: ldloc.s V_8
IL_01b5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_01ba: nop
IL_01bb: leave.s IL_01d2
~IL_01ae: stloc.s V_6
IL_01b0: nop
IL_01b1: ldarg.0
IL_01b2: ldc.i4.s -2
IL_01b4: stfld ""int Test.<G>d__1.<>1__state""
IL_01b9: ldarg.0
IL_01ba: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_01bf: ldloc.s V_6
IL_01c1: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_01c6: nop
IL_01c7: leave.s IL_01de
}
-IL_01bd: ldarg.0
IL_01be: ldc.i4.s -2
IL_01c0: stfld ""int Test.<G>d__1.<>1__state""
~IL_01c5: ldarg.0
IL_01c6: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_01cb: ldloc.1
IL_01cc: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_01d1: nop
IL_01d2: ret
-IL_01c9: ldarg.0
IL_01ca: ldc.i4.s -2
IL_01cc: stfld ""int Test.<G>d__1.<>1__state""
~IL_01d1: ldarg.0
IL_01d2: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_01d7: ldloc.1
IL_01d8: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_01dd: nop
IL_01de: ret
}", sequencePoints: "Test+<G>d__1.MoveNext");
}
......@@ -1064,7 +1105,7 @@ .maxstack 3
IL_0036: stfld ""int Test.<G>d__1.<>1__state""
IL_003b: ldarg.0
IL_003c: ldloc.s V_4
IL_003e: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_003e: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0043: ldarg.0
IL_0044: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<G>d__1.<>t__builder""
IL_0049: ldloca.s V_4
......@@ -1072,10 +1113,10 @@ .maxstack 3
IL_004c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<G>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<G>d__1)""
IL_0051: leave.s IL_00b1
IL_0053: ldarg.0
IL_0054: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_0054: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0059: stloc.s V_4
IL_005b: ldarg.0
IL_005c: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__$awaiter0""
IL_005c: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<G>d__1.<>u__0""
IL_0061: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0067: ldarg.0
IL_0068: ldc.i4.m1
......
......@@ -165,9 +165,9 @@ public static async Task M(int x, int y, int z)
"<>1__state",
"<>t__builder",
"x",
"y",
"z",
"<>u__$awaiter0",
"y",
"<>u__0",
}, module.GetFieldNames("C.<M>d__1"));
});
......@@ -180,7 +180,7 @@ public static async Task M(int x, int y, int z)
"x",
"y",
"z",
"<>u__$awaiter0",
"<>u__0",
}, module.GetFieldNames("C.<M>d__1"));
});
}
......@@ -210,7 +210,7 @@ public async Task M(IDisposable disposable)
lock (this) { }
}
}";
CompileAndVerify(source, additionalRefs: AsyncRefs, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
CompileAndVerify(source, AsyncRefs, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
{
......@@ -221,38 +221,78 @@ public async Task M(IDisposable disposable)
"<>7__wrap1",
"<>7__wrap2",
"<>7__wrap3",
"<>u__$awaiter0",
"<>u__0",
"<>7__wrap4",
"<>7__wrap5",
"<>7__wrap6",
}, module.GetFieldNames("C.<M>d__1"));
});
#if TODO
CompileAndVerify(source, additionalRefs: AsyncRefs, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
var vd = CompileAndVerify(source, AsyncRefs, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
{
"<>1__state",
"<>t__builder",
"<>4__this",
"disposable",
"<>s__6$1",
"<>s__7$2",
"<item>5__1",
"<>s__3$3",
"<item>5__2",
"<>s__3$6",
"<>s__3$7",
"<>s__530$1$1",
"<>s__530$1$2",
"<>s__530$1$3",
"<>s__530$1$4",
"<>s__3$8",
"<>u__$awaiter0",
"<>4__this",
"<>s__1",
"<>s__2",
"<item>5__3",
"<>s__4",
"<>s__5",
"<>s__6",
"<item>5__7",
"<>s__8",
"<>s__9",
"<>s__10",
"<>s__11",
"<>s__12",
"<>s__13",
"<>s__14",
"<>s__15",
"<>s__16",
"<>s__17",
"<>s__18",
"<>s__19",
"<>u__0",
}, module.GetFieldNames("C.<M>d__1"));
});
#endif
vd.VerifyPdb("C.M", @"
<symbols>
<methods>
<method containingType=""C"" name=""M"" parameterNames=""disposable"">
<customDebugInfo version=""4"" count=""2"">
<forwardIterator version=""4"" kind=""ForwardIterator"" size=""24"" name=""&lt;M&gt;d__1"" />
<encLocalSlotMap version=""4"" kind=""EditAndContinueLocalSlotMap"" size=""64"">
<slot kind=""6"" offset=""11"" />
<slot kind=""8"" offset=""11"" />
<slot kind=""0"" offset=""11"" />
<slot kind=""4"" offset=""53"" />
<slot kind=""6"" offset=""98"" />
<slot kind=""8"" offset=""98"" />
<slot kind=""0"" offset=""98"" />
<slot kind=""4"" offset=""151"" />
<slot kind=""4"" offset=""220"" />
<slot kind=""28"" offset=""261"" />
<slot kind=""28"" offset=""261"" ordinal=""1"" />
<slot kind=""28"" offset=""281"" />
<slot kind=""28"" offset=""281"" ordinal=""1"" />
<slot kind=""28"" offset=""281"" ordinal=""2"" />
<slot kind=""28"" offset=""261"" ordinal=""2"" />
<slot kind=""4"" offset=""307"" />
<slot kind=""4"" offset=""376"" />
<slot kind=""3"" offset=""410"" />
<slot kind=""2"" offset=""410"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencepoints total=""0"" />
<locals />
</method>
</methods>
</symbols>
");
}
[Fact]
......@@ -507,13 +547,13 @@ class Test<U>
IL_005b: stloc.0
IL_005c: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_0062: ldloc.1
IL_0063: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0063: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0068: ldarg.0
IL_0069: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test<U>.<M>d__1<S, T>.<>t__builder""
IL_007b: ldarg.0
IL_007c: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_007c: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0082: ldarg.0
IL_0083: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0083: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0091: stloc.0
IL_0092: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_00a7: ldarg.0
......@@ -531,13 +571,13 @@ class Test<U>
IL_0110: stloc.0
IL_0111: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_0117: ldloc.1
IL_0118: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0118: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_011d: ldarg.0
IL_011e: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test<U>.<M>d__1<S, T>.<>t__builder""
IL_0130: ldarg.0
IL_0131: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0131: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0137: ldarg.0
IL_0138: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0138: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0146: stloc.0
IL_0147: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_015c: ldarg.0
......@@ -555,13 +595,13 @@ class Test<U>
IL_01c5: stloc.0
IL_01c6: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_01cc: ldloc.1
IL_01cd: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_01cd: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_01d2: ldarg.0
IL_01d3: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test<U>.<M>d__1<S, T>.<>t__builder""
IL_01e5: ldarg.0
IL_01e6: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_01e6: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_01ec: ldarg.0
IL_01ed: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_01ed: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_01fb: stloc.0
IL_01fc: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_0211: ldarg.0
......@@ -579,13 +619,13 @@ class Test<U>
IL_027a: stloc.0
IL_027b: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_0281: ldloc.1
IL_0282: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0282: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0287: ldarg.0
IL_0288: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test<U>.<M>d__1<S, T>.<>t__builder""
IL_029a: ldarg.0
IL_029b: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_029b: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_02a1: ldarg.0
IL_02a2: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_02a2: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_02b0: stloc.0
IL_02b1: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_02c6: ldarg.0
......@@ -603,13 +643,13 @@ class Test<U>
IL_032f: stloc.0
IL_0330: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_0336: ldloc.1
IL_0337: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0337: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_033c: ldarg.0
IL_033d: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test<U>.<M>d__1<S, T>.<>t__builder""
IL_034f: ldarg.0
IL_0350: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0350: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0356: ldarg.0
IL_0357: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__$awaiter0""
IL_0357: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test<U>.<M>d__1<S, T>.<>u__0""
IL_0365: stloc.0
IL_0366: stfld ""int Test<U>.<M>d__1<S, T>.<>1__state""
IL_037b: ldarg.0
......@@ -665,13 +705,13 @@ public static async void M()
IL_004b: stloc.0
IL_004c: stfld ""int Test.<M>d__1.<>1__state""
IL_0052: ldloc.1
IL_0053: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__$awaiter0""
IL_0053: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__0""
IL_0058: ldarg.0
IL_0059: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.<M>d__1.<>t__builder""
IL_006b: ldarg.0
IL_006c: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__$awaiter0""
IL_006c: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__0""
IL_0072: ldarg.0
IL_0073: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__$awaiter0""
IL_0073: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__0""
IL_0081: stloc.0
IL_0082: stfld ""int Test.<M>d__1.<>1__state""
IL_0097: ldarg.0
......@@ -689,13 +729,13 @@ public static async void M()
IL_0100: stloc.0
IL_0101: stfld ""int Test.<M>d__1.<>1__state""
IL_0107: ldloc.1
IL_0108: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__$awaiter0""
IL_0108: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__0""
IL_010d: ldarg.0
IL_010e: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.<M>d__1.<>t__builder""
IL_0120: ldarg.0
IL_0121: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__$awaiter0""
IL_0121: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__0""
IL_0127: ldarg.0
IL_0128: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__$awaiter0""
IL_0128: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<M>d__1.<>u__0""
IL_0136: stloc.0
IL_0137: stfld ""int Test.<M>d__1.<>1__state""
IL_014c: ldarg.0
......
......@@ -600,18 +600,16 @@ public static async Task<int> F(int[] array)
v.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
{
// Code size 297 (0x129)
.maxstack 4
// Code size 309 (0x135)
.maxstack 5
.locals init (int V_0,
int V_1,
int& V_2,
int V_3,
int V_4,
int V_5,
int& V_6,
System.Runtime.CompilerServices.TaskAwaiter<int> V_7,
Test.<F>d__1 V_8,
System.Exception V_9)
int& V_4,
System.Runtime.CompilerServices.TaskAwaiter<int> V_5,
Test.<F>d__1 V_6,
System.Exception V_7)
~IL_0000: ldarg.0
IL_0001: ldfld ""int Test.<F>d__1.<>1__state""
IL_0006: stloc.0
......@@ -620,7 +618,7 @@ .maxstack 4
~IL_0007: ldloc.0
IL_0008: brfalse.s IL_000c
IL_000a: br.s IL_0011
IL_000c: br IL_0096
IL_000c: br IL_0094
-IL_0011: nop
-IL_0012: ldarg.0
IL_0013: ldfld ""int[] Test.<F>d__1.array""
......@@ -634,114 +632,118 @@ .maxstack 4
IL_0023: ldc.i4.2
IL_0024: add
IL_0025: dup
IL_0026: stloc.s V_5
IL_0028: stind.i4
IL_0029: ldloc.s V_5
IL_002b: stfld ""int Test.<F>d__1.<>7__wrap1""
IL_0030: ldarg.0
IL_0031: ldarg.0
IL_0032: ldfld ""int[] Test.<F>d__1.array""
IL_0037: stfld ""int[] Test.<F>d__1.<>7__wrap3""
IL_003c: ldarg.0
IL_003d: ldfld ""int[] Test.<F>d__1.<>7__wrap3""
IL_0042: ldc.i4.3
IL_0043: ldelema ""int""
IL_0048: stloc.s V_6
IL_004a: ldarg.0
IL_004b: ldarg.0
IL_004c: ldfld ""int[] Test.<F>d__1.<>7__wrap3""
IL_0051: ldc.i4.3
IL_0052: ldelem.i4
IL_0053: stfld ""int Test.<F>d__1.<>7__wrap2""
IL_0058: call ""System.Threading.Tasks.Task<int> Test.G()""
IL_005d: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0062: stloc.s V_7
IL_0064: ldloca.s V_7
IL_0066: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_006b: brtrue.s IL_00b3
IL_006d: ldarg.0
IL_006e: ldc.i4.0
IL_006f: dup
IL_0070: stloc.0
IL_0071: stfld ""int Test.<F>d__1.<>1__state""
IL_0076: ldarg.0
IL_0077: ldloc.s V_7
IL_0079: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter0""
IL_007e: ldarg.0
IL_007f: stloc.s V_8
IL_0081: ldarg.0
IL_0082: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0087: ldloca.s V_7
IL_0089: ldloca.s V_8
IL_008b: 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_0090: nop
IL_0091: leave IL_0128
IL_0096: ldarg.0
IL_0097: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter0""
IL_009c: stloc.s V_7
IL_009e: ldarg.0
IL_009f: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter0""
IL_00a4: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00aa: ldarg.0
IL_00ab: ldc.i4.m1
IL_00ac: dup
IL_00ad: stloc.0
IL_00ae: stfld ""int Test.<F>d__1.<>1__state""
IL_00b3: ldloca.s V_7
IL_00b5: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_00ba: stloc.s V_5
IL_00bc: ldloca.s V_7
IL_00be: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00c4: ldloc.s V_5
IL_00c6: stloc.3
IL_00c7: ldarg.0
IL_00c8: ldfld ""int[] Test.<F>d__1.<>7__wrap3""
IL_00cd: ldc.i4.3
IL_00ce: ldarg.0
IL_00cf: ldfld ""int Test.<F>d__1.<>7__wrap2""
IL_00d4: ldloc.3
IL_00d5: add
IL_00d6: dup
IL_00d7: stloc.s V_5
IL_00d9: stelem.i4
IL_00da: ldloc.s V_5
IL_00dc: stloc.s V_4
IL_00de: ldarg.0
IL_00df: ldfld ""int Test.<F>d__1.<>7__wrap1""
IL_00e4: ldloc.s V_4
IL_00e6: ldc.i4.4
IL_00e7: call ""int Test.H(int, int, int)""
IL_00ec: pop
IL_00ed: ldarg.0
IL_00ee: ldnull
IL_00ef: stfld ""int[] Test.<F>d__1.<>7__wrap3""
-IL_00f4: ldc.i4.1
IL_00f5: stloc.1
IL_00f6: leave.s IL_0113
IL_0026: stloc.3
IL_0027: stind.i4
IL_0028: ldloc.3
IL_0029: stfld ""int Test.<F>d__1.<>s__1""
IL_002e: ldarg.0
IL_002f: ldarg.0
IL_0030: ldfld ""int[] Test.<F>d__1.array""
IL_0035: stfld ""int[] Test.<F>d__1.<>s__5""
IL_003a: ldarg.0
IL_003b: ldfld ""int[] Test.<F>d__1.<>s__5""
IL_0040: ldc.i4.3
IL_0041: ldelema ""int""
IL_0046: stloc.s V_4
IL_0048: ldarg.0
IL_0049: ldarg.0
IL_004a: ldfld ""int[] Test.<F>d__1.<>s__5""
IL_004f: ldc.i4.3
IL_0050: ldelem.i4
IL_0051: stfld ""int Test.<F>d__1.<>s__2""
IL_0056: call ""System.Threading.Tasks.Task<int> Test.G()""
IL_005b: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0060: stloc.s V_5
IL_0062: ldloca.s V_5
IL_0064: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_0069: brtrue.s IL_00b1
IL_006b: ldarg.0
IL_006c: ldc.i4.0
IL_006d: dup
IL_006e: stloc.0
IL_006f: stfld ""int Test.<F>d__1.<>1__state""
IL_0074: ldarg.0
IL_0075: ldloc.s V_5
IL_0077: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__0""
IL_007c: ldarg.0
IL_007d: stloc.s V_6
IL_007f: ldarg.0
IL_0080: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0085: ldloca.s V_5
IL_0087: ldloca.s V_6
IL_0089: 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_008e: nop
IL_008f: leave IL_0134
IL_0094: ldarg.0
IL_0095: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__0""
IL_009a: stloc.s V_5
IL_009c: ldarg.0
IL_009d: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__0""
IL_00a2: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00a8: ldarg.0
IL_00a9: ldc.i4.m1
IL_00aa: dup
IL_00ab: stloc.0
IL_00ac: stfld ""int Test.<F>d__1.<>1__state""
IL_00b1: ldloca.s V_5
IL_00b3: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_00b8: stloc.3
IL_00b9: ldloca.s V_5
IL_00bb: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00c1: ldarg.0
IL_00c2: ldloc.3
IL_00c3: stfld ""int Test.<F>d__1.<>s__3""
IL_00c8: ldarg.0
IL_00c9: ldarg.0
IL_00ca: ldfld ""int[] Test.<F>d__1.<>s__5""
IL_00cf: ldc.i4.3
IL_00d0: ldarg.0
IL_00d1: ldfld ""int Test.<F>d__1.<>s__2""
IL_00d6: ldarg.0
IL_00d7: ldfld ""int Test.<F>d__1.<>s__3""
IL_00dc: add
IL_00dd: dup
IL_00de: stloc.3
IL_00df: stelem.i4
IL_00e0: ldloc.3
IL_00e1: stfld ""int Test.<F>d__1.<>s__4""
IL_00e6: ldarg.0
IL_00e7: ldfld ""int Test.<F>d__1.<>s__1""
IL_00ec: ldarg.0
IL_00ed: ldfld ""int Test.<F>d__1.<>s__4""
IL_00f2: ldc.i4.4
IL_00f3: call ""int Test.H(int, int, int)""
IL_00f8: pop
IL_00f9: ldarg.0
IL_00fa: ldnull
IL_00fb: stfld ""int[] Test.<F>d__1.<>s__5""
-IL_0100: ldc.i4.1
IL_0101: stloc.1
IL_0102: leave.s IL_011f
}
catch System.Exception
{
~IL_00f8: stloc.s V_9
IL_00fa: nop
IL_00fb: ldarg.0
IL_00fc: ldc.i4.s -2
IL_00fe: stfld ""int Test.<F>d__1.<>1__state""
IL_0103: ldarg.0
IL_0104: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0109: ldloc.s V_9
IL_010b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_0110: nop
IL_0111: leave.s IL_0128
~IL_0104: stloc.s V_7
IL_0106: nop
IL_0107: ldarg.0
IL_0108: ldc.i4.s -2
IL_010a: stfld ""int Test.<F>d__1.<>1__state""
IL_010f: ldarg.0
IL_0110: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0115: ldloc.s V_7
IL_0117: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_011c: nop
IL_011d: leave.s IL_0134
}
-IL_0113: ldarg.0
IL_0114: ldc.i4.s -2
IL_0116: stfld ""int Test.<F>d__1.<>1__state""
~IL_011b: ldarg.0
IL_011c: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0121: ldloc.1
IL_0122: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_0127: nop
IL_0128: ret
-IL_011f: ldarg.0
IL_0120: ldc.i4.s -2
IL_0122: stfld ""int Test.<F>d__1.<>1__state""
~IL_0127: ldarg.0
IL_0128: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_012d: ldloc.1
IL_012e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_0133: nop
IL_0134: ret
}", sequencePoints: "Test+<F>d__1.MoveNext");
}
......@@ -812,12 +814,12 @@ public static async Task<int> F(int[] array)
"array",
"<>7__wrap1",
"<>7__wrap2",
"<>u__$awaiter0",
"<>u__0",
"<>7__wrap3",
"<>7__wrap4",
}, module.GetFieldNames("C.<F>d__1"));
});
#if TODO
CompileAndVerify(source, additionalRefs: AsyncRefs, verify:false, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
......@@ -825,14 +827,17 @@ public static async Task<int> F(int[] array)
"<>1__state",
"<>t__builder",
"array",
"<>s__530$1$2",
"<>s__530$1$7",
"<>s__531$1$1",
"<>u__$awaiter0",
"<>s__531$1$2",
"<>s__1",
"<>s__2",
"<>s__3",
"<>s__4",
"<>s__5",
"<>s__6",
"<>s__7",
"<>u__0",
"<>s__8"
}, module.GetFieldNames("C.<F>d__1"));
});
#endif
}
[Fact]
......@@ -2797,68 +2802,5 @@ public static void Main()
";
CompileAndVerify(source, expected);
}
[Fact]
public void SynthesizedVariables1()
{
var source =
@"
using System;
using System.Threading.Tasks;
class C
{
static void F1(ref int x, int y, int z)
{
x += y + z;
}
static int F0()
{
Console.WriteLine(-1);
return 0;
}
static async Task<int> F2()
{
int[] x = new int[1] { 21 };
x = null;
F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21));
F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21));
return x[0];
}
}";
CompileAndVerify(source, additionalRefs: AsyncRefs, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
{
"<>1__state",
"<>t__builder",
"<x>5__1",
"<>7__wrap1",
"<>7__wrap2",
"<>u__$awaiter5",
"<>7__wrap3",
}, module.GetFieldNames("C.<F2>d__1"));
});
#if TODO
CompileAndVerify(source, additionalRefs: AsyncRefs, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
{
"<>1__state",
"<>t__builder",
"<x>5__1",
"<>s__530$1$2",
"<>s__530$2$5",
"<>s__531$1$1",
"<>u__$awaiter4",
"<>s__531$2$2",
}, module.GetFieldNames("C.<F2>d__1"));
});
#endif
}
}
}
......@@ -2113,7 +2113,7 @@ .maxstack 3
IL_0046: stfld ""int Test.<F>d__1.<>1__state""
IL_004b: ldarg.0
IL_004c: ldloc.2
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_0052: ldarg.0
IL_0053: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0058: ldloca.s V_2
......@@ -2121,10 +2121,10 @@ .maxstack 3
IL_005b: 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_0060: leave.s IL_00bb
IL_0062: ldarg.0
IL_0063: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_0063: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_0068: stloc.2
IL_0069: ldarg.0
IL_006a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_006a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_006f: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0075: ldarg.0
IL_0076: ldc.i4.m1
......@@ -2229,15 +2229,14 @@ .maxstack 2
c.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
{
// Code size 209 (0xd1)
// Code size 217 (0xd9)
.maxstack 3
.locals init (int V_0,
int V_1,
int V_2,
System.Runtime.CompilerServices.TaskAwaiter<int> V_3,
int V_4,
Test.<F>d__1 V_5,
System.Exception V_6)
System.Runtime.CompilerServices.TaskAwaiter<int> V_2,
int V_3,
Test.<F>d__1 V_4,
System.Exception V_5)
~IL_0000: ldarg.0
IL_0001: ldfld ""int Test.<F>d__1.<>1__state""
IL_0006: stloc.0
......@@ -2260,8 +2259,8 @@ .maxstack 3
IL_002e: stsfld ""System.Func<int> Test.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2""
IL_0033: callvirt ""System.Threading.Tasks.Task<int> System.Threading.Tasks.TaskFactory.StartNew<int>(System.Func<int>)""
IL_0038: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_003d: stloc.3
IL_003e: ldloca.s V_3
IL_003d: stloc.2
IL_003e: ldloca.s V_2
IL_0040: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_0045: brtrue.s IL_0088
IL_0047: ldarg.0
......@@ -2270,62 +2269,64 @@ .maxstack 3
IL_004a: stloc.0
IL_004b: stfld ""int Test.<F>d__1.<>1__state""
IL_0050: ldarg.0
IL_0051: ldloc.3
IL_0052: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_0051: ldloc.2
IL_0052: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_0057: ldarg.0
IL_0058: stloc.s V_5
IL_0058: stloc.s V_4
IL_005a: ldarg.0
IL_005b: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0060: ldloca.s V_3
IL_0062: ldloca.s V_5
IL_0060: ldloca.s V_2
IL_0062: ldloca.s V_4
IL_0064: 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_0069: nop
IL_006a: leave.s IL_00d0
IL_006a: leave.s IL_00d8
IL_006c: ldarg.0
IL_006d: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_0072: stloc.3
IL_006d: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_0072: stloc.2
IL_0073: ldarg.0
IL_0074: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_0074: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_0079: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_007f: ldarg.0
IL_0080: ldc.i4.m1
IL_0081: dup
IL_0082: stloc.0
IL_0083: stfld ""int Test.<F>d__1.<>1__state""
IL_0088: ldloca.s V_3
IL_0088: ldloca.s V_2
IL_008a: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_008f: stloc.s V_4
IL_0091: ldloca.s V_3
IL_0093: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0099: ldloc.s V_4
IL_009b: stloc.2
IL_009c: ldloc.2
IL_009d: stloc.1
IL_009e: leave.s IL_00bb
IL_008f: stloc.3
IL_0090: ldloca.s V_2
IL_0092: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0098: ldarg.0
IL_0099: ldloc.3
IL_009a: stfld ""int Test.<F>d__1.<>s__1""
IL_009f: ldarg.0
IL_00a0: ldfld ""int Test.<F>d__1.<>s__1""
IL_00a5: stloc.1
IL_00a6: leave.s IL_00c3
}
catch System.Exception
{
~IL_00a0: stloc.s V_6
IL_00a2: nop
IL_00a3: ldarg.0
IL_00a4: ldc.i4.s -2
IL_00a6: stfld ""int Test.<F>d__1.<>1__state""
~IL_00a8: stloc.s V_5
IL_00aa: nop
IL_00ab: ldarg.0
IL_00ac: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00b1: ldloc.s V_6
IL_00b3: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00b8: nop
IL_00b9: leave.s IL_00d0
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_5
IL_00bb: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00c0: nop
IL_00c1: leave.s IL_00d8
}
-IL_00bb: ldarg.0
IL_00bc: ldc.i4.s -2
IL_00be: stfld ""int Test.<F>d__1.<>1__state""
~IL_00c3: ldarg.0
IL_00c4: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00c9: ldloc.1
IL_00ca: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00cf: nop
IL_00d0: ret
-IL_00c3: ldarg.0
IL_00c4: ldc.i4.s -2
IL_00c6: stfld ""int Test.<F>d__1.<>1__state""
~IL_00cb: ldarg.0
IL_00cc: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00d1: ldloc.1
IL_00d2: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00d7: nop
IL_00d8: ret
}
", sequencePoints: "Test+<F>d__1.MoveNext");
......@@ -2422,7 +2423,7 @@ .maxstack 3
IL_0046: stfld ""int Test.<F>d__1.<>1__state""
IL_004b: ldarg.0
IL_004c: ldloc.1
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_0052: ldarg.0
IL_0053: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Test.<F>d__1.<>t__builder""
IL_0058: ldloca.s V_1
......@@ -2430,10 +2431,10 @@ .maxstack 3
IL_005b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<F>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<F>d__1)""
IL_0060: leave.s IL_00c1
IL_0062: ldarg.0
IL_0063: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_0063: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_0068: stloc.1
IL_0069: ldarg.0
IL_006a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter3""
IL_006a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__3""
IL_006f: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0075: ldarg.0
IL_0076: ldc.i4.m1
......@@ -2561,7 +2562,7 @@ .maxstack 3
IL_0046: stfld ""int Test.<F>d__1.<>1__state""
IL_004b: ldarg.0
IL_004c: ldloc.1
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter Test.<F>d__1.<>u__$awaiter3""
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter Test.<F>d__1.<>u__3""
IL_0052: ldarg.0
IL_0053: ldflda ""System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.<F>d__1.<>t__builder""
IL_0058: ldloca.s V_1
......@@ -2569,10 +2570,10 @@ .maxstack 3
IL_005b: call ""void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter, Test.<F>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.<F>d__1)""
IL_0060: leave.s IL_00c5
IL_0062: ldarg.0
IL_0063: ldfld ""System.Runtime.CompilerServices.TaskAwaiter Test.<F>d__1.<>u__$awaiter3""
IL_0063: ldfld ""System.Runtime.CompilerServices.TaskAwaiter Test.<F>d__1.<>u__3""
IL_0068: stloc.1
IL_0069: ldarg.0
IL_006a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter Test.<F>d__1.<>u__$awaiter3""
IL_006a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter Test.<F>d__1.<>u__3""
IL_006f: initobj ""System.Runtime.CompilerServices.TaskAwaiter""
IL_0075: ldarg.0
IL_0076: ldc.i4.m1
......@@ -3176,17 +3177,15 @@ public static async Task<int> F()
v.VerifyIL("Test.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"
{
// Code size 222 (0xde)
// Code size 238 (0xee)
.maxstack 3
.locals init (int V_0,
int V_1,
S[] V_2, //array
int V_3,
S& V_4,
System.Runtime.CompilerServices.TaskAwaiter<int> V_5,
int V_6,
Test.<F>d__1 V_7,
System.Exception V_8)
S& V_2,
System.Runtime.CompilerServices.TaskAwaiter<int> V_3,
int V_4,
Test.<F>d__1 V_5,
System.Exception V_6)
IL_0000: ldarg.0
IL_0001: ldfld ""int Test.<F>d__1.<>1__state""
IL_0006: stloc.0
......@@ -3195,92 +3194,96 @@ .maxstack 3
IL_0007: ldloc.0
IL_0008: brfalse.s IL_000c
IL_000a: br.s IL_000e
IL_000c: br.s IL_0067
IL_000c: br.s IL_006e
IL_000e: nop
IL_000f: ldc.i4.s 10
IL_0011: newarr ""S""
IL_0016: stloc.2
IL_0017: ldarg.0
IL_0018: ldloc.2
IL_0019: stfld ""S[] Test.<F>d__1.<>7__wrap1""
IL_001e: ldarg.0
IL_001f: ldfld ""S[] Test.<F>d__1.<>7__wrap1""
IL_0024: ldc.i4.1
IL_0025: ldelema ""S""
IL_002a: stloc.s V_4
IL_002c: call ""System.Threading.Tasks.Task<int> Test.G()""
IL_0031: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0036: stloc.s V_5
IL_0038: ldloca.s V_5
IL_003a: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_003f: brtrue.s IL_0084
IL_0041: ldarg.0
IL_0042: ldc.i4.0
IL_0043: dup
IL_0044: stloc.0
IL_0045: stfld ""int Test.<F>d__1.<>1__state""
IL_004a: ldarg.0
IL_004b: ldloc.s V_5
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter0""
IL_000f: ldarg.0
IL_0010: ldc.i4.s 10
IL_0012: newarr ""S""
IL_0017: stfld ""S[] Test.<F>d__1.<array>5__1""
IL_001c: ldarg.0
IL_001d: ldarg.0
IL_001e: ldfld ""S[] Test.<F>d__1.<array>5__1""
IL_0023: stfld ""S[] Test.<F>d__1.<>s__3""
IL_0028: ldarg.0
IL_0029: ldfld ""S[] Test.<F>d__1.<>s__3""
IL_002e: ldc.i4.1
IL_002f: ldelema ""S""
IL_0034: stloc.2
IL_0035: call ""System.Threading.Tasks.Task<int> Test.G()""
IL_003a: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_003f: stloc.3
IL_0040: ldloca.s V_3
IL_0042: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_0047: brtrue.s IL_008a
IL_0049: ldarg.0
IL_004a: ldc.i4.0
IL_004b: dup
IL_004c: stloc.0
IL_004d: stfld ""int Test.<F>d__1.<>1__state""
IL_0052: ldarg.0
IL_0053: stloc.s V_7
IL_0055: ldarg.0
IL_0056: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_005b: ldloca.s V_5
IL_005d: ldloca.s V_7
IL_005f: 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_0064: nop
IL_0065: leave.s IL_00dd
IL_0067: ldarg.0
IL_0068: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter0""
IL_006d: stloc.s V_5
IL_006f: ldarg.0
IL_0070: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__$awaiter0""
IL_0075: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_007b: ldarg.0
IL_007c: ldc.i4.m1
IL_007d: dup
IL_007e: stloc.0
IL_007f: stfld ""int Test.<F>d__1.<>1__state""
IL_0084: ldloca.s V_5
IL_0086: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_008b: stloc.s V_6
IL_008d: ldloca.s V_5
IL_008f: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0095: ldloc.s V_6
IL_0097: stloc.3
IL_0098: ldarg.0
IL_0099: ldfld ""S[] Test.<F>d__1.<>7__wrap1""
IL_009e: ldc.i4.1
IL_009f: ldelema ""S""
IL_00a4: ldloc.3
IL_00a5: call ""int S.Mutate(int)""
IL_00aa: stloc.1
IL_00ab: leave.s IL_00c8
IL_0053: ldloc.3
IL_0054: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__0""
IL_0059: ldarg.0
IL_005a: stloc.s V_5
IL_005c: ldarg.0
IL_005d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_0062: ldloca.s V_3
IL_0064: ldloca.s V_5
IL_0066: 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_006b: nop
IL_006c: leave.s IL_00ed
IL_006e: ldarg.0
IL_006f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__0""
IL_0074: stloc.3
IL_0075: ldarg.0
IL_0076: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__1.<>u__0""
IL_007b: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0081: ldarg.0
IL_0082: ldc.i4.m1
IL_0083: dup
IL_0084: stloc.0
IL_0085: stfld ""int Test.<F>d__1.<>1__state""
IL_008a: ldloca.s V_3
IL_008c: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_0091: stloc.s V_4
IL_0093: ldloca.s V_3
IL_0095: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_009b: ldarg.0
IL_009c: ldloc.s V_4
IL_009e: stfld ""int Test.<F>d__1.<>s__2""
IL_00a3: ldarg.0
IL_00a4: ldfld ""S[] Test.<F>d__1.<>s__3""
IL_00a9: ldc.i4.1
IL_00aa: ldelema ""S""
IL_00af: ldarg.0
IL_00b0: ldfld ""int Test.<F>d__1.<>s__2""
IL_00b5: call ""int S.Mutate(int)""
IL_00ba: stloc.1
IL_00bb: leave.s IL_00d8
}
catch System.Exception
{
IL_00ad: stloc.s V_8
IL_00af: nop
IL_00b0: ldarg.0
IL_00b1: ldc.i4.s -2
IL_00b3: stfld ""int Test.<F>d__1.<>1__state""
IL_00b8: ldarg.0
IL_00b9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00be: ldloc.s V_8
IL_00c0: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00c5: nop
IL_00c6: leave.s IL_00dd
IL_00bd: stloc.s V_6
IL_00bf: nop
IL_00c0: ldarg.0
IL_00c1: ldc.i4.s -2
IL_00c3: stfld ""int Test.<F>d__1.<>1__state""
IL_00c8: ldarg.0
IL_00c9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00ce: ldloc.s V_6
IL_00d0: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00d5: nop
IL_00d6: leave.s IL_00ed
}
IL_00c8: ldarg.0
IL_00c9: ldc.i4.s -2
IL_00cb: stfld ""int Test.<F>d__1.<>1__state""
IL_00d0: ldarg.0
IL_00d1: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00d6: ldloc.1
IL_00d7: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00dc: nop
IL_00dd: ret
IL_00d8: ldarg.0
IL_00d9: ldc.i4.s -2
IL_00db: stfld ""int Test.<F>d__1.<>1__state""
IL_00e0: ldarg.0
IL_00e1: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__1.<>t__builder""
IL_00e6: ldloc.1
IL_00e7: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00ec: nop
IL_00ed: ret
}
");
}
......
......@@ -14534,7 +14534,7 @@ .maxstack 10
IL_00f6: stfld ""int C.<M>d__1.<>1__state""
IL_00fb: ldarg.0
IL_00fc: ldloc.2
IL_00fd: stfld ""object C.<M>d__1.<>u__$awaiter4""
IL_00fd: stfld ""object C.<M>d__1.<>u__4""
IL_0102: ldloc.2
IL_0103: isinst ""System.Runtime.CompilerServices.ICriticalNotifyCompletion""
IL_0108: stloc.3
......@@ -14560,11 +14560,11 @@ .maxstack 10
IL_0136: stloc.3
IL_0137: leave IL_035a
IL_013c: ldarg.0
IL_013d: ldfld ""object C.<M>d__1.<>u__$awaiter4""
IL_013d: ldfld ""object C.<M>d__1.<>u__4""
IL_0142: stloc.2
IL_0143: ldarg.0
IL_0144: ldnull
IL_0145: stfld ""object C.<M>d__1.<>u__$awaiter4""
IL_0145: stfld ""object C.<M>d__1.<>u__4""
IL_014a: ldarg.0
IL_014b: ldc.i4.m1
IL_014c: dup
......@@ -14664,7 +14664,7 @@ .maxstack 10
IL_0280: stfld ""int C.<M>d__1.<>1__state""
IL_0285: ldarg.0
IL_0286: ldloc.2
IL_0287: stfld ""object C.<M>d__1.<>u__$awaiter4""
IL_0287: stfld ""object C.<M>d__1.<>u__4""
IL_028c: ldloc.2
IL_028d: isinst ""System.Runtime.CompilerServices.ICriticalNotifyCompletion""
IL_0292: stloc.3
......@@ -14690,11 +14690,11 @@ .maxstack 10
IL_02c0: stloc.3
IL_02c1: leave IL_035a
IL_02c6: ldarg.0
IL_02c7: ldfld ""object C.<M>d__1.<>u__$awaiter4""
IL_02c7: ldfld ""object C.<M>d__1.<>u__4""
IL_02cc: stloc.2
IL_02cd: ldarg.0
IL_02ce: ldnull
IL_02cf: stfld ""object C.<M>d__1.<>u__$awaiter4""
IL_02cf: stfld ""object C.<M>d__1.<>u__4""
IL_02d4: ldarg.0
IL_02d5: ldc.i4.m1
IL_02d6: dup
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -1180,8 +1176,6 @@ .maxstack 1
IL_0001: newobj ""Program.<M>d__0..ctor(int)""
IL_0006: ret
}");
#if TODO
var dbg = CompileAndVerify(source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
......@@ -1210,9 +1204,8 @@ .maxstack 2
IL_0010: ldloc.1
IL_0011: ret
}");
#endif
}
[Fact]
public void HoistedParameters_Enumerable()
{
......@@ -1238,10 +1231,10 @@ public static IEnumerable<int> F(int x, int y, int z)
"<>l__initialThreadId",
"x",
"<>3__x",
"y",
"<>3__y",
"z",
"<>3__z",
"y",
"<>3__y",
}, module.GetFieldNames("Test.<F>d__0"));
});
......@@ -1284,8 +1277,8 @@ public static IEnumerator<int> F(int x, int y, int z)
"<>1__state",
"<>2__current",
"x",
"y",
"z",
"y",
}, module.GetFieldNames("Test.<F>d__0"));
});
......@@ -1302,71 +1295,6 @@ public static IEnumerator<int> F(int x, int y, int z)
});
}
[Fact]
public void SynthesizedVariables1()
{
var source =
@"
using System;
using System.Collections.Generic;
class C
{
public IEnumerable<int> M(IDisposable disposable)
{
foreach (var item in new[] { 1, 2, 3 }) { lock (this) { yield return 1; } }
foreach (var item in new[] { 1, 2, 3 }) { }
lock (this) { yield return 2; }
if (disposable != null) { using (disposable) { yield return 3; } }
lock (this) { yield return 4; }
if (disposable != null) { using (disposable) { } }
lock (this) { }
}
}";
CompileAndVerify(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
{
"<>1__state",
"<>2__current",
"<>l__initialThreadId",
"disposable",
"<>3__disposable",
"<>4__this",
"<>7__wrap1",
"<>7__wrap2",
"<>7__wrap3",
"<>7__wrap4",
"<>7__wrap5",
}, module.GetFieldNames("C.<M>d__0"));
});
#if TODO
CompileAndVerify(source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
AssertEx.Equal(new[]
{
"<>1__state",
"<>2__current",
"<>l__initialThreadId",
"<>4__this",
"disposable",
"<>3__disposable",
"<>s__6$1",
"<>s__7$2",
"<item>5__1",
"<>s__2$3",
"<>s__520$4",
"<item>5__2",
"<>s__2$5",
"<>s__520$6",
"<>s__3$7",
"<>s__2$8",
"<>s__520$9",
}, module.GetFieldNames("C.<M>d__0"));
});
#endif
}
[Fact]
public void IteratorForEach()
{
......
......@@ -9,8 +9,10 @@
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests;
using Microsoft.CodeAnalysis.Emit;
using Roslyn.Test.MetadataUtilities;
using Roslyn.Test.PdbUtilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities.Pdb;
......@@ -20,8 +22,18 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests
{
public abstract class EditAndContinueTestBase : EmitMetadataTestBase
{
// PDB reader can only be accessed from a single thread, so avoid concurrent compilation:
protected readonly CSharpCompilationOptions ComSafeDebugDll = TestOptions.DebugDll.WithConcurrentBuild(false);
internal static readonly Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> EmptyLocalsProvider = handle => default(EditAndContinueMethodDebugInformation);
internal static string Visualize(ModuleMetadata baseline, PinnedMetadata delta)
{
var result = new StringWriter();
new MetadataVisualizer(new[] { baseline.MetadataReader, delta.Reader }, result).VisualizeAllGenerations();
return result.ToString();
}
internal static ImmutableArray<SyntaxNode> GetAllLocals(MethodSymbol method)
{
var sourceMethod = method as SourceMethodSymbol;
......
......@@ -17,9 +17,6 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests
{
public class LocalSlotMappingTests : EditAndContinueTestBase
{
// PDB reader can only be accessed from a single thread, so avoid concurrent compilation:
private readonly CSharpCompilationOptions ComSafeDebugDll = TestOptions.DebugDll.WithConcurrentBuild(false);
[Fact]
public void OutOfOrderUserLocals()
{
......@@ -2838,7 +2835,7 @@ .maxstack 2
}
[Fact]
public void SyntheziedVariablesInIterator1()
public void SynthesizedVariablesInIterator1()
{
var source = @"
using System.Collections.Generic;
......@@ -2859,12 +2856,10 @@ public IEnumerable<int> F()
v0.VerifyIL("C.<F>d__0.System.Collections.IEnumerator.MoveNext", @"
{
// Code size 101 (0x65)
// Code size 137 (0x89)
.maxstack 2
.locals init (int V_0,
bool V_1,
System.Collections.Generic.IEnumerable<int> V_2,
bool V_3)
bool V_1)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<F>d__0.<>1__state""
IL_0006: stloc.0
......@@ -2876,7 +2871,7 @@ .maxstack 2
IL_000e: beq.s IL_0014
IL_0010: br.s IL_0016
IL_0012: br.s IL_001a
IL_0014: br.s IL_005a
IL_0014: br.s IL_007e
IL_0016: ldc.i4.0
IL_0017: stloc.1
IL_0018: ldloc.1
......@@ -2885,46 +2880,55 @@ .maxstack 2
IL_001b: ldc.i4.m1
IL_001c: stfld ""int C.<F>d__0.<>1__state""
IL_0021: nop
IL_0022: ldc.i4.0
IL_0023: stloc.3
IL_0022: ldarg.0
IL_0023: ldc.i4.0
IL_0024: stfld ""bool C.<F>d__0.<>s__2""
.try
{
IL_0024: ldarg.0
IL_0025: ldfld ""C C.<F>d__0.<>4__this""
IL_002a: callvirt ""System.Collections.Generic.IEnumerable<int> C.F()""
IL_002f: stloc.2
IL_0030: ldloc.2
IL_0031: ldloca.s V_3
IL_0033: call ""void System.Threading.Monitor.Enter(object, ref bool)""
IL_0038: nop
IL_0039: nop
IL_003a: nop
IL_003b: leave.s IL_0048
IL_0029: ldarg.0
IL_002a: ldarg.0
IL_002b: ldfld ""C C.<F>d__0.<>4__this""
IL_0030: callvirt ""System.Collections.Generic.IEnumerable<int> C.F()""
IL_0035: stfld ""System.Collections.Generic.IEnumerable<int> C.<F>d__0.<>s__1""
IL_003a: ldarg.0
IL_003b: ldfld ""System.Collections.Generic.IEnumerable<int> C.<F>d__0.<>s__1""
IL_0040: ldarg.0
IL_0041: ldflda ""bool C.<F>d__0.<>s__2""
IL_0046: call ""void System.Threading.Monitor.Enter(object, ref bool)""
IL_004b: nop
IL_004c: nop
IL_004d: nop
IL_004e: leave.s IL_0065
}
finally
{
IL_003d: ldloc.3
IL_003e: brfalse.s IL_0047
IL_0040: ldloc.2
IL_0041: call ""void System.Threading.Monitor.Exit(object)""
IL_0046: nop
IL_0047: endfinally
IL_0050: ldarg.0
IL_0051: ldfld ""bool C.<F>d__0.<>s__2""
IL_0056: brfalse.s IL_0064
IL_0058: ldarg.0
IL_0059: ldfld ""System.Collections.Generic.IEnumerable<int> C.<F>d__0.<>s__1""
IL_005e: call ""void System.Threading.Monitor.Exit(object)""
IL_0063: nop
IL_0064: endfinally
}
IL_0048: ldarg.0
IL_0049: ldc.i4.1
IL_004a: stfld ""int C.<F>d__0.<>2__current""
IL_004f: ldarg.0
IL_0050: ldc.i4.1
IL_0051: stfld ""int C.<F>d__0.<>1__state""
IL_0056: ldc.i4.1
IL_0057: stloc.1
IL_0058: br.s IL_0018
IL_005a: ldarg.0
IL_005b: ldc.i4.m1
IL_005c: stfld ""int C.<F>d__0.<>1__state""
IL_0061: ldc.i4.0
IL_0062: stloc.1
IL_0063: br.s IL_0018
IL_0065: ldarg.0
IL_0066: ldnull
IL_0067: stfld ""System.Collections.Generic.IEnumerable<int> C.<F>d__0.<>s__1""
IL_006c: ldarg.0
IL_006d: ldc.i4.1
IL_006e: stfld ""int C.<F>d__0.<>2__current""
IL_0073: ldarg.0
IL_0074: ldc.i4.1
IL_0075: stfld ""int C.<F>d__0.<>1__state""
IL_007a: ldc.i4.1
IL_007b: stloc.1
IL_007c: br.s IL_0018
IL_007e: ldarg.0
IL_007f: ldc.i4.m1
IL_0080: stfld ""int C.<F>d__0.<>1__state""
IL_0085: ldc.i4.0
IL_0086: stloc.1
IL_0087: br.s IL_0018
}");
#if TODO
......@@ -2944,7 +2948,7 @@ .maxstack 2
}
[Fact]
public void SyntheziedVariablesInAsyncMethod1()
public void SynthesizedVariablesInAsyncMethod1()
{
var source = @"
using System.Threading.Tasks;
......@@ -2966,15 +2970,13 @@ public async Task<int> F()
v0.VerifyIL("C.<F>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
{
// Code size 220 (0xdc)
// Code size 255 (0xff)
.maxstack 3
.locals init (int V_0,
int V_1,
System.Threading.Tasks.Task<int> V_2,
bool V_3,
System.Runtime.CompilerServices.TaskAwaiter<int> V_4,
C.<F>d__1 V_5,
System.Exception V_6)
System.Runtime.CompilerServices.TaskAwaiter<int> V_2,
C.<F>d__1 V_3,
System.Exception V_4)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<F>d__1.<>1__state""
IL_0006: stloc.0
......@@ -2982,105 +2984,114 @@ .maxstack 3
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_000c
IL_000a: br.s IL_000e
IL_000c: br.s IL_007a
IL_000e: nop
IL_000f: ldc.i4.0
IL_0010: stloc.3
IL_000a: br.s IL_0011
IL_000c: br IL_009e
IL_0011: nop
IL_0012: ldarg.0
IL_0013: ldc.i4.0
IL_0014: stfld ""bool C.<F>d__1.<>s__2""
.try
{
IL_0011: ldarg.0
IL_0012: ldfld ""C C.<F>d__1.<>4__this""
IL_0017: callvirt ""System.Threading.Tasks.Task<int> C.F()""
IL_001c: stloc.2
IL_001d: ldloc.2
IL_001e: ldloca.s V_3
IL_0020: call ""void System.Threading.Monitor.Enter(object, ref bool)""
IL_0025: nop
IL_0026: nop
IL_0027: nop
IL_0028: leave.s IL_0039
IL_0019: ldarg.0
IL_001a: ldarg.0
IL_001b: ldfld ""C C.<F>d__1.<>4__this""
IL_0020: callvirt ""System.Threading.Tasks.Task<int> C.F()""
IL_0025: stfld ""System.Threading.Tasks.Task<int> C.<F>d__1.<>s__1""
IL_002a: ldarg.0
IL_002b: ldfld ""System.Threading.Tasks.Task<int> C.<F>d__1.<>s__1""
IL_0030: ldarg.0
IL_0031: ldflda ""bool C.<F>d__1.<>s__2""
IL_0036: call ""void System.Threading.Monitor.Enter(object, ref bool)""
IL_003b: nop
IL_003c: nop
IL_003d: nop
IL_003e: leave.s IL_0059
}
finally
{
IL_002a: ldloc.0
IL_002b: ldc.i4.0
IL_002c: bge.s IL_0038
IL_002e: ldloc.3
IL_002f: brfalse.s IL_0038
IL_0031: ldloc.2
IL_0032: call ""void System.Threading.Monitor.Exit(object)""
IL_0037: nop
IL_0038: endfinally
IL_0040: ldloc.0
IL_0041: ldc.i4.0
IL_0042: bge.s IL_0058
IL_0044: ldarg.0
IL_0045: ldfld ""bool C.<F>d__1.<>s__2""
IL_004a: brfalse.s IL_0058
IL_004c: ldarg.0
IL_004d: ldfld ""System.Threading.Tasks.Task<int> C.<F>d__1.<>s__1""
IL_0052: call ""void System.Threading.Monitor.Exit(object)""
IL_0057: nop
IL_0058: endfinally
}
IL_0039: ldarg.0
IL_003a: ldfld ""C C.<F>d__1.<>4__this""
IL_003f: callvirt ""System.Threading.Tasks.Task<int> C.F()""
IL_0044: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0049: stloc.s V_4
IL_004b: ldloca.s V_4
IL_004d: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_0052: brtrue.s IL_0097
IL_0054: ldarg.0
IL_0055: ldc.i4.0
IL_0056: dup
IL_0057: stloc.0
IL_0058: stfld ""int C.<F>d__1.<>1__state""
IL_005d: ldarg.0
IL_005e: ldloc.s V_4
IL_0060: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> C.<F>d__1.<>u__$awaiter0""
IL_0065: ldarg.0
IL_0066: stloc.s V_5
IL_0068: ldarg.0
IL_0069: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> C.<F>d__1.<>t__builder""
IL_006e: ldloca.s V_4
IL_0070: ldloca.s V_5
IL_0072: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, C.<F>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref C.<F>d__1)""
IL_0077: nop
IL_0078: leave.s IL_00db
IL_0059: ldarg.0
IL_005a: ldnull
IL_005b: stfld ""System.Threading.Tasks.Task<int> C.<F>d__1.<>s__1""
IL_0060: ldarg.0
IL_0061: ldfld ""C C.<F>d__1.<>4__this""
IL_0066: callvirt ""System.Threading.Tasks.Task<int> C.F()""
IL_006b: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0070: stloc.2
IL_0071: ldloca.s V_2
IL_0073: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_0078: brtrue.s IL_00ba
IL_007a: ldarg.0
IL_007b: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> C.<F>d__1.<>u__$awaiter0""
IL_0080: stloc.s V_4
IL_0082: ldarg.0
IL_0083: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> C.<F>d__1.<>u__$awaiter0""
IL_0088: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_008e: ldarg.0
IL_008f: ldc.i4.m1
IL_0090: dup
IL_0091: stloc.0
IL_0092: stfld ""int C.<F>d__1.<>1__state""
IL_0097: ldloca.s V_4
IL_0099: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_009e: pop
IL_009f: ldloca.s V_4
IL_00a1: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00a7: ldc.i4.1
IL_00a8: stloc.1
IL_00a9: leave.s IL_00c6
IL_007b: ldc.i4.0
IL_007c: dup
IL_007d: stloc.0
IL_007e: stfld ""int C.<F>d__1.<>1__state""
IL_0083: ldarg.0
IL_0084: ldloc.2
IL_0085: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> C.<F>d__1.<>u__0""
IL_008a: ldarg.0
IL_008b: stloc.3
IL_008c: ldarg.0
IL_008d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> C.<F>d__1.<>t__builder""
IL_0092: ldloca.s V_2
IL_0094: ldloca.s V_3
IL_0096: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, C.<F>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref C.<F>d__1)""
IL_009b: nop
IL_009c: leave.s IL_00fe
IL_009e: ldarg.0
IL_009f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> C.<F>d__1.<>u__0""
IL_00a4: stloc.2
IL_00a5: ldarg.0
IL_00a6: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> C.<F>d__1.<>u__0""
IL_00ab: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00b1: ldarg.0
IL_00b2: ldc.i4.m1
IL_00b3: dup
IL_00b4: stloc.0
IL_00b5: stfld ""int C.<F>d__1.<>1__state""
IL_00ba: ldloca.s V_2
IL_00bc: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_00c1: pop
IL_00c2: ldloca.s V_2
IL_00c4: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_00ca: ldc.i4.1
IL_00cb: stloc.1
IL_00cc: leave.s IL_00e9
}
catch System.Exception
{
IL_00ab: stloc.s V_6
IL_00ad: nop
IL_00ae: ldarg.0
IL_00af: ldc.i4.s -2
IL_00b1: stfld ""int C.<F>d__1.<>1__state""
IL_00b6: ldarg.0
IL_00b7: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> C.<F>d__1.<>t__builder""
IL_00bc: ldloc.s V_6
IL_00be: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00c3: nop
IL_00c4: leave.s IL_00db
IL_00ce: stloc.s V_4
IL_00d0: nop
IL_00d1: ldarg.0
IL_00d2: ldc.i4.s -2
IL_00d4: stfld ""int C.<F>d__1.<>1__state""
IL_00d9: ldarg.0
IL_00da: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> C.<F>d__1.<>t__builder""
IL_00df: ldloc.s V_4
IL_00e1: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00e6: nop
IL_00e7: leave.s IL_00fe
}
IL_00c6: ldarg.0
IL_00c7: ldc.i4.s -2
IL_00c9: stfld ""int C.<F>d__1.<>1__state""
IL_00ce: ldarg.0
IL_00cf: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> C.<F>d__1.<>t__builder""
IL_00d4: ldloc.1
IL_00d5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00da: nop
IL_00db: ret
IL_00e9: ldarg.0
IL_00ea: ldc.i4.s -2
IL_00ec: stfld ""int C.<F>d__1.<>1__state""
IL_00f1: ldarg.0
IL_00f2: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> C.<F>d__1.<>t__builder""
IL_00f7: ldloc.1
IL_00f8: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00fd: nop
IL_00fe: ret
}
");
......
......@@ -153,9 +153,19 @@ IEnumerable<int> M()
// all of the changes look reasonable. The main thing for this test is that
// Dev10 creates fields for the locals in the iterator class. Roslyn doesn't
// do that - the <constant> in the <scope> is sufficient.
var compilation = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugDll);
var v = CompileAndVerify(source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
Assert.Equal(new[]
{
"<>1__state",
"<>2__current",
"<>l__initialThreadId",
"<>4__this",
"<i>5__1"
}, module.GetFieldNames("C.<M>d__0"));
});
compilation.VerifyPdb(@"
v.VerifyPdb("C+<M>d__0.MoveNext", @"
<symbols>
<methods>
<method containingType=""C+&lt;M&gt;d__0"" name=""MoveNext"" parameterNames="""">
......@@ -163,9 +173,9 @@ IEnumerable<int> M()
<using version=""4"" kind=""UsingInfo"" size=""12"" namespaceCount=""1"">
<namespace usingCount=""1"" />
</using>
<iteratorLocals version=""4"" kind=""IteratorLocals"" size=""20"" bucketCount=""1"">
<bucket startOffset=""0x22"" endOffset=""0x6b"" />
</iteratorLocals>
<hoistedLocalScopes version=""4"" kind=""StateMachineHoistedLocalScopes"" size=""20"" count=""1"">
<slot startOffset=""0x22"" endOffset=""0x6b"" />
</hoistedLocalScopes>
<encLocalSlotMap version=""4"" kind=""EditAndContinueLocalSlotMap"" size=""16"">
<slot kind=""27"" offset=""0"" />
<slot kind=""temp"" />
......
......@@ -52,6 +52,7 @@
<Compile Include="CodeGen\LocalDefinition.cs" />
<Compile Include="CodeGen\LocalOrParameter.cs" />
<Compile Include="CodeGen\LocalScopeManager.cs" />
<Compile Include="CodeGen\LocalSlotDebugInfo.cs" />
<Compile Include="CodeGen\LocalSlotManager.cs" />
<Compile Include="CodeGen\MetadataConstant.cs" />
<Compile Include="CodeGen\MetadataCreateArray.cs" />
......@@ -68,6 +69,7 @@
<Compile Include="CodeGen\SwitchIntegralJumpTableEmitter.cs" />
<Compile Include="CodeGen\SwitchIntegralJumpTableEmitter.SwitchBucket.cs" />
<Compile Include="CodeGen\SwitchStringJumpTableEmitter.cs" />
<Compile Include="CodeGen\SynthesizedLocalOrdinalsDispenser.cs" />
<Compile Include="CodeGen\TokenMap.cs" />
<Compile Include="CodeGen\VariableSlotAllocator.cs" />
<Compile Include="CodeGen\Win32Res.cs" />
......@@ -88,7 +90,9 @@
<Compile Include="Collections\IdentifierCollection.cs" />
<Compile Include="Collections\ImmutableArrayExtensions.cs" />
<Compile Include="Collections\ImmutableMemoryStream.cs" />
<Compile Include="Collections\OrderedSet.cs" />
<Compile Include="Emit\DebugInformationFormat.cs" />
<Compile Include="Emit\EditAndContinue\EncHoistedLocalMetadata.cs" />
<Compile Include="Emit\EmitOptions.cs" />
<Compile Include="DiagnosticAnalyzer\AsyncQueue.TaskCompletionSourceWithCancellation.cs" />
<Compile Include="Emit\EditAndContinue\EncVariableSlotAllocator.cs" />
......@@ -302,6 +306,7 @@
<Compile Include="MetadataReference\PortableExecutableReference.cs" />
<Compile Include="MetadataReference\ReferenceDirective.cs" />
<Compile Include="MetadataReference\UnresolvedMetadataReference.cs" />
<Compile Include="PEWriter\StateMachineHoistedLocalScope.cs" />
<Compile Include="SymbolDisplay\ObjectDisplayExtensions.cs" />
<Compile Include="SymbolDisplay\ObjectDisplayOptions.cs" />
<Compile Include="Optional.cs" />
......
......@@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -214,14 +215,14 @@ internal ImmutableArray<Cci.LocalScope> GetAllScopes(bool edgeInclusive = false)
/// <param name="edgeInclusive">Specifies whether scope spans should be reported as edge inclusive
/// (position at "start + length" is IN the scope). VB EE expects that.</param>
/// <returns></returns>
internal ImmutableArray<Cci.LocalScope> GetIteratorScopes(bool edgeInclusive = false)
internal ImmutableArray<Cci.StateMachineHoistedLocalScope> GetHoistedLocalScopes(bool edgeInclusive = false)
{
// The iterator scopes are enumerated and returned here, sorted by variable "index",
// The hoisted local scopes are enumerated and returned here, sorted by variable "index",
// which is a number appearing after the "__" at the end of the field's name. The index should
// correspond to the location in the returned sequence. Indices are 1-based, which means that the
// "first" element at the resulting list (i.e. index 0) corresponds to the variable whose name ends
// with "__1".
return scopeManager.GetIteratorScopes(edgeInclusive);
return scopeManager.GetHoistedLocalScopes(edgeInclusive);
}
internal void FreeBasicBlocks()
......@@ -1170,15 +1171,11 @@ internal void OpenIteratorScope()
OpenLocalScope(ScopeType.IteratorVariable);
}
/// <summary>
/// See Microsoft.Cci.ILocalScopeProvider.GetIteratorScopes. This is called within an
/// iterator scope to identify that a field named "&lt;Xyzzy&gt;5__i" is used to represent
/// a local variable named Xyzzy. The "i" is an integer, which is passed to this method
/// as the index parameter.
/// </summary>
internal void DefineIteratorLocal(int index)
internal void DefineUserDefinedStateMachineHoistedLocal(int slotIndex)
{
scopeManager.AddIteratorVariable(index - 1);
// Add user-defined local into the current scope.
// We emit custom debug information for these locals that is used by the EE to reconstruct their scopes.
scopeManager.AddUserHoistedLocal(slotIndex);
}
internal void CloseIteratorScope()
......
......@@ -29,8 +29,8 @@ internal struct LocalDebugId : IEquatable<LocalDebugId>
/// <summary>
/// If a single node is a declarator for multiple variables of the same synthesized kind (it can only happen for synthesized variables)
/// we calculate additional number "ordinal" for such variable.We assign the ordinals to the synthesized variables with the same kind
/// and syntax offset in the order as they appear in the lowered bound tree. It is important that a valid EnC edit can't change
/// we calculate additional number "ordinal" for such variable. We assign the ordinals to the synthesized variables with the same kind
/// and syntax offset in the order as they appear in the lowered bound tree. It is important that a valid EnC edit can't change
/// the ordinal of a synthesized variable. If it could it would need to be assigned a different kind or associated with a different declarator node.
/// </summary>
public readonly int Ordinal;
......@@ -47,16 +47,15 @@ private LocalDebugId(bool isNone)
{
Debug.Assert(isNone);
this.SyntaxOffset = 0;
this.SyntaxOffset = -1;
this.Ordinal = -1;
this.Subordinal = 0;
this.Subordinal = -1;
}
public LocalDebugId(int syntaxOffset, int ordinal = 0, int subordinal = 0)
{
Debug.Assert(ordinal >= 0);
Debug.Assert(subordinal >= 0);
Debug.Assert(subordinal == 0 || ordinal > 0);
this.SyntaxOffset = syntaxOffset;
this.Ordinal = ordinal;
......
......@@ -129,7 +129,7 @@ internal void AddLocalConstant(LocalConstantDefinition constant)
internal ImmutableArray<Cci.LocalScope> GetAllScopesWithLocals(bool edgeInclusive = false)
{
var result = ArrayBuilder<Cci.LocalScope>.GetInstance();
ScopeBounds rootBounds = rootScope.GetScopesWithLocals(result, edgeInclusive);
ScopeBounds rootBounds = rootScope.GetLocalScopes(result, edgeInclusive);
uint expectedRootScopeLength = rootBounds.End - rootBounds.Begin;
if (edgeInclusive)
......@@ -166,17 +166,17 @@ internal ImmutableArray<Cci.ExceptionHandlerRegion> GetExceptionHandlerRegions()
return result.ToImmutableAndFree();
}
internal ImmutableArray<Cci.LocalScope> GetIteratorScopes(bool edgeInclusive)
internal ImmutableArray<Cci.StateMachineHoistedLocalScope> GetHoistedLocalScopes(bool edgeInclusive)
{
var result = ArrayBuilder<Cci.LocalScope>.GetInstance();
rootScope.GetIteratorScopes(result, edgeInclusive);
var result = ArrayBuilder<Cci.StateMachineHoistedLocalScope>.GetInstance();
rootScope.GetHoistedLocalScopes(result, edgeInclusive);
return result.ToImmutableAndFree();
}
internal void AddIteratorVariable(int index)
internal void AddUserHoistedLocal(int slotIndex)
{
var scope = (LocalScopeInfo)CurrentScope;
scope.AddIteratorVariable(index);
scope.AddUserHoistedLocal(slotIndex);
}
internal void FreeBasicBlocks()
......@@ -266,9 +266,9 @@ public bool IsExceptionHandler
/// Recursively calculates the start and end of the given scope.
/// Only scopes with locals are actually dumped to the list.
/// </summary>
internal abstract ScopeBounds GetScopesWithLocals(ArrayBuilder<Cci.LocalScope> scopesWithVariables, bool edgeInclusive);
internal abstract ScopeBounds GetLocalScopes(ArrayBuilder<Cci.LocalScope> result, bool edgeInclusive);
protected static ScopeBounds GetScopesWithLocals<TScopeInfo>(ArrayBuilder<Cci.LocalScope> scopesWithVariables, ImmutableArray<TScopeInfo>.Builder scopes, bool edgeInclusive)
protected static ScopeBounds GetLocalScopes<TScopeInfo>(ArrayBuilder<Cci.LocalScope> result, ImmutableArray<TScopeInfo>.Builder scopes, bool edgeInclusive)
where TScopeInfo : ScopeInfo
{
Debug.Assert(scopes.Count > 0);
......@@ -278,7 +278,7 @@ protected static ScopeBounds GetScopesWithLocals<TScopeInfo>(ArrayBuilder<Cci.Lo
foreach (var scope in scopes)
{
ScopeBounds bounds = scope.GetScopesWithLocals(scopesWithVariables, edgeInclusive);
ScopeBounds bounds = scope.GetLocalScopes(result, edgeInclusive);
begin = Math.Min(begin, bounds.Begin);
end = Math.Max(end, bounds.End);
}
......@@ -290,9 +290,9 @@ protected static ScopeBounds GetScopesWithLocals<TScopeInfo>(ArrayBuilder<Cci.Lo
/// Recursively calculates the start and end of the given scope.
/// Only scopes with locals are actually dumped to the list.
/// </summary>
internal abstract ScopeBounds GetIteratorScopes(ArrayBuilder<Cci.LocalScope> scopesWithIteratorLocals, bool edgeInclusive);
internal abstract ScopeBounds GetHoistedLocalScopes(ArrayBuilder<Cci.StateMachineHoistedLocalScope> result, bool edgeInclusive);
protected static ScopeBounds GetIteratorScopes<TScopeInfo>(ArrayBuilder<Cci.LocalScope> scopesWithIteratorLocals, ImmutableArray<TScopeInfo>.Builder scopes, bool edgeInclusive)
protected static ScopeBounds GetHoistedLocalScopes<TScopeInfo>(ArrayBuilder<Cci.StateMachineHoistedLocalScope> result, ImmutableArray<TScopeInfo>.Builder scopes, bool edgeInclusive)
where TScopeInfo : ScopeInfo
{
Debug.Assert(scopes.Count > 0);
......@@ -302,7 +302,7 @@ protected static ScopeBounds GetIteratorScopes<TScopeInfo>(ArrayBuilder<Cci.Loca
foreach (var scope in scopes)
{
ScopeBounds bounds = scope.GetIteratorScopes(scopesWithIteratorLocals, edgeInclusive);
ScopeBounds bounds = scope.GetHoistedLocalScopes(result, edgeInclusive);
begin = Math.Min(begin, bounds.Begin);
end = Math.Max(end, bounds.End);
}
......@@ -329,7 +329,7 @@ internal class LocalScopeInfo : ScopeInfo
{
private ImmutableArray<LocalDefinition>.Builder LocalVariables;
private ImmutableArray<LocalConstantDefinition>.Builder LocalConstants;
private ImmutableArray<int>.Builder IteratorVariables;
private ImmutableArray<int>.Builder StateMachineUserHoistedLocalSlotIndices;
// Nested scopes and blocks are not relevant for PDB.
// We need these only to figure scope bounds.
......@@ -379,14 +379,15 @@ internal void AddLocalConstant(LocalConstantDefinition constant)
LocalConstants.Add(constant);
}
internal void AddIteratorVariable(int index)
internal void AddUserHoistedLocal(int slotIndex)
{
if (IteratorVariables == null)
if (StateMachineUserHoistedLocalSlotIndices == null)
{
IteratorVariables = ImmutableArray.CreateBuilder<int>(1);
StateMachineUserHoistedLocalSlotIndices = ImmutableArray.CreateBuilder<int>(1);
}
IteratorVariables.Add(index);
Debug.Assert(slotIndex >= 0);
StateMachineUserHoistedLocalSlotIndices.Add(slotIndex);
}
internal override bool ContainsLocal(LocalDefinition local)
......@@ -441,7 +442,7 @@ internal override void GetExceptionHandlerRegions(ArrayBuilder<Cci.ExceptionHand
}
}
internal override ScopeBounds GetScopesWithLocals(ArrayBuilder<Cci.LocalScope> scopesWithVariables, bool edgeInclusive)
internal override ScopeBounds GetLocalScopes(ArrayBuilder<Cci.LocalScope> result, bool edgeInclusive)
{
uint begin = uint.MaxValue;
uint end = 0;
......@@ -466,7 +467,7 @@ internal override ScopeBounds GetScopesWithLocals(ArrayBuilder<Cci.LocalScope> s
// also may need to adjust current scope bounds.
if (NestedScopes != null)
{
ScopeBounds nestedBounds = GetScopesWithLocals(scopesWithVariables, NestedScopes, edgeInclusive);
ScopeBounds nestedBounds = GetLocalScopes(result, NestedScopes, edgeInclusive);
begin = Math.Min(begin, nestedBounds.Begin);
end = Math.Max(end, nestedBounds.End);
}
......@@ -482,13 +483,13 @@ internal override ScopeBounds GetScopesWithLocals(ArrayBuilder<Cci.LocalScope> s
this.LocalConstants.AsImmutableOrEmpty<Cci.ILocalDefinition>(),
this.LocalVariables.AsImmutableOrEmpty<Cci.ILocalDefinition>());
scopesWithVariables.Add(newScope);
result.Add(newScope);
}
return new ScopeBounds(begin, end);
}
internal override ScopeBounds GetIteratorScopes(ArrayBuilder<Cci.LocalScope> scopesWithIteratorLocals, bool edgeInclusive)
internal override ScopeBounds GetHoistedLocalScopes(ArrayBuilder<Cci.StateMachineHoistedLocalScope> result, bool edgeInclusive)
{
uint begin = uint.MaxValue;
uint end = 0;
......@@ -513,30 +514,26 @@ internal override ScopeBounds GetIteratorScopes(ArrayBuilder<Cci.LocalScope> sco
// also may need to adjust current scope bounds.
if (NestedScopes != null)
{
ScopeBounds nestedBounds = GetIteratorScopes(scopesWithIteratorLocals, NestedScopes, edgeInclusive);
ScopeBounds nestedBounds = GetHoistedLocalScopes(result, NestedScopes, edgeInclusive);
begin = Math.Min(begin, nestedBounds.Begin);
end = Math.Max(end, nestedBounds.End);
}
// we are not interested in scopes with no variables or no code in them.
if (this.IteratorVariables != null && end > begin)
if (this.StateMachineUserHoistedLocalSlotIndices != null && end > begin)
{
uint endAdjusted = edgeInclusive ? end - 1 : end;
var newScope = new Cci.LocalScope(
begin,
endAdjusted - begin,
ImmutableArray<Cci.ILocalDefinition>.Empty,
ImmutableArray<Cci.ILocalDefinition>.Empty);
var newScope = new Cci.StateMachineHoistedLocalScope(begin, endAdjusted - begin);
foreach (var iv in this.IteratorVariables)
foreach (var slotIndex in this.StateMachineUserHoistedLocalSlotIndices)
{
while (scopesWithIteratorLocals.Count <= iv)
while (result.Count <= slotIndex)
{
scopesWithIteratorLocals.Add(default(Cci.LocalScope));
result.Add(default(Cci.StateMachineHoistedLocalScope));
}
scopesWithIteratorLocals[iv] = newScope;
result[slotIndex] = newScope;
}
}
......@@ -870,20 +867,20 @@ internal override void GetExceptionHandlerRegions(ArrayBuilder<Cci.ExceptionHand
}
}
internal override ScopeBounds GetScopesWithLocals(ArrayBuilder<Cci.LocalScope> scopesWithVariables, bool edgeInclusive)
internal override ScopeBounds GetLocalScopes(ArrayBuilder<Cci.LocalScope> scopesWithVariables, bool edgeInclusive)
{
return GetScopesWithLocals(scopesWithVariables, this.handlers, edgeInclusive);
return GetLocalScopes(scopesWithVariables, this.handlers, edgeInclusive);
}
internal override ScopeBounds GetIteratorScopes(ArrayBuilder<Cci.LocalScope> scopesWithIteratorVariables, bool edgeInclusive)
internal override ScopeBounds GetHoistedLocalScopes(ArrayBuilder<Cci.StateMachineHoistedLocalScope> result, bool edgeInclusive)
{
return GetIteratorScopes(scopesWithIteratorVariables, this.handlers, edgeInclusive);
return GetHoistedLocalScopes(result, this.handlers, edgeInclusive);
}
private static ScopeBounds GetBounds(ExceptionHandlerScope scope)
{
var scopes = ArrayBuilder<Cci.LocalScope>.GetInstance();
var result = scope.GetScopesWithLocals(scopes, edgeInclusive: false);
var result = scope.GetLocalScopes(scopes, edgeInclusive: false);
scopes.Free();
return result;
}
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.CodeGen
{
internal struct LocalSlotDebugInfo
{
public readonly SynthesizedLocalKind SynthesizedKind;
public readonly LocalDebugId Id;
public LocalSlotDebugInfo(SynthesizedLocalKind synthesizedKind, LocalDebugId id)
{
this.SynthesizedKind = synthesizedKind;
this.Id = id;
}
}
}
......@@ -2,6 +2,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGen
......@@ -21,8 +22,9 @@ internal sealed class MethodBody : Cci.IMethodBody
private readonly ImmutableArray<Cci.ExceptionHandlerRegion> exceptionHandlers;
private readonly ImmutableArray<Cci.LocalScope> localScopes;
private readonly ImmutableArray<Cci.NamespaceScope> namespaceScopes;
private readonly string iteratorClassName;
private readonly ImmutableArray<Cci.LocalScope> iteratorScopes;
private readonly string stateMachineTypeNameOpt;
private readonly ImmutableArray<Cci.StateMachineHoistedLocalScope> stateMachineHoistedLocalScopes;
private readonly ImmutableArray<LocalSlotDebugInfo> stateMachineHoistedLocalSlots;
private readonly Cci.NamespaceScopeEncoding namespaceScopeEncoding;
private readonly bool hasDynamicLocalVariables;
......@@ -38,8 +40,9 @@ internal sealed class MethodBody : Cci.IMethodBody
bool hasDynamicLocalVariables,
ImmutableArray<Cci.NamespaceScope> namespaceScopes,
Cci.NamespaceScopeEncoding namespaceScopeEncoding,
string iteratorClassName,
ImmutableArray<Cci.LocalScope> iteratorScopes,
string stateMachineTypeNameOpt,
ImmutableArray<Cci.StateMachineHoistedLocalScope> stateMachineHoistedLocalScopes,
ImmutableArray<LocalSlotDebugInfo> stateMachineHoistedLocalSlots,
Cci.AsyncMethodBodyDebugInfo asyncMethodDebugInfo)
{
Debug.Assert(!locals.IsDefault);
......@@ -58,8 +61,9 @@ internal sealed class MethodBody : Cci.IMethodBody
this.namespaceScopeEncoding = namespaceScopeEncoding;
this.hasDynamicLocalVariables = hasDynamicLocalVariables;
this.namespaceScopes = namespaceScopes.IsDefault ? ImmutableArray<Cci.NamespaceScope>.Empty : namespaceScopes;
this.iteratorClassName = iteratorClassName;
this.iteratorScopes = iteratorScopes.IsDefault ? ImmutableArray<Cci.LocalScope>.Empty : iteratorScopes;
this.stateMachineTypeNameOpt = stateMachineTypeNameOpt;
this.stateMachineHoistedLocalScopes = stateMachineHoistedLocalScopes;
this.stateMachineHoistedLocalSlots = stateMachineHoistedLocalSlots;
}
void Cci.IMethodBody.Dispatch(Cci.MetadataVisitor visitor)
......@@ -149,19 +153,27 @@ ImmutableArray<Cci.NamespaceScope> Cci.IMethodBody.NamespaceScopes
}
}
string Cci.IMethodBody.IteratorClassName
string Cci.IMethodBody.StateMachineTypeName
{
get
{
return iteratorClassName;
return stateMachineTypeNameOpt;
}
}
ImmutableArray<Cci.LocalScope> Cci.IMethodBody.IteratorScopes
ImmutableArray<Cci.StateMachineHoistedLocalScope> Cci.IMethodBody.StateMachineHoistedLocalScopes
{
get
{
return this.iteratorScopes;
return this.stateMachineHoistedLocalScopes;
}
}
ImmutableArray<LocalSlotDebugInfo> Cci.IMethodBody.StateMachineHoistedLocalSlots
{
get
{
return this.stateMachineHoistedLocalSlots;
}
}
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using Microsoft.CodeAnalysis.Collections;
namespace Microsoft.CodeAnalysis.CodeGen
{
/// <summary>
/// Dispenser of unique ordinals for synthesized variable names that have the same kind and syntax offset.
/// </summary>
internal sealed class SynthesizedLocalOrdinalsDispenser
{
// The key is (local.SyntaxOffset << 8) | local.SynthesizedKind.
private PooledDictionary<long, int> lazyMap;
private static long MakeKey(SynthesizedLocalKind localKind, int syntaxOffset)
{
return (long)syntaxOffset << 8 | (long)localKind;
}
public void Free()
{
if (lazyMap != null)
{
lazyMap.Free();
lazyMap = null;
}
}
public int AssignLocalOrdinal(SynthesizedLocalKind localKind, int syntaxOffset)
{
#if !DEBUG
// Optimization (avoid growing the dictionary below):
// User-defined locals have to have a distinct syntax offset, thus ordinal is always 0.
if (localKind == SynthesizedLocalKind.UserDefined)
{
return 0;
}
#endif
int ordinal;
long key = MakeKey(localKind, syntaxOffset);
// Group by syntax offset and kind.
// Variables associated with the same syntax and kind will be assigned different ordinals.
if (lazyMap == null)
{
lazyMap = PooledDictionary<long, int>.GetInstance();
ordinal = 0;
}
else if (!lazyMap.TryGetValue(key, out ordinal))
{
ordinal = 0;
}
lazyMap[key] = ordinal + 1;
Debug.Assert(ordinal == 0 || localKind != SynthesizedLocalKind.UserDefined);
return ordinal;
}
}
}
......@@ -19,5 +19,15 @@ internal abstract class VariableSlotAllocator
LocalSlotConstraints constraints,
bool isDynamic,
ImmutableArray<TypedConstant> dynamicTransformFlags);
public abstract string GetPreviousHoistedLocal(
SyntaxNode currentDeclarator,
Cci.ITypeReference currentType,
SynthesizedLocalKind synthesizedKind,
LocalDebugId currentId);
public abstract int HoistedLocalSlotCount { get; }
public abstract string GetPreviousAwaiter(Cci.ITypeReference currentType);
}
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册