private static MethodCallCSharpExpression MakeAccess(ConditionalReceiver receiver, MethodInfo method, ReadOnlyCollection <ParameterAssignment> arguments)
        {
            if (method.IsStatic && method.IsDefined(typeof(ExtensionAttribute)))
            {
                var thisPar = method.GetParametersCached()[0];
                var thisArg = CSharpExpression.Bind(thisPar, receiver);

                var newArgs = new ParameterAssignment[arguments.Count + 1];
                newArgs[0] = thisArg;

                var i = 1;
                foreach (var arg in arguments)
                {
                    newArgs[i++] = arg;
                }

                var newArguments = new TrueReadOnlyCollection <ParameterAssignment>(newArgs);

                return(CSharpExpression.Call(null, method, newArguments)); // TODO: call ctor directly
            }
            else
            {
                return(CSharpExpression.Call(receiver, method, arguments)); // TODO: call ctor directly
            }
        }
예제 #2
0
        protected internal override Expression VisitConditionalReceiver(ConditionalReceiver node)
        {
            var id = _parent.MakeInstanceId(node);

            var res = new XElement(nameof(ConditionalReceiver), new XAttribute("Id", id), new XAttribute(nameof(node.Type), node.Type));

            _nodes.Push(res);

            return(node);
        }
            protected internal override Expression VisitConditionalReceiver(ConditionalReceiver node)
            {
                // TODO: We could check that we find the receiver exactly once.

                if (node == _receiver)
                {
                    return(_nonNull);
                }

                return(base.VisitConditionalReceiver(node));
            }
        internal static void CheckConditionalAccess(Expression receiver, ConditionalReceiver nonNullReceiver, Expression whenNotNull)
        {
            RequiresCanRead(receiver, nameof(receiver));
            RequiresNotNull(nonNullReceiver, nameof(nonNullReceiver));
            RequiresCanRead(whenNotNull, nameof(whenNotNull));

            var receiverType = receiver.Type;

            if (receiverType == typeof(void) || receiverType.IsByRef || (receiverType.IsValueType && !receiverType.IsNullableType()))
            {
                throw Error.InvalidConditionalReceiverExpressionType(receiverType);
            }

            var nonNullReceiverType = receiverType.GetNonNullReceiverType();

            if (nonNullReceiverType != nonNullReceiver.Type)
            {
                throw Error.ConditionalReceiverTypeMismatch(receiverType, nonNullReceiverType);
            }
        }
 internal override ConditionalAccessCSharpExpression <Expression> Rewrite(Expression receiver, ConditionalReceiver nonNullReceiver, Expression whenNotNull) =>
 new ConditionalAccessCSharpExpression(receiver, nonNullReceiver, whenNotNull);
 public SubstituteConditionalReceiver(ConditionalReceiver receiver, Expression nonNull)
 {
     _receiver = receiver;
     _nonNull  = nonNull;
 }
 internal override ConditionalAccessCSharpExpression <MemberExpression> Rewrite(Expression receiver, ConditionalReceiver nonNullReceiver, MemberExpression whenNotNull)
 {
     return(new ConditionalMemberCSharpExpression(receiver, nonNullReceiver, whenNotNull));
 }
 private ConditionalMemberCSharpExpression(Expression expression, ConditionalReceiver receiver, MemberExpression access)
     : base(expression, receiver, access)
 {
 }
예제 #9
0
 internal override ConditionalAccessCSharpExpression <IndexCSharpExpression> Rewrite(Expression receiver, ConditionalReceiver nonNullReceiver, IndexCSharpExpression whenNotNull)
 {
     return(new MethodBased(receiver, nonNullReceiver, whenNotNull));
 }
예제 #10
0
 private MethodBased(Expression @object, ConditionalReceiver receiver, IndexCSharpExpression access)
     : base(@object, receiver, access)
 {
 }
예제 #11
0
 private ConditionalArrayIndexCSharpExpression(Expression array, ConditionalReceiver receiver, IndexExpression access)
     : base(array, receiver, access)
 {
 }
예제 #12
0
 private ConditionalArrayIndexCSharpExpression(Expression array, ConditionalReceiver receiver, ReadOnlyCollection <Expression> indexes)
     : this(array, receiver, MakeAccess(receiver, indexes))
 {
 }
예제 #13
0
 private XNode Visit(ConditionalReceiver node)
 {
     VisitConditionalReceiver(node);
     return(_nodes.Pop());
 }
 public ConditionalReceiverProxy(ConditionalReceiver node)
 {
     _node = node;
 }
