csharp/71/Cometary/src/Cometary.Expressions/Expressions/LinkedExpression.cs

LinkedExpression.cs
using System;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;

namespace Cometary.Expressions
{
    /// 
    /// Represents an expression whose value in the compiled expression
    /// tree is synced with the  property.
    /// 
    /// The type of the value stored by this expression.
    [DebuggerStepThrough]
    public sealed clast LinkedExpression : Expression
    {
        private static readonly FieldInfo ValueField = typeof(Link).GetRuntimeField(nameof(Link.Value));

        private sealed clast Link
        {
            public T Value;
        }

        private readonly Link _link;
        private readonly Expression _reduced;

        private readonly Func _compiledGetter;
        private readonly Action _compiledSetter;
        private readonly bool _isConstantLink;

        /// 
        public override bool CanReduce => true;

        /// 
        public override ExpressionType NodeType => ExpressionType.Extension;

        /// 
        public override Type Type => typeof(T);

        /// 
        /// Gets whether or not the value of this expression is read-only.
        /// 
        public bool IsReadOnly { get; }

        /// 
        /// Gets the value of this .
        /// 
        public T Value
        {
            get => _isConstantLink ? _link.Value : _compiledGetter();
            set
            {
                if (IsReadOnly)
                    throw new InvalidOperationException();

                if (_isConstantLink)
                    _link.Value = value;
                else
                    _compiledSetter(value);
            }
        }

        internal LinkedExpression(T val, bool readOnly)
        {
            IsReadOnly = readOnly;

            _link = new Link { Value = val };
            _isConstantLink = true;
            _reduced = typeof(T).GetTypeInfo().IsValueType ? (Expression)Field(Constant(_link), ValueField) : Constant(val);
        }

        internal LinkedExpression(Expression lambda, bool readOnly)
        {
            IsReadOnly = readOnly;

            _compiledGetter = GetCompiledGetter(lambda);
            _compiledSetter = GetCompiledSetter(lambda);
            _reduced = lambda.Body;

            if (_compiledSetter == null && !readOnly)
                throw new InvalidOperationException("Cannot create read/write link with read-only expression.");
        }

        private static Func GetCompiledGetter(Expression lambda)
        {
            return lambda.Compile();
        }

        private static Action GetCompiledSetter(Expression lambda)
        {
            ParameterExpression parameter = Parameter(typeof(T), "value");

            return Lambda(astign(lambda.Body, parameter), parameter).Compile();
        }

        /// 
        /// Returns a new  whose 
        /// property set to the given .
        /// 
        public static implicit operator LinkedExpression(T value) => new LinkedExpression(value, true);

        /// 
        public override Expression Reduce() => _reduced;

        /// 
        protected override Expression Accept(ExpressionVisitor visitor) => visitor.Visit(Reduce());

        /// 
        public override string ToString() => Value.ToString();
    }

    partial clast Expressive
    {
        /// 
        /// Creates a read-only  that represents
        /// a link between a variable and its compiled value.
        /// 
        public static LinkedExpression Link(T value)
        {
            return new LinkedExpression(value, true);
        }

        /// 
        /// Creates a  that represents
        /// a link between a variable and its compiled value.
        /// 
        public static LinkedExpression Link(T value, bool isReadOnly)
        {
            return new LinkedExpression(value, isReadOnly);
        }

        /// 
        /// 
        /// This method accepts method bodies of the form:
        ///  - ;
        ///  - ;
        ///  - .
        /// 
        public static LinkedExpression Link(Expression expression)
        {
            Requires.NotNull(expression, nameof(expression));

            return new LinkedExpression(expression, true);
        }

        /// 
        /// 
        /// This method accepts method bodies of the form:
        ///  - ;
        ///  - ;
        ///  - .
        /// 
        public static LinkedExpression Link(Expression expression, bool isReadOnly)
        {
            Requires.NotNull(expression, nameof(expression));

            return new LinkedExpression(expression, isReadOnly);
        }
    }
}