private static void AssertEqual(Expression a, Expression b)
        {
            var fva = FreeVariableScanner.Scan(a);
            var fvb = FreeVariableScanner.Scan(b);

            var la = Expression.Lambda(a, fva);
            var lb = Expression.Lambda(b, fvb);

            Assert.IsTrue(new ExpressionEqualityComparer().Equals(la, lb));
        }
        public void FreeVariableScanner_NestedScopes()
        {
            var p = Expression.Parameter(typeof(int));
            var e = Expression.Lambda(Expression.Lambda(p, p), p);

            var res = FreeVariableScanner.Scan(e);

            Assert.AreEqual(0, res.Count());

            Assert.IsFalse(FreeVariableScanner.HasFreeVariables(e));
        }
                protected override Expression VisitLambda <T>(Expression <T> node)
                {
                    if (!FreeVariableScanner.HasFreeVariables(node))
                    {
                        var d = CompileImpl(node, _cache, outliningEnabled: true, _hoister);
                        var c = Expression.Constant(d, node.Type);
                        return(c);
                    }

                    return(base.VisitLambda <T>(node));
                }
        public void FreeVariableScanner_Block()
        {
            var p = Expression.Parameter(typeof(int));
            var e = Expression.Block(new[] { p }, p);

            var res = FreeVariableScanner.Scan(e);

            Assert.AreEqual(0, res.Count());

            Assert.IsFalse(FreeVariableScanner.HasFreeVariables(e));
        }
        public void FreeVariableScanner_Lambda_Unbound()
        {
            var l = (Expression <Func <int, Func <int, int> > >)(x => y => x + y);
            var e = l.Body;

            var res = FreeVariableScanner.Scan(e);

            Assert.IsTrue(new[] { l.Parameters[0] }.SequenceEqual(res));

            Assert.IsTrue(FreeVariableScanner.HasFreeVariables(e));
        }
        public void FreeVariableScanner_Block_Unbound()
        {
            var p = Expression.Parameter(typeof(int));
            var q = Expression.Parameter(typeof(int));
            var b = Expression.Block(new[] { p }, Expression.Block(new[] { q }, Expression.Add(p, q)));
            var e = b.Expressions[0];

            var res = FreeVariableScanner.Scan(e);

            Assert.IsTrue(new[] { p }.SequenceEqual(res));

            Assert.IsTrue(FreeVariableScanner.HasFreeVariables(e));
        }
        protected override Expression <Func <IReactiveProxy, Task> > VisitObserverOnCompletedCore <T>(ObserverOnCompleted <T> operation)
        {
            var binder            = new IdentityAwareBinder();
            var boundParameter    = binder.Bind(Expression.Parameter(typeof(IAsyncReactiveQbserver <T>), operation.TargetObjectUri.ToCanonicalString()));
            var thisParameter     = FreeVariableScanner.Scan(boundParameter).Single();
            var onCompletedMethod = boundParameter.Type.GetMethod("OnCompletedAsync");

            return(Expression.Lambda <Func <IReactiveProxy, Task> >(
                       Expression.Call(
                           boundParameter,
                           onCompletedMethod,
                           Expression.Constant(CancellationToken.None)),
                       thisParameter));
        }
        public void FreeVariableScanner_CatchBlock()
        {
            var exMessage = (PropertyInfo)ReflectionHelpers.InfoOf((Exception ex) => ex.Message);
            var writeLine = (MethodInfo)ReflectionHelpers.InfoOf((string s) => Console.WriteLine(s));

            var p = Expression.Parameter(typeof(Exception));
            var e = Expression.TryCatch(Expression.Call(writeLine, Expression.Constant("Hello")), Expression.Catch(p, Expression.Call(writeLine, Expression.MakeMemberAccess(p, exMessage))));

            var res = FreeVariableScanner.Scan(e);

            Assert.AreEqual(0, res.Count());

            Assert.IsFalse(FreeVariableScanner.HasFreeVariables(e));
        }