예제 #15
0
 protected internal virtual Expression VisitConditionalReceiver(ConditionalReceiver node)
 {
     return(node);
 }
 private ConditionalMethodCallCSharpExpression(Expression expression, ConditionalReceiver receiver, MethodInfo method, ReadOnlyCollection <ParameterAssignment> arguments)
     : this(expression, receiver, MakeAccess(receiver, method, arguments))
 {
 }
예제 #17
0
 private MethodBased(Expression @object, ConditionalReceiver receiver, MethodInfo method, ReadOnlyCollection <ParameterAssignment> arguments)
     : this(@object, receiver, MakeAccess(receiver, method, arguments))
 {
     _method = method;
 }
예제 #18
0
 private static IndexExpression MakeAccess(ConditionalReceiver receiver, ReadOnlyCollection <Expression> indexes)
 {
     return(Expression.ArrayAccess(receiver, indexes)); // TODO: call ctor directly
 }
예제 #19
0
 private static IndexCSharpExpression MakeAccess(ConditionalReceiver receiver, MethodInfo method, ReadOnlyCollection <ParameterAssignment> arguments)
 {
     return(CSharpExpression.Index(receiver, method, arguments)); // TODO: call ctor directly
 }
예제 #20
0
 private ConditionalInvocationCSharpExpression(Expression expression, ConditionalReceiver receiver, ReadOnlyCollection <ParameterAssignment> arguments)
     : this(expression, receiver, MakeAccess(receiver, arguments))
 {
 }
 private ConditionalMemberCSharpExpression(Expression expression, ConditionalReceiver receiver, MemberInfo member)
     : this(expression, receiver, MakeAccess(receiver, member))
 {
 }
예제 #22
0
 private ConditionalInvocationCSharpExpression(Expression expression, ConditionalReceiver receiver, InvocationCSharpExpression access)
     : base(expression, receiver, access)
 {
 }
 private static MemberExpression MakeAccess(ConditionalReceiver receiver, MemberInfo member)
 {
     return(Expression.MakeMemberAccess(receiver, member)); // TODO: call ctor directly
 }
예제 #24
0
 private static InvocationCSharpExpression MakeAccess(ConditionalReceiver receiver, ReadOnlyCollection <ParameterAssignment> arguments)
 {
     return(CSharpExpression.Invoke(receiver, arguments)); // TODO: call ctor directly
 }
 internal ConditionalAccessCSharpExpression(Expression receiver, ConditionalReceiver nonNullReceiver, Expression whenNotNull)
     : base(receiver, nonNullReceiver, whenNotNull)
 {
 }
예제 #26
0
 internal override ConditionalAccessCSharpExpression <InvocationCSharpExpression> Rewrite(Expression receiver, ConditionalReceiver nonNullReceiver, InvocationCSharpExpression whenNotNull)
 {
     return(new ConditionalInvocationCSharpExpression(receiver, nonNullReceiver, whenNotNull));
 }
 public ConditionalReceiverProxy(ConditionalReceiver node)
 {
     _node = node;
 }
