protected override void GenerateControlFields() { this.stateField = F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeStateMachineStateFieldName()); // Add a field: T current _currentField = F.StateMachineField(_elementType, GeneratedNames.MakeIteratorCurrentFieldName()); }
private BoundExpression HoistExpression( BoundExpression expr, AwaitExpressionSyntax awaitSyntaxOpt, int syntaxOffset, bool isRef, ArrayBuilder <BoundExpression> sideEffects, ArrayBuilder <StateMachineFieldSymbol> hoistedFields, ref bool needsSacrificialEvaluation) { switch (expr.Kind) { case BoundKind.ArrayAccess: { var array = (BoundArrayAccess)expr; BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, false, sideEffects, hoistedFields, ref needsSacrificialEvaluation); var indices = ArrayBuilder <BoundExpression> .GetInstance(); foreach (var index in array.Indices) { indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, false, sideEffects, hoistedFields, ref needsSacrificialEvaluation)); } needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions return(array.Update(expression, indices.ToImmutableAndFree(), array.Type)); } case BoundKind.FieldAccess: { var field = (BoundFieldAccess)expr; if (field.FieldSymbol.IsStatic) { // the address of a static field, and the value of a readonly static field, is stable if (isRef || field.FieldSymbol.IsReadOnly) { return(expr); } goto default; } if (!isRef) { goto default; } var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, isFieldOfStruct, sideEffects, hoistedFields, ref needsSacrificialEvaluation); if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) { needsSacrificialEvaluation = true; // need the null check in field receiver } return(F.Field(receiver, field.FieldSymbol)); } case BoundKind.ThisReference: case BoundKind.BaseReference: case BoundKind.DefaultExpression: return(expr); default: if (expr.ConstantValue != null) { return(expr); } if (isRef) { throw ExceptionUtilities.UnexpectedValue(expr.Kind); } TypeSymbol fieldType = expr.Type; StateMachineFieldSymbol hoistedField; if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) { const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; Debug.Assert(awaitSyntaxOpt != null); int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); var id = new LocalDebugId(syntaxOffset, ordinal); // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. // However, it may happen that the type changes, in which case we need to allocate a new slot. int slotIndex; if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex( awaitSyntaxOpt, F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics), kind, id, Diagnostics, out slotIndex)) { slotIndex = _nextFreeHoistedLocalSlot++; } string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); } else { hoistedField = GetOrAllocateReusableHoistedField(fieldType); } hoistedFields.Add(hoistedField); var replacement = F.Field(F.This(), hoistedField); sideEffects.Add(F.AssignmentExpression(replacement, expr)); return(replacement); } }
protected override void GenerateFields() { builderField = F.SynthesizeField(asyncMethodBuilderMemberCollection.BuilderType, GeneratedNames.AsyncBuilderName(), isPublic: true); }
internal override bool IsHiddenMember(System.Reflection.MemberInfo member) { // Generated fields, e.g. "<property_name>k__BackingField" return(GeneratedNames.IsGeneratedMemberName(member.Name)); }
private void CreateInitialProxies(MultiDictionary <Symbol, CSharpSyntaxNode> captured) { var typeMap = stateMachineClass.TypeMap; var orderedCaptured = from local in captured.Keys orderby local.Name, (local.Locations.Length == 0) ? 0 : local.Locations[0].SourceSpan.Start select local; foreach (var sym in orderedCaptured) { var local = sym as LocalSymbol; if ((object)local != null && (local.SynthesizedLocalKind == SynthesizedLocalKind.None || local.SynthesizedLocalKind == SynthesizedLocalKind.LambdaDisplayClass)) { // create proxies for user-defined variables and for lambda closures: Debug.Assert(local.RefKind == RefKind.None); MakeInitialProxy(typeMap, captured, local); continue; } var parameter = sym as ParameterSymbol; if ((object)parameter != null) { if (parameter.IsThis) { var proxyField = F.StateMachineField(method.ContainingType, GeneratedNames.ThisProxyName(), isPublic: true); variableProxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField)); if (PreserveInitialParameterValues) { var initialThis = method.ContainingType.IsStructType() ? F.StateMachineField(method.ContainingType, GeneratedNames.StateMachineThisParameterProxyName(), isPublic: true) : proxyField; initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement(initialThis)); } } else { var proxyField = F.StateMachineField(typeMap.SubstituteType(parameter.Type), parameter.Name, isPublic: true); variableProxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField)); if (PreserveInitialParameterValues) { string proxyName = GeneratedNames.StateMachineParameterProxyName(parameter.Name); initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement( F.StateMachineField(typeMap.SubstituteType(parameter.Type), proxyName, isPublic: true))); } 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); } } } } }
protected override void GenerateControlFields() { this.stateField = F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeStateMachineStateFieldName()); // Add a field: T current _currentField = F.StateMachineField(_elementType, GeneratedNames.MakeIteratorCurrentFieldName()); // if it is an enumerable, and either Environment.CurrentManagedThreadId or Thread.ManagedThreadId are available // add a field: int initialThreadId bool addInitialThreadId = _isEnumerable && ((object)F.WellKnownMember(WellKnownMember.System_Threading_Thread__ManagedThreadId, isOptional: true) != null || (object)F.WellKnownMember(WellKnownMember.System_Environment__CurrentManagedThreadId, isOptional: true) != null); _initialThreadIdField = addInitialThreadId ? F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeIteratorCurrentThreadIdFieldName()) : null; }
protected override void GenerateControlFields() { // the fields are initialized from async method, so they need to be public: this.stateField = F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeStateMachineStateFieldName(), isPublic: true); _builderField = F.StateMachineField(_asyncMethodBuilderMemberCollection.BuilderType, GeneratedNames.AsyncBuilderFieldName(), isPublic: true); }
public void TryParseSynthesizedDelegateName_Failure(string name) { Assert.False(GeneratedNames.TryParseSynthesizedDelegateName(name, out _, out _, out _, out _)); }
private static void CheckIteratorOverloading(string source, Func <MethodSymbol, bool> isDesiredOverload) { var comp1 = CreateCompilation(source, options: TestOptions.DebugDll); var ref1 = comp1.EmitToImageReference(); var comp2 = CreateCompilation("", new[] { ref1 }, options: TestOptions.DebugDll); var originalType = comp2.GlobalNamespace.GetMember <NamedTypeSymbol>("C"); var iteratorMethod = originalType.GetMembers("M").OfType <MethodSymbol>().Single(isDesiredOverload); var stateMachineType = originalType.GetMembers().OfType <NamedTypeSymbol>().Single(t => GeneratedNames.GetKind(t.Name) == GeneratedNameKind.StateMachineType); var moveNextMethod = stateMachineType.GetMember <MethodSymbol>("MoveNext"); var guessedIterator = CompilationContext.GetSubstitutedSourceMethod(moveNextMethod, sourceMethodMustBeInstance: true); Assert.Equal(iteratorMethod, guessedIterator.OriginalDefinition); }
internal LambdaFrameLocalSymbol(MethodSymbol containingMethod, TypeSymbol type, TypeCompilationState compilationState) : base(containingMethod, type, GeneratedNames.MakeLambdaDisplayClassLocalName(compilationState.GenerateTempNumber()), declarationKind: LocalDeclarationKind.CompilerGeneratedLambdaDisplayClassLocal) { }
internal override void AppendFullName(StringBuilder builder, MethodSymbol method) { var displayFormat = (method.MethodKind == MethodKind.PropertyGet || method.MethodKind == MethodKind.PropertySet) ? s_propertyDisplayFormat : DisplayFormat; var parts = method.ToDisplayParts(displayFormat); var numParts = parts.Length; for (int i = 0; i < numParts; i++) { var part = parts[i]; var displayString = part.ToString(); switch (part.Kind) { case SymbolDisplayPartKind.ClassName: if (GeneratedNames.GetKind(displayString) != GeneratedNameKind.LambdaDisplayClass) { builder.Append(displayString); } else { // Drop any remaining display class name parts and the subsequent dot... do { i++; }while (i < numParts && parts[i].Kind != SymbolDisplayPartKind.MethodName); i--; } break; case SymbolDisplayPartKind.MethodName: GeneratedNameKind kind; int openBracketOffset, closeBracketOffset; if (GeneratedNames.TryParseGeneratedName(displayString, out kind, out openBracketOffset, out closeBracketOffset) && (kind == GeneratedNameKind.LambdaMethod || kind == GeneratedNameKind.LocalFunction)) { builder.Append(displayString, openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1); // source method name builder.Append('.'); if (kind == GeneratedNameKind.LambdaMethod) { builder.Append(AnonymousMethodName); } // NOTE: Local functions include the local function name inside the suffix ("<Main>__Local1_1") // NOTE: The old implementation only appended the first ordinal number. Since this is not useful // in uniquely identifying the lambda, we'll append the entire ordinal suffix (which may contain // multiple numbers, as well as '-' or '_'). builder.Append(displayString.Substring(closeBracketOffset + 2)); // ordinal suffix (e.g. "__1") } else { builder.Append(displayString); } break; default: builder.Append(displayString); break; } } }
internal LambdaFrame(MethodSymbol topLevelMethod, TypeCompilationState compilationState) : base(topLevelMethod, GeneratedNames.MakeAnonymousDisplayClassName(compilationState.GenerateTempNumber()), TypeKind.Class) { this.constructor = new SynthesizedInstanceConstructor(this); }
protected override void GetStateMachineFieldMapFromMetadata( ITypeSymbol stateMachineType, ImmutableArray <LocalSlotDebugInfo> localSlotDebugInfo, out IReadOnlyDictionary <EncHoistedLocalInfo, int> hoistedLocalMap, out IReadOnlyDictionary <Cci.ITypeReference, int> awaiterMap, out int awaiterSlotCount) { // we are working with PE symbols Debug.Assert(stateMachineType.ContainingAssembly is PEAssemblySymbol); var hoistedLocals = new Dictionary <EncHoistedLocalInfo, int>(); var awaiters = new Dictionary <Cci.ITypeReference, int>(); int maxAwaiterSlotIndex = -1; foreach (var member in stateMachineType.GetMembers()) { if (member.Kind == SymbolKind.Field) { string name = member.Name; int slotIndex; switch (GeneratedNames.GetKind(name)) { case GeneratedNameKind.AwaiterField: if (GeneratedNames.TryParseSlotIndex(name, out slotIndex)) { var field = (IFieldSymbol)member; // correct metadata won't contain duplicates, but malformed might, ignore the duplicate: awaiters[(Cci.ITypeReference)field.Type] = slotIndex; if (slotIndex > maxAwaiterSlotIndex) { maxAwaiterSlotIndex = slotIndex; } } break; case GeneratedNameKind.HoistedLocalField: case GeneratedNameKind.HoistedSynthesizedLocalField: if (GeneratedNames.TryParseSlotIndex(name, out slotIndex)) { var field = (IFieldSymbol)member; if (slotIndex >= localSlotDebugInfo.Length) { // invalid or missing metadata continue; } var key = new EncHoistedLocalInfo(localSlotDebugInfo[slotIndex], (Cci.ITypeReference)field.Type); // correct metadata won't contain duplicate ids, but malformed might, ignore the duplicate: hoistedLocals[key] = slotIndex; } break; } } } hoistedLocalMap = hoistedLocals; awaiterMap = awaiters; awaiterSlotCount = maxAwaiterSlotIndex + 1; }
private void CreateInitialProxies( TypeMap TypeMap, IOrderedEnumerable <Symbol> captured, MultiDictionary <Symbol, CSharpSyntaxNode> locations) { foreach (var sym in captured) { var local = sym as LocalSymbol; if ((object)local != null && local.DeclarationKind != LocalDeclarationKind.CompilerGenerated) { Debug.Assert(local.RefKind == RefKind.None); // there are no user-declared ref variables MakeInitialProxy(TypeMap, locations, local); continue; } var parameter = sym as ParameterSymbol; if ((object)parameter != null) { if (parameter.IsThis) { var proxyField = F.StateMachineField(method.ContainingType, GeneratedNames.IteratorThisProxyName(), isPublic: true); variableProxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField)); if (PreserveInitialLocals) { var initialThis = method.ContainingType.IsStructType() ? F.StateMachineField(method.ContainingType, GeneratedNames.IteratorThisProxyProxyName(), isPublic: true) : proxyField; initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement(initialThis)); } } else { var proxyField = F.StateMachineField(TypeMap.SubstituteType(parameter.Type), parameter.Name, isPublic: true); variableProxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField)); if (PreserveInitialLocals) { string proxyName = GeneratedNames.IteratorParameterProxyName(parameter.Name); initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement( F.StateMachineField(TypeMap.SubstituteType(parameter.Type), proxyName, isPublic: true))); } 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); } } } } }
protected BoundStatement Rewrite() { if (this.body.HasErrors) { return(this.body); } F.OpenNestedType(stateMachineType); GenerateControlFields(); if (PreserveInitialParameterValuesAndThreadId && CanGetThreadId()) { // if it is an enumerable or async-enumerable, and either Environment.CurrentManagedThreadId or Thread.ManagedThreadId are available // add a field: int initialThreadId initialThreadIdField = F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeIteratorCurrentThreadIdFieldName()); } // fields for the initial values of all the parameters of the method if (PreserveInitialParameterValuesAndThreadId) { initialParameters = new Dictionary <Symbol, CapturedSymbolReplacement>(); } // fields for the captured variables of the method 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 kickoff method return(GenerateKickoffMethodBody()); }
public void LambdaLocations_Instance() { var source = @" using System; class C { int _toBeCaptured; C() { int l = ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 4))() + x))(1); } ~C() { int l = ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 6))() + x))(1); } int P { get { return ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 7))() + x))(1); } set { value = ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 8))() + x))(1); } } int this[int p] { get { return ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 9))() + x))(1); } set { value = ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 10))() + x))(1); } } event Action E { add { int l = ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 11))() + x))(1); } remove { int l = ((Func<int, int>)(x => ((Func<int>)(() => _toBeCaptured + x + 12))() + x))(1); } } } "; var expectedILTemplate = @" {{ // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld ""C C.{0}.<>4__this"" IL_0006: ret }}"; var comp = CreateCompilation(source, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName()); WithRuntimeInstance(comp, runtime => { var dummyComp = CreateCompilation("", new[] { comp.EmitToImageReference() }, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); var typeC = dummyComp.GlobalNamespace.GetMember <NamedTypeSymbol>("C"); var displayClassTypes = typeC.GetMembers().OfType <NamedTypeSymbol>(); Assert.True(displayClassTypes.Any()); foreach (var displayClassType in displayClassTypes) { var displayClassName = displayClassType.Name; Assert.Equal(GeneratedNameKind.LambdaDisplayClass, GeneratedNames.GetKind(displayClassName)); foreach (var displayClassMethod in displayClassType.GetMembers().OfType <MethodSymbol>().Where(m => GeneratedNames.GetKind(m.Name) == GeneratedNameKind.LambdaMethod)) { var lambdaMethodName = string.Format("C.{0}.{1}", displayClassName, displayClassMethod.Name); var context = CreateMethodContext(runtime, lambdaMethodName); var expectedIL = string.Format(expectedILTemplate, displayClassName); VerifyHasThis(context, "C", expectedIL); } } }); }
internal EEMethodSymbol( EENamedTypeSymbol container, string name, Location location, MethodSymbol sourceMethod, ImmutableArray <LocalSymbol> sourceLocals, ImmutableArray <LocalSymbol> sourceLocalsForBinding, ImmutableDictionary <string, DisplayClassVariable> sourceDisplayClassVariables, GenerateMethodBody generateMethodBody) { Debug.Assert(sourceMethod.IsDefinition); Debug.Assert(sourceMethod.ContainingSymbol == container.SubstitutedSourceType.OriginalDefinition); Debug.Assert(sourceLocals.All(l => l.ContainingSymbol == sourceMethod)); _container = container; _name = name; _locations = ImmutableArray.Create(location); // What we want is to map all original type parameters to the corresponding new type parameters // (since the old ones have the wrong owners). Unfortunately, we have a circular dependency: // 1) Each new type parameter requires the entire map in order to be able to construct its constraint list. // 2) The map cannot be constructed until all new type parameters exist. // Our solution is to pass each new type parameter a lazy reference to the type map. We then // initialize the map as soon as the new type parameters are available - and before they are // handed out - so that there is never a period where they can require the type map and find // it uninitialized. var sourceMethodTypeParameters = sourceMethod.TypeParameters; var allSourceTypeParameters = container.SourceTypeParameters.Concat(sourceMethodTypeParameters); var getTypeMap = new Func <TypeMap>(() => this.TypeMap); _typeParameters = sourceMethodTypeParameters.SelectAsArray( (tp, i, arg) => (TypeParameterSymbol) new EETypeParameterSymbol(this, tp, i, getTypeMap), (object)null); _allTypeParameters = container.TypeParameters.Concat(_typeParameters); this.TypeMap = new TypeMap(allSourceTypeParameters, _allTypeParameters); EENamedTypeSymbol.VerifyTypeParameters(this, _typeParameters); var substitutedSourceType = container.SubstitutedSourceType; this.SubstitutedSourceMethod = sourceMethod.AsMember(substitutedSourceType); if (sourceMethod.Arity > 0) { this.SubstitutedSourceMethod = this.SubstitutedSourceMethod.Construct(_typeParameters.As <TypeSymbol>()); } TypeParameterChecker.Check(this.SubstitutedSourceMethod, _allTypeParameters); // Create a map from original parameter to target parameter. var parameterBuilder = ArrayBuilder <ParameterSymbol> .GetInstance(); var substitutedSourceThisParameter = this.SubstitutedSourceMethod.ThisParameter; var substitutedSourceHasThisParameter = (object)substitutedSourceThisParameter != null; if (substitutedSourceHasThisParameter) { _thisParameter = MakeParameterSymbol(0, GeneratedNames.ThisProxyFieldName(), substitutedSourceThisParameter); Debug.Assert(_thisParameter.Type == this.SubstitutedSourceMethod.ContainingType); parameterBuilder.Add(_thisParameter); } var ordinalOffset = (substitutedSourceHasThisParameter ? 1 : 0); foreach (var substitutedSourceParameter in this.SubstitutedSourceMethod.Parameters) { var ordinal = substitutedSourceParameter.Ordinal + ordinalOffset; Debug.Assert(ordinal == parameterBuilder.Count); var parameter = MakeParameterSymbol(ordinal, substitutedSourceParameter.Name, substitutedSourceParameter); parameterBuilder.Add(parameter); } _parameters = parameterBuilder.ToImmutableAndFree(); var localsBuilder = ArrayBuilder <LocalSymbol> .GetInstance(); var localsMap = PooledDictionary <LocalSymbol, LocalSymbol> .GetInstance(); foreach (var sourceLocal in sourceLocals) { var local = sourceLocal.ToOtherMethod(this, this.TypeMap); localsMap.Add(sourceLocal, local); localsBuilder.Add(local); } this.Locals = localsBuilder.ToImmutableAndFree(); localsBuilder = ArrayBuilder <LocalSymbol> .GetInstance(); foreach (var sourceLocal in sourceLocalsForBinding) { LocalSymbol local; if (!localsMap.TryGetValue(sourceLocal, out local)) { local = sourceLocal.ToOtherMethod(this, this.TypeMap); localsMap.Add(sourceLocal, local); } localsBuilder.Add(local); } this.LocalsForBinding = localsBuilder.ToImmutableAndFree(); // Create a map from variable name to display class field. var displayClassVariables = PooledDictionary <string, DisplayClassVariable> .GetInstance(); foreach (var pair in sourceDisplayClassVariables) { var variable = pair.Value; var oldDisplayClassInstance = variable.DisplayClassInstance; // Note: we don't call ToOtherMethod in the local case because doing so would produce // a new LocalSymbol that would not be ReferenceEquals to the one in this.LocalsForBinding. var oldDisplayClassInstanceFromLocal = oldDisplayClassInstance as DisplayClassInstanceFromLocal; var newDisplayClassInstance = (oldDisplayClassInstanceFromLocal == null) ? oldDisplayClassInstance.ToOtherMethod(this, this.TypeMap) : new DisplayClassInstanceFromLocal((EELocalSymbol)localsMap[oldDisplayClassInstanceFromLocal.Local]); variable = variable.SubstituteFields(newDisplayClassInstance, this.TypeMap); displayClassVariables.Add(pair.Key, variable); } _displayClassVariables = displayClassVariables.ToImmutableDictionary(); displayClassVariables.Free(); localsMap.Free(); _generateMethodBody = generateMethodBody; }
public void LambdaLocations_Static() { var source = @" using System; class C { static int f = ((Func<int, int>)(x => ((Func<int>)(() => x + 2))() + x))(1); static C() { int l = ((Func<int, int>)(x => ((Func<int>)(() => x + 4))() + x))(1); } static int P { get { return ((Func<int, int>)(x => ((Func<int>)(() => x + 7))() + x))(1); } set { value = ((Func<int, int>)(x => ((Func<int>)(() => x + 8))() + x))(1); } } static event Action E { add { int l = ((Func<int, int>)(x => ((Func<int>)(() => x + 11))() + x))(1); } remove { int l = ((Func<int, int>)(x => ((Func<int>)(() => x + 12))() + x))(1); } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName()); WithRuntimeInstance(comp, runtime => { var dummyComp = CreateCompilation("", new[] { comp.EmitToImageReference() }, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); var typeC = dummyComp.GlobalNamespace.GetMember <NamedTypeSymbol>("C"); var displayClassTypes = typeC.GetMembers().OfType <NamedTypeSymbol>(); Assert.True(displayClassTypes.Any()); foreach (var displayClassType in displayClassTypes) { var displayClassName = displayClassType.Name; Assert.Equal(GeneratedNameKind.LambdaDisplayClass, GeneratedNames.GetKind(displayClassName)); foreach (var displayClassMethod in displayClassType.GetMembers().OfType <MethodSymbol>().Where(m => GeneratedNames.GetKind(m.Name) == GeneratedNameKind.LambdaMethod)) { var lambdaMethodName = string.Format("C.{0}.{1}", displayClassName, displayClassMethod.Name); var context = CreateMethodContext(runtime, lambdaMethodName); VerifyNoThis(context); } } }); }
private void CreateNonReusableLocalProxies( IEnumerable <Symbol> variablesToHoist, out IReadOnlyDictionary <Symbol, CapturedSymbolReplacement> proxies, out int nextFreeHoistedLocalSlot) { var proxiesBuilder = new Dictionary <Symbol, CapturedSymbolReplacement>(); var typeMap = stateMachineType.TypeMap; bool isDebugBuild = F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug; bool mapToPreviousFields = isDebugBuild && slotAllocatorOpt != null; nextFreeHoistedLocalSlot = mapToPreviousFields ? slotAllocatorOpt.PreviousHoistedLocalSlotCount : 0; foreach (var variable in variablesToHoist) { Debug.Assert(variable.Kind == SymbolKind.Local || variable.Kind == SymbolKind.Parameter); if (variable.Kind == SymbolKind.Local) { 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 (ShouldPreallocateNonReusableProxy(local)) { // variable needs to be hoisted var fieldType = typeMap.SubstituteType(local.Type).Type; LocalDebugId id; 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); // map local id to the previous id, if available: int previousSlotIndex; if (mapToPreviousFields && slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex( declaratorSyntax, F.ModuleBuilderOpt.Translate(fieldType, declaratorSyntax, diagnostics), synthesizedKind, id, diagnostics, out previousSlotIndex)) { slotIndex = previousSlotIndex; } } else { id = LocalDebugId.None; } if (slotIndex == -1) { slotIndex = nextFreeHoistedLocalSlot++; } string fieldName = GeneratedNames.MakeHoistedLocalFieldName(synthesizedKind, slotIndex, local.Name); field = F.StateMachineField(fieldType, fieldName, new LocalSlotDebugInfo(synthesizedKind, id), slotIndex); } if (field != null) { proxiesBuilder.Add(local, new CapturedToStateMachineFieldReplacement(field, isReusable: false)); } } else { var parameter = (ParameterSymbol)variable; if (parameter.IsThis) { var containingType = method.ContainingType; var proxyField = F.StateMachineField(containingType, GeneratedNames.ThisProxyFieldName(), isPublic: true, isThis: true); proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false)); if (PreserveInitialParameterValues) { var initialThis = containingType.IsStructType() ? F.StateMachineField(containingType, GeneratedNames.StateMachineThisParameterProxyName(), isPublic: true, isThis: true) : proxyField; initialParameters.Add(parameter, new CapturedToStateMachineFieldReplacement(initialThis, isReusable: false)); } } else { // 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).Type, parameter.Name, isPublic: !PreserveInitialParameterValues); proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false)); if (PreserveInitialParameterValues) { var field = F.StateMachineField(typeMap.SubstituteType(parameter.Type).Type, GeneratedNames.StateMachineParameterProxyFieldName(parameter.Name), isPublic: true); initialParameters.Add(parameter, new CapturedToStateMachineFieldReplacement(field, isReusable: false)); } } } } proxies = proxiesBuilder; }
private BoundExpression HoistExpression( BoundExpression expr, AwaitExpressionSyntax awaitSyntaxOpt, int syntaxOffset, RefKind refKind, ArrayBuilder <BoundExpression> sideEffects, ArrayBuilder <StateMachineFieldSymbol> hoistedFields, ref bool needsSacrificialEvaluation) { switch (expr.Kind) { case BoundKind.ArrayAccess: { var array = (BoundArrayAccess)expr; BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); var indices = ArrayBuilder <BoundExpression> .GetInstance(); foreach (var index in array.Indices) { indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation)); } needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions return(array.Update(expression, indices.ToImmutableAndFree(), array.Type)); } case BoundKind.FieldAccess: { var field = (BoundFieldAccess)expr; if (field.FieldSymbol.IsStatic) { // the address of a static field, and the value of a readonly static field, is stable if (refKind != RefKind.None || field.FieldSymbol.IsReadOnly) { return(expr); } goto default; } if (refKind == RefKind.None) { goto default; } var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, isFieldOfStruct ? refKind : RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) { needsSacrificialEvaluation = true; // need the null check in field receiver } return(F.Field(receiver, field.FieldSymbol)); } case BoundKind.ThisReference: case BoundKind.BaseReference: case BoundKind.DefaultExpression: return(expr); case BoundKind.Call: var call = (BoundCall)expr; // NOTE: There are two kinds of 'In' arguments that we may see at this point: // - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier) // - `RefKind.In` (specified with no modifiers and matched an 'In' parameter) // // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. // The "strict" ones do not permit implicit copying, so the same situation should result in an error. if (refKind != RefKind.None && refKind != RefKind.In) { Debug.Assert(call.Method.RefKind != RefKind.None); F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, F.Syntax.Location, call.Method); } // method call is not referentially transparent, we can only spill the result value. refKind = RefKind.None; goto default; case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; // NOTE: There are two kinds of 'In' arguments that we may see at this point: // - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier) // - `RefKind.In` (specified with no modifiers and matched an 'In' parameter) // // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. // The "strict" ones do not permit implicit copying, so the same situation should result in an error. if (refKind != RefKind.None && refKind != RefKind.RefReadOnly) { Debug.Assert(conditional.IsRef); F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, F.Syntax.Location); } // conditional expr is not referentially transparent, we can only spill the result value. refKind = RefKind.None; goto default; default: if (expr.ConstantValue != null) { return(expr); } if (refKind != RefKind.None) { throw ExceptionUtilities.UnexpectedValue(expr.Kind); } TypeSymbol fieldType = expr.Type; StateMachineFieldSymbol hoistedField; if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) { const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; Debug.Assert(awaitSyntaxOpt != null); int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); var id = new LocalDebugId(syntaxOffset, ordinal); // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. // However, it may happen that the type changes, in which case we need to allocate a new slot. int slotIndex; if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex( awaitSyntaxOpt, F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics), kind, id, Diagnostics, out slotIndex)) { slotIndex = _nextFreeHoistedLocalSlot++; } string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); } else { hoistedField = GetOrAllocateReusableHoistedField(fieldType, reused: out _); } hoistedFields.Add(hoistedField); var replacement = F.Field(F.This(), hoistedField); sideEffects.Add(F.AssignmentExpression(replacement, expr)); return(replacement); } }
internal IteratorConstructor(StateMachineTypeSymbol container) : base(container) { var intType = container.DeclaringCompilation.GetSpecialType(SpecialType.System_Int32); _parameters = ImmutableArray.Create <ParameterSymbol>( SynthesizedParameterSymbol.Create(this, TypeWithAnnotations.Create(intType), 0, RefKind.None, GeneratedNames.MakeStateMachineStateFieldName())); }
private static string MakeName(VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, MethodSymbol kickoffMethod, int kickoffMethodOrdinal) { return(slotAllocatorOpt?.PreviousStateMachineTypeName ?? GeneratedNames.MakeStateMachineTypeName(kickoffMethod.Name, kickoffMethodOrdinal, compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal)); }