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)); }
/// <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); }
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(); }
/// <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()); }
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);
public void Placeholder() { Assert.Throws <NotImplementedException>(() => FreeVariableScanner.HasFreeVariables(Expression.Default(typeof(int)))); }
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)); }