Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using Xtensive.Linq;
using Xtensive.Reflection;

namespace Xtensive.Orm.BulkOperations
Expand All @@ -23,7 +24,7 @@ public static bool IsContainsQuery(this Expression expression)

internal static object Invoke(this Expression expression)
{
return Expression.Lambda(typeof (Func<>).MakeGenericType(expression.Type), expression).Compile().DynamicInvoke();
return FastExpression.Lambda(typeof (Func<>).MakeGenericType(expression.Type), expression).Compile().DynamicInvoke();
}

internal static Expression Visit<T>(this Expression exp, Func<T, Expression> visitor) where T : Expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Xtensive.Sql;
using Xtensive.Sql.Dml;
using Xtensive.Core;
using Xtensive.Linq;

namespace Xtensive.Orm.BulkOperations
{
Expand Down Expand Up @@ -139,7 +140,7 @@ private void AddConstantValue(AddValueContext addContext)
{
SqlTableColumn column = SqlDml.TableColumn(addContext.Statement.Table, addContext.Field.Column.Name);
SqlExpression value;
object constant = Expression.Lambda(addContext.Lambda.Body, null).Compile().DynamicInvoke();
object constant = FastExpression.Lambda(addContext.Lambda.Body, null).Compile().DynamicInvoke();
if (constant==null)
value = SqlDml.Null;
else {
Expand Down Expand Up @@ -199,7 +200,7 @@ private void AddEntityValue(AddValueContext addContext)
i++;
ParameterExpression p = Expression.Parameter(info.UnderlyingType);
LambdaExpression lambda =
Expression.Lambda(
FastExpression.Lambda(
typeof (Func<,>).MakeGenericType(info.UnderlyingType, field.ValueType),
Expression.MakeMemberAccess(p, field.UnderlyingProperty),
p);
Expand All @@ -215,7 +216,7 @@ private void AddEntityValue(AddValueContext addContext)
}
}
i = -1;
var entity = (IEntity) Expression.Lambda(addContext.Lambda.Body, null).Compile().DynamicInvoke();
var entity = (IEntity) FastExpression.Lambda(addContext.Lambda.Body, null).Compile().DynamicInvoke();
foreach (ColumnInfo column in addContext.Field.Columns) {
i++;
SqlExpression value;
Expand All @@ -238,7 +239,7 @@ public void AddValues()
var addContext = new AddValueContext {
Descriptor = descriptor,
Lambda =
Expression.Lambda(
FastExpression.Lambda(
typeof (Func<,>).MakeGenericType(typeof (T), descriptor.Expression.Type),
descriptor.Expression,
descriptor.Parameter),
Expand Down
3 changes: 2 additions & 1 deletion Extensions/Xtensive.Orm.Security/SecureQueryRootBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Xtensive.Linq;
using Xtensive.Orm;

namespace Xtensive.Orm.Security
Expand Down Expand Up @@ -54,7 +55,7 @@ private IQueryable<T> GetSecureQuery<T>(ImpersonationContext context) where T: c
continue;
}
var p = Expression.Parameter(queryType, "p");
var where = (Expression<Func<T, bool>>) Expression.Lambda(Expression.Not(Expression.TypeIs(p, permissionType)), p);
var where = (Expression<Func<T, bool>>) FastExpression.Lambda(Expression.Not(Expression.TypeIs(p, permissionType)), p);
candidates.Add(InsecureQuery.All<T>().Where(where).Concat(permission.Query(context, InsecureQuery).OfType<T>()));
}
// Query<Dog> && Permission<Animal>
Expand Down
54 changes: 54 additions & 0 deletions Orm/Xtensive.Orm.Tests.Core/Linq/LambdaExpressionFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (C) 2020 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.

using System;
using System.Linq.Expressions;
using NUnit.Framework;
using Xtensive.Core;
using Xtensive.Linq;

namespace Xtensive.Orm.Tests.Core.Linq
{
[TestFixture]
public class LambdaExpressionFactoryTests
{
private readonly Type delegateType = typeof(Func<int, int>);
private readonly Expression<Func<int, int>> plusOne = i => i + 1;

[Test]
public void ShouldUseFastFactory() => Assert.That(LambdaExpressionFactory.CanUseFastFactory());

[Test]
public void ShouldCreateFactoryFast()
{
var lambda = (Func<int, int>) LambdaExpressionFactory
.CreateFactoryFast(delegateType)
.Invoke(plusOne.Body, plusOne.Parameters.ToArray())
.Compile();

Assert.That(lambda.Invoke(1), Is.EqualTo(2));
}

[Test]
public void ShouldCreateFactorySlow()
{
var lambda = (Func<int, int>) LambdaExpressionFactory.Instance
.CreateFactorySlow(delegateType)
.Invoke(plusOne.Body, plusOne.Parameters.ToArray())
.Compile();

Assert.That(lambda.Invoke(1), Is.EqualTo(2));
}

[Test]
public void ShouldCreateLambda()
{
var lambda = (Func<int, int>) LambdaExpressionFactory.Instance
.CreateLambda(delegateType, plusOne.Body, plusOne.Parameters.ToArray())
.Compile();

Assert.That(lambda.Invoke(1), Is.EqualTo(2));
}
}
}
2 changes: 1 addition & 1 deletion Orm/Xtensive.Orm/Core/Extensions/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public static LambdaExpression StripQuotes(this Expression expression)
if (!typeof (LambdaExpression).IsAssignableFrom(expression.Type))
throw new InvalidOperationException(string.Format("Unable to process expression '{0}'", expression));
var typeAs = Expression.TypeAs(expression, typeof (LambdaExpression));
return Expression.Lambda<Func<LambdaExpression>>(typeAs).CachingCompile()();
return FastExpression.Lambda<Func<LambdaExpression>>(typeAs).CachingCompile()();
}
return lambda;
}
Expand Down
2 changes: 1 addition & 1 deletion Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ protected override Expression VisitLambda(LambdaExpression l)
{
Expression body = Visit(l.Body);
if (body!=l.Body)
return Expression.Lambda(l.Type, body, l.Parameters);
return FastExpression.Lambda(l.Type, body, l.Parameters);
return l;
}

Expand Down
12 changes: 12 additions & 0 deletions Orm/Xtensive.Orm/Linq/FastExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ public static LambdaExpression Lambda(Type delegateType, Expression body, params
return LambdaExpressionFactory.Instance.CreateLambda(delegateType, body, parameters);
}

/// <summary>
/// Generates <see cref="LambdaExpression"/> faster than <see cref="Expression.Lambda(Type,Expression,ParameterExpression[])"/>.
/// </summary>
/// <param name="delegateType">A type that represents a delegate type.</param>
/// <param name="body">The body of lambda expression.</param>
/// <param name="parameters">The parameters of lambda expression.</param>
/// <returns>Constructed lambda expression.</returns>
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters)
{
return (Expression<TDelegate>) LambdaExpressionFactory.Instance.CreateLambda(typeof(TDelegate), body, parameters);
}

