示例#1
0
        private BoundExpression MakePropertyAssignment(
            SyntaxNode syntax,
            BoundExpression rewrittenReceiver,
            PropertySymbol property,
            ImmutableArray <BoundExpression> rewrittenArguments,
            ImmutableArray <RefKind> argumentRefKindsOpt,
            bool expanded,
            ImmutableArray <int> argsToParamsOpt,
            BoundExpression rewrittenRight,
            TypeSymbol type,
            bool used)
        {
            // Rewrite property assignment into call to setter.
            var setMethod = property.GetOwnOrInheritedSetMethod();

            if ((object)setMethod == null)
            {
                Debug.Assert((property as SourcePropertySymbol)?.IsAutoProperty == true,
                             "only autoproperties can be assignable without having setters");

                var backingField = (property as SourcePropertySymbol).BackingField;
                return(_factory.AssignmentExpression(
                           _factory.Field(rewrittenReceiver, backingField),
                           rewrittenRight));
            }

            // We have already lowered each argument, but we may need some additional rewriting for the arguments,
            // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            ImmutableArray <LocalSymbol> argTemps;

            rewrittenArguments = MakeArguments(
                syntax,
                rewrittenArguments,
                property,
                setMethod,
                expanded,
                argsToParamsOpt,
                ref argumentRefKindsOpt,
                out argTemps,
                invokedAsExtensionMethod: false,
                enableCallerInfo: ThreeState.True);

            if (used)
            {
                // Save expression value to a temporary before calling the
                // setter, and restore the temporary after the setter, so the
                // assignment can be used as an embedded expression.
                TypeSymbol exprType = rewrittenRight.Type;

                LocalSymbol rhsTemp = _factory.SynthesizedLocal(exprType);

                BoundExpression boundRhs = new BoundLocal(syntax, rhsTemp, null, exprType);

                BoundExpression rhsAssignment = new BoundAssignmentOperator(
                    syntax,
                    boundRhs,
                    rewrittenRight,
                    exprType);

                BoundExpression setterCall = BoundCall.Synthesized(
                    syntax,
                    rewrittenReceiver,
                    setMethod,
                    AppendToPossibleNull(rewrittenArguments, rhsAssignment));

                return(new BoundSequence(
                           syntax,
                           AppendToPossibleNull(argTemps, rhsTemp),
                           ImmutableArray.Create(setterCall),
                           boundRhs,
                           type));
            }
            else
            {
                BoundCall setterCall = BoundCall.Synthesized(
                    syntax,
                    rewrittenReceiver,
                    setMethod,
                    AppendToPossibleNull(rewrittenArguments, rewrittenRight));

                if (argTemps.IsDefaultOrEmpty)
                {
                    return(setterCall);
                }
                else
                {
                    return(new BoundSequence(
                               syntax,
                               argTemps,
                               ImmutableArray <BoundExpression> .Empty,
                               setterCall,
                               setMethod.ReturnType));
                }
            }
        }
        private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAccess, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps)
        {
            var receiverOpt = indexerAccess.ReceiverOpt;

            Debug.Assert(receiverOpt != null);

            BoundExpression transformedReceiver;

            if (CanChangeValueBetweenReads(receiverOpt))
            {
                BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                BoundAssignmentOperator assignmentToTemp;

                // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order),
                // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs.
                // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable
                // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp)
                // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed
                // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not,
                // SPEC VIOLATION: as value types.
                var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter;

                var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None);
                transformedReceiver = receiverTemp;
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);
            }
            else
            {
                transformedReceiver = VisitExpression(receiverOpt);
            }

            // Dealing with the arguments is a bit tricky because they can be named out-of-order arguments;
            // we have to preserve both the source-code order of the side effects and the side effects
            // only being executed once.
            //
            // This is a subtly different problem than the problem faced by the conventional call
            // rewriter; with the conventional call rewriter we already know that the side effects
            // will only be executed once because the arguments are only being pushed on the stack once.
            // In a compound equality operator on an indexer the indices are placed on the stack twice.
            // That is to say, if you have:
            //
            // C().M(z : Z(), x : X(), y : Y())
            //
            // then we can rewrite that into
            //
            // tempc = C()
            // tempz = Z()
            // tempc.M(X(), Y(), tempz)
            //
            // See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the
            // temporaries in
            //
            // C().Collection[z : Z(), x : X(), y : Y()] += 1;
            //
            // because we have to ensure not just that Z() happens first, but in addition that X() and Y() are only
            // called once.  We have to generate this as
            //
            // tempc = C().Collection
            // tempz = Z()
            // tempx = X()
            // tempy = Y()
            // tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1;
            //
            // Fortunately arguments to indexers are never ref or out, so we don't need to worry about that.
            // However, we can still do the optimization where constants are not stored in
            // temporaries; if we have
            //
            // C().Collection[z : 123, y : Y(), x : X()] += 1;
            //
            // Then we can generate that as
            //
            // tempc = C().Collection
            // tempx = X()
            // tempy = Y()
            // tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1;

            ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments);

            SyntaxNode               syntax           = indexerAccess.Syntax;
            PropertySymbol           indexer          = indexerAccess.Indexer;
            ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt;
            bool expanded = indexerAccess.Expanded;
            ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt;

            ImmutableArray <ParameterSymbol> parameters = indexer.Parameters;

            BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
            ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length);

            ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None);

            // Step one: Store everything that is non-trivial into a temporary; record the
            // stores in storesToTemps and make the actual argument a reference to the temp.
            // Do not yet attempt to deal with params arrays or optional arguments.
            BuildStoresToTemps(
                expanded,
                argsToParamsOpt,
                parameters,
                argumentRefKinds,
                rewrittenArguments,
                forceLambdaSpilling: true, // lambdas must produce exactly one delegate so they must be spilled into a temp
                actualArguments,
                refKinds,
                storesToTemps);

            // Step two: If we have a params array, build the array and fill in the argument.
            if (expanded)
            {
                BoundExpression         array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
                BoundAssignmentOperator storeToTemp;
                var boundTemp = _factory.StoreToTemp(array, out storeToTemp);
                stores.Add(storeToTemp);
                temps.Add(boundTemp.LocalSymbol);
                actualArguments[actualArguments.Length - 1] = boundTemp;
            }

            // Step three: Now fill in the optional arguments. (Dev11 uses the getter for optional arguments in
            // compound assignments, but for deconstructions we use the setter if the getter is missing.)
            var accessor = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod();

            InsertMissingOptionalArguments(syntax, accessor.Parameters, actualArguments, refKinds);

            // For a call, step four would be to optimize away some of the temps.  However, we need them all to prevent
            // duplicate side-effects, so we'll skip that step.

            if (indexer.ContainingType.IsComImport)
            {
                RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps);
            }

            rewrittenArguments = actualArguments.AsImmutableOrNull();

            foreach (BoundAssignmentOperator tempAssignment in storesToTemps)
            {
                temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol);
                stores.Add(tempAssignment);
            }

            storesToTemps.Free();
            argumentRefKinds = GetRefKindsOrNull(refKinds);
            refKinds.Free();

            // This is a temporary object that will be rewritten away before the lowering completes.
            return(new BoundIndexerAccess(
                       syntax,
                       transformedReceiver,
                       indexer,
                       rewrittenArguments,
                       default(ImmutableArray <string>),
                       argumentRefKinds,
                       false,
                       default(ImmutableArray <int>),
                       null,
                       indexerAccess.UseSetterForDefaultArgumentGeneration,
                       indexerAccess.Type));
        }