Пример #1
0
        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);
            }
        }
Пример #3
0
 protected override void GenerateFields()
 {
     builderField = F.SynthesizeField(asyncMethodBuilderMemberCollection.BuilderType, GeneratedNames.AsyncBuilderName(), isPublic: true);
 }
Пример #4
0
 internal override bool IsHiddenMember(System.Reflection.MemberInfo member)
 {
     // Generated fields, e.g. "<property_name>k__BackingField"
     return(GeneratedNames.IsGeneratedMemberName(member.Name));
 }
Пример #5
0
        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);
                        }
                    }
                }
            }
        }
Пример #6
0
        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;
        }
Пример #7
0
        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);
        }
Пример #8
0
 public void TryParseSynthesizedDelegateName_Failure(string name)
 {
     Assert.False(GeneratedNames.TryParseSynthesizedDelegateName(name, out _, out _, out _, out _));
 }
Пример #9
0
        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);
        }
Пример #10
0
 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;
                }
            }
        }
Пример #12
0
 internal LambdaFrame(MethodSymbol topLevelMethod, TypeCompilationState compilationState)
     : base(topLevelMethod, GeneratedNames.MakeAnonymousDisplayClassName(compilationState.GenerateTempNumber()), TypeKind.Class)
 {
     this.constructor = new SynthesizedInstanceConstructor(this);
 }
Пример #13
0
        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;
        }
Пример #14
0
        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);
                        }
                    }
                }
            }
        }
Пример #15
0
        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());
        }
Пример #16
0
        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);
                    }
                }
            });
        }
Пример #17
0
        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;
        }
Пример #18
0
        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);
                    }
                }
            });
        }
Пример #19
0
        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);
            }
        }
Пример #21
0
        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));
 }