/// <summary>
/// Generates <see cref="LambdaExpression"/> faster than <see cref="Expression.Lambda(Type,Expression,IEnumerable{ParameterExpression})"/>.
/// </summary>
Expand Down
77 changes: 62 additions & 15 deletions Orm/Xtensive.Orm/Linq/Internals/LambdaExpressionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,33 @@
using Xtensive.Reflection;

using Factory = System.Func<
System.Linq.Expressions.Expression,
System.Collections.Generic.IEnumerable<System.Linq.Expressions.ParameterExpression>,
System.Linq.Expressions.LambdaExpression
System.Linq.Expressions.Expression,
System.Linq.Expressions.ParameterExpression[],
System.Linq.Expressions.LambdaExpression
>;

using SlowFactory = System.Func<
System.Linq.Expressions.Expression,
System.Collections.Generic.IEnumerable<System.Linq.Expressions.ParameterExpression>,
System.Linq.Expressions.LambdaExpression
>;

using FastFactory = System.Func<
System.Linq.Expressions.Expression,
string,
bool,
System.Collections.Generic.IReadOnlyList<System.Linq.Expressions.ParameterExpression>,
System.Linq.Expressions.LambdaExpression
>;

namespace Xtensive.Linq
{
internal sealed class LambdaExpressionFactory
{
private static readonly Type[] internalFactorySignature = new[] {
typeof(Expression), typeof(string), typeof(bool), typeof(IReadOnlyList<ParameterExpression>)
};

private static readonly object _lock = new object();
private static volatile LambdaExpressionFactory instance;

Expand All @@ -32,27 +50,54 @@ public static LambdaExpressionFactory Instance {
return instance;
}
}

private readonly MethodInfo factoryMethod;

private readonly ThreadSafeDictionary<Type, Factory> cache;
private readonly Func<Type, Factory> createHandler;
private readonly MethodInfo slowFactoryMethod;

public LambdaExpression CreateLambda(Type delegateType, Expression body, ParameterExpression[] parameters)
{
var factory = cache.GetValue(delegateType, (_delegateType, _this) => _this.CreateFactory(_delegateType), this);
var factory = cache.GetValue(delegateType, createHandler);
return factory.Invoke(body, parameters);
}

public LambdaExpression CreateLambda(Expression body, ParameterExpression[] parameters)
{
var delegateType = DelegateHelper.MakeDelegateType(body.Type, parameters.Select(p => p.Type));
return CreateLambda(delegateType, body, parameters);
}

#region Private / internal methods

private Factory CreateFactory(Type delegateType)

internal Factory CreateFactorySlow(Type delegateType)
{
var factory = (SlowFactory) Delegate.CreateDelegate(
typeof(SlowFactory), slowFactoryMethod.MakeGenericMethod(delegateType));

return (body, parameters) => factory.Invoke(body, parameters);
}

internal static Factory CreateFactoryFast(Type delegateType)
{
return (Factory) Delegate.CreateDelegate(typeof (Factory), factoryMethod.MakeGenericMethod(delegateType));
var method = typeof(Expression<>).MakeGenericType(delegateType).GetMethod(
"Create", BindingFlags.Static | BindingFlags.NonPublic, null, internalFactorySignature, null);

if (method == null) {
return null;
}

var factory = (FastFactory) Delegate.CreateDelegate(typeof(FastFactory), null, method);
return (body, parameters) => factory.Invoke(body, null, false, parameters);
}

internal static bool CanUseFastFactory()
{
try {
return CreateFactoryFast(typeof(Func<int>)) != null;
}
catch {
return false;
}
}

#endregion
Expand All @@ -62,11 +107,13 @@ private Factory CreateFactory(Type delegateType)
private LambdaExpressionFactory()
{
cache = ThreadSafeDictionary<Type, Factory>.Create(new object());
factoryMethod = typeof (Expression).GetMethods()
.Where(m => m.IsGenericMethod
&& m.Name == "Lambda"
&& m.GetParameters()[1].ParameterType == typeof(IEnumerable<ParameterExpression>))
.Single();

slowFactoryMethod = typeof(Expression).GetMethods().Single(m =>
m.IsGenericMethod &&
m.Name == "Lambda" &&
m.GetParameters()[1].ParameterType == typeof(IEnumerable<ParameterExpression>));

createHandler = CanUseFastFactory() ? (Func<Type, Factory>) CreateFactoryFast : CreateFactorySlow;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq.Expressions;
using System.Reflection;
using Xtensive.Core;
using Xtensive.Linq;
using Xtensive.Orm;
using Xtensive.Reflection;
using Xtensive.Orm.Linq;
Expand Down Expand Up @@ -37,7 +38,7 @@ public static void BuildFilter(IndexInfo index)
var builder = new PartialIndexFilterBuilder(index, parameter);
var body = builder.Visit(index.FilterExpression.Body);
var filter = new PartialIndexFilterInfo {
Expression = Expression.Lambda(body, parameter),
Expression = FastExpression.Lambda(body, parameter),
Fields = builder.usedFields,
};
index.Filter = filter;
Expand Down
3 changes: 2 additions & 1 deletion Orm/Xtensive.Orm/Orm/Linq/ExpressionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq.Expressions;
using System.Reflection;
using Xtensive.Core;
using Xtensive.Linq;
using Xtensive.Orm.Rse;
using Xtensive.Reflection;
using ExpressionVisitor = Xtensive.Linq.ExpressionVisitor;
Expand Down Expand Up @@ -50,7 +51,7 @@ public static ConstantExpression Evaluate(Expression e)
Type type = e.Type;
if (type.IsValueType)
e = Expression.Convert(e, typeof (object));
var lambda = Expression.Lambda<Func<object>>(e);
var lambda = FastExpression.Lambda<Func<object>>(e);
var func = lambda.CachingCompile();
return Expression.Constant(func(), type);
}
Expand Down
3 changes: 2 additions & 1 deletion Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Reflection;
using Xtensive.Collections;
using Xtensive.Core;
using Xtensive.Linq;
using Xtensive.Reflection;
using Xtensive.Tuples;
using Tuple = Xtensive.Tuples.Tuple;
Expand Down Expand Up @@ -62,7 +63,7 @@ public override Expression<Func<IEnumerable<Tuple>>> GetEnumerable()
var call = Expression.Call(Expression.Constant(enumerableFunc.Target), enumerableFunc.Method);
MethodInfo selectMethod = WellKnownMembers.Enumerable.Select.MakeGenericMethod(typeof (TItem), typeof (Tuple));
var select = Expression.Call(selectMethod, call, Expression.Constant(converter));
return Expression.Lambda<Func<IEnumerable<Tuple>>>(select);
return FastExpression.Lambda<Func<IEnumerable<Tuple>>>(select);
}