예제 #28
0
 private PropertyBased(Expression @object, ConditionalReceiver receiver, PropertyInfo indexer, ReadOnlyCollection <ParameterAssignment> arguments)
     : this(@object, receiver, MakeAccess(receiver, indexer, arguments))
 {
 }
        /// <summary>
        /// Creates a <see cref="ConditionalAccessCSharpExpression"/> representing a null-conditional access operation.
        /// </summary>
        /// <param name="receiver">The receiver to access conditionally.</param>
        /// <param name="nonNullReceiver">The non-null receiver used in the <paramref name="whenNotNull"/> expression.</param>
        /// <param name="whenNotNull">The operation to apply to the receiver when it's non-null.</param>
        /// <returns>A <see cref="Microsoft.CSharp.Expressions.ConditionalReceiver"/> that has the <see cref="CSharpNodeType" /> property equal to <see cref="CSharpExpressionType.ConditionalReceiver" /> and the <see cref="Expression.Type" /> property equal to the specified type.</returns>
        public static ConditionalAccessCSharpExpression ConditionalAccess(Expression receiver, ConditionalReceiver nonNullReceiver, Expression whenNotNull)
        {
            CheckConditionalAccess(receiver, nonNullReceiver, whenNotNull);

            // TODO: More elaborate checking of `whenNotNull` would be possible, in particular to check the following:
            //
            //         - only supported operations can occur in the access expression
            //         - the conditional receiver is used in an input position
            //         - the conditional receiver is used exactly once
            //
            //       The only drawback is that those checks will involve a visit to the access expression, which doesn't have a
            //       constant cost. When those nodes are deeply nested, we'd be going over the same tree many times, unless we
            //       relax the check for the conditional receiver usage count to be at least once. This would also happen during
            //       a rewrite of the tree by means of a visitor, which causes factory invocations.
            //
            //       Note that the expression compiler can still catch such violations during reduction by only reducing one
            //       occurrence of each conditional receiver, therefore leaving any other occurrences unbound, which on its turn
            //       causes a reduction error for the unbound conditional receiver (which is not a reducible node).
            //
            //       What are the implications of not performing those checks? Nothing unsafe will happen but the node type will
            //       effectively capture a superset of the possibilities provided by the C# language for the construct. E.g.
            //       you could construct a null-conditional call where the conditional receiver is used for a parameter that's
            //       different from the receiving instance (or the `this` parameter for an extension method):
            //
            //         ConditionalAccess(bar, receiver, Call(foo, qux, { receiver }))  ==  foo.qux(bar) iff bar != null
            //                                               ^^^         ~~~~~~~~
            //                                            instance       argument
            //
            //       We could decide the cost of checking doesn't yield enough benefits given that it only applies to hand-
            //       crafted expressions (i.e. the compiler will never emit a non-standard form). We'd still have to support
            //       this behavior in future versions and consider it to have well-defined behavior, or document it as having
            //       undefined behavior (with little predecents) from the get-go.
            //
            //       Finally, note that we could do away with this problem by modeling the nodes differently without relying on
            //       a node for the conditional receiver. To do so, we'd have to introduce expressions for argument lists that
            //       don't include the receiver, which duplicates a lot of existing LINQ nodes. In such a design, the receiver
            //       would be implicit and always correspond to the "left-most source" in the access expression. It comes at
            //       the cost of not being able to reuse existing LINQ nodes which always have a bound receiver, but we could
            //       piggyback on the work to introduce C#-specific nodes for e.g. ParameterAssignment to create nodes for e.g.
            //       argument lists. Old LINQ nodes could be considered applications of more primitive partial nodes:
            //
            //         Call(o, m, args)   ==  Access(o, Call(m, args))   ==  o.m(args)
            //         Member(o, p)       ==  Access(o, Member(p))       ==  o.p
            //         Index(o, i, args)  ==  Access(o, Index(i, args))  ==  o.i[args]
            //         Invoke(f, args)    ==  Access(f, Invoke(args))    ==  o(args)
            //
            //       In this setting, we could still allow further composition of the access expressions by passing a list of
            //       access operations in lieu of the second operand to `Access`, e.g.
            //
            //                    Access(o, { Member(p), Call(m, args2) })  ==  o.p.m(args2)
            //
            //       Substituting `Access` for `ConditionalAccess` would mean the following:
            //
            //         ConditionalAccess(o, { Member(p), Call(m, args2) })  ==  o?.p.m(args2)
            //
            //       Note that it makes for a much stranger tree representation (though arguably the current form is weird too)
            //       with new partially applied `Member`, `Call`, `Index`, and `Invoke` constructs. Instead, we could nest them
            //       as well, by only having one (inner-most, left-most) access-unbound node in the access expression:
            //
            //         ConditionalAccess(o, Call(Member(   p), m, args2))
            //                                          ^^
            //                                         hole
            //
            //       With this form, the check is still expensive, because we need to check for exactly one unbound form in the
            //       source chain of the access expression.

            // DESIGN: We could make instances of type ConditionalAccessCSharpExpression<TExpression> and/or specialized subtypes
            //         if we decide to keep those. However, it may look strange if `whenNotNull` is not just a single access but
            //         consists of a 'chain' of operations. Right now, the idea is to have the specialized subtypes merely as a
            //         convenience when using factories by hand, but we could scrap it all and just stick with the primitive node.
            return(new ConditionalAccessCSharpExpression(receiver, nonNullReceiver, whenNotNull));
        }
예제 #30
0
 internal ConditionalIndexCSharpExpression(Expression expression, ConditionalReceiver receiver, IndexCSharpExpression access)
     : base(expression, receiver, access)
 {
 }
 protected internal virtual Expression VisitConditionalReceiver(ConditionalReceiver node)
 {
     return node;
 }
 internal override ConditionalAccessCSharpExpression <IndexCSharpExpression> Rewrite(Expression receiver, ConditionalReceiver nonNullReceiver, IndexCSharpExpression whenNotNull) =>
 new PropertyBased(receiver, nonNullReceiver, whenNotNull);