示例#9
0
            /// <summary>
            /// Rewrites a stream to an untyped stream if the stream definition
            /// is closed over wildcard types, and the stream factory returns
            /// an untyped stream.
            /// </summary>
            /// <param name="key">The stream identifier.</param>
            /// <param name="expr">The stream expression.</param>
            /// <returns>
            /// The stream expression with types rewritten to the untyped stream
            /// variant if the expression meets the expected criteria.
            /// </returns>
            private Expression RewriteQuotedReactiveStreamToUntyped(string key, Expression expr)
            {
                // Search for free variables
                var freeVariables = FreeVariableScanner.Scan(expr).ToArray();

                // For now, with the absence of stream factory operators, it is safe to
                // throw if there is more than one free variable as all stream expressions
                // will be invocations of exactly one unbound stream factory parameter.
                if (freeVariables.Length > 1)
                {
                    throw new InvalidOperationException(
                              string.Format(CultureInfo.InvariantCulture, "Unexpected stream expression '{0}' for key '{1}' on query engine '{2}'.", expr.ToTraceString(), key, _queryEngine.Uri.ToCanonicalString()));
                }

                if (freeVariables.Length == 1)
                {
                    // Find single stream factory parameter expression
                    var streamFactoryParam = freeVariables[0];
                    if (streamFactoryParam != null)
                    {
                        // Resolve stream factory type from metadata
                        if (_queryEngine._registry.SubjectFactories.TryGetValue(streamFactoryParam.Name, out var streamFactoryDefinition))
                        {
                            // TODO: support rewrites based on stream factory classes
                            // If definition is a lambda expression, rewrite to the return type
                            var streamFactoryType = streamFactoryDefinition.Expression.Type;
                            var invokeMethod      = streamFactoryType.GetMethod("Invoke");
                            if (invokeMethod != null && invokeMethod.ReturnType == typeof(IMultiSubject))
                            {
                                var actualStreamType   = expr.Type;
                                var expectedStreamType = invokeMethod.ReturnType;
                                if (actualStreamType != expectedStreamType)
                                {
                                    var substitutionVisitor = new TypeSubstitutionExpressionVisitor(new Dictionary <Type, Type>
                                    {
                                        { actualStreamType, expectedStreamType }
                                    });

                                    _queryEngine.TraceSource.LazyStream_TypeRewrite(key, actualStreamType, expectedStreamType, _queryEngine.Uri);

                                    return(substitutionVisitor.Apply(expr));
                                }
                            }
                        }
                    }
                }

                return(expr);
            }
示例#10
0
        public void Evaluate(Expression expression)
        {
            var subst = new TypeSubstitutionExpressionVisitor(new Dictionary <Type, Type>
            {
                { typeof(IQubjectFactory <>), typeof(ISubjectFactory <>) },
                { typeof(IQubject <>), typeof(ISubject <>) },
                { typeof(IQbservable <>), typeof(IObservable <>) },
                { typeof(IQbserver <>), typeof(IObserver <>) },
                { typeof(IQubscription), typeof(IDisposable) },
            });

            var expr = subst.Visit(expression);

            var fvs = FreeVariableScanner.Scan(expr);

            var map = new Dictionary <ParameterExpression, Expression>();

            foreach (var fv in fvs)
            {
                if (_registry.TryGetValue(fv.Name, out var res))
                {
                    if (res is Expression e)
                    {
                        var u = e.Type.UnifyExact(fv.Type);
                        var f = new TypeSubstitutionExpressionVisitor(u).Visit(e);
                        map.Add(fv, f);
                        continue;
                    }
                    else
                    {
                        e = Expression.Constant(res, fv.Type);
                        map.Add(fv, e);
                        continue;
                    }
                }

                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not bind '{0}'.", fv.Name));
            }

            var bound = new Binder(map).Bind(expr);

            if (bound.Type == typeof(void))
            {
                // TODO: Evaluate method could be more forgiving for void-returning delegates (no automatic coercion to object)
                bound = Expression.Block(bound, Expression.Default(typeof(object)));
            }

            bound.Evaluate();
        }
示例#11
0
        /// <summary>
        /// Gets a collection of free variables in the specified expression.
        /// </summary>
        /// <param name="expression">The expression to scan for free variables.</param>
        /// <returns>A collection of free variables that occur in the expression.</returns>
        public static ICollection <ParameterExpression> FindFreeVariables(Expression expression)
        {
            var freeVariables = FreeVariableScanner.Scan(expression);

            //
            // PERF: The current implementation of Scan returns either a HashSet<T> or an empty T[]
            //       singleton for the free variables sequence. Both are convertible to ICollection<T>,
            //       so the call to AsCollection below will return the original object and not create
            //       a copy. This is cheaper than calling HasFreeVariables followed by a Scan in case
            //       an expression has free variables, because we'll visit the expression twice (with
            //       early bail-out for HasFreeVariables but still a lot of virtual dispatching). By
            //       returning a collection, we can also avoid dynamically growing array allocations
            //       when mapping variables using a .Select(...).ToArray() approach.
            //

            Debug.Assert(freeVariables is ICollection <ParameterExpression>, "Potential performance hazard.");

            return(freeVariables.AsCollection());
        }