Expand Down
3 changes: 2 additions & 1 deletion Orm/Xtensive.Orm/Orm/Linq/ParameterExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System;
using System.Linq.Expressions;
using Xtensive.Linq;
using ExpressionVisitor = Xtensive.Linq.ExpressionVisitor;

namespace Xtensive.Orm.Linq
Expand Down Expand Up @@ -45,7 +46,7 @@ public Expression<Func<T>> ExtractParameter<T>(Expression expression)
Type type = expression.Type;
if (type.IsValueType)
expression = Expression.Convert(expression, typeof (T));
var lambda = Expression.Lambda<Func<T>>(expression);
var lambda = FastExpression.Lambda<Func<T>>(expression);
return lambda;
}

Expand Down
2 changes: 1 addition & 1 deletion Orm/Xtensive.Orm/Orm/Linq/QueryHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static Expression<Func<Tuple, bool>> BuildFilterLambda(int startIndex, IR
filterExpression = Expression.And(filterExpression,
Expression.Equal(tupleParameterFieldAccess, keyParameterFieldAccess));
}
return Expression.Lambda<Func<Tuple, bool>>(filterExpression, tupleParameter);
return FastExpression.Lambda<Func<Tuple, bool>>(filterExpression, tupleParameter);
}

private static Expression CreateEntityQuery(Type elementType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System.Linq;
using System.Linq.Expressions;
using Xtensive.Linq;
using ExpressionVisitor = Xtensive.Linq.ExpressionVisitor;

namespace Xtensive.Orm.Linq.Rewriters
Expand Down Expand Up @@ -42,7 +43,7 @@ public static bool TryRewrite(LambdaExpression resultSelector,
result = null;
return false;
}
result = Expression.Lambda(body, parameters);
result = FastExpression.Lambda(body, parameters);
return true;
}

Expand Down
Loading