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
43 changes: 21 additions & 22 deletions src/DynamicExpresso.Core/Parsing/ParseSignatures.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using DynamicExpresso.Reflection;

namespace DynamicExpresso.Parsing
{
/// <summary>
/// Contains all the signatures for the binary and unary operators supported by DynamicExpresso.
/// It allows to reuse the existing method resolutions logic in <see cref="Resolution.MethodResolution"/>.
/// </summary>
internal static class ParseSignatures
{
private static MethodData[] MakeUnarySignatures(params Type[] possibleOperandTypes)
private static MethodBase[] MakeUnarySignatures(params Type[] possibleOperandTypes)
{
var signatures = new MethodData[possibleOperandTypes.Length];
var signatures = new MethodBase[possibleOperandTypes.Length];
for (var i = 0; i < possibleOperandTypes.Length; i++)
{
signatures[i] = new MethodData
{
Parameters = new[] { new SimpleParameterInfo(possibleOperandTypes[i]) },
};
signatures[i] = new SimpleMethodSignature(possibleOperandTypes[i]);
}
return signatures;
}

private static MethodData[] MakeBinarySignatures(IList<(Type, Type)> possibleOperandTypes)
private static MethodBase[] MakeBinarySignatures(IList<(Type, Type)> possibleOperandTypes)
{
var signatures = new MethodData[possibleOperandTypes.Count];
var signatures = new MethodBase[possibleOperandTypes.Count];
for (var i = 0; i < possibleOperandTypes.Count; i++)
{
var (left, right) = possibleOperandTypes[i];
signatures[i] = new MethodData
{
Parameters = new[] { new SimpleParameterInfo(left), new SimpleParameterInfo(right) },
};
signatures[i] = new SimpleMethodSignature(left, right);
}
return signatures;
}

/// <summary>
/// Signatures for the binary logical operators.
/// </summary>
public static MethodData[] LogicalSignatures = MakeBinarySignatures(new[]
public static MethodBase[] LogicalSignatures = MakeBinarySignatures(new[]
{
(typeof(bool), typeof(bool) ),
(typeof(bool?), typeof(bool?)),
Expand All @@ -46,7 +45,7 @@ private static MethodData[] MakeBinarySignatures(IList<(Type, Type)> possibleOpe
/// <summary>
/// Signatures for the binary arithmetic operators.
/// </summary>
public static MethodData[] ArithmeticSignatures = MakeBinarySignatures(new[]
public static MethodBase[] ArithmeticSignatures = MakeBinarySignatures(new[]
{
(typeof(int), typeof(int) ),
(typeof(uint), typeof(uint) ),
Expand All @@ -67,7 +66,7 @@ private static MethodData[] MakeBinarySignatures(IList<(Type, Type)> possibleOpe
/// <summary>
/// Signatures for the binary relational operators.
/// </summary>
public static MethodData[] RelationalSignatures = ArithmeticSignatures.Concat(MakeBinarySignatures(new[]
public static MethodBase[] RelationalSignatures = ArithmeticSignatures.Concat(MakeBinarySignatures(new[]
{
(typeof(string), typeof(string) ),
(typeof(char), typeof(char) ),
Expand All @@ -81,12 +80,12 @@ private static MethodData[] MakeBinarySignatures(IList<(Type, Type)> possibleOpe
/// <summary>
/// Signatures for the binary equality operators.
/// </summary>
public static MethodData[] EqualitySignatures = RelationalSignatures.Concat(LogicalSignatures).ToArray();
public static MethodBase[] EqualitySignatures = RelationalSignatures.Concat(LogicalSignatures).ToArray();

/// <summary>
/// Signatures for the binary + operators.
/// </summary>
public static MethodData[] AddSignatures = ArithmeticSignatures.Concat(MakeBinarySignatures(new[]
public static MethodBase[] AddSignatures = ArithmeticSignatures.Concat(MakeBinarySignatures(new[]
{
(typeof(DateTime), typeof(TimeSpan) ),
(typeof(TimeSpan), typeof(TimeSpan) ),
Expand All @@ -97,7 +96,7 @@ private static MethodData[] MakeBinarySignatures(IList<(Type, Type)> possibleOpe
/// <summary>
/// Signatures for the binary - operators.
/// </summary>
public static MethodData[] SubtractSignatures = AddSignatures.Concat(MakeBinarySignatures(new[]
public static MethodBase[] SubtractSignatures = AddSignatures.Concat(MakeBinarySignatures(new[]
{
(typeof(DateTime), typeof(DateTime)),
(typeof(DateTime?), typeof(DateTime?)),
Expand All @@ -106,28 +105,28 @@ private static MethodData[] MakeBinarySignatures(IList<(Type, Type)> possibleOpe
/// <summary>
/// Signatures for the unary - operators.
/// </summary>
public static MethodData[] NegationSignatures = MakeUnarySignatures(
public static MethodBase[] NegationSignatures = MakeUnarySignatures(
typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal),
typeof(int?), typeof(uint?), typeof(long?), typeof(ulong?), typeof(float?), typeof(double?), typeof(decimal?)
);

/// <summary>
/// Signatures for the unary not (!) operator.
/// </summary>
public static MethodData[] NotSignatures = MakeUnarySignatures(typeof(bool), typeof(bool?));
public static MethodBase[] NotSignatures = MakeUnarySignatures(typeof(bool), typeof(bool?));

/// <summary>
/// Signatures for the bitwise completement operators.
/// </summary>
public static MethodData[] BitwiseComplementSignatures = MakeUnarySignatures(
public static MethodBase[] BitwiseComplementSignatures = MakeUnarySignatures(
typeof(int), typeof(uint), typeof(long), typeof(ulong),
typeof(int?), typeof(uint?), typeof(long?), typeof(ulong?)
);

/// <summary>
/// Signatures for the left and right shift operators.
/// </summary>
public static MethodData[] ShiftSignatures = MakeBinarySignatures(new[]
public static MethodBase[] ShiftSignatures = MakeBinarySignatures(new[]
{
(typeof(int), typeof(int) ),
(typeof(uint), typeof(int) ),
Expand Down
6 changes: 3 additions & 3 deletions src/DynamicExpresso.Core/Parsing/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1893,14 +1893,14 @@ private bool IsDynamicExpression(Expression instance)
// return GetNonNullableType(type).IsEnum;
//}

private void CheckAndPromoteOperand(MethodData[] unarySignatures, ref Expression expr)
private void CheckAndPromoteOperand(MethodBase[] unarySignatures, ref Expression expr)
{
var args = PrepareOperandArguments(unarySignatures, new[] { expr });

expr = args[0];
}

private void CheckAndPromoteOperands(MethodData[] binarySignatures, ref Expression left, ref Expression right)
private void CheckAndPromoteOperands(MethodBase[] binarySignatures, ref Expression left, ref Expression right)
{
// if one of the operands is the nullable version of the other, promote the non-nullablte to the nullable version
if (TypeUtils.TryGetNonNullableType(left.Type, out var nonNullableLeftType) && nonNullableLeftType == right.Type)
Expand All @@ -1914,7 +1914,7 @@ private void CheckAndPromoteOperands(MethodData[] binarySignatures, ref Expressi
right = args[1];
}

private IList<Expression> PrepareOperandArguments(MethodData[] signatures, Expression[] args)
private IList<Expression> PrepareOperandArguments(MethodBase[] signatures, Expression[] args)
{
var applicableMethods = MethodResolution.FindBestMethod(signatures, args);
if (applicableMethods.Count == 1)
Expand Down
71 changes: 71 additions & 0 deletions src/DynamicExpresso.Core/Reflection/SimpleMethodSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Globalization;
using System.Reflection;

namespace DynamicExpresso.Reflection
{
/// <summary>
/// A simple implementation of <see cref="MethodBase"/> that only provides the method signature (ie. the parameter types).
/// </summary>
internal class SimpleMethodSignature : MethodBase
{
private class SimpleParameterInfo : ParameterInfo
{
public SimpleParameterInfo(Type parameterType)
{
ClassImpl = parameterType;
DefaultValueImpl = null;
}

public override bool HasDefaultValue => false;
}

public override MethodAttributes Attributes { get; } = MethodAttributes.Public;
public override MemberTypes MemberType { get; } = MemberTypes.Method;

private readonly ParameterInfo[] _parameterInfos;
public SimpleMethodSignature(params Type[] parameterTypes)
{
_parameterInfos = new ParameterInfo[parameterTypes.Length];
for (var i = 0; i < parameterTypes.Length; i++)
{
_parameterInfos[i] = new SimpleParameterInfo(parameterTypes[i]);
}
}

public override ParameterInfo[] GetParameters()
{
return _parameterInfos;
}

public override RuntimeMethodHandle MethodHandle => throw new NotImplementedException();
public override string Name => throw new NotImplementedException();
public override Type DeclaringType => throw new NotImplementedException();
public override Type ReflectedType => throw new NotImplementedException();

public override object[] GetCustomAttributes(bool inherit)
{
throw new NotImplementedException();
}

public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}

public override MethodImplAttributes GetMethodImplementationFlags()
{
throw new NotImplementedException();
}

public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
throw new NotImplementedException();
}

public override bool IsDefined(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
}
}
21 changes: 0 additions & 21 deletions src/DynamicExpresso.Core/Reflection/SimpleParameterInfo.cs

This file was deleted.

96 changes: 96 additions & 0 deletions test/DynamicExpresso.UnitTest/ThreadingTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;

namespace DynamicExpresso.UnitTest
{
[TestFixture]
public class ThreadingTest
{
private static void DynamicExpresso_Interpreter_Eval1()
{
var parser = new Interpreter();
parser.SetVariable("C0", 5);
parser.SetVariable("x", 50);
Assert.That(parser.Identifiers.First(i => i.Name == "C0").Expression.ToString(), Is.EqualTo("5"));
Assert.That(parser.Identifiers.First(i => i.Name == "x").Expression.ToString(), Is.EqualTo("50"));

var result = parser.Eval<double>("x*C0");
Assert.That(result, Is.EqualTo(250d));
}

private static void DynamicExpresso_Interpreter_Eval2()
{
var parser = new Interpreter();
parser.SetVariable("C0", 5);
parser.SetVariable("y", 250);
Assert.That(parser.Identifiers.First(i => i.Name == "C0").Expression.ToString(), Is.EqualTo("5"));
Assert.That(parser.Identifiers.First(i => i.Name == "y").Expression.ToString(), Is.EqualTo("250"));

var result = parser.Eval<double>("y/C0");
Assert.That(result, Is.EqualTo(50d));
}

private static void DynamicExpresso_Interpreter_Eval_Sequence()
{
for (var i = 0; i < 10; i++)
{
DynamicExpresso_Interpreter_Eval1();
DynamicExpresso_Interpreter_Eval2();
}
}

[Test]
public async Task DynamicExpresso_Interpreter_Eval_Threading()
{
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

const int NumTasks = 5;

var tasksToRun = new List<Task>();
for (var i = 0; i < NumTasks; i++)
{
var taskToRunLater = new Task(DynamicExpresso_Interpreter_Eval_Sequence);
tasksToRun.Add(taskToRunLater);
}

foreach (var taskToRunLater in tasksToRun)
{
taskToRunLater.Start();
}

foreach (var taskToRunLater in tasksToRun)
{
await taskToRunLater;
}
}

[Test]
public void Should_Pass_Parallel_Eval()
{
var target = new Interpreter();
var conds = Enumerable.Repeat("Country != \"France\"", 10);
var parameters = new[] { new Parameter("Country", "Italy") };
Parallel.ForEach(conds, exp =>
{
Assert.That(target.Eval(exp, parameters), Is.True);
});
}

[Test]
public void Should_Fail_Parallel_Eval()
{
var target = new Interpreter();
var conds = Enumerable.Repeat("Country != \"France\"", 10);

Parallel.ForEach(conds, exp =>
{
var parameters = new[] { new Parameter("Country", "Italy") };
Assert.That(target.Eval(exp, parameters), Is.True);
});
}
}
}
Loading