示例#12
0
            protected override Expression VisitExpression(Expression expression)
            {
                var freeVariables = FreeVariableScanner.Scan(expression);

                var replacements = new Dictionary <ParameterExpression, ParameterExpression>();

                foreach (var parameter in freeVariables)
                {
                    var uri    = new Uri(parameter.Name);
                    var newUri = _uriMapping(uri);
                    if (newUri != uri)
                    {
                        replacements.Add(parameter, Expression.Parameter(parameter.Type, newUri.ToCanonicalString()));
                    }
                }

                var substitutor = new FreeVariableSubstitutor(replacements);

                return(substitutor.Visit(expression));
            }
                protected override Expression VisitMember(MemberExpression node)
                {
                    var result = base.VisitMember(node);

                    if (result is MemberExpression asMember)
                    {
                        // Assuming only metadata collection properties exist on the `IReactiveMetadataProxy` interface.
                        if (asMember.Member is PropertyInfo property && IsInterfaceProperty(typeof(IReactiveMetadataProxy), property))
                        {
                            var fvs = FreeVariableScanner.HasFreeVariables(asMember);
                            if (!fvs)
                            {
                                // For now, all properties on `IReactiveMetadataProxy` are `IQueryable`.
                                // If this is no longer the case, this call should be revisited.
                                return(asMember.Evaluate <IQueryable>().Expression);
                            }
                        }
                    }

                    return(result);
                }
 private static bool IsFullyBound(Expression expr) => !FreeVariableScanner.HasFreeVariables(expr);
示例#15
0
 public void Placeholder()
 {
     Assert.Throws <NotImplementedException>(() => FreeVariableScanner.HasFreeVariables(Expression.Default(typeof(int))));
 }
示例#16
0
        private static void UnpackImpl(LambdaExpression expression, Type voidType, out Expression body, out IEnumerable <ParameterExpression> parameters)
        {
            var count = expression.Parameters.Count;

            if (count == 0)
            {
                body       = expression.Body;
                parameters = expression.Parameters;
                return;
            }

            if (count != 1)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Expression '{0}' has more than one parameter. In order to detupletize a lambda expression, it should have exactly one parameter of a tuple type.", expression));
            }

            var tupleParameter = expression.Parameters[0];
            var parameterType  = tupleParameter.Type;

            if (parameterType == voidType)
            {
#if USE_SLIM
                if (FreeVariableScanner.Find(expression.Body).Contains(tupleParameter))
#else
                if (FreeVariableScanner.Scan(expression.Body).Contains(tupleParameter))
#endif
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The 'void' parameter is bound in the body of lambda expression '{0}'. In order to detupletize a lambda expression with a 'void' parameter, the parameter should be unbound.", expression));
                }

                body       = expression.Body;
                parameters = Array.Empty <ParameterExpression>();
                return;
            }

            if (!IsTuple(parameterType))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The type of parameter '{0}' in '{1}' is not a valid tuple type: '{2}'.", tupleParameter, expression, parameterType));
            }

            var tupleType = tupleParameter.Type;

            var newParameters = new List <ParameterExpression>();

            const int max = TupleTypeCount - 1 /* T1..T7,TRest */;

            var type = tupleType;

            var i = 0;
            while (true)
            {
                var args = type.GetGenericArguments();

                var n = args.Length;

                if (args.Length == max)
                {
                    n--;
                }

                for (var j = 0; j < n; j++)
                {
                    var arg = args[j];
                    newParameters.Add(Expression.Parameter(arg, "p" + i++));
                }

                if (args.Length == max)
                {
                    type = args.Last();
                }
                else
                {
                    break;
                }
            }

            var subst = new ParameterBasedDetupletizer(tupleParameter, newParameters);

            body       = subst.Visit(expression.Body);
            parameters = newParameters;
        }
 public void FreeVariableScanner_ArgumentChecks()
 {
     AssertEx.ThrowsException <ArgumentNullException>(() => FreeVariableScanner.Scan(expression: null), ex => Assert.AreEqual("expression", ex.ParamName));
     AssertEx.ThrowsException <ArgumentNullException>(() => FreeVariableScanner.HasFreeVariables(expression: null), ex => Assert.AreEqual("expression", ex.ParamName));
 }