public void ExpressionTupletizer_IsTuple() { AssertEx.ThrowsException <ArgumentNullException>(() => ExpressionTupletizer.IsTuple(type: null), ex => Assert.AreEqual("type", ex.ParamName)); foreach (var t in new[] { typeof(Tuple <int>), typeof(Tuple <int, int>), typeof(Tuple <int, int, int>), typeof(Tuple <int, int, int, int>), typeof(Tuple <int, int, int, int, int>), typeof(Tuple <int, int, int, int, int, int>), typeof(Tuple <int, int, int, int, int, int, int>), typeof(Tuple <int, int, int, int, int, int, int, Tuple <int> >), typeof(Tuple <int, int, int, int, int, int, int, Tuple <int, int> >), typeof(Tuple <int, int, int, int, int, int, int, Tuple <int, int, int, int, int, int, int, Tuple <int, int, int> > >), }) { Assert.IsTrue(IsTuple(t)); } foreach (var t in new[] { typeof(int), typeof(List <int>), typeof(Tuple <>), typeof(Tuple <,>), typeof(Tuple <int, int, int, int, int, int, int, /*TRest*/ int>), }) { Assert.IsFalse(IsTuple(t)); } }
public void ExpressionTupletizer_PackAndUnpack_Lambda_NoVoid() { #if USE_SLIM var packs = new Func <LambdaExpression, LambdaExpression>[] { f => (LambdaExpression)ExpressionSlimTupletizer.Pack((LambdaExpressionSlim)f.ToExpressionSlim()).ToExpression(), f => { var s = (LambdaExpressionSlim)f.ToExpressionSlim(); return((LambdaExpression)ExpressionSlimTupletizer.Pack(s.Body, s.Parameters).ToExpression()); }, }; #else var packs = new Func <LambdaExpression, LambdaExpression>[] { f => ExpressionTupletizer.Pack(f), f => ExpressionTupletizer.Pack(f.Body, f.Parameters), }; #endif foreach (var f in new LambdaExpression[] { #pragma warning disable IDE0004 // Remove Unnecessary Cast. (Only unnecessary on C# 10 or later.) (Expression <Func <int> >)(() => 42), #pragma warning restore IDE0004 // Remove Unnecessary Cast (Expression <Func <string, int> >)(s => s.Length), (Expression <Func <string, int, int> >)((s, i) => s.Length + i), (Expression <Func <int, int, int, int> >)((a, b, c) => a * b + c), (Expression <Func <int, int, int, int, int> >)((a, b, c, d) => a * b + c - d), (Expression <Func <int, int, int, int, int, int> >)((a, b, c, d, e) => a * b + c - d / e), (Expression <Func <int, int, int, int, int, int, int> >)((a, b, c, d, e, f) => a * b + c - d / e + f), (Expression <Func <int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g) => a * b + c - d / e + f * g), (Expression <Func <int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h) => a * b + c - d / e + f * g / h), (Expression <Func <int, int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h, i) => a * b + c - d / e + f * g / h + i), (Expression <Func <int, int, int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h, i, j) => a * b + c - d / e + f * g / h + i - j), (Expression <Func <int, int, int, int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h, i, j, k) => a * b + c - d / e + f * g / h + i - j * k), (Expression <Func <int, int, int, int, int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h, i, j, k, l) => a * b + c - d / e + f * g / h + i - j * k / l), (Expression <Func <int, int, int, int, int, int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h, i, j, k, l, m) => a * b + c - d / e + f * g / h + i - j * k / l + m), (Expression <Func <int, int, int, int, int, int, int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h, i, j, k, l, m, n) => a * b + c - d / e + f * g / h + i - j * k / l + m % n), (Expression <Func <int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int> >)((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => a * b + c - d / e + f * g / h + i - j * k / l + m % n + o), (Expression <Func <string, Uri, int, double, DateTime, float, byte, TimeSpan, long, short, Guid, char, uint, AppDomain, decimal[], List <int>, bool> >)((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => (a + b.ToString() + new string(l, 10) + n.FriendlyName).Length + i * d - e.Year % j + f * g / h.Days + k.ToByteArray().Length + m * p[c] > 0 && o[c] < 12.45m), }) { foreach (var pack in packs) { var g = pack(f); var n = g.Parameters.Count; Assert.IsTrue(n is 0 or 1, "Parameter count: " + f.ToString()); if (n == 1) { Assert.IsTrue(g.Parameters[0].Type.FullName.StartsWith("System.Tuple`"), "Parameter type: " + f.ToString()); } var h = ExpressionTupletizer.Unpack(g); var res = new ExpressionEqualityComparer().Equals(f, h); Assert.IsTrue(res, "Equality: " + f.ToString()); } } }
public override Expression Normalize(Expression expression) { var normalized = base.Normalize(expression); var tupletized = new InvocationTupletizer().Visit(normalized); if (tupletized is LambdaExpression lambda) { tupletized = ExpressionTupletizer.Pack(lambda); } return(tupletized); }
public static Expression Detupletize(Expression expression) { var detupletized = new InvocationDetupletizer().Visit(expression); // TODO: Check if the lambda parameter is tuple. if (detupletized is LambdaExpression lambda) { detupletized = ExpressionTupletizer.Unpack(lambda); } return(detupletized); }
private static Expression Tupletize(Expression expression) { var inv = new InvocationTupletizer(); var result = inv.Visit(expression); if (result is LambdaExpression lambda) { result = ExpressionTupletizer.Pack(lambda); } return(result); }
private static bool IsTuple(Expression expression) { if (expression.NodeType == ExpressionType.New) { // TODO: All of this code really should move up, closer to where we know we're using tuples. if (ExpressionTupletizer.IsTuple(expression.Type)) { return(true); } } return(false); }
public override Expression Normalize(Expression expression) { var normalized = base.Normalize(expression); var tupletized = new InvocationTupletizer().Visit(normalized); if (tupletized is LambdaExpression lambda) { tupletized = ExpressionTupletizer.Pack(lambda); } var sync = new AsyncToSyncRewriter(new Dictionary <Type, Type>()).Rewrite(tupletized); return(sync); }
protected override Expression VisitInvocation(InvocationExpression node) { if (node.Expression is ParameterExpression function && IsUnboundParameter(function) && node.Arguments.Count == 1 && IsTuple(node.Arguments[0])) { var args = ExpressionTupletizer.Unpack(Visit(node.Arguments[0])); var newFunctionType = Expression.GetDelegateType(args.Select(a => a.Type).Concat(new[] { node.Type }).ToArray()); var newFunction = Expression.Parameter(newFunctionType, function.Name); return(Expression.Invoke(newFunction, args)); } return(base.VisitInvocation(node)); }
public void ExpressionTupletizer_Pack_TheWorks() { for (int i = 1; i < 18; i++) { var t = Enumerable.Range(1, i).Select(x => Expression.Constant(x)); var e = ExpressionTupletizer.Pack(t); var n = 0; var u = e; while (u != null) { var c = u.Type.GetGenericArguments().Length; n += c; var f = (NewExpression)u; if (u.Type.GetGenericTypeDefinition() == typeof(Tuple <, , , , , , ,>)) { u = f.Arguments.Last(); Assert.IsTrue(u.Type.Name.StartsWith("Tuple")); Assert.IsTrue(f.Members.Take(7).Select(m => m.Name).SequenceEqual(Enumerable.Range(1, 7).Select(j => "Item" + j))); Assert.IsTrue(f.Members.Last().Name == "Rest"); n--; } else { Assert.IsTrue(f.Members.Select(m => m.Name).SequenceEqual(Enumerable.Range(1, c).Select(j => "Item" + j))); u = null; } } Assert.AreEqual(i, n); var s = e.Evaluate(); var z = "(" + string.Join(", ", t.Select(c => c.Value)) + ")"; Assert.AreEqual(z, s.ToString()); } }
private void SerializeTemplateArgument(Expression argument) { var args = ExpressionTupletizer.Unpack(argument); foreach (Expression arg in args) { _serializer.Serialize((int)arg.NodeType, _stream); switch (arg.NodeType) { case ExpressionType.Constant: _serializer.Serialize(((ConstantExpression)arg).Value, arg.Type, _stream); break; case ExpressionType.Parameter: _serializer.Serialize(((ParameterExpression)arg).Name, _stream); break; default: throw new InvalidOperationException("Only constant and parameter expressions are supported."); } } }
public override Expression Visit(Expression node) { if (node != null && node.IsTemplatized(out ParameterExpression parameter, out Expression argument)) { var template = _bindings[parameter]; if (argument != null) { var unpackedTemplate = ExpressionTupletizer.Unpack((LambdaExpression)template); var unpackedArgument = ExpressionTupletizer.Unpack(argument); // Note that beta reduction is not required here as the caller to the // `Inline` method will reduce the result of the inlining step. return(Expression.Invoke(unpackedTemplate, unpackedArgument)); } else { return(Expression.Invoke((LambdaExpression)template)); } } return(base.Visit(node)); }
/// <summary> /// Visits the children of the <see cref="System.Linq.Expressions.InvocationExpression" />. /// </summary> /// <param name="node">The expression to visit.</param> /// <returns> /// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression. /// </returns> protected override Expression VisitInvocation(InvocationExpression node) { var expr = Visit(node.Expression); var args = Visit(node.Arguments); if (expr.NodeType == ExpressionType.Parameter) { var parameter = (ParameterExpression)expr; // Turns f(x, y, z) into f((x, y, z)) when f is an unbound parameter, i.e. representing a known resource. if (IsUnboundParameter(parameter)) { if (args.Count > 0) { var tuple = ExpressionTupletizer.Pack(args); var funcType = Expression.GetDelegateType(tuple.Type, node.Type); var function = Expression.Parameter(funcType, parameter.Name); return(Expression.Invoke(function, tuple)); } } } return(node.Update(expr, args)); }
public bool TryConstruct(out NewExpression rewritten) { var results = new List <MapEntry>(); if (TryConstruct(_expression, new List <MemberInfo>(), results)) { rewritten = (NewExpression)ExpressionTupletizer.Pack(results.Select(x => x.ConstructorArgument)); var nestedTypes = Array.Empty <MemberInfo>(); var currentType = rewritten.Type; for (int resultIdx = 0, tupleIdx = 0, count = results.Count; resultIdx < count; resultIdx++, tupleIdx++) { if (tupleIdx == s_tupleItems.Length - 1) { var temp = new MemberInfo[nestedTypes.Length + 1]; Array.Copy(nestedTypes, temp, nestedTypes.Length); temp[nestedTypes.Length] = currentType.GetMember(s_tupleItems[tupleIdx]).Single(); nestedTypes = temp; tupleIdx = 0; currentType = currentType.GetGenericArguments()[s_tupleItems.Length - 1]; } var members = new MemberInfo[nestedTypes.Length + 1]; Array.Copy(nestedTypes, members, nestedTypes.Length); members[nestedTypes.Length] = currentType.GetMember(s_tupleItems[tupleIdx]).Single(); _mapper.Add(results[resultIdx].MemberChain, members); } return(true); } rewritten = null; return(false); }
/// <summary> /// Unpacks the template lambda. /// </summary> /// <param name="lambda">The template lambda.</param> /// <param name="body">The unpacked template lambda body.</param> /// <param name="parameters">The unpacked template lambda parameters.</param> protected virtual void UnpackTemplateLambda(LambdaExpression lambda, out Expression body, out IEnumerable <ParameterExpression> parameters) { ExpressionTupletizer.Unpack(lambda, out body, out parameters); }
/// <summary> /// Unpacks the template arguments. /// </summary> /// <param name="argument">The packed template argument.</param> /// <returns>The unpacked arguments.</returns> protected virtual IEnumerable <Expression> UnpackTemplateArguments(Expression argument) { return(ExpressionTupletizer.Unpack(argument)); }
private static Expression Pack(Expression expression, IEnumerable <ParameterExpression> parameters, Type voidType) { return(ExpressionTupletizer.Pack(expression, parameters, voidType)); }
private static Expression Pack(Expression expression, params ParameterExpression[] parameters) { return(ExpressionTupletizer.Pack(expression, parameters)); }
private static bool IsTuple(Type type) { return(ExpressionTupletizer.IsTuple(type)); }
private static void Unpack(LambdaExpression expression, Type voidType, out Expression body, out IEnumerable <ParameterExpression> parameters) { ExpressionTupletizer.Unpack(expression, voidType, out body, out parameters); }
private static IEnumerable <Expression> Unpack(Expression expression) { return(ExpressionTupletizer.Unpack(expression)); }
private static LambdaExpression Unpack(LambdaExpression expression, Type voidType) { return(ExpressionTupletizer.Unpack(expression, voidType)); }
private static LambdaExpression Unpack(LambdaExpression expression) { return(ExpressionTupletizer.Unpack(expression)); }
public static ExpressionTemplate Templatize(Expression e, IConstantHoister hoister) { var env = hoister.Hoist(e); var bindings = env.Bindings; var n = bindings.Count; var res = new ExpressionTemplate(); if (n == 0) { res.Template = Expression.Lambda(e); } else { var parameters = new ParameterExpression[n]; var arguments = new Expression[n]; for (var i = 0; i < n; i++) { var c = bindings[i]; parameters[i] = c.Parameter; arguments[i] = c.Value; } // // In case you wonder why we're not building a LambdaExpression from the parameters // and the visited body, there are two reasons. // // // The first reason is due to the way the LambdaCompiler generates code for closures, // which can be illustrated with this example: // // (c1, c2, c3) => (arg1, arg2) => f(arg1, c1, arg2, c2, c3) // // In here, the outermost lambda contains the hoisted constants, and the innermost // lambda matches the original lambda's signature. When we compile this higher-order // expression and then invoke the outer delegate to re-supply the constants, we end // up with a delegate whose target is a System.Runtime.CompilerServices.Closure which // contains two fields: // // object[] Constants; // object[] Locals; // // Due to constant hoisting, the first array is empty. The second array will hold // the variables that are closed over, in the form of StrongBox<T> objects, so we // end up with the Locals containing: // // new object[] // { // new StrongBox<T1> { Value = c1 }, // new StrongBox<T2> { Value = c2 }, // new StrongBox<T3> { Value = c3 }, // } // // Uses of c1, c2, and c3 inside the inner lambda will effectively become accesses // to the closure using a field traversal like this: // // ((StrongBox<T1>)closure.Locals[0]).Value // // For N constants we have N allocations of a StrongBox<T>. If instead we use a // single tuple to hold all of the constants, we reduce this cost slightly, at the // expense of requiring one more property lookup to access the constant at runtime. // // NB: We could consider using a ValueTuple in the future (which was added to .NET // much later than the original implementation of this library) to avoid the // cost of accessing properties, though we should have a hard look at code gen // to a) make sure that the JITted code does not already elide the call, and // more importantly b) that no copies of ValueTuple values are made, and c) that // we don't end up just boxing the ValueTuple and thus undo the potential gains. // // // The second (and original) reason is quite subtle. For lambda expressions with an // arity of 17 and beyond the Expression.Lambda factory method will use lightweight // code generation to create a delegate type. The code for this can be found in: // // %DDROOT%\sources\ndp\fx\src\Core\Microsoft\Scripting\Compiler\AssemblyGen.cs // // The dynamic assembly used to host those delegate types is generated with the Run // option rather than RunAndCollect. If we end up creating a lambda expression that // uses LCG-generated types that are marked as RunAndCollect, an exception occurs: // // System.NotSupportedException: A non-collectible assembly may not reference a collectible assembly. // at System.Reflection.Emit.ModuleBuilder.GetTypeRef(RuntimeModule module, String strFullName, RuntimeModule refedModule, String strRefedModuleFileName, Int32 tkResolution) // at System.Reflection.Emit.ModuleBuilder.GetTypeRefNested(Type type, Module refedModule, String strRefedModuleFileName) // at System.Reflection.Emit.ModuleBuilder.GetTypeTokenWorkerNoLock(Type type, Boolean getGenericDefinition) // at System.Reflection.Emit.ModuleBuilder.GetTypeTokenInternal(Type type, Boolean getGenericDefinition) // at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelperWorker(Type clsArgument, Boolean lastWasGenericInst) // at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelperWorker(Type clsArgument, Boolean lastWasGenericInst) // at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelper(Type clsArgument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers) // at System.Reflection.Emit.SignatureHelper.AddArguments(Type[] arguments, Type[][] requiredCustomModifiers, Type[][] optionalCustomModifiers) // at System.Reflection.Emit.SignatureHelper.GetMethodSigHelper(Module scope, CallingConventions callingConvention, Int32 cGenericParam, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers) // at System.Reflection.Emit.MethodBuilder.GetMethodSignature() // at System.Reflection.Emit.MethodBuilder.GetTokenNoLock() // at System.Reflection.Emit.MethodBuilder.GetToken() // at System.Reflection.Emit.MethodBuilder.SetImplementationFlags(MethodImplAttributes attributes) // at System.Linq.Expressions.Compiler.DelegateHelpers.MakeNewCustomDelegate(Type[] types) // at System.Linq.Expressions.Compiler.DelegateHelpers.MakeDelegateType(Type[] types) // at System.Linq.Expressions.Expression.Lambda(Expression body, String name, Boolean tailCall, IEnumerable`1 parameters) // at System.Linq.Expressions.Expression.Lambda(Expression body, ParameterExpression[] parameters) // // To work around this limitation, we sidestep the creation of a lambda altogether // and use a specialized overload to Pack that builds a tupletized lambda from the // specified body and parameters collection. // res.Template = ExpressionTupletizer.Pack(env.Expression, parameters); res.Argument = ExpressionTupletizer.Pack(arguments, setNewExpressionMembers: false); } return(res); }
private static Expression Pack(IEnumerable <Expression> expressions) { return(ExpressionTupletizer.Pack(expressions)); }
/// <summary> /// Templatizes an expression. /// </summary> /// <param name="expression">The expression.</param> /// <returns>The expression template.</returns> public static ExpressionTemplate Templatize(this Expression expression) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } ICollection <ParameterExpression> globals = ExpressionHelpers.FindFreeVariables(expression); IExpressionWithEnvironment hoisted = s_constantHoister.Hoist(expression); IReadOnlyList <System.Linq.CompilerServices.Binding> hoistedBindings = hoisted.Bindings; var hoistedBindingsCount = hoistedBindings.Count; var n = globals.Count + hoistedBindingsCount; var res = new ExpressionTemplate(); if (n == 0) { res.Template = Expression.Lambda(expression); } else { var parameters = new ParameterExpression[n]; var arguments = new Expression[n]; int i = 0; while (i < hoistedBindingsCount) { var c = hoistedBindings[i]; parameters[i] = c.Parameter; arguments[i] = c.Value; i++; } foreach (var p in globals) { parameters[i] = p; arguments[i] = p; i++; } Debug.Assert(i == n); // // In case you wonder why we're not building a LambdaExpression from the parameters // and the visited body, the reason is quite subtle. For lambda expressions with an // arity of 17 and beyond the Expression.Lambda factory method will use lightweight // code generation to create a delegate type. The code for this can be found in: // // %DDROOT%\sources\ndp\fx\src\Core\Microsoft\Scripting\Compiler\AssemblyGen.cs // // The dynamic assembly used to host those delegate types is generated with the Run // option rather than RunAndCollect. If we end up creating a lambda expression that // uses LCG-generated types that are marked as RunAndCollect, an exception occurs: // // System.NotSupportedException: A non-collectible assembly may not reference a collectible assembly. // at System.Reflection.Emit.ModuleBuilder.GetTypeRef(RuntimeModule module, String strFullName, RuntimeModule refedModule, String strRefedModuleFileName, Int32 tkResolution) // at System.Reflection.Emit.ModuleBuilder.GetTypeRefNested(Type type, Module refedModule, String strRefedModuleFileName) // at System.Reflection.Emit.ModuleBuilder.GetTypeTokenWorkerNoLock(Type type, Boolean getGenericDefinition) // at System.Reflection.Emit.ModuleBuilder.GetTypeTokenInternal(Type type, Boolean getGenericDefinition) // at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelperWorker(Type clsArgument, Boolean lastWasGenericInst) // at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelperWorker(Type clsArgument, Boolean lastWasGenericInst) // at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelper(Type clsArgument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers) // at System.Reflection.Emit.SignatureHelper.AddArguments(Type[] arguments, Type[][] requiredCustomModifiers, Type[][] optionalCustomModifiers) // at System.Reflection.Emit.SignatureHelper.GetMethodSigHelper(Module scope, CallingConventions callingConvention, Int32 cGenericParam, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers) // at System.Reflection.Emit.MethodBuilder.GetMethodSignature() // at System.Reflection.Emit.MethodBuilder.GetTokenNoLock() // at System.Reflection.Emit.MethodBuilder.GetToken() // at System.Reflection.Emit.MethodBuilder.SetImplementationFlags(MethodImplAttributes attributes) // at System.Linq.Expressions.Compiler.DelegateHelpers.MakeNewCustomDelegate(Type[] types) // at System.Linq.Expressions.Compiler.DelegateHelpers.MakeDelegateType(Type[] types) // at System.Linq.Expressions.Expression.Lambda(Expression body, String name, Boolean tailCall, IEnumerable`1 parameters) // at System.Linq.Expressions.Expression.Lambda(Expression body, ParameterExpression[] parameters) // // To work around this limitation, we sidestep the creation of a lambda altogether // and use a specialized overload to Pack that builds a tupletized lambda from the // specified body and parameters collection. // res.Template = ExpressionTupletizer.Pack(hoisted.Expression, parameters); res.Argument = ExpressionTupletizer.Pack(arguments); } return(res); }