Beispiel #1
0
        /// <summary>
        /// Rewrites arguments of an invocation according to the receiving method. It is assumed
        /// that arguments match parameters, but may need to be expanded/reordered.
        /// </summary>
        private void RewriteArguments(
            MethodSymbol method,
            bool expanded,
            ReadOnlyArray <int> argsToParamsOpt,
            ref ReadOnlyArray <RefKind> argumentRefKinds,
            ref ReadOnlyArray <BoundExpression> rewrittenArguments,
            out ReadOnlyArray <LocalSymbol> temporaries)
        {
            // We have:
            // * a list of arguments, already converted to their proper types,
            //   in source code order. Some optional arguments might be missing.
            // * a map showing which parameter each argument corresponds to. If
            //   this is null, then the argument to parameter mapping is one-to-one.
            // * the ref kind of each argument, in source code order. That is, whether
            //   the argument was marked as ref, out, or value (neither).
            // * a method symbol.
            // * whether the call is expanded or normal form.

            // We rewrite the call so that:
            // * if in its expanded form, we create the params array.
            // * if the call requires reordering of arguments because of named arguments, temporaries are generated as needed

            // Doing this transformation can move around refness in interesting ways. For example, consider
            //
            // A().M(y : ref B()[C()], x : out D());
            //
            // This will be created as a call with receiver A(), symbol M, argument list ( B()[C()], D() ),
            // name list ( y, x ) and ref list ( ref, out ).  We can rewrite this into temporaries:
            //
            // A().M(
            //    seq ( ref int temp_y = ref B()[C()], out D() ),
            //    temp_y );
            //
            // Now we have a call with receiver A(), symbol M, argument list as shown, no name list,
            // and ref list ( out, value ). We do not want to pass a *ref* to temp_y; the temporary
            // storage is not the thing being ref'd! We want to pass the *value* of temp_y, which
            // *contains* a reference.

            // We attempt to minimize the number of temporaries required. Arguments which neither
            // produce nor observe a side effect can be placed into their proper position without
            // recourse to a temporary. For example:
            //
            // Where(predicate: x=>x.Length!=0, sequence: S())
            //
            // can be rewritten without any temporaries because the conversion from lambda to
            // delegate does not produce any side effect that could be observed by S().
            //
            // By contrast:
            //
            // Foo(z: this.p, y: this.Q(), x: (object)10)
            //
            // The boxing of 10 can be reordered, but the fetch of this.p has to happen before the
            // call to this.Q() because the call could change the value of this.p.
            //
            // We start by binding everything that is not obviously reorderable as a temporary, and
            // then run an optimizer to remove unnecessary temporaries.

            ReadOnlyArray <ParameterSymbol> parameters = method.Parameters;
            var parameterCount = parameters.Count;
            var arguments      = new BoundExpression[parameterCount];

            temporaries = ReadOnlyArray <LocalSymbol> .Null;  // not using temps by default.

            List <RefKind> refKinds = null;

            if (argumentRefKinds.IsNotNull)
            {
                refKinds = new List <RefKind>(parameterCount);
                for (int p = 0; p < parameterCount; ++p)
                {
                    refKinds.Add(RefKind.None);
                }
            }

            ArrayBuilder <BoundAssignmentOperator> storesToTemps = null;
            ArrayBuilder <BoundExpression>         paramArray    = null;

            if (expanded)
            {
                paramArray = ArrayBuilder <BoundExpression> .GetInstance();
            }

            for (int a = 0; a < rewrittenArguments.Count; ++a)
            {
                var argument = rewrittenArguments[a];
                var p        = (argsToParamsOpt.IsNotNull) ? argsToParamsOpt[a] : a;
                var refKind  = argumentRefKinds.RefKinds(a);
                Debug.Assert(arguments[p] == null);
                if (expanded && p == parameterCount - 1)
                {
                    paramArray.Add(argument);
                    Debug.Assert(refKind == RefKind.None);
                }
                else if (IsSafeForReordering(argument, refKind))
                {
                    arguments[p] = argument;
                    if (refKinds != null)
                    {
                        refKinds[p] = refKind;
                    }
                }
                else
                {
                    if (storesToTemps == null)
                    {
                        storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Count);
                    }

                    var tempStore = TempHelpers.StoreToTemp(argument, refKind, containingSymbol);
                    storesToTemps.Add(tempStore.Item1);
                    arguments[p] = tempStore.Item2;
                }
            }

            if (expanded)
            {
                var paramArrayType = parameters[parameterCount - 1].Type;
                var arrayArgs      = paramArray.ToReadOnlyAndFree();

                var int32Type = method.ContainingAssembly.GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode.Int32);

                arguments[parameterCount - 1] = new BoundArrayCreation(
                    null,
                    null,
                    ReadOnlyArray.Singleton <BoundExpression>(
                        new BoundLiteral(null, null, ConstantValue.Create(arrayArgs.Count), int32Type)),
                    new BoundArrayInitialization(null, null, arrayArgs),
                    paramArrayType);
            }

            for (int p = 0; p < parameterCount; ++p)
            {
                if (arguments[p] == null)
                {
                    Debug.Assert(parameters[p].IsOptional);

                    // UNDONE: Add optional arguments.
                }
            }

            if (storesToTemps != null)
            {
                int tempsNeeded = MergeArgumentsAndSideEffects(storesToTemps, arguments);

                if (tempsNeeded > 0)
                {
                    var temps = new LocalSymbol[tempsNeeded];
                    for (int i = 0, j = 0; i < storesToTemps.Count; i++)
                    {
                        var s = storesToTemps[i];
                        if (s != null)
                        {
                            temps[j++] = ((BoundLocal)s.Left).LocalSymbol;
                        }
                    }

                    temporaries = temps.AsReadOnlyWrap();
                }

                storesToTemps.Free();
            }

            // * The rewritten list of names is now null because the arguments have been reordered.
            // * The args-to-params map is now null because every argument exactly matches its parameter.
            // * The call is no longer in its expanded form.

            argumentRefKinds   = refKinds == null ? ReadOnlyArray <RefKind> .Null : refKinds.AsReadOnly <RefKind>();
            rewrittenArguments = arguments.AsReadOnlyWrap();
        }
        /// <summary>
        /// Rewrites arguments of an invocation according to the receiving method. It is assumed
        /// that arguments match parameters, but may need to be expanded/reordered.
        /// </summary>
        private void RewriteArguments(
                MethodSymbol method,
                bool expanded,
                ReadOnlyArray<int> argsToParamsOpt,
                ref ReadOnlyArray<RefKind> argumentRefKinds,
                ref ReadOnlyArray<BoundExpression> rewrittenArguments,
                out ReadOnlyArray<LocalSymbol> temporaries)
        {

            // We have:
            // * a list of arguments, already converted to their proper types, 
            //   in source code order. Some optional arguments might be missing.
            // * a map showing which parameter each argument corresponds to. If
            //   this is null, then the argument to parameter mapping is one-to-one.
            // * the ref kind of each argument, in source code order. That is, whether
            //   the argument was marked as ref, out, or value (neither).
            // * a method symbol.
            // * whether the call is expanded or normal form.

            // We rewrite the call so that:
            // * if in its expanded form, we create the params array.
            // * if the call requires reordering of arguments because of named arguments, temporaries are generated as needed

            // Doing this transformation can move around refness in interesting ways. For example, consider
            //
            // A().M(y : ref B()[C()], x : out D());
            //
            // This will be created as a call with receiver A(), symbol M, argument list ( B()[C()], D() ),
            // name list ( y, x ) and ref list ( ref, out ).  We can rewrite this into temporaries:
            //
            // A().M( 
            //    seq ( ref int temp_y = ref B()[C()], out D() ),
            //    temp_y );
            // 
            // Now we have a call with receiver A(), symbol M, argument list as shown, no name list,
            // and ref list ( out, value ). We do not want to pass a *ref* to temp_y; the temporary
            // storage is not the thing being ref'd! We want to pass the *value* of temp_y, which
            // *contains* a reference.

            // We attempt to minimize the number of temporaries required. Arguments which neither
            // produce nor observe a side effect can be placed into their proper position without
            // recourse to a temporary. For example:
            //
            // Where(predicate: x=>x.Length!=0, sequence: S())
            //
            // can be rewritten without any temporaries because the conversion from lambda to
            // delegate does not produce any side effect that could be observed by S().
            //
            // By contrast:
            //
            // Foo(z: this.p, y: this.Q(), x: (object)10)
            //
            // The boxing of 10 can be reordered, but the fetch of this.p has to happen before the
            // call to this.Q() because the call could change the value of this.p. 
            //
            // We start by binding everything that is not obviously reorderable as a temporary, and
            // then run an optimizer to remove unnecessary temporaries.

            ReadOnlyArray<ParameterSymbol> parameters = method.Parameters;
            var parameterCount = parameters.Count;
            var arguments = new BoundExpression[parameterCount];
            temporaries = ReadOnlyArray<LocalSymbol>.Null;  // not using temps by default.

            List<RefKind> refKinds = null;
            if (argumentRefKinds.IsNotNull)
            {
                refKinds = new List<RefKind>(parameterCount);
                for (int p = 0; p < parameterCount; ++p)
                {
                    refKinds.Add(RefKind.None);
                }
            }

            ArrayBuilder<BoundAssignmentOperator> storesToTemps = null;
            ArrayBuilder<BoundExpression> paramArray = null;

            if (expanded)
            {
                paramArray = ArrayBuilder<BoundExpression>.GetInstance();
            }

            for (int a = 0; a < rewrittenArguments.Count; ++a)
            {
                var argument = rewrittenArguments[a];
                var p = (argsToParamsOpt.IsNotNull) ? argsToParamsOpt[a] : a;
                var refKind = argumentRefKinds.RefKinds(a);
                Debug.Assert(arguments[p] == null);
                if (expanded && p == parameterCount - 1)
                {
                    paramArray.Add(argument);
                    Debug.Assert(refKind == RefKind.None);
                }
                else if (IsSafeForReordering(argument, refKind))
                {
                    arguments[p] = argument;
                    if (refKinds != null)
                    {
                        refKinds[p] = refKind;
                    }
                }
                else
                {
                    if (storesToTemps == null)
                    {
                        storesToTemps = ArrayBuilder<BoundAssignmentOperator>.GetInstance(rewrittenArguments.Count);
                    }

                    var tempStore = TempHelpers.StoreToTemp(argument, refKind, containingSymbol);
                    storesToTemps.Add(tempStore.Item1);
                    arguments[p] = tempStore.Item2;
                }
            }

            if (expanded)
            {
                var paramArrayType = parameters[parameterCount - 1].Type;
                var arrayArgs = paramArray.ToReadOnlyAndFree();

                var int32Type = method.ContainingAssembly.GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode.Int32);

                arguments[parameterCount - 1] = new BoundArrayCreation(
                            null,
                            null,
                            ReadOnlyArray.Singleton<BoundExpression>(
                                new BoundLiteral(null, null, ConstantValue.Create(arrayArgs.Count), int32Type)),
                            new BoundArrayInitialization(null, null, arrayArgs),
                            paramArrayType);
            }

            for (int p = 0; p < parameterCount; ++p)
            {
                if (arguments[p] == null)
                {
                    Debug.Assert(parameters[p].IsOptional);

                    // UNDONE: Add optional arguments.
                }
            }

            if (storesToTemps != null)
            {
                int tempsNeeded = MergeArgumentsAndSideEffects(storesToTemps, arguments);

                if (tempsNeeded > 0)
                {
                    var temps = new LocalSymbol[tempsNeeded];
                    for (int i = 0, j = 0; i < storesToTemps.Count; i++)
                    {
                        var s = storesToTemps[i];
                        if (s != null)
                        {
                            temps[j++] = ((BoundLocal)s.Left).LocalSymbol;
                        }
                    }

                    temporaries = temps.AsReadOnlyWrap();
                }

                storesToTemps.Free();
            }

            // * The rewritten list of names is now null because the arguments have been reordered.
            // * The args-to-params map is now null because every argument exactly matches its parameter.
            // * The call is no longer in its expanded form.

            argumentRefKinds = refKinds == null ? ReadOnlyArray<RefKind>.Null : refKinds.AsReadOnly<RefKind>();
            rewrittenArguments = arguments.AsReadOnlyWrap();